app

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

commit 425661f7c3c7154a3d79ba0d47dc532351f86004
parent 7693ea7bc8f48443067705442e1e9d841a1b80f6
Author: triesap <tyson@radroots.org>
Date:   Mon, 20 Apr 2026 15:06:22 +0000

sidebar: keep active farmer destination first

Diffstat:
Mcrates/launchers/desktop/src/window.rs | 165++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 131 insertions(+), 34 deletions(-)

diff --git a/crates/launchers/desktop/src/window.rs b/crates/launchers/desktop/src/window.rs @@ -52,7 +52,7 @@ use radroots_app_ui::{ utility_title_row, }; use radroots_nostr::prelude::RadrootsNostrClient; -use std::time::Duration; +use std::{sync::Arc, time::Duration}; use tracing::error; use crate::runtime::{DesktopAppRuntime, DesktopAppRuntimeSummary}; @@ -5266,6 +5266,67 @@ fn home_sidebar( let selected_section = selected_farmer_section(runtime); let workspace_available = farmer_products_available(runtime); let pack_day_available = farmer_pack_day_available(runtime); + let navigation_sections = + home_sidebar_navigation_sections(selected_section, workspace_available, pack_day_available); + let on_select_today = Arc::new(on_select_today); + let on_select_products = Arc::new(on_select_products); + let on_select_orders = Arc::new(on_select_orders); + let on_select_pack_day = Arc::new(on_select_pack_day); + let mut navigation_elements = Vec::with_capacity(navigation_sections.len()); + for section in navigation_sections { + let element = match section { + FarmerSection::Today => { + let on_click = Arc::clone(&on_select_today); + home_sidebar_nav_button( + "home-nav-today", + AppTextKey::HomeNavToday, + true, + selected_section == FarmerSection::Today, + move |event, window, app| on_click(event, window, app), + cx, + ) + .into_any_element() + } + FarmerSection::Products => { + let on_click = Arc::clone(&on_select_products); + home_sidebar_nav_button( + "home-nav-products", + AppTextKey::HomeNavProducts, + true, + selected_section == FarmerSection::Products, + move |event, window, app| on_click(event, window, app), + cx, + ) + .into_any_element() + } + FarmerSection::Orders => { + let on_click = Arc::clone(&on_select_orders); + home_sidebar_nav_button( + "home-nav-orders", + AppTextKey::HomeNavOrders, + true, + selected_section == FarmerSection::Orders, + move |event, window, app| on_click(event, window, app), + cx, + ) + .into_any_element() + } + FarmerSection::PackDay => { + let on_click = Arc::clone(&on_select_pack_day); + home_sidebar_nav_button( + "home-nav-pack-day", + AppTextKey::PackDayTitle, + true, + selected_section == FarmerSection::PackDay, + move |event, window, app| on_click(event, window, app), + cx, + ) + .into_any_element() + } + FarmerSection::Farm => unreachable!("farm is not a sidebar destination"), + }; + navigation_elements.push(element); + } app_surface_sidebar( div() @@ -5282,38 +5343,7 @@ fn home_sidebar( .flex_col() .justify_start() .gap(px(APP_UI_THEME.shells.home_stack_gap_px)) - .child(home_sidebar_nav_button( - "home-nav-today", - AppTextKey::HomeNavToday, - selected_section == FarmerSection::Today, - on_select_today, - cx, - )) - .when(workspace_available, |this| { - this.child(home_sidebar_nav_button( - "home-nav-products", - AppTextKey::HomeNavProducts, - selected_section == FarmerSection::Products, - on_select_products, - cx, - )) - .child(home_sidebar_nav_button( - "home-nav-orders", - AppTextKey::HomeNavOrders, - selected_section == FarmerSection::Orders, - on_select_orders, - cx, - )) - }) - .when(pack_day_available, |this| { - this.child(home_sidebar_nav_button( - "home-nav-pack-day", - AppTextKey::PackDayTitle, - selected_section == FarmerSection::PackDay, - on_select_pack_day, - cx, - )) - }), + .children(navigation_elements), ) .child( div().child(div().when_some(home_saved_farm(runtime), |this, farm| { @@ -5347,6 +5377,31 @@ fn holding_home_sidebar(runtime: &DesktopAppRuntimeSummary) -> impl IntoElement ) } +fn home_sidebar_navigation_sections( + selected_section: FarmerSection, + workspace_available: bool, + pack_day_available: bool, +) -> Vec<FarmerSection> { + let mut sections = vec![FarmerSection::Today]; + if workspace_available { + sections.push(FarmerSection::Products); + sections.push(FarmerSection::Orders); + } + if pack_day_available { + sections.push(FarmerSection::PackDay); + } + + if let Some(selected_index) = sections + .iter() + .position(|section| *section == selected_section) + { + let selected = sections.remove(selected_index); + sections.insert(0, selected); + } + + sections +} + fn selected_farmer_section(runtime: &DesktopAppRuntimeSummary) -> FarmerSection { match runtime.shell_projection.selected_section { ShellSection::Farmer(section) => section, @@ -5378,10 +5433,15 @@ fn home_content_scroll_id(section: FarmerSection) -> &'static str { fn home_sidebar_nav_button( id: &'static str, key: AppTextKey, + is_available: bool, is_active: bool, on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, cx: &App, ) -> impl IntoElement { + if !is_available { + return div().id(id).into_any_element(); + } + if is_active { div() .id(id) @@ -7581,7 +7641,7 @@ mod tests { SETTINGS_OPERATIONS_PANEL_SECTIONS, SettingsInventorySectionSpec, SettingsPanelViewKey, StartupHomeSurface, StartupSignerConnectState, farm_setup_onboarding_card_spec, farmer_home_farm_state, farmer_pack_day_available, home_content_scroll_id, home_saved_farm, - home_window_launch_size_px, home_window_minimum_size_px, + home_sidebar_navigation_sections, home_window_launch_size_px, home_window_minimum_size_px, parse_optional_product_editor_stock_input, parse_product_editor_price_input, product_display_title, startup_home_surface, startup_signer_preview_summary, startup_signer_preview_summary_for_connect_state, startup_signer_source_input_is_editable, @@ -7859,6 +7919,43 @@ mod tests { } #[test] + fn sidebar_navigation_keeps_the_active_destination_first() { + assert_eq!( + home_sidebar_navigation_sections(FarmerSection::Today, true, false), + vec![ + FarmerSection::Today, + FarmerSection::Products, + FarmerSection::Orders, + ] + ); + assert_eq!( + home_sidebar_navigation_sections(FarmerSection::Products, true, false), + vec![ + FarmerSection::Products, + FarmerSection::Today, + FarmerSection::Orders, + ] + ); + assert_eq!( + home_sidebar_navigation_sections(FarmerSection::Orders, true, false), + vec![ + FarmerSection::Orders, + FarmerSection::Today, + FarmerSection::Products, + ] + ); + assert_eq!( + home_sidebar_navigation_sections(FarmerSection::PackDay, true, true), + vec![ + FarmerSection::PackDay, + FarmerSection::Today, + FarmerSection::Products, + FarmerSection::Orders, + ] + ); + } + + #[test] fn saved_farm_falls_back_to_local_projection_when_today_is_empty() { let saved_farm = FarmSummary { farm_id: FarmId::new(),