Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ bundled.css
/assets/gen_assets/
/src/extras/

Cargo.lock

# NixOs output folder
/result

Expand Down
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ ssr = [
"leptos/ssr",
"leptos_meta/ssr",
"leptos_router/ssr",
"leptos-use/ssr"
"leptos-use/ssr",
"leptos-use/actix"
]
development = []

Expand Down
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,36 @@ Agrega esto en tu `settings.json`
}
```

## Configura tu Zed

Agrega esto en tu `settings.json`

```json
{
"lsp": {
"rust-analyzer": {
"binary": {
"path": "rust-analyzer"
},
"initialization_options": {
"cargo": {
"features": [
"ssr"
]
},
"check": {
"command": "check",
"extraArgs": [
"--features",
"ssr"
]
}
}
}
}
}
```

## Resumen

Este proyecto utiliza una rama personalizada de Leptos para poder servir
Expand Down
2 changes: 1 addition & 1 deletion design-system-components
14 changes: 4 additions & 10 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use leptos_router::{

use crate::{
components::{HeadInformation, Header},
pages::{Aprende, Communities, Index},
pages::{Aprende, Blog, Communities, Events, Index},
};

pub fn shell(options: LeptosOptions) -> impl IntoView {
Expand Down Expand Up @@ -60,16 +60,10 @@ pub fn App() -> impl IntoView {
<Header />
<Routes fallback=|| "Not found.">
<Route path=path!("/") view=Index />
<Route path=path!("comunidad") view=Communities />
// <Route
// path=path!("colaboradores")
// view=Contributors
// />
// <Route
// path=path!("proyectos")
// view=Projects
// />
<Route path=path!("aprende") view=Aprende />
<Route path=path!("comunidad") view=Communities />
<Route path=path!("eventos") view=Events />
<Route path=path!("blog") view=Blog />
</Routes>
</main>
// <Footer />
Expand Down
152 changes: 129 additions & 23 deletions src/components/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ use crate::{
components::icons::{NewLogoRustDarkPageIcon, NewLogoRustLightPageIcon},
context::theme_provider::{Theme, use_theme},
};
use leptos::{leptos_dom::logging::console_log, prelude::*};
use leptos::prelude::*;
use leptos_use::{use_media_query, use_window};
use rustlanges_components::{
button::{Button, Variant},
icons::{Moon, SunLine, SunMoon},
icons::{Close, Menu, Moon, SunLine, SunMoon},
};

const BOOK_PATH: &str = "https://book.rustlang-es.org/";
const JOIN_PATH: &str = "https://discord.rustlang-es.org/";

#[island]
pub fn Header() -> impl IntoView {
let this = use_window();
let path = RwSignal::new("/".to_string());
let menu_open = RwSignal::new(false);

Effect::new(move |_| {
let result = format!(
Expand All @@ -35,6 +39,18 @@ pub fn Header() -> impl IntoView {
Theme::System => view! { <NewLogoRustDarkPageIcon size=60 /> }.into_any(),
};

let menu_handler = move |_| {
menu_open.update(|open| *open = !*open);
};

let menu_icon = move || {
if menu_open() {
view! { <Close size=20 class="z-50 block" /> }.into_any()
} else {
view! { <Menu size=20 class="block" /> }.into_any()
}
};

let theme_switcher_icon = move || match theme() {
Theme::Dark => view! { <Moon /> }.into_any(),
Theme::Light => view! { <SunLine /> }.into_any(),
Expand All @@ -43,25 +59,35 @@ pub fn Header() -> impl IntoView {

let active_link_class = move |link: &str| {
if path() == format!("{link:?}") {
"text-red-500 dark:text-orange-300"
"font-bold text-red-500 dark:text-orange-300"
} else {
""
}
};

let handler = move |_| {
let theme_handler = move |_| {
let current_theme = theme.get();
match current_theme {
Theme::System => theme.set(Theme::Dark),
Theme::Dark => theme.set(Theme::Light),
Theme::Light => theme.set(Theme::System),
Theme::Light => theme.set(Theme::Dark),
Theme::Dark | Theme::System => theme.set(Theme::Light),
Comment thread
MarioYellowy marked this conversation as resolved.
}
};

view! {
<header class="w-full py-[8px] px-[24px] flex flex-column items-center justify-between">
{move || logo()} <div class="flex flex-column gap-[24px] items-center">
<div class="gap-[16px] hidden md:flex">
<header class="w-full py-[8px] px-[0px] flex flex-column items-center justify-between md:px-[24px] relative">
<div class="flex justify-between items-center">
<Button
variant=Variant::Text
class="px-[10px] md:hidden flex items-center justify-center"
on_click=menu_handler
icon=(move || menu_icon()).into_any()
/>
<div class=move || {
format!(
"md:hidden absolute top-full left-0 w-full bg-white dark:bg-dark flex flex-col gap-[32px] p-[16px] z-50 {}",
if menu_open() { "flex" } else { "hidden" },
)
}>
<a href="/" class=move || active_link_class("/")>
Inicio
</a>
Expand All @@ -74,23 +100,103 @@ pub fn Header() -> impl IntoView {
<a href="/eventos" class=move || active_link_class("/eventos")>
Eventos
</a>
<a href="https://blog.rustlang-es.org">Blog</a>
</div>
<div class="flex gap-[16px] items-center flex-wrap">
<Button
variant=Variant::Secondary
label="El Libro"
on_click=|_| {}
class="hidden md:block"
/>
<a href="/blog" class=move || active_link_class("/blog")>
Blog
</a>

<Button
variant=Variant::Primary
label="¡Únete!"
on_click=move |_| console_log("hola")
variant=Variant::Icon
on_click=theme_handler
icon=(move || theme_switcher_icon()).into_any()
/>

<div class="grid grid-cols-2 gap-[8px] w-full">
<a href=BOOK_PATH target="_blank" rel="noopener noreferrer">
<Button
class="w-full"
variant=Variant::Secondary
label="El Libro"
on_click=|_| {}
/>
</a>
<a href=JOIN_PATH target="_blank" rel="noopener noreferrer">
<Button
class="w-full"
variant=Variant::Primary
label="¡Únete!"
on_click=move |_| {}
/>
</a>
</div>
<hr />
<p class="text-center opacity-50">Comunidad - Rust Lang en Español</p>
</div>
<div
class=move || {
format!(
"fixed inset-0 bg-black/50 z-40 md:hidden {}",
if menu_open() { "block" } else { "hidden" },
)
}
on:click=move |_| menu_open.set(false)
></div>
<a href="/" class=move || active_link_class("/")>
{move || logo()}
</a>
</div>
<div class="flex flex-column gap-[24px] items-center">
<nav class="gap-[16px] hidden md:flex">
<a href="/" class=move || format!("font-semibold {}", active_link_class("/"))>
Inicio
</a>
<a
href="/aprende"
class=move || format!("font-semibold {}", active_link_class("/aprende"))
>
Aprende Rust
</a>
<a
href="/comunidad"
class=move || format!("font-semibold {}", active_link_class("/comunidad"))
>
Comunidad
</a>
<a
href="/eventos"
class=move || format!("font-semibold {}", active_link_class("/eventos"))
>
Eventos
</a>
<a
href="/blog"
class=move || format!("font-semibold {}", active_link_class("/blog"))
>
Blog
</a>
</nav>
<div class="flex gap-[16px] items-center flex-wrap">
<a
href=BOOK_PATH
class="hidden md:flex"
target="_blank"
rel="noopener noreferrer"
>
<Button variant=Variant::Secondary label="El Libro" on_click=|_| {} />
</a>

<a
href=JOIN_PATH
class="px-[10px] md:px-[0px]"
target="_blank"
rel="noopener noreferrer"
>
<Button variant=Variant::Primary label="¡Únete!" on_click=move |_| {} />
</a>

<Button
variant=Variant::Icon
on_click=handler
class="hidden md:flex"
on_click=theme_handler
icon=(move || theme_switcher_icon()).into_any()
/>
</div>
Expand Down
43 changes: 22 additions & 21 deletions src/context/theme_provider.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use std::fmt::Display;

use leptos::{
children::Children,
prelude::{RwSignal, use_context, *},
server::codee::string::JsonSerdeCodec,
*,
};
use leptos_use::{storage::use_local_storage, use_media_query};
use leptos_use::{use_cookie, use_media_query};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
use leptos_use::{use_cookie, use_media_query};
use leptos_use::{storage::use_local_storage, use_media_query};

use serde::{Deserialize, Serialize};
/// Defines an enumeration for UI themes.
///

/// This enum can be cloned, copied, and compared for equality.
/// It also supports serialization and deserialization for local storage.
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize, Debug)]
Expand All @@ -17,28 +18,28 @@ pub enum Theme {
System,
}

// Implementation of the default value for the `Theme` enum
impl Default for Theme {
/// provides the default theme as `Dark`
fn default() -> Self {
Theme::Dark
}
}

#[allow(clippy::inherent_to_string)]
impl Theme {
/// Converts the `Theme` variant into a corresponding string.
pub fn to_string(self) -> String {
String::from(match self {
Theme::Light => "light",
Theme::Dark => "dark",
Theme::System => "system",
})
impl Display for Theme {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Theme::Light => "light",
Theme::Dark => "dark",
Theme::System => "system",
}
)
}
}

/// Define a constant for the local storage key used to store the theme setting.
const STORAGE_KEY: &str = "theme";
/// Define a constant for the cookie key used to store the theme setting.
const COOKIE_KEY: &str = "theme";
Comment on lines +41 to +42

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Define a constant for the cookie key used to store the theme setting.
const COOKIE_KEY: &str = "theme";
/// Define a constant for the local storage key used to store the theme setting.
const STORAGE_KEY: &str = "theme";

Es mejor mediante storage, no tiene sentido enviar esa información en requests

@MarioYellowy MarioYellowy Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Estaba configurado previamente con storage, pero como es SSR, la config que enviaba el cliente no matcheaba con el por defecto que generaba el server, entonces entraba eso en conflicto, por ello se realizó este cambio a cookies.
Entraba en conflicto si se cambiaba de tema la primera vez y se volvía a abrir la web

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pero igual el target no es SSR, es CSR, en todo caso lo que podemos hacer es un feature flag para hacer conditional compilation ✨

Eso

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dejame ver Ted Lasso Mario, espabila que la vida te va a comer

@Phosphorus-M Phosphorus-M Aug 24, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Como puedes hacer eso?

if cfg!(feature = "ssr") {
    // ...
}

o como

#[cfg(feature = "ssr")]
use holy::shit::omg;

de esa forma ese código solo es valido si la feature flag ssr esta habilitada, tal vez deba ser otra feature flag, no recuerdo las que había en el cargo.toml pero eso ✨

image

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumiendo el privilegio de mac user

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sactamente 🙏

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pero igual el target no es SSR, es CSR, en todo caso lo que podemos hacer es un feature flag para hacer conditional compilation ✨

Eso

Dime mañana, en tu video no dices nada de eso:
chttps://www.youtube.com/watch?v=wwRleErAWZA
Dislike

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maldición Mario, tienes razón, y encima no tengo ningún video acerca de conditional compilation (CREO) 😭


/// Updates the class selector for the respective theme.
/// This function is responsible for applying the correct CSS class to the HTML and body elements based on the current theme.
Expand Down Expand Up @@ -102,16 +103,16 @@ pub fn ThemeProvider(children: Children) -> impl IntoView {
let is_dark_preferred_signal = use_media_query("(prefers-color-scheme: dark)");

// Attempt to retrieve the theme from local storage
let (theme_storage_state, set_theme_storage_state, _) =
use_local_storage::<Theme, JsonSerdeCodec>(STORAGE_KEY);
let (theme_storage_state, set_theme_storage_state) =
use_cookie::<Theme, JsonSerdeCodec>(COOKIE_KEY);
Comment on lines +106 to +107

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let (theme_storage_state, set_theme_storage_state) =
use_cookie::<Theme, JsonSerdeCodec>(COOKIE_KEY);
let (theme_storage_state, set_theme_storage_state, _) =
use_local_storage::<Theme, JsonSerdeCodec>(STORAGE_KEY);


let theme_state = RwSignal::new(theme_storage_state.get_untracked());
let theme_state = RwSignal::new(theme_storage_state.get_untracked().unwrap_or_default());
provide_context(theme_state);

// Update local storage and CSS whenever the theme state changes
Effect::new(move |_| {
let current_theme = theme_state();
set_theme_storage_state.set(current_theme);
let current_theme: Theme = theme_state();
set_theme_storage_state.set(Some(current_theme));
update_css_for_theme(
current_theme,
is_dark_preferred_signal(),
Expand Down
4 changes: 4 additions & 0 deletions src/pages/blog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
use leptos::{IntoView, component};

#[component]
pub fn Blog() -> impl IntoView {}
Loading