app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit bf1b2f48dc2ee605b80ea220d31e52aa050361ff
parent 42884064df5846d1ba214c3e5c6608ee62b20ef9
Author: triesap <triesap@radroots.dev>
Date:   Thu, 22 Jan 2026 17:03:45 +0000

app: wire theme selection

- read stored color mode and seed select value

- apply and persist theme changes on select

- expose theme storage helpers in app

- add system option to appearance list

Diffstat:
Mapp/src/lib.rs | 2++
Mapp/src/settings.rs | 23+++++++++++++++++++++--
Mapp/src/theme.rs | 33+++++++++++++++++++++++++++++----
3 files changed, 52 insertions(+), 6 deletions(-)

diff --git a/app/src/lib.rs b/app/src/lib.rs @@ -69,6 +69,8 @@ pub use ui_demo::RadrootsAppUiDemoPage; pub use theme::{ app_theme_apply_mode, app_theme_init, + app_theme_read_mode, + app_theme_store_mode, app_theme_mode_from_value, app_theme_mode_to_name, RadrootsAppThemeError, diff --git a/app/src/settings.rs b/app/src/settings.rs @@ -3,6 +3,13 @@ use leptos::ev::MouseEvent; use leptos::prelude::*; +use crate::{ + app_theme_apply_mode, + app_theme_mode_from_value, + app_theme_read_mode, + app_theme_store_mode, + RadrootsAppThemeMode, +}; use radroots_app_ui_components::{ RadrootsAppUiList, RadrootsAppUiListIcon, @@ -50,8 +57,15 @@ fn settings_label(value: &str, classes: Option<&str>) -> RadrootsAppUiListLabelV #[component] pub fn RadrootsAppSettingsPage() -> impl IntoView { - let color_mode_callback = Callback::new(move |_value: String| { + let initial_mode = app_theme_read_mode().unwrap_or(RadrootsAppThemeMode::System); + let color_mode_value = initial_mode.as_str().to_string(); + let color_mode_callback = Callback::new(move |value: String| { log_settings_action("settings_color_mode"); + let Some(mode) = app_theme_mode_from_value(&value) else { + return; + }; + let _ = app_theme_store_mode(mode); + let _ = app_theme_apply_mode(mode); }); let appearance_list = RadrootsAppUiList { id: Some("settings-appearance".to_string()), @@ -68,9 +82,14 @@ pub fn RadrootsAppSettingsPage() -> impl IntoView { list: Some(vec![Some(RadrootsAppUiListItem { kind: RadrootsAppUiListItemKind::Select(RadrootsAppUiListSelect { field: RadrootsAppUiListSelectField { - value: "light".to_string(), + value: color_mode_value, options: vec![ RadrootsAppUiListSelectOption { + label: "System".to_string(), + value: "system".to_string(), + classes: None, + }, + RadrootsAppUiListSelectOption { label: "Light".to_string(), value: "light".to_string(), classes: None, diff --git a/app/src/theme.rs b/app/src/theme.rs @@ -117,12 +117,33 @@ fn app_theme_read_storage() -> Option<String> { None } -pub fn app_theme_init() -> RadrootsAppThemeResult<&'static str> { - let prefers_dark = app_theme_prefers_dark(); - let mode = app_theme_read_storage() +#[cfg(target_arch = "wasm32")] +fn app_theme_write_storage(value: &str) -> RadrootsAppThemeResult<()> { + let window = web_sys::window().ok_or(RadrootsAppThemeError::Unavailable)?; + let storage = window + .local_storage() + .map_err(|_| RadrootsAppThemeError::Storage)? + .ok_or(RadrootsAppThemeError::Storage)?; + storage + .set_item(APP_THEME_STORAGE_KEY, value) + .map_err(|_| RadrootsAppThemeError::Storage)?; + Ok(()) +} + +#[cfg(not(target_arch = "wasm32"))] +fn app_theme_write_storage(_value: &str) -> RadrootsAppThemeResult<()> { + Ok(()) +} + +pub fn app_theme_read_mode() -> Option<RadrootsAppThemeMode> { + app_theme_read_storage() .as_deref() .and_then(app_theme_mode_from_value) - .unwrap_or(RadrootsAppThemeMode::System); +} + +pub fn app_theme_init() -> RadrootsAppThemeResult<&'static str> { + let prefers_dark = app_theme_prefers_dark(); + let mode = app_theme_read_mode().unwrap_or(RadrootsAppThemeMode::System); let theme_name = app_theme_mode_to_name(mode, prefers_dark); app_theme_apply_name(theme_name)?; Ok(theme_name) @@ -135,6 +156,10 @@ pub fn app_theme_apply_mode(mode: RadrootsAppThemeMode) -> RadrootsAppThemeResul Ok(name) } +pub fn app_theme_store_mode(mode: RadrootsAppThemeMode) -> RadrootsAppThemeResult<()> { + app_theme_write_storage(mode.as_str()) +} + #[cfg(test)] mod tests { use super::{