diff --git a/web/src/api.rs b/web/src/api.rs index 4f33b7a..48bf81e 100644 --- a/web/src/api.rs +++ b/web/src/api.rs @@ -99,7 +99,6 @@ impl LocalStore { .expect("Failed to set our app state"); } - // TODO(jwall): Is this never used? pub fn fetch_app_state(&self) -> Option { self.store.get("app_state").map_or(None, |val| { val.map(|s| from_str(&s).expect("Failed to deserialize app state")) diff --git a/web/src/app_state.rs b/web/src/app_state.rs index 71bfd32..bb5212c 100644 --- a/web/src/app_state.rs +++ b/web/src/app_state.rs @@ -31,6 +31,10 @@ use crate::{ components, linear::LinearSignal, }; +fn bool_true() -> bool { + true +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct AppState { pub recipe_counts: BTreeMap, @@ -46,6 +50,8 @@ pub struct AppState { pub auth: Option, pub plan_dates: BTreeSet, pub selected_plan_date: Option, + #[serde(default = "bool_true")] + pub use_staples: bool, } impl AppState { @@ -62,6 +68,7 @@ impl AppState { auth: None, plan_dates: BTreeSet::new(), selected_plan_date: None, + use_staples: true, } } } @@ -84,6 +91,7 @@ pub enum Message { UpdateStaples(String, Option>), DeletePlan(NaiveDate, Option>), SelectPlanDate(NaiveDate, Option>), + UpdateUseStaples(bool), // TODO(jwall): Should this just be various settings? } impl Debug for Message { @@ -121,6 +129,7 @@ impl Debug for Message { Self::SaveState(_) => write!(f, "SaveState"), Self::LoadState(_) => write!(f, "LoadState"), Self::UpdateStaples(arg, _) => f.debug_tuple("UpdateStaples").field(arg).finish(), + Self::UpdateUseStaples(arg) => f.debug_tuple("UpdateUseStaples").field(arg).finish(), Self::SelectPlanDate(arg, _) => f.debug_tuple("SelectPlanDate").field(arg).finish(), Self::DeletePlan(arg, _) => f.debug_tuple("DeletePlan").field(arg).finish(), } @@ -165,8 +174,12 @@ impl StateMachine { local_store: &LocalStore, original: &Signal, ) -> Result<(), crate::api::Error> { - // TODO(jwall): Load plan state from local_store first. - let original: LinearSignal = original.into(); + // TODO(jwall): We use a linear Signal in here to ensure that we only + // call set on the signal once. + let mut original: LinearSignal = original.into(); + if let Some(state) = local_store.fetch_app_state() { + original = original.update(state); + } let mut state = original.get().as_ref().clone(); info!("Synchronizing Recipes"); let recipe_entries = &store.fetch_recipes().await?; @@ -449,6 +462,9 @@ impl MessageMapper for StateMachine { }); return; } + Message::UpdateUseStaples(value) => { + original_copy.use_staples = value; + } Message::SelectPlanDate(date, callback) => { let store = self.store.clone(); let local_store = self.local_store.clone(); diff --git a/web/src/components/shopping_list.rs b/web/src/components/shopping_list.rs index 273d0bd..772923b 100644 --- a/web/src/components/shopping_list.rs +++ b/web/src/components/shopping_list.rs @@ -194,11 +194,16 @@ fn make_shopping_table<'ctx, G: Html>( #[instrument(skip_all)] #[component] pub fn ShoppingList<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> View { - let show_staples = create_signal(cx, true); + let show_staples = sh.get_selector(cx, |state| { + state.get().use_staples + }); view! {cx, h1 { "Shopping List " } label(for="show_staples_cb") { "Show staples" } - input(id="show_staples_cb", type="checkbox", bind:checked=show_staples) + input(id="show_staples_cb", type="checkbox", checked=*show_staples.get(), on:change=move|_| { + let value = !*show_staples.get_untracked(); + sh.dispatch(cx, Message::UpdateUseStaples(value)); + }) (make_shopping_table(cx, sh, show_staples)) span(role="button", class="no-print", on:click=move |_| { info!("Registering add item request for inventory"); diff --git a/web/src/web.rs b/web/src/web.rs index bff2228..90ac17b 100644 --- a/web/src/web.rs +++ b/web/src/web.rs @@ -23,7 +23,12 @@ pub fn UI(cx: Scope) -> View { api::HttpStore::provide_context(cx, "/api".to_owned()); let store = api::HttpStore::get_from_context(cx).as_ref().clone(); info!("Starting UI"); - let app_state = crate::app_state::AppState::new(); + let local_store = api::LocalStore::new(); + let app_state = if let Some(app_state) = local_store.fetch_app_state() { + app_state + } else { + crate::app_state::AppState::new() + }; let sh = crate::app_state::get_state_handler(cx, app_state, store); let view = create_signal(cx, View::empty()); spawn_local_scoped(cx, {