mirror of
https://github.com/zaphar/kitchen.git
synced 2025-07-22 19:40:14 -04:00
Fix infinite signal loop in shopping list
This commit is contained in:
parent
0167e6070d
commit
77cae25c74
@ -20,6 +20,7 @@ use sycamore::futures::spawn_local_scoped;
|
||||
use sycamore::prelude::*;
|
||||
use sycamore_state::{Handler, MessageMapper};
|
||||
use tracing::{debug, error, info, instrument, warn};
|
||||
use wasm_bindgen::throw_str;
|
||||
|
||||
use crate::api::HttpStore;
|
||||
use crate::js_lib;
|
||||
@ -27,7 +28,7 @@ use crate::js_lib;
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct AppState {
|
||||
pub recipe_counts: BTreeMap<String, usize>,
|
||||
pub extras: BTreeSet<(String, String)>,
|
||||
pub extras: Vec<(String, String)>,
|
||||
pub staples: Option<Recipe>,
|
||||
pub recipes: BTreeMap<String, Recipe>,
|
||||
pub category_map: String,
|
||||
@ -40,7 +41,7 @@ impl AppState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
recipe_counts: BTreeMap::new(),
|
||||
extras: BTreeSet::new(),
|
||||
extras: Vec::new(),
|
||||
staples: None,
|
||||
recipes: BTreeMap::new(),
|
||||
category_map: String::new(),
|
||||
@ -56,7 +57,8 @@ pub enum Message {
|
||||
ResetRecipeCounts,
|
||||
UpdateRecipeCount(String, usize),
|
||||
AddExtra(String, String),
|
||||
RemoveExtra(String, String),
|
||||
RemoveExtra(usize),
|
||||
UpdateExtra(usize, String, String),
|
||||
SaveRecipe(RecipeEntry),
|
||||
SetRecipe(String, Recipe),
|
||||
// TODO(jwall): Remove this annotation when safe to do so.
|
||||
@ -171,7 +173,7 @@ impl StateMachine {
|
||||
Ok((filtered_ingredients, modified_amts, extra_items)) => {
|
||||
state.modified_amts = modified_amts;
|
||||
state.filtered_ingredients = filtered_ingredients;
|
||||
state.extras = BTreeSet::from_iter(extra_items);
|
||||
state.extras = extra_items;
|
||||
}
|
||||
Err(e) => {
|
||||
error!("{:?}", e);
|
||||
@ -198,11 +200,20 @@ impl MessageMapper<Message, AppState> for StateMachine {
|
||||
original_copy.recipe_counts.insert(id, count);
|
||||
}
|
||||
Message::AddExtra(amt, name) => {
|
||||
original_copy.extras.insert((amt, name));
|
||||
original_copy.extras.push((amt, name));
|
||||
}
|
||||
Message::RemoveExtra(amt, name) => {
|
||||
original_copy.extras.remove(&(amt, name));
|
||||
Message::RemoveExtra(idx) => {
|
||||
original_copy.extras.remove(idx);
|
||||
}
|
||||
Message::UpdateExtra(idx, amt, name) => match original_copy.extras.get_mut(idx) {
|
||||
Some(extra) => {
|
||||
extra.0 = amt;
|
||||
extra.1 = name;
|
||||
}
|
||||
None => {
|
||||
throw_str("Attempted to remove extra that didn't exist");
|
||||
}
|
||||
},
|
||||
Message::SetStaples(staples) => {
|
||||
original_copy.staples = staples;
|
||||
}
|
||||
@ -240,7 +251,7 @@ impl MessageMapper<Message, AppState> for StateMachine {
|
||||
Message::ResetInventory => {
|
||||
original_copy.filtered_ingredients = BTreeSet::new();
|
||||
original_copy.modified_amts = BTreeMap::new();
|
||||
original_copy.extras = BTreeSet::new();
|
||||
original_copy.extras = Vec::new();
|
||||
}
|
||||
Message::AddFilteredIngredient(key) => {
|
||||
original_copy.filtered_ingredients.insert(key);
|
||||
|
@ -15,17 +15,20 @@ use std::collections::BTreeSet;
|
||||
|
||||
use recipes::{IngredientAccumulator, IngredientKey};
|
||||
use sycamore::prelude::*;
|
||||
use tracing::{info, instrument};
|
||||
use tracing::{debug, info, instrument};
|
||||
|
||||
use crate::app_state::{Message, StateHandler};
|
||||
|
||||
#[instrument(skip_all)]
|
||||
fn make_ingredients_rows<'ctx, G: Html>(
|
||||
cx: Scope<'ctx>,
|
||||
sh: StateHandler<'ctx>,
|
||||
show_staples: &'ctx ReadSignal<bool>,
|
||||
) -> View<G> {
|
||||
debug!("Making ingredients rows");
|
||||
let ingredients = sh.get_selector(cx, move |state| {
|
||||
let state = state.get();
|
||||
debug!("building ingredient list from state");
|
||||
let mut acc = IngredientAccumulator::new();
|
||||
for (id, count) in state.recipe_counts.iter() {
|
||||
for _ in 0..(*count) {
|
||||
@ -45,7 +48,7 @@ fn make_ingredients_rows<'ctx, G: Html>(
|
||||
acc.ingredients()
|
||||
.into_iter()
|
||||
// First we filter out any filtered ingredients
|
||||
.filter(|(i, _)| state.filtered_ingredients.contains(i))
|
||||
.filter(|(i, _)| !state.filtered_ingredients.contains(i))
|
||||
// Then we take into account our modified amts
|
||||
.map(|(k, (i, rs))| {
|
||||
if state.modified_amts.contains_key(&k) {
|
||||
@ -89,9 +92,6 @@ fn make_ingredients_rows<'ctx, G: Html>(
|
||||
};
|
||||
let amt_signal = create_signal(cx, amt);
|
||||
let k_clone = k.clone();
|
||||
sh.bind_trigger(cx, &amt_signal, move |val| {
|
||||
Message::UpdateAmt(k_clone.clone(), val.as_ref().clone())
|
||||
});
|
||||
let form = form.map(|form| format!("({})", form)).unwrap_or_default();
|
||||
let recipes = rs
|
||||
.iter()
|
||||
@ -101,7 +101,9 @@ fn make_ingredients_rows<'ctx, G: Html>(
|
||||
view! {cx,
|
||||
tr {
|
||||
td {
|
||||
input(bind:value=amt_signal, type="text")
|
||||
input(bind:value=amt_signal, type="text", on:change=move |_| {
|
||||
sh.dispatch(cx, Message::UpdateAmt(k_clone.clone(), amt_signal.get_untracked().as_ref().clone()));
|
||||
})
|
||||
}
|
||||
td {
|
||||
input(type="button", class="no-print destructive", value="X", on:click={
|
||||
@ -118,44 +120,38 @@ fn make_ingredients_rows<'ctx, G: Html>(
|
||||
)
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
fn make_extras_rows<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> View<G> {
|
||||
debug!("Making extras rows");
|
||||
let extras_read_signal = sh.get_selector(cx, |state| {
|
||||
state
|
||||
.get()
|
||||
.extras
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<(String, String)>>()
|
||||
state.get().extras.iter().cloned().enumerate().collect()
|
||||
});
|
||||
view! {cx,
|
||||
Indexed(
|
||||
iterable=extras_read_signal,
|
||||
view= move |cx, (amt, name)| {
|
||||
view= move |cx, (idx, (amt, name))| {
|
||||
let amt_signal = create_signal(cx, amt.clone());
|
||||
let name_signal = create_signal(cx, name.clone());
|
||||
create_effect(cx, {
|
||||
let amt_clone = amt.clone();
|
||||
let name_clone = name.clone();
|
||||
move || {
|
||||
let new_amt = amt_signal.get();
|
||||
let new_name = name_signal.get();
|
||||
sh.dispatch(cx, Message::RemoveExtra(amt_clone.clone(), name_clone.clone()));
|
||||
sh.dispatch(cx, Message::AddExtra(new_amt.as_ref().clone(), new_name.as_ref().clone()));
|
||||
}
|
||||
});
|
||||
view! {cx,
|
||||
tr {
|
||||
td {
|
||||
input(bind:value=amt_signal, type="text")
|
||||
input(bind:value=amt_signal, type="text", on:change=move |_| {
|
||||
sh.dispatch(cx, Message::UpdateExtra(idx,
|
||||
amt_signal.get_untracked().as_ref().clone(),
|
||||
name_signal.get_untracked().as_ref().clone()));
|
||||
})
|
||||
}
|
||||
td {
|
||||
input(type="button", class="no-print destructive", value="X", on:click={
|
||||
move |_| {
|
||||
sh.dispatch(cx, Message::RemoveExtra(amt.clone(), name.clone()));
|
||||
}})
|
||||
input(type="button", class="no-print destructive", value="X", on:click=move |_| {
|
||||
sh.dispatch(cx, Message::RemoveExtra(idx));
|
||||
})
|
||||
}
|
||||
td {
|
||||
input(bind:value=name_signal, type="text")
|
||||
input(bind:value=name_signal, type="text", on:change=move |_| {
|
||||
sh.dispatch(cx, Message::UpdateExtra(idx,
|
||||
amt_signal.get_untracked().as_ref().clone(),
|
||||
name_signal.get_untracked().as_ref().clone()));
|
||||
})
|
||||
}
|
||||
td { "Misc" }
|
||||
}
|
||||
@ -170,8 +166,7 @@ fn make_shopping_table<'ctx, G: Html>(
|
||||
sh: StateHandler<'ctx>,
|
||||
show_staples: &'ctx ReadSignal<bool>,
|
||||
) -> View<G> {
|
||||
let extra_rows_view = make_extras_rows(cx, sh);
|
||||
let ingredient_rows = make_ingredients_rows(cx, sh, show_staples);
|
||||
debug!("Making shopping table");
|
||||
view! {cx,
|
||||
table(class="pad-top shopping-list page-breaker container-fluid", role="grid") {
|
||||
tr {
|
||||
@ -181,8 +176,8 @@ fn make_shopping_table<'ctx, G: Html>(
|
||||
th { " Recipes " }
|
||||
}
|
||||
tbody {
|
||||
(ingredient_rows)
|
||||
(extra_rows_view)
|
||||
(make_ingredients_rows(cx, sh, show_staples))
|
||||
(make_extras_rows(cx, sh))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -198,9 +193,11 @@ pub fn ShoppingList<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> V
|
||||
input(id="show_staples_cb", type="checkbox", bind:checked=show_staples)
|
||||
(make_shopping_table(cx, sh, show_staples))
|
||||
input(type="button", value="Add Item", class="no-print", on:click=move |_| {
|
||||
info!("Registering add item request for inventory");
|
||||
sh.dispatch(cx, Message::AddExtra(String::new(), String::new()));
|
||||
})
|
||||
input(type="button", value="Reset", class="no-print", on:click=move |_| {
|
||||
info!("Registering reset request for inventory");
|
||||
sh.dispatch(cx, Message::ResetInventory);
|
||||
})
|
||||
input(type="button", value="Save", class="no-print", on:click=move |_| {
|
||||
|
Loading…
x
Reference in New Issue
Block a user