From 45737f24e4258c9b5f1970cd0d4368ad71f3adaa Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 27 Nov 2023 19:51:46 -0500 Subject: [PATCH 01/28] UI: Cleansheet CSS redesign Initial skeleton and layout is working. Still needs a bunch of tweaks. --- web/index.html | 3 +- web/src/components/header.rs | 4 +- web/src/components/number_field.rs | 4 +- web/src/components/plan_list.rs | 4 +- web/src/components/recipe.rs | 70 ++++----- web/src/components/recipe_plan.rs | 10 +- web/src/components/recipe_selection.rs | 4 +- web/src/components/shopping_list.rs | 6 +- web/src/components/staples.rs | 6 +- web/src/components/tabs.rs | 6 +- web/src/pages/login.rs | 2 +- web/src/pages/planning/select.rs | 2 +- web/src/routing/mod.rs | 7 +- web/static/app.css | 187 +++++++++++++++++++------ 14 files changed, 212 insertions(+), 103 deletions(-) diff --git a/web/index.html b/web/index.html index 19de9be..aa42eaf 100644 --- a/web/index.html +++ b/web/index.html @@ -19,7 +19,6 @@ - @@ -35,4 +34,4 @@ - \ No newline at end of file + diff --git a/web/src/components/header.rs b/web/src/components/header.rs index bda1fc4..7f53e43 100644 --- a/web/src/components/header.rs +++ b/web/src/components/header.rs @@ -23,9 +23,9 @@ pub fn Header<'ctx, G: Html>(cx: Scope<'ctx>, h: StateHandler<'ctx>) -> View None => "Login".to_owned(), }); view! {cx, - nav(class="no-print") { + nav(class="no-print row-flex align-center header-bg heavy-bottom-border") { h1(class="title") { "Kitchen" } - ul { + ul(class="row-flex align-center") { li { a(href="/ui/planning/select") { "MealPlan" } } li { a(href="/ui/manage/ingredients") { "Manage" } } li { a(href="/ui/login") { (login.get()) } } diff --git a/web/src/components/number_field.rs b/web/src/components/number_field.rs index e0fd487..92b07b8 100644 --- a/web/src/components/number_field.rs +++ b/web/src/components/number_field.rs @@ -211,6 +211,7 @@ where F: Fn(Event), { name: String, + class: String, on_change: Option, min: f64, counter: &'ctx Signal, @@ -223,6 +224,7 @@ where { let NumberProps { name, + class, on_change, min, counter, @@ -241,7 +243,7 @@ where }); let id = name.clone(); view! {cx, - number-spinner(id=id, val=*counter.get(), min=min, on:updated=move |evt: Event| { + number-spinner(id=id, class=(class), val=*counter.get(), min=min, on:updated=move |evt: Event| { let target: HtmlElement = evt.target().unwrap().dyn_into().unwrap(); let val: f64 = target.get_attribute("val").unwrap().parse().unwrap(); counter.set(val); diff --git a/web/src/components/plan_list.rs b/web/src/components/plan_list.rs index c7e8fb6..1e44bca 100644 --- a/web/src/components/plan_list.rs +++ b/web/src/components/plan_list.rs @@ -38,12 +38,12 @@ pub fn PlanList<'ctx, G: Html>(cx: Scope<'ctx>, props: PlanListProps<'ctx>) -> V view!{cx, tr() { td() { - span(role="button", class="outline", on:click=move |_| { + button(class="outline", on:click=move |_| { sh.dispatch(cx, Message::SelectPlanDate(date, None)) }) { (date_display) } } td() { - span(role="button", class="destructive", on:click=move |_| { + button(class="destructive", on:click=move |_| { sh.dispatch(cx, Message::DeletePlan(date, None)) }) { "Delete Plan" } } diff --git a/web/src/components/recipe.rs b/web/src/components/recipe.rs index 2e9ca50..ab9c737 100644 --- a/web/src/components/recipe.rs +++ b/web/src/components/recipe.rs @@ -79,12 +79,14 @@ pub fn Editor<'ctx, G: Html>(cx: Scope<'ctx>, props: RecipeComponentProps<'ctx>) debug!("creating editor view"); view! {cx, - label(for="recipe_category") { "Category" } - input(name="recipe_category", bind:value=category, on:change=move |_| dirty.set(true)) - div(class="grid") { - div { - label(for="recipe_text") { "Recipe" } - textarea(name="recipe_text", bind:value=text, aria-invalid=aria_hint.get(), rows=20, on:change=move |_| { + div { + label(for="recipe_category") { "Category" } + input(name="recipe_category", bind:value=category, on:change=move |_| dirty.set(true)) + } + div { + div(class="row-flex") { + label(for="recipe_text", class="block align-stretch expand-height") { "Recipe: " } + textarea(class="width-third", name="recipe_text", bind:value=text, aria-invalid=aria_hint.get(), cols="50", rows=20, on:change=move |_| { dirty.set(true); check_recipe_parses(text.get_untracked().as_str(), error_text, aria_hint); }, on:input=move |_| { @@ -97,34 +99,36 @@ pub fn Editor<'ctx, G: Html>(cx: Scope<'ctx>, props: RecipeComponentProps<'ctx>) } div(class="parse") { (error_text.get()) } } - span(role="button", on:click=move |_| { - let unparsed = text.get_untracked(); - if check_recipe_parses(unparsed.as_str(), error_text, aria_hint) { - debug!("triggering a save"); - if !*dirty.get_untracked() { - debug!("Recipe text is unchanged"); - return; + div { + button(on:click=move |_| { + let unparsed = text.get_untracked(); + if check_recipe_parses(unparsed.as_str(), error_text, aria_hint) { + debug!("triggering a save"); + if !*dirty.get_untracked() { + debug!("Recipe text is unchanged"); + return; + } + debug!("Recipe text is changed"); + let category = category.get_untracked(); + let category = if category.is_empty() { + None + } else { + Some(category.as_ref().clone()) + }; + let recipe_entry = RecipeEntry( + id.get_untracked().as_ref().clone(), + text.get_untracked().as_ref().clone(), + category, + ); + sh.dispatch(cx, Message::SaveRecipe(recipe_entry, None)); + dirty.set(false); } - debug!("Recipe text is changed"); - let category = category.get_untracked(); - let category = if category.is_empty() { - None - } else { - Some(category.as_ref().clone()) - }; - let recipe_entry = RecipeEntry( - id.get_untracked().as_ref().clone(), - text.get_untracked().as_ref().clone(), - category, - ); - sh.dispatch(cx, Message::SaveRecipe(recipe_entry, None)); - dirty.set(false); - } - // TODO(jwall): Show error message if trying to save when recipe doesn't parse. - }) { "Save" } " " - span(role="button", on:click=move |_| { - sh.dispatch(cx, Message::RemoveRecipe(id.get_untracked().as_ref().to_owned(), Some(Box::new(|| sycamore_router::navigate("/ui/planning/plan"))))); - }) { "delete" } " " + // TODO(jwall): Show error message if trying to save when recipe doesn't parse. + }) { "Save" } " " + button(on:click=move |_| { + sh.dispatch(cx, Message::RemoveRecipe(id.get_untracked().as_ref().to_owned(), Some(Box::new(|| sycamore_router::navigate("/ui/planning/plan"))))); + }) { "delete" } " " + } } } diff --git a/web/src/components/recipe_plan.rs b/web/src/components/recipe_plan.rs index 31c6968..845e1e0 100644 --- a/web/src/components/recipe_plan.rs +++ b/web/src/components/recipe_plan.rs @@ -52,7 +52,7 @@ pub fn CategoryGroup<'ctx, G: Html>( }); view! {cx, h2 { (category) } - div(class="recipe_selector no-print") { + div(class="no-print flex-wrap-start align-stretch") { (View::new_fragment( rows.get().iter().cloned().map(|r| { view ! {cx, @@ -61,7 +61,7 @@ pub fn CategoryGroup<'ctx, G: Html>( view=move |cx, sig| { let title = create_memo(cx, move || sig.get().1.title.clone()); view! {cx, - div(class="cell") { RecipeSelection(i=sig.get().0.to_owned(), title=title, sh=sh) } + div(class="cell column-flex justify-end align-stretch") { RecipeSelection(i=sig.get().0.to_owned(), title=title, sh=sh) } } }, key=|sig| sig.get().0.to_owned(), @@ -108,13 +108,13 @@ pub fn RecipePlan<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> Vie }, key=|(ref cat, _)| cat.clone(), ) - span(role="button", on:click=move |_| { + button(on:click=move |_| { sh.dispatch(cx, Message::LoadState(None)); }) { "Reset" } " " - span(role="button", on:click=move |_| { + button(on:click=move |_| { sh.dispatch(cx, Message::ResetRecipeCounts); }) { "Clear All" } " " - span(role="button", on:click=move |_| { + button(on:click=move |_| { // Poor man's click event signaling. sh.dispatch(cx, Message::SaveState(None)); }) { "Save Plan" } " " diff --git a/web/src/components/recipe_selection.rs b/web/src/components/recipe_selection.rs index 4fb66c7..3e6777b 100644 --- a/web/src/components/recipe_selection.rs +++ b/web/src/components/recipe_selection.rs @@ -65,8 +65,8 @@ pub fn RecipeSelection<'ctx, G: Html>( let name = format!("recipe_id:{}", id); let for_id = name.clone(); view! {cx, - label(for=for_id) { a(href=href) { (*title) } } - NumberField(name=name, counter=count, min=0.0, on_change=Some(move |_| { + label(for=for_id, class="flex-item-grow") { a(href=href) { (*title) } } + NumberField(name=name, class="flex-item-shrink".to_string(), counter=count, min=0.0, on_change=Some(move |_| { debug!(idx=%id, count=%(*count.get_untracked()), "setting recipe count"); sh.dispatch(cx, Message::UpdateRecipeCount(id.as_ref().clone(), *count.get_untracked() as usize)); })) diff --git a/web/src/components/shopping_list.rs b/web/src/components/shopping_list.rs index 772923b..b59cb71 100644 --- a/web/src/components/shopping_list.rs +++ b/web/src/components/shopping_list.rs @@ -205,15 +205,15 @@ pub fn ShoppingList<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> V sh.dispatch(cx, Message::UpdateUseStaples(value)); }) (make_shopping_table(cx, sh, show_staples)) - span(role="button", class="no-print", on:click=move |_| { + button(class="no-print", on:click=move |_| { info!("Registering add item request for inventory"); sh.dispatch(cx, Message::AddExtra(String::new(), String::new())); }) { "Add Item" } " " - span(role="button", class="no-print", on:click=move |_| { + button(class="no-print", on:click=move |_| { info!("Registering reset request for inventory"); sh.dispatch(cx, Message::ResetInventory); }) { "Reset" } " " - span(role="button", class="no-print", on:click=move |_| { + button(class="no-print", on:click=move |_| { info!("Registering save request for inventory"); sh.dispatch(cx, Message::SaveState(None)); }) { "Save" } " " diff --git a/web/src/components/staples.rs b/web/src/components/staples.rs index 3cb420f..98894e2 100644 --- a/web/src/components/staples.rs +++ b/web/src/components/staples.rs @@ -72,8 +72,8 @@ pub fn IngredientsEditor<'ctx, G: Html>( debug!("creating editor view"); view! {cx, - div(class="grid") { - textarea(bind:value=text, aria-invalid=aria_hint.get(), rows=20, on:change=move |_| { + div { + textarea(class="width-third", bind:value=text, aria-invalid=aria_hint.get(), rows=20, on:change=move |_| { dirty.set(true); }, on:input=move |_| { let current_ts = js_lib::get_ms_timestamp(); @@ -84,7 +84,7 @@ pub fn IngredientsEditor<'ctx, G: Html>( }) div(class="parse") { (error_text.get()) } } - span(role="button", on:click=move |_| { + button(on:click=move |_| { let unparsed = text.get(); if !*dirty.get_untracked() { debug!("Staples text is unchanged"); diff --git a/web/src/components/tabs.rs b/web/src/components/tabs.rs index 2e876ef..b907c58 100644 --- a/web/src/components/tabs.rs +++ b/web/src/components/tabs.rs @@ -47,12 +47,12 @@ pub fn TabbedView<'a, G: Html>(cx: Scope<'a>, state: TabState<'a, G>) -> View .collect(), ); view! {cx, - nav { - ul(class="tabs") { + nav(class="menu-bg expand-height") { + ul(class="tabs pad-left") { (menu) } } - main(class=".conatiner-fluid") { + main { (children) } } diff --git a/web/src/pages/login.rs b/web/src/pages/login.rs index 09d3a0d..811ea60 100644 --- a/web/src/pages/login.rs +++ b/web/src/pages/login.rs @@ -27,7 +27,7 @@ pub fn LoginForm<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> View input(type="text", id="username", bind:value=username) label(for="password") { "Password" } input(type="password", bind:value=password) - span(role="button", on:click=move |_| { + button(on:click=move |_| { info!("Attempting login request"); let (username, password) = ((*username.get_untracked()).clone(), (*password.get_untracked()).clone()); if username != "" && password != "" { diff --git a/web/src/pages/planning/select.rs b/web/src/pages/planning/select.rs index b361879..f037003 100644 --- a/web/src/pages/planning/select.rs +++ b/web/src/pages/planning/select.rs @@ -37,7 +37,7 @@ pub fn SelectPage<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> Vie selected=Some("Select".to_owned()), ) { PlanList(sh=sh, list=plan_dates) - span(role="button", on:click=move |_| { + button(on:click=move |_| { sh.dispatch(cx, Message::SelectPlanDate(chrono::offset::Local::now().naive_local().date(), Some(Box::new(|| { sycamore_router::navigate("/ui/planning/plan"); })))) diff --git a/web/src/routing/mod.rs b/web/src/routing/mod.rs index 5108c27..1bd2033 100644 --- a/web/src/routing/mod.rs +++ b/web/src/routing/mod.rs @@ -136,11 +136,12 @@ pub fn Handler<'ctx, G: Html>(cx: Scope<'ctx>, props: HandlerProps<'ctx>) -> Vie integration=HistoryIntegration::new(), view=move |cx: Scope, route: &ReadSignal| { view!{cx, - div(class="app") { - Header(sh) + div { + Header(sh) + div(class="app row-flex flex-item-grow expand-height align-stretch") { (route_switch(route.get().as_ref(), cx, sh)) - Footer { } } + } } }, ) diff --git a/web/static/app.css b/web/static/app.css index 70a6006..eb6be9d 100644 --- a/web/static/app.css +++ b/web/static/app.css @@ -1,5 +1,5 @@ /** - * Copyright 2022 Jeremy Wall + * Copyright 2023 Jeremy Wall * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,12 +21,33 @@ --unicode-button-size: 2em; --toast-anim-duration: 3s; --notification-font-size: calc(var(--font-size) / 2); - --error-message-color: rgba(255, 98, 0, 0.797); + --error-message-color: #CD5C08; --error-message-bg: grey; - --border-width: 2px; + --border-width: 3px; --cell-margin: 1em; + --nav-margin: 2em; + --main-color: #A9907E; + --light-accent: #F3DEBA; + --dark-accent: #ABC4AA; + --heavy-accent: #675D50; + --text-color: black; + --menu-bg: var(--main-color); + --header-bg: var(--light-accent); + --font-size: 20px; + --cell-target: 30%; } +/** TODO(jwall): Dark color scheme? +@media (prefers-color-scheme: dark) { + :root { + --text-color: white; + --menu-bg: var(--main-color); + --header-bg: var(--dark-accent); + } +} +**/ + +/** TODO(jwall): Seperate these out into composable classes **/ @media print { .no-print, @@ -39,28 +60,101 @@ } } -@media (min-width: 768px) { - :root { - --font-size: 35px; - } -} - -@media (prefers-color-scheme: dark) { - :root { - --tab-border-color: lightgrey; - } -} - +/** Resets **/ body { - padding: 10px; - margin: 10px; + margin: 0px; + padding: 0px; + background-color: var(--header-bg); + font-size: var(--font-size) } -nav>ul.tabs>li { - border-style: none; +body, body * { + color: black; } -nav>ul.tabs>li.selected { +a { + text-decoration: none; +} + +/** Our specific page elements **/ + +/** TODO(jwall): Move these onto the html elements themselves. **/ +.column-flex { + display: flex; + flex-direction: column; +} + +.row-flex { + display: flex; + flex-direction: row; +} + +.flex-item-grow { + flex: 1 0 auto; +} + +.flex-item-shrink { + flex: 0 1 auto; +} + +.flex-wrap-start { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} + +.expand-height { + height: 100%; + min-height: fit-content; +} + +.align-center { + align-items: center; +} + +.align-stretch { + align-items: stretch; +} + +.width-third { + min-width: fit-content; + width: 33%; +} + +.inline-block { + display: inline-block; +} + +.block { + display: block; +} + +/** nav elements **/ +nav ul { + list-style-type: none; +} + +nav li { + margin-right: var(--nav-margin); +} + +nav li a::after { + content: '| "; +} + +nav li a { + color: black; +} + +.header-bg { + background-color: var(--header-bg); +} + +.heavy-bottom-border { + border-bottom: var(--border-width) solid var(--heavy-accent) +} + +.selected { border-style: none; border-bottom-style: var(--tab-border-style); border-bottom-color: var(--tab-border-color); @@ -74,10 +168,40 @@ nav>h1 { display: inline; vertical-align: middle; text-align: left; + color: black; +} + +main { + border-bottom-left-radius: 1em; + padding: 1em; + width: 100%; + overflow-block: scroll; +} + +.cell { + margin: 1em; + width: var(--cell-target); +} + +.justify-end { + justify-content: flex-end; +} + +.menu-bg { + background-color: var(--menu-bg); +} + +.pad-left { + padding-left: .5em; +} + +.app nav li { + margin-bottom: var(--nav-margin); } .destructive { - background-color: firebrick !important; + background-color: #CD5C08 !important; + font-weight: bold; } .item-count-inc-dec { @@ -129,24 +253,3 @@ nav>h1 { opacity: 0 } } - -.recipe_selector { - display: flex; - flex-wrap: wrap; - justify-content: flex-start; - align-items: stretch; - align-content: stretch; -} - -.recipe_selector .cell { - margin: 1em; - width: calc(100% / 5); -} - -.cell { - display: flex; - flex-direction: column; - justify-content: flex-end; - align-items: stretch; - align-content: stretch; -} From d7cea46427d723a1e5ca8b700db3fb6d185a8534 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sat, 2 Dec 2023 15:28:08 -0500 Subject: [PATCH 02/28] fix: Issue with request blocking occuring on login --- web/src/pages/login.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/src/pages/login.rs b/web/src/pages/login.rs index 811ea60..e44d376 100644 --- a/web/src/pages/login.rs +++ b/web/src/pages/login.rs @@ -27,9 +27,12 @@ pub fn LoginForm<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> View input(type="text", id="username", bind:value=username) label(for="password") { "Password" } input(type="password", bind:value=password) - button(on:click=move |_| { + button(on:click=move |evt: web_sys::Event| { info!("Attempting login request"); let (username, password) = ((*username.get_untracked()).clone(), (*password.get_untracked()).clone()); + // NOTE(jwall): This is required if we want to keep the below auth request from + // failing to send with blocked by browser. + evt.prevent_default(); if username != "" && password != "" { spawn_local_scoped(cx, async move { let store = crate::api::HttpStore::get_from_context(cx); From c64605f9e70150c3710efb6518c10f366d5759f7 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sat, 2 Dec 2023 15:29:24 -0500 Subject: [PATCH 03/28] maint: Use gloo_net directly --- web/Cargo.toml | 4 +- web/src/api.rs | 110 ++++++++++++++++++++++++------------------------- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/web/Cargo.toml b/web/Cargo.toml index 45bfb4a..a7fc67a 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -43,8 +43,8 @@ features = ["fmt", "time"] version = "0.4.22" features = ["serde"] -[dependencies.reqwasm] -version = "0.5.0" +[dependencies.gloo-net] +version = "0.4.0" [dependencies.wasm-bindgen] # we need wasm-bindgen v0.2.84 exactly diff --git a/web/src/api.rs b/web/src/api.rs index a4a90db..5badbdc 100644 --- a/web/src/api.rs +++ b/web/src/api.rs @@ -15,7 +15,7 @@ use std::collections::{BTreeMap, BTreeSet}; use base64::{self, Engine}; use chrono::NaiveDate; -use reqwasm; +use gloo_net; use serde_json::{from_str, to_string}; use sycamore::prelude::*; use tracing::{debug, error, instrument}; @@ -66,8 +66,8 @@ impl From for Error { } } -impl From for Error { - fn from(item: reqwasm::Error) -> Self { +impl From for Error { + fn from(item: gloo_net::Error) -> Self { Error(format!("{:?}", item)) } } @@ -94,8 +94,15 @@ impl LocalStore { pub fn store_app_state(&self, state: &AppState) { self.migrate_local_store(); + let state = match to_string(state) { + Ok(state) => state, + Err(err) => { + error!(?err, ?state, "Error deserializing app_state"); + return; + } + }; self.store - .set("app_state", &to_string(state).unwrap()) + .set("app_state", &state) .expect("Failed to set our app state"); } @@ -266,13 +273,16 @@ impl HttpStore { debug!("attempting login request against api."); let mut path = self.v2_path(); path.push_str("/auth"); - let result = reqwasm::http::Request::get(&path) + let request = gloo_net::http::Request::get(&path) .header( - "Authorization", + "authorization", format!("Basic {}", token68(user, pass)).as_str(), ) - .send() - .await; + .mode(web_sys::RequestMode::SameOrigin) + .credentials(web_sys::RequestCredentials::SameOrigin) + .build().expect("Failed to build request"); + debug!(?request, "Sending auth request"); + let result = request.send().await; if let Ok(resp) = &result { if resp.status() == 200 { let user_data = resp @@ -294,7 +304,7 @@ impl HttpStore { debug!("Retrieving User Account data"); let mut path = self.v2_path(); path.push_str("/account"); - let result = reqwasm::http::Request::get(&path).send().await; + let result = gloo_net::http::Request::get(&path).send().await; if let Ok(resp) = &result { if resp.status() == 200 { let user_data = resp @@ -315,9 +325,9 @@ impl HttpStore { pub async fn fetch_categories(&self) -> Result>, Error> { let mut path = self.v2_path(); path.push_str("/category_map"); - let resp = match reqwasm::http::Request::get(&path).send().await { + let resp = match gloo_net::http::Request::get(&path).send().await { Ok(resp) => resp, - Err(reqwasm::Error::JsError(err)) => { + Err(gloo_net::Error::JsError(err)) => { error!(path, ?err, "Error hitting api"); return Ok(None); } @@ -345,9 +355,9 @@ impl HttpStore { pub async fn fetch_recipes(&self) -> Result>, Error> { let mut path = self.v2_path(); path.push_str("/recipes"); - let resp = match reqwasm::http::Request::get(&path).send().await { + let resp = match gloo_net::http::Request::get(&path).send().await { Ok(resp) => resp, - Err(reqwasm::Error::JsError(err)) => { + Err(gloo_net::Error::JsError(err)) => { error!(path, ?err, "Error hitting api"); return Ok(self.local_store.get_recipes()); } @@ -375,9 +385,9 @@ impl HttpStore { let mut path = self.v2_path(); path.push_str("/recipe/"); path.push_str(id.as_ref()); - let resp = match reqwasm::http::Request::get(&path).send().await { + let resp = match gloo_net::http::Request::get(&path).send().await { Ok(resp) => resp, - Err(reqwasm::Error::JsError(err)) => { + Err(gloo_net::Error::JsError(err)) => { error!(path, ?err, "Error hitting api"); return Ok(self.local_store.get_recipe_entry(id.as_ref())); } @@ -413,7 +423,7 @@ impl HttpStore { let mut path = self.v2_path(); path.push_str("/recipe"); path.push_str(&format!("/{}", recipe.as_ref())); - let resp = reqwasm::http::Request::delete(&path).send().await?; + let resp = gloo_net::http::Request::delete(&path).send().await?; if resp.status() != 200 { Err(format!("Status: {}", resp.status()).into()) } else { @@ -431,10 +441,9 @@ impl HttpStore { return Err("Recipe Ids can not be empty".into()); } } - let serialized = to_string(&recipes).expect("Unable to serialize recipe entries"); - let resp = reqwasm::http::Request::post(&path) - .body(&serialized) - .header("content-type", "application/json") + let resp = gloo_net::http::Request::post(&path) + .json(&recipes) + .expect("Failed to set body") .send() .await?; if resp.status() != 200 { @@ -449,9 +458,9 @@ impl HttpStore { pub async fn store_categories(&self, categories: &Vec<(String, String)>) -> Result<(), Error> { let mut path = self.v2_path(); path.push_str("/category_map"); - let resp = reqwasm::http::Request::post(&path) - .body(to_string(&categories).expect("Unable to encode categories as json")) - .header("content-type", "application/json") + let resp = gloo_net::http::Request::post(&path) + .json(&categories) + .expect("Failed to set body") .send() .await?; if resp.status() != 200 { @@ -503,9 +512,9 @@ impl HttpStore { pub async fn store_plan(&self, plan: Vec<(String, i32)>) -> Result<(), Error> { let mut path = self.v2_path(); path.push_str("/plan"); - let resp = reqwasm::http::Request::post(&path) - .body(to_string(&plan).expect("Unable to encode plan as json")) - .header("content-type", "application/json") + let resp = gloo_net::http::Request::post(&path) + .json(&plan) + .expect("Failed to set body") .send() .await?; if resp.status() != 200 { @@ -525,9 +534,9 @@ impl HttpStore { path.push_str("/plan"); path.push_str("/at"); path.push_str(&format!("/{}", date)); - let resp = reqwasm::http::Request::post(&path) - .body(to_string(&plan).expect("Unable to encode plan as json")) - .header("content-type", "application/json") + let resp = gloo_net::http::Request::post(&path) + .json(&plan) + .expect("Failed to set body") .send() .await?; if resp.status() != 200 { @@ -542,7 +551,7 @@ impl HttpStore { let mut path = self.v2_path(); path.push_str("/plan"); path.push_str("/all"); - let resp = reqwasm::http::Request::get(&path).send().await?; + let resp = gloo_net::http::Request::get(&path).send().await?; if resp.status() != 200 { Err(format!("Status: {}", resp.status()).into()) } else { @@ -561,7 +570,7 @@ impl HttpStore { path.push_str("/plan"); path.push_str("/at"); path.push_str(&format!("/{}", date)); - let resp = reqwasm::http::Request::delete(&path).send().await?; + let resp = gloo_net::http::Request::delete(&path).send().await?; if resp.status() != 200 { Err(format!("Status: {}", resp.status()).into()) } else { @@ -577,7 +586,7 @@ impl HttpStore { path.push_str("/plan"); path.push_str("/at"); path.push_str(&format!("/{}", date)); - let resp = reqwasm::http::Request::get(&path).send().await?; + let resp = gloo_net::http::Request::get(&path).send().await?; if resp.status() != 200 { Err(format!("Status: {}", resp.status()).into()) } else { @@ -594,7 +603,7 @@ impl HttpStore { //pub async fn fetch_plan(&self) -> Result>, Error> { // let mut path = self.v2_path(); // path.push_str("/plan"); - // let resp = reqwasm::http::Request::get(&path).send().await?; + // let resp = gloo_net::http::Request::get(&path).send().await?; // if resp.status() != 200 { // Err(format!("Status: {}", resp.status()).into()) // } else { @@ -623,7 +632,7 @@ impl HttpStore { path.push_str("/inventory"); path.push_str("/at"); path.push_str(&format!("/{}", date)); - let resp = reqwasm::http::Request::get(&path).send().await?; + let resp = gloo_net::http::Request::get(&path).send().await?; if resp.status() != 200 { Err(format!("Status: {}", resp.status()).into()) } else { @@ -658,7 +667,7 @@ impl HttpStore { > { let mut path = self.v2_path(); path.push_str("/inventory"); - let resp = reqwasm::http::Request::get(&path).send().await?; + let resp = gloo_net::http::Request::get(&path).send().await?; if resp.status() != 200 { Err(format!("Status: {}", resp.status()).into()) } else { @@ -695,13 +704,10 @@ impl HttpStore { path.push_str(&format!("/{}", date)); let filtered_ingredients: Vec = filtered_ingredients.into_iter().collect(); let modified_amts: Vec<(IngredientKey, String)> = modified_amts.into_iter().collect(); - debug!("Storing inventory data in cache"); - let serialized_inventory = to_string(&(filtered_ingredients, modified_amts, extra_items)) - .expect("Unable to encode plan as json"); debug!("Storing inventory data via API"); - let resp = reqwasm::http::Request::post(&path) - .body(&serialized_inventory) - .header("content-type", "application/json") + let resp = gloo_net::http::Request::post(&path) + .json(&(filtered_ingredients, modified_amts, extra_items)) + .expect("Failed to set body") .send() .await?; if resp.status() != 200 { @@ -724,13 +730,10 @@ impl HttpStore { path.push_str("/inventory"); let filtered_ingredients: Vec = filtered_ingredients.into_iter().collect(); let modified_amts: Vec<(IngredientKey, String)> = modified_amts.into_iter().collect(); - debug!("Storing inventory data in cache"); - let serialized_inventory = to_string(&(filtered_ingredients, modified_amts, extra_items)) - .expect("Unable to encode plan as json"); debug!("Storing inventory data via API"); - let resp = reqwasm::http::Request::post(&path) - .body(&serialized_inventory) - .header("content-type", "application/json") + let resp = gloo_net::http::Request::post(&path) + .json(&(filtered_ingredients, modified_amts, extra_items)) + .expect("Failed to set body") .send() .await?; if resp.status() != 200 { @@ -745,7 +748,7 @@ impl HttpStore { pub async fn fetch_staples(&self) -> Result, Error> { let mut path = self.v2_path(); path.push_str("/staples"); - let resp = reqwasm::http::Request::get(&path).send().await?; + let resp = gloo_net::http::Request::get(&path).send().await?; if resp.status() != 200 { debug!("Invalid response back"); Err(format!("Status: {}", resp.status()).into()) @@ -759,15 +762,12 @@ impl HttpStore { } } - pub async fn store_staples>(&self, content: S) -> Result<(), Error> { + pub async fn store_staples + serde::Serialize>(&self, content: S) -> Result<(), Error> { let mut path = self.v2_path(); path.push_str("/staples"); - let serialized_staples: String = - to_string(content.as_ref()).expect("Failed to serialize staples to json"); - - let resp = reqwasm::http::Request::post(&path) - .body(&serialized_staples) - .header("content-type", "application/json") + let resp = gloo_net::http::Request::post(&path) + .json(&content) + .expect("Failed to set body") .send() .await?; if resp.status() != 200 { From e80953e9876d8ec360e9badb6d97ae1cd2b55a94 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sat, 2 Dec 2023 15:29:47 -0500 Subject: [PATCH 04/28] maint: upgrade wasm-bindgen version --- Cargo.lock | 107 ++++++++++++++++++----------------- nix/wasm-bindgen/default.nix | 10 ++-- web/Cargo.toml | 2 +- 3 files changed, 60 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 19eeba8..a442793 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,7 +224,7 @@ checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -383,7 +383,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn", + "syn 1.0.107", ] [[package]] @@ -750,7 +750,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -777,7 +777,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 1.0.107", ] [[package]] @@ -794,7 +794,7 @@ checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -983,7 +983,7 @@ checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1056,14 +1056,15 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "gloo-net" -version = "0.1.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2899cb1a13be9020b010967adc6b2a8a343b6f1428b90238c9d53ca24decc6db" +checksum = "8ac9e8288ae2c632fa9f8657ac70bfe38a1530f345282d7ba66a1f70b72b7dc4" dependencies = [ "futures-channel", "futures-core", "futures-sink", "gloo-utils", + "http", "js-sys", "pin-project", "serde", @@ -1088,9 +1089,9 @@ dependencies = [ [[package]] name = "gloo-utils" -version = "0.1.6" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" dependencies = [ "js-sys", "serde", @@ -1233,9 +1234,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -1448,10 +1449,10 @@ dependencies = [ "base64 0.21.0", "chrono", "console_error_panic_hook", + "gloo-net", "js-sys", "maud", "recipes", - "reqwasm", "serde", "serde_json", "sycamore", @@ -1602,7 +1603,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1658,7 +1659,7 @@ checksum = "731f8ecebd9f3a4aa847dfe75455e4757a45da40a7793d2f0b1f9b6ed18b23f3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1923,7 +1924,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1989,7 +1990,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.107", "version_check", ] @@ -2006,9 +2007,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -2044,9 +2045,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -2133,15 +2134,6 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" -[[package]] -name = "reqwasm" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b89870d729c501fa7a68c43bf4d938bbb3a8c156d333d90faa0e8b3e3212fb" -dependencies = [ - "gloo-net", -] - [[package]] name = "ring" version = "0.16.20" @@ -2186,7 +2178,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn", + "syn 1.0.107", "walkdir", ] @@ -2311,7 +2303,7 @@ checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -2525,7 +2517,7 @@ dependencies = [ "sha2 0.10.6", "sqlx-core", "sqlx-rt", - "syn", + "syn 1.0.107", "url", ] @@ -2618,7 +2610,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -2653,7 +2645,7 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn", + "syn 1.0.107", "unicode-xid", ] @@ -2694,6 +2686,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sync_wrapper" version = "0.1.1" @@ -2732,7 +2735,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -2823,7 +2826,7 @@ checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -2938,7 +2941,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -3158,28 +3161,26 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if 1.0.0", - "serde", - "serde_json", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.39", "wasm-bindgen-shared", ] @@ -3197,9 +3198,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3207,22 +3208,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "wasm-bindgen-test" @@ -3269,7 +3270,7 @@ dependencies = [ "proc-macro2", "quote", "str_inflector", - "syn", + "syn 1.0.107", ] [[package]] diff --git a/nix/wasm-bindgen/default.nix b/nix/wasm-bindgen/default.nix index f9f8d19..9ff0c2c 100644 --- a/nix/wasm-bindgen/default.nix +++ b/nix/wasm-bindgen/default.nix @@ -20,14 +20,14 @@ rustPlatform.buildRustPackage rec { pname = "wasm-bindgen-cli"; # NOTE(jwall): This must exactly match the version of the wasm-bindgen crate # we are using. - version = "0.2.84"; + version = "0.2.89"; src = fetchCrate { inherit pname version; - sha256 = "sha256-0rK+Yx4/Jy44Fw5VwJ3tG243ZsyOIBBehYU54XP/JGk="; + sha256 = "sha256-IPxP68xtNSpwJjV2yNMeepAS0anzGl02hYlSTvPocz8="; }; - cargoSha256 = "sha256-vcpxcRlW1OKoD64owFF6mkxSqmNrvY+y3Ckn5UwEQ50="; + cargoSha256 = "sha256-pBeQaG6i65uJrJptZQLuIaCb/WCQMhba1Z1OhYqA8Zc="; nativeBuildInputs = [ pkg-config ]; @@ -36,5 +36,5 @@ rustPlatform.buildRustPackage rec { nativeCheckInputs = [ nodejs ]; # other tests require it to be ran in the wasm-bindgen monorepo - cargoTestFlags = [ "--test=interface-types" ]; -} \ No newline at end of file + cargoTestFlags = [ "--test=reference" ]; +} diff --git a/web/Cargo.toml b/web/Cargo.toml index a7fc67a..aae28c8 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -48,7 +48,7 @@ version = "0.4.0" [dependencies.wasm-bindgen] # we need wasm-bindgen v0.2.84 exactly -version = "= 0.2.84" +version = "= 0.2.89" [dependencies.web-sys] version = "0.3" From 8942eb59a51ecd7c4e746907c6f5d56eabde85cd Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 4 Dec 2023 14:48:31 -0500 Subject: [PATCH 05/28] ui: Menu font sizes --- web/src/components/footer.rs | 2 +- web/src/components/header.rs | 2 +- web/src/components/tabs.rs | 2 +- web/static/app.css | 10 ++++++++-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/web/src/components/footer.rs b/web/src/components/footer.rs index 7d522ac..a2c8171 100644 --- a/web/src/components/footer.rs +++ b/web/src/components/footer.rs @@ -17,7 +17,7 @@ use sycamore::prelude::*; #[component] pub fn Footer(cx: Scope) -> View { view! {cx, - nav(class="no-print") { + nav(class="no-print menu-font") { ul { li { a(href="https://github.com/zaphar/kitchen") { "On Github" } } } diff --git a/web/src/components/header.rs b/web/src/components/header.rs index 7f53e43..504bd75 100644 --- a/web/src/components/header.rs +++ b/web/src/components/header.rs @@ -23,7 +23,7 @@ pub fn Header<'ctx, G: Html>(cx: Scope<'ctx>, h: StateHandler<'ctx>) -> View None => "Login".to_owned(), }); view! {cx, - nav(class="no-print row-flex align-center header-bg heavy-bottom-border") { + nav(class="no-print row-flex align-center header-bg heavy-bottom-border menu-font") { h1(class="title") { "Kitchen" } ul(class="row-flex align-center") { li { a(href="/ui/planning/select") { "MealPlan" } } diff --git a/web/src/components/tabs.rs b/web/src/components/tabs.rs index b907c58..667a51a 100644 --- a/web/src/components/tabs.rs +++ b/web/src/components/tabs.rs @@ -47,7 +47,7 @@ pub fn TabbedView<'a, G: Html>(cx: Scope<'a>, state: TabState<'a, G>) -> View .collect(), ); view! {cx, - nav(class="menu-bg expand-height") { + nav(class="menu-bg expand-height menu-font") { ul(class="tabs pad-left") { (menu) } diff --git a/web/static/app.css b/web/static/app.css index eb6be9d..f010853 100644 --- a/web/static/app.css +++ b/web/static/app.css @@ -34,6 +34,7 @@ --menu-bg: var(--main-color); --header-bg: var(--light-accent); --font-size: 20px; + --menu-font-size: 1.5em; --cell-target: 30%; } @@ -76,9 +77,8 @@ a { text-decoration: none; } -/** Our specific page elements **/ +/** layout classes **/ -/** TODO(jwall): Move these onto the html elements themselves. **/ .column-flex { display: flex; flex-direction: column; @@ -129,6 +129,12 @@ a { display: block; } +/** Typography classes **/ + +.menu-font { + font-size: var(--menu-font-size); +} + /** nav elements **/ nav ul { list-style-type: none; From 6e0e00c7f339be727f0a2b16688913090201f042 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 4 Dec 2023 14:48:50 -0500 Subject: [PATCH 06/28] docs: comments for the event handling in login --- web/src/pages/login.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/pages/login.rs b/web/src/pages/login.rs index e44d376..9c2510c 100644 --- a/web/src/pages/login.rs +++ b/web/src/pages/login.rs @@ -31,7 +31,8 @@ pub fn LoginForm<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> View info!("Attempting login request"); let (username, password) = ((*username.get_untracked()).clone(), (*password.get_untracked()).clone()); // NOTE(jwall): This is required if we want to keep the below auth request from - // failing to send with blocked by browser. + // failing to send with blocked by browser. This is because it's on a click and + // the form tries to do a submit event and aborts our network request. evt.prevent_default(); if username != "" && password != "" { spawn_local_scoped(cx, async move { From dac529e8e8719c8bfc3a2eab61ddee7b0619ea28 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 4 Dec 2023 15:05:45 -0500 Subject: [PATCH 07/28] ui: Typography tweaks --- web/src/components/footer.rs | 2 +- web/src/components/header.rs | 2 +- web/src/components/recipe.rs | 2 +- web/src/components/tabs.rs | 2 +- web/static/app.css | 22 ++++++++-------------- 5 files changed, 12 insertions(+), 18 deletions(-) diff --git a/web/src/components/footer.rs b/web/src/components/footer.rs index a2c8171..f89a15d 100644 --- a/web/src/components/footer.rs +++ b/web/src/components/footer.rs @@ -18,7 +18,7 @@ use sycamore::prelude::*; pub fn Footer(cx: Scope) -> View { view! {cx, nav(class="no-print menu-font") { - ul { + ul(class="no-list") { li { a(href="https://github.com/zaphar/kitchen") { "On Github" } } } } diff --git a/web/src/components/header.rs b/web/src/components/header.rs index 504bd75..3513721 100644 --- a/web/src/components/header.rs +++ b/web/src/components/header.rs @@ -25,7 +25,7 @@ pub fn Header<'ctx, G: Html>(cx: Scope<'ctx>, h: StateHandler<'ctx>) -> View view! {cx, nav(class="no-print row-flex align-center header-bg heavy-bottom-border menu-font") { h1(class="title") { "Kitchen" } - ul(class="row-flex align-center") { + ul(class="row-flex align-center no-list") { li { a(href="/ui/planning/select") { "MealPlan" } } li { a(href="/ui/manage/ingredients") { "Manage" } } li { a(href="/ui/login") { (login.get()) } } diff --git a/web/src/components/recipe.rs b/web/src/components/recipe.rs index ab9c737..9bd75d7 100644 --- a/web/src/components/recipe.rs +++ b/web/src/components/recipe.rs @@ -146,7 +146,7 @@ fn Steps(cx: Scope, steps: Vec) -> View { view! {cx, div { h3 { "Step " (idx + 1) } - ul(class="ingredients") { + ul(class="ingredients no-list") { (ingredient_fragments) } div(class="instructions") { diff --git a/web/src/components/tabs.rs b/web/src/components/tabs.rs index 667a51a..1ab80e5 100644 --- a/web/src/components/tabs.rs +++ b/web/src/components/tabs.rs @@ -48,7 +48,7 @@ pub fn TabbedView<'a, G: Html>(cx: Scope<'a>, state: TabState<'a, G>) -> View ); view! {cx, nav(class="menu-bg expand-height menu-font") { - ul(class="tabs pad-left") { + ul(class="tabs pad-left no-list") { (menu) } } diff --git a/web/static/app.css b/web/static/app.css index f010853..3348706 100644 --- a/web/static/app.css +++ b/web/static/app.css @@ -33,7 +33,7 @@ --text-color: black; --menu-bg: var(--main-color); --header-bg: var(--light-accent); - --font-size: 20px; + --font-size: 1.2em; --menu-font-size: 1.5em; --cell-target: 30%; } @@ -129,29 +129,22 @@ a { display: block; } +.no-list { + list-style-type: none; +} + /** Typography classes **/ .menu-font { font-size: var(--menu-font-size); } -/** nav elements **/ -nav ul { - list-style-type: none; -} - +/** element specific styling **/ nav li { margin-right: var(--nav-margin); } -nav li a::after { - content: '| "; -} - -nav li a { - color: black; -} - +/** color and borders **/ .header-bg { background-color: var(--header-bg); } @@ -160,6 +153,7 @@ nav li a { border-bottom: var(--border-width) solid var(--heavy-accent) } +/** Situational **/ .selected { border-style: none; border-bottom-style: var(--tab-border-style); From a104ef5f47e61227ba729a5a514103e29daaad40 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 4 Dec 2023 18:54:09 -0500 Subject: [PATCH 08/28] ui: normalization and font tweaks --- web/index.html | 1 + web/src/components/recipe_plan.rs | 2 +- web/src/components/tabs.rs | 2 +- web/static/app.css | 12 +- web/static/normalize.css | 349 ++++++++++++++++++++++++++++++ 5 files changed, 360 insertions(+), 6 deletions(-) create mode 100644 web/static/normalize.css diff --git a/web/index.html b/web/index.html index aa42eaf..30e81d5 100644 --- a/web/index.html +++ b/web/index.html @@ -19,6 +19,7 @@ + diff --git a/web/src/components/recipe_plan.rs b/web/src/components/recipe_plan.rs index 845e1e0..83b2081 100644 --- a/web/src/components/recipe_plan.rs +++ b/web/src/components/recipe_plan.rs @@ -52,7 +52,7 @@ pub fn CategoryGroup<'ctx, G: Html>( }); view! {cx, h2 { (category) } - div(class="no-print flex-wrap-start align-stretch") { + div(class="no-print row-flex flex-wrap-start align-stretch") { (View::new_fragment( rows.get().iter().cloned().map(|r| { view ! {cx, diff --git a/web/src/components/tabs.rs b/web/src/components/tabs.rs index 1ab80e5..e327b3c 100644 --- a/web/src/components/tabs.rs +++ b/web/src/components/tabs.rs @@ -52,7 +52,7 @@ pub fn TabbedView<'a, G: Html>(cx: Scope<'a>, state: TabState<'a, G>) -> View (menu) } } - main { + main(class="content-font") { (children) } } diff --git a/web/static/app.css b/web/static/app.css index 3348706..5d81b9a 100644 --- a/web/static/app.css +++ b/web/static/app.css @@ -33,8 +33,8 @@ --text-color: black; --menu-bg: var(--main-color); --header-bg: var(--light-accent); - --font-size: 1.2em; - --menu-font-size: 1.5em; + --font-size: 1.5rem; + --menu-font-size: 2em; --cell-target: 30%; } @@ -69,8 +69,9 @@ body { font-size: var(--font-size) } -body, body * { +body * { color: black; + font-size: inherit; } a { @@ -98,7 +99,6 @@ a { } .flex-wrap-start { - display: flex; flex-wrap: wrap; justify-content: flex-start; } @@ -139,6 +139,10 @@ a { font-size: var(--menu-font-size); } +.content-font { + font-size: 1.5em; +} + /** element specific styling **/ nav li { margin-right: var(--nav-margin); diff --git a/web/static/normalize.css b/web/static/normalize.css new file mode 100644 index 0000000..192eb9c --- /dev/null +++ b/web/static/normalize.css @@ -0,0 +1,349 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + +/** + * Render the `main` element consistently in IE. + */ + +main { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} From 94e1987f09f83aa2bcc502f2732623f03ba5cf81 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sat, 23 Dec 2023 14:28:18 -0600 Subject: [PATCH 09/28] ui: more layout tweaks --- web/src/components/categories.rs | 2 +- web/src/components/plan_list.rs | 23 ++++++++----------- web/src/components/shopping_list.rs | 8 +++---- web/src/components/tabs.rs | 6 ++--- web/src/routing/mod.rs | 6 ++--- web/static/app.css | 34 +++++++++++++++++++++++++++++ 6 files changed, 53 insertions(+), 26 deletions(-) diff --git a/web/src/components/categories.rs b/web/src/components/categories.rs index f792067..06aa8db 100644 --- a/web/src/components/categories.rs +++ b/web/src/components/categories.rs @@ -49,7 +49,7 @@ fn CategoryRow<'ctx, G: Html>(cx: Scope<'ctx>, props: CategoryRowProps<'ctx>) -> }); view! {cx, tr() { - td() { + td(class="margin-bot-1 border-bottom") { (ingredient_clone) br() Indexed( iterable=recipes, diff --git a/web/src/components/plan_list.rs b/web/src/components/plan_list.rs index 1e44bca..a083cbc 100644 --- a/web/src/components/plan_list.rs +++ b/web/src/components/plan_list.rs @@ -1,4 +1,3 @@ -use chrono::NaiveDate; // Copyright 2023 Jeremy Wall (Jeremy@marzhilsltudios.com) // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,6 +12,7 @@ use chrono::NaiveDate; // See the License for the specific language governing permissions and // limitations under the License. use sycamore::prelude::*; +use chrono::NaiveDate; use crate::app_state::{Message, StateHandler}; use tracing::instrument; @@ -23,30 +23,25 @@ pub struct PlanListProps<'ctx> { list: &'ctx ReadSignal>, } -// TODO(jwall): We also need a "new plan button" #[instrument(skip_all, fields(dates=?props.list))] #[component] pub fn PlanList<'ctx, G: Html>(cx: Scope<'ctx>, props: PlanListProps<'ctx>) -> View { let PlanListProps { sh, list } = props; view! {cx, div() { - table() { + div(class="column-flex") { Indexed( iterable=list, view=move |cx, date| { let date_display = format!("{}", date); view!{cx, - tr() { - td() { - button(class="outline", on:click=move |_| { - sh.dispatch(cx, Message::SelectPlanDate(date, None)) - }) { (date_display) } - } - td() { - button(class="destructive", on:click=move |_| { - sh.dispatch(cx, Message::DeletePlan(date, None)) - }) { "Delete Plan" } - } + div(class="row-flex margin-bot-half") { + button(class="outline margin-right-1", on:click=move |_| { + sh.dispatch(cx, Message::SelectPlanDate(date, None)) + }) { (date_display) } + button(class="destructive", on:click=move |_| { + sh.dispatch(cx, Message::DeletePlan(date, None)) + }) { "Delete Plan" } } } }, diff --git a/web/src/components/shopping_list.rs b/web/src/components/shopping_list.rs index b59cb71..b0ea1e8 100644 --- a/web/src/components/shopping_list.rs +++ b/web/src/components/shopping_list.rs @@ -109,12 +109,12 @@ fn make_ingredients_rows<'ctx, G: Html>( view! {cx, tr { td { - input(bind:value=amt_signal, type="text", on:change=move |_| { + input(bind:value=amt_signal, class="width-5", 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={ + input(type="button", class="fit-content no-print destructive", value="X", on:click={ move |_| { sh.dispatch(cx, Message::AddFilteredIngredient(k.clone())); }}) @@ -143,14 +143,14 @@ fn make_extras_rows<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> V view! {cx, tr { td { - input(bind:value=amt_signal, type="text", on:change=move |_| { + input(bind:value=amt_signal, class="width-5", 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 |_| { + input(type="button", class="fit-content no-print destructive", value="X", on:click=move |_| { sh.dispatch(cx, Message::RemoveExtra(idx)); }) } diff --git a/web/src/components/tabs.rs b/web/src/components/tabs.rs index e327b3c..6b38811 100644 --- a/web/src/components/tabs.rs +++ b/web/src/components/tabs.rs @@ -47,12 +47,12 @@ pub fn TabbedView<'a, G: Html>(cx: Scope<'a>, state: TabState<'a, G>) -> View .collect(), ); view! {cx, - nav(class="menu-bg expand-height menu-font") { - ul(class="tabs pad-left no-list") { + nav(class="menu-bg menu-font-2 flex-item-shrink") { + ul(class="tabs pad-left no-list row-flex align-center") { (menu) } } - main(class="content-font") { + main(class="flex-item-grow content-font") { (children) } } diff --git a/web/src/routing/mod.rs b/web/src/routing/mod.rs index 1bd2033..cef073d 100644 --- a/web/src/routing/mod.rs +++ b/web/src/routing/mod.rs @@ -136,11 +136,9 @@ pub fn Handler<'ctx, G: Html>(cx: Scope<'ctx>, props: HandlerProps<'ctx>) -> Vie integration=HistoryIntegration::new(), view=move |cx: Scope, route: &ReadSignal| { view!{cx, - div { + div(class="column-flex") { Header(sh) - div(class="app row-flex flex-item-grow expand-height align-stretch") { - (route_switch(route.get().as_ref(), cx, sh)) - } + (route_switch(route.get().as_ref(), cx, sh)) } } }, diff --git a/web/static/app.css b/web/static/app.css index 5d81b9a..f31b032 100644 --- a/web/static/app.css +++ b/web/static/app.css @@ -133,12 +133,46 @@ a { list-style-type: none; } +.fit-content { + width: fit-content; +} + +.width-10 { + width: 10em; +} + +.width-5 { + width: 5em; +} + +.border-bottom { + border-bottom-style: solid; + border-bottom-color: var(--tab-border-color); + border-bottom-width: var(--tab-border-width); +} + +.margin-bot-1 { + margin-bottom: 1em; +} + +.margin-bot-half { + margin-bottom: .5em; +} + +.margin-right-1 { + margin-right: 1em; +} + /** Typography classes **/ .menu-font { font-size: var(--menu-font-size); } +.menu-font-2 { + font-size: calc(var(--menu-font-size) / 1.5); +} + .content-font { font-size: 1.5em; } From 9022503e76f8144d9b03579a7823eb77391047e1 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Wed, 3 Jan 2024 14:02:48 -0500 Subject: [PATCH 10/28] Have a packaging unit for measures --- recipes/src/lib.rs | 25 ++++++++---- recipes/src/parse.rs | 12 +++++- recipes/src/test.rs | 90 +++++++++++++++++++++++++++++++------------- recipes/src/unit.rs | 59 +++++++++++++++++++++++++++-- 4 files changed, 148 insertions(+), 38 deletions(-) diff --git a/recipes/src/lib.rs b/recipes/src/lib.rs index 416b5dd..53d40a7 100644 --- a/recipes/src/lib.rs +++ b/recipes/src/lib.rs @@ -156,16 +156,25 @@ impl IngredientAccumulator { set.insert(recipe_title.clone()); self.inner.insert(key, (i.clone(), set)); } else { - let amt = match (self.inner[&key].0.amt, i.amt) { - (Volume(rvm), Volume(lvm)) => Volume(lvm + rvm), - (Count(lqty), Count(rqty)) => Count(lqty + rqty), - (Weight(lqty), Weight(rqty)) => Weight(lqty + rqty), + let amts = match (&self.inner[&key].0.amt, &i.amt) { + (Volume(rvm), Volume(lvm)) => vec![Volume(lvm + rvm)], + (Count(lqty), Count(rqty)) => vec![Count(lqty + rqty)], + (Weight(lqty), Weight(rqty)) => vec![Weight(lqty + rqty)], + (Package(lnm, lqty), Package(rnm, rqty)) => { + if lnm == rnm { + vec![Package(lnm.clone(), lqty + rqty)] + } else { + vec![Package(lnm.clone(), lqty.clone()), Package(rnm.clone(), rqty.clone())] + } + } _ => unreachable!(), }; - self.inner.get_mut(&key).map(|(i, set)| { - i.amt = amt; - set.insert(recipe_title.clone()); - }); + for amt in amts { + self.inner.get_mut(&key).map(|(i, set)| { + i.amt = amt; + set.insert(recipe_title.clone()); + }); + } } } } diff --git a/recipes/src/parse.rs b/recipes/src/parse.rs index bf2e30e..38fac0f 100644 --- a/recipes/src/parse.rs +++ b/recipes/src/parse.rs @@ -334,7 +334,14 @@ make_fn!(unit, text_token!("kg"), text_token!("grams"), text_token!("gram"), - text_token!("g")), + text_token!("g"), + text_token!("pkg"), + text_token!("package"), + text_token!("bottle"), + text_token!("bot"), + text_token!("bag"), + text_token!("can") + ), _ => ws, (u.to_lowercase().to_singular()) ) @@ -393,6 +400,7 @@ pub fn measure(i: StrIter) -> abortable_parser::Result { "oz" => Weight(Oz(qty)), "kg" | "kilogram" => Weight(Kilogram(qty)), "g" | "gram" => Weight(Gram(qty)), + "pkg" | "package" | "can" | "bag" | "bottle" | "bot" => Measure::pkg(s, qty), _u => { eprintln!("Invalid unit: {}", _u); unreachable!() @@ -411,6 +419,8 @@ pub fn measure(i: StrIter) -> abortable_parser::Result { } } +// TODO(jwall): I think this is a mistake. We should rethink what noralizing means or we should +// remove it. pub fn normalize_name(name: &str) -> String { let parts: Vec<&str> = name.split_whitespace().collect(); if parts.len() >= 2 { diff --git a/recipes/src/test.rs b/recipes/src/test.rs index ff5ebd6..62d0633 100644 --- a/recipes/src/test.rs +++ b/recipes/src/test.rs @@ -235,32 +235,30 @@ fn test_ingredient_name_parse() { #[test] fn test_ingredient_parse() { for (i, expected) in vec![ - //( - // "1 cup flour ", - // Ingredient::new("flour", None, Volume(Cup(Quantity::Whole(1))), ""), - //), - //( - // "\t1 cup flour ", - // Ingredient::new("flour", None, Volume(Cup(Quantity::Whole(1))), ""), - //), - //( - // "1 cup apple (chopped)", - // Ingredient::new( - // "apple", - // Some("chopped".to_owned()), - // Volume(Cup(Quantity::Whole(1))), - // "", - // ), - //), - //( - // "1 cup apple (chopped) ", - // Ingredient::new( - // "apple", - // Some("chopped".to_owned()), - // Volume(Cup(Quantity::Whole(1))), - // "", - // ), - //), + ( + "1 cup flour ", + Ingredient::new("flour", None, Volume(Cup(Quantity::Whole(1)))), + ), + ( + "\t1 cup flour ", + Ingredient::new("flour", None, Volume(Cup(Quantity::Whole(1)))), + ), + ( + "1 cup apple (chopped)", + Ingredient::new( + "apple", + Some("chopped".to_owned()), + Volume(Cup(Quantity::Whole(1))), + ), + ), + ( + "1 cup apple (chopped) ", + Ingredient::new( + "apple", + Some("chopped".to_owned()), + Volume(Cup(Quantity::Whole(1))), + ), + ), ( "1 green bell pepper (chopped) ", Ingredient::new( @@ -269,6 +267,46 @@ fn test_ingredient_parse() { Count(Quantity::Whole(1)), ), ), + ( + "1 pkg green onion", + Ingredient::new( + "green onion", + None, + Package("pkg".into(), Quantity::Whole(1)), + ), + ), + ( + "1 bottle green onion", + Ingredient::new( + "green onion", + None, + Package("bottle".into(), Quantity::Whole(1)), + ), + ), + ( + "1 bot green onion", + Ingredient::new( + "green onion", + None, + Package("bot".into(), Quantity::Whole(1)), + ), + ), + ( + "1 bag green onion", + Ingredient::new( + "green onion", + None, + Package("bag".into(), Quantity::Whole(1)), + ), + ), + ( + "1 can baked beans", + Ingredient::new( + "baked bean", + None, + Package("can".into(), Quantity::Whole(1)), + ), + ), ] { match parse::ingredient(StrIter::new(i)) { ParseResult::Complete(_, ing) => assert_eq!(ing, expected), diff --git a/recipes/src/unit.rs b/recipes/src/unit.rs index f31333a..2ba223d 100644 --- a/recipes/src/unit.rs +++ b/recipes/src/unit.rs @@ -21,7 +21,7 @@ use std::{ cmp::{Ordering, PartialEq, PartialOrd}, convert::TryFrom, fmt::Display, - ops::{Add, Div, Mul, Sub}, + ops::{Add, Div, Mul, Sub}, rc::Rc, }; use num_rational::Ratio; @@ -179,6 +179,20 @@ impl VolumeMeasure { macro_rules! volume_op { ($trait:ident, $method:ident) => { + impl $trait for &VolumeMeasure { + type Output = VolumeMeasure; + + fn $method(self, lhs: Self) -> Self::Output { + let (l, r) = (self.get_ml(), lhs.get_ml()); + let result = ML($trait::$method(l, r)); + if self.metric() { + result.normalize() + } else { + result.into_tsp().normalize() + } + } + } + impl $trait for VolumeMeasure { type Output = Self; @@ -293,6 +307,20 @@ impl WeightMeasure { macro_rules! weight_op { ($trait:ident, $method:ident) => { + impl $trait for &WeightMeasure { + type Output = WeightMeasure; + + fn $method(self, lhs: Self) -> Self::Output { + let (l, r) = (self.get_grams(), lhs.get_grams()); + let result = WeightMeasure::Gram($trait::$method(l, r)); + if self.metric() { + result.normalize() + } else { + result.into_oz().normalize() + } + } + } + impl $trait for WeightMeasure { type Output = Self; @@ -335,18 +363,19 @@ impl Display for WeightMeasure { use WeightMeasure::{Gram, Kilogram, Oz, Pound}; -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord)] +#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)] /// Measurements in a Recipe with associated units for them. pub enum Measure { /// Volume measurements as meter cubed base unit Volume(VolumeMeasure), /// Simple count of items Count(Quantity), + Package(Rc, Quantity), /// Weight measure as Grams base unit Weight(WeightMeasure), } -use Measure::{Count, Volume, Weight}; +use Measure::{Count, Volume, Weight, Package}; impl Measure { pub fn tsp(qty: Quantity) -> Self { @@ -407,11 +436,16 @@ impl Measure { Weight(Oz(qty)) } + pub fn pkg>>(name: S, qty: Quantity) -> Self { + Package(name.into(), qty) + } + pub fn measure_type(&self) -> String { match self { Volume(_) => "Volume", Count(_) => "Count", Weight(_) => "Weight", + Package(_, _) => "Package", } .to_owned() } @@ -421,6 +455,7 @@ impl Measure { Volume(vm) => vm.plural(), Count(qty) => qty.plural(), Weight(wm) => wm.plural(), + Package(_, qty) => qty.plural(), } } @@ -429,6 +464,7 @@ impl Measure { Volume(vm) => Volume(vm.normalize()), Count(qty) => Count(qty.clone()), Weight(wm) => Weight(wm.normalize()), + Package(nm, qty) => Package(nm.clone(), qty.clone()), } } } @@ -439,6 +475,7 @@ impl Display for Measure { Volume(vm) => write!(w, "{}", vm), Count(qty) => write!(w, "{}", qty), Weight(wm) => write!(w, "{}", wm), + Package(nm, qty) => write!(w, "{} {}", qty, nm), } } } @@ -533,6 +570,22 @@ impl TryFrom for Quantity { macro_rules! quantity_op { ($trait:ident, $method:ident) => { + impl $trait for &Quantity { + type Output = Quantity; + + fn $method(self, lhs: Self) -> Self::Output { + match (self, lhs) { + (Whole(rhs), Whole(lhs)) => Frac($trait::$method( + Ratio::from_integer(*rhs), + Ratio::from_integer(*lhs), + )), + (Frac(rhs), Frac(lhs)) => Frac($trait::$method(rhs, lhs)), + (Whole(rhs), Frac(lhs)) => Frac($trait::$method(Ratio::from_integer(*rhs), lhs)), + (Frac(rhs), Whole(lhs)) => Frac($trait::$method(rhs, Ratio::from_integer(*lhs))), + } + } + } + impl $trait for Quantity { type Output = Self; From bb092212ac8032e1c1a78dd2c0001144e68b5f86 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Wed, 3 Jan 2024 15:26:33 -0500 Subject: [PATCH 11/28] Stop using singular for normalization --- recipes/src/parse.rs | 5 +---- recipes/src/test.rs | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/recipes/src/parse.rs b/recipes/src/parse.rs index 38fac0f..987b287 100644 --- a/recipes/src/parse.rs +++ b/recipes/src/parse.rs @@ -419,8 +419,6 @@ pub fn measure(i: StrIter) -> abortable_parser::Result { } } -// TODO(jwall): I think this is a mistake. We should rethink what noralizing means or we should -// remove it. pub fn normalize_name(name: &str) -> String { let parts: Vec<&str> = name.split_whitespace().collect(); if parts.len() >= 2 { @@ -428,9 +426,8 @@ pub fn normalize_name(name: &str) -> String { // NOTE(jwall): The below unwrap is safe because of the length // check above. let last = parts.last().unwrap(); - let normalized = last.to_singular(); prefix.push(' '); - prefix.push_str(&normalized); + prefix.push_str(&last.to_string()); return prefix; } return name.trim().to_lowercase().to_owned(); diff --git a/recipes/src/test.rs b/recipes/src/test.rs index 62d0633..8d89421 100644 --- a/recipes/src/test.rs +++ b/recipes/src/test.rs @@ -302,7 +302,7 @@ fn test_ingredient_parse() { ( "1 can baked beans", Ingredient::new( - "baked bean", + "baked beans", None, Package("can".into(), Quantity::Whole(1)), ), From 874a5fdb577a2215bd1abe459e5ba57929b1f498 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Fri, 5 Jan 2024 18:58:48 -0500 Subject: [PATCH 12/28] cargo fmt --- kitchen/build.rs | 2 +- kitchen/src/web/mod.rs | 2 +- recipes/src/lib.rs | 5 ++++- recipes/src/parse.rs | 2 +- recipes/src/unit.rs | 15 +++++++++----- web/src/api.rs | 32 ++++++++++++++++++----------- web/src/components/number_field.rs | 19 +++++++++++++---- web/src/components/plan_list.rs | 2 +- web/src/components/shopping_list.rs | 4 +--- web/src/js_lib.rs | 6 +++--- web/src/lib.rs | 2 +- web/src/linear.rs | 5 ++++- web/src/web.rs | 2 +- 13 files changed, 63 insertions(+), 35 deletions(-) diff --git a/kitchen/build.rs b/kitchen/build.rs index 7609593..d506869 100644 --- a/kitchen/build.rs +++ b/kitchen/build.rs @@ -2,4 +2,4 @@ fn main() { // trigger recompilation when a new migration is added println!("cargo:rerun-if-changed=migrations"); -} \ No newline at end of file +} diff --git a/kitchen/src/web/mod.rs b/kitchen/src/web/mod.rs index 09d1b03..c0ec8d0 100644 --- a/kitchen/src/web/mod.rs +++ b/kitchen/src/web/mod.rs @@ -1,4 +1,3 @@ -use std::collections::BTreeMap; // Copyright 2022 Jeremy Wall // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,6 +11,7 @@ use std::collections::BTreeMap; // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +use std::collections::BTreeMap; use std::path::PathBuf; use std::sync::Arc; use std::{collections::BTreeSet, net::SocketAddr}; diff --git a/recipes/src/lib.rs b/recipes/src/lib.rs index 53d40a7..c1e9aed 100644 --- a/recipes/src/lib.rs +++ b/recipes/src/lib.rs @@ -164,7 +164,10 @@ impl IngredientAccumulator { if lnm == rnm { vec![Package(lnm.clone(), lqty + rqty)] } else { - vec![Package(lnm.clone(), lqty.clone()), Package(rnm.clone(), rqty.clone())] + vec![ + Package(lnm.clone(), lqty.clone()), + Package(rnm.clone(), rqty.clone()), + ] } } _ => unreachable!(), diff --git a/recipes/src/parse.rs b/recipes/src/parse.rs index 987b287..9ee2714 100644 --- a/recipes/src/parse.rs +++ b/recipes/src/parse.rs @@ -400,7 +400,7 @@ pub fn measure(i: StrIter) -> abortable_parser::Result { "oz" => Weight(Oz(qty)), "kg" | "kilogram" => Weight(Kilogram(qty)), "g" | "gram" => Weight(Gram(qty)), - "pkg" | "package" | "can" | "bag" | "bottle" | "bot" => Measure::pkg(s, qty), + "pkg" | "package" | "can" | "bag" | "bottle" | "bot" => Measure::pkg(s, qty), _u => { eprintln!("Invalid unit: {}", _u); unreachable!() diff --git a/recipes/src/unit.rs b/recipes/src/unit.rs index 2ba223d..ea60379 100644 --- a/recipes/src/unit.rs +++ b/recipes/src/unit.rs @@ -21,7 +21,8 @@ use std::{ cmp::{Ordering, PartialEq, PartialOrd}, convert::TryFrom, fmt::Display, - ops::{Add, Div, Mul, Sub}, rc::Rc, + ops::{Add, Div, Mul, Sub}, + rc::Rc, }; use num_rational::Ratio; @@ -320,7 +321,7 @@ macro_rules! weight_op { } } } - + impl $trait for WeightMeasure { type Output = Self; @@ -375,7 +376,7 @@ pub enum Measure { Weight(WeightMeasure), } -use Measure::{Count, Volume, Weight, Package}; +use Measure::{Count, Package, Volume, Weight}; impl Measure { pub fn tsp(qty: Quantity) -> Self { @@ -580,8 +581,12 @@ macro_rules! quantity_op { Ratio::from_integer(*lhs), )), (Frac(rhs), Frac(lhs)) => Frac($trait::$method(rhs, lhs)), - (Whole(rhs), Frac(lhs)) => Frac($trait::$method(Ratio::from_integer(*rhs), lhs)), - (Frac(rhs), Whole(lhs)) => Frac($trait::$method(rhs, Ratio::from_integer(*lhs))), + (Whole(rhs), Frac(lhs)) => { + Frac($trait::$method(Ratio::from_integer(*rhs), lhs)) + } + (Frac(rhs), Whole(lhs)) => { + Frac($trait::$method(rhs, Ratio::from_integer(*lhs))) + } } } } diff --git a/web/src/api.rs b/web/src/api.rs index 5badbdc..dc6fe36 100644 --- a/web/src/api.rs +++ b/web/src/api.rs @@ -25,7 +25,10 @@ use recipes::{IngredientKey, RecipeEntry}; use wasm_bindgen::JsValue; use web_sys::Storage; -use crate::{app_state::{AppState, parse_recipes}, js_lib}; +use crate::{ + app_state::{parse_recipes, AppState}, + js_lib, +}; #[derive(Debug)] pub struct Error(String); @@ -111,7 +114,8 @@ impl LocalStore { self.store.get("app_state").map_or(None, |val| { val.map(|s| { debug!("Found an app_state object"); - let mut app_state: AppState = from_str(&s).expect("Failed to deserialize app state"); + let mut app_state: AppState = + from_str(&s).expect("Failed to deserialize app state"); let recipes = parse_recipes(&self.get_recipes()).expect("Failed to parse recipes"); if let Some(recipes) = recipes { debug!("Populating recipes"); @@ -160,12 +164,12 @@ impl LocalStore { } fn migrate_local_store(&self) { - for k in self.get_storage_keys() - .into_iter() - .filter(|k| k.starts_with("categor") || k == "inventory" || k.starts_with("plan") || k == "staples") { - // Deleting old local store key - debug!("Deleting old local store key {}", k); - self.store.delete(&k).expect("Failed to delete storage key"); + for k in self.get_storage_keys().into_iter().filter(|k| { + k.starts_with("categor") || k == "inventory" || k.starts_with("plan") || k == "staples" + }) { + // Deleting old local store key + debug!("Deleting old local store key {}", k); + self.store.delete(&k).expect("Failed to delete storage key"); } } @@ -280,9 +284,10 @@ impl HttpStore { ) .mode(web_sys::RequestMode::SameOrigin) .credentials(web_sys::RequestCredentials::SameOrigin) - .build().expect("Failed to build request"); - debug!(?request, "Sending auth request"); - let result = request.send().await; + .build() + .expect("Failed to build request"); + debug!(?request, "Sending auth request"); + let result = request.send().await; if let Ok(resp) = &result { if resp.status() == 200 { let user_data = resp @@ -762,7 +767,10 @@ impl HttpStore { } } - pub async fn store_staples + serde::Serialize>(&self, content: S) -> Result<(), Error> { + pub async fn store_staples + serde::Serialize>( + &self, + content: S, + ) -> Result<(), Error> { let mut path = self.v2_path(); path.push_str("/staples"); let resp = gloo_net::http::Request::post(&path) diff --git a/web/src/components/number_field.rs b/web/src/components/number_field.rs index 92b07b8..0486afa 100644 --- a/web/src/components/number_field.rs +++ b/web/src/components/number_field.rs @@ -16,7 +16,7 @@ use sycamore::prelude::*; use tracing::{debug, error}; use wasm_bindgen::JsCast; use wasm_web_component::{web_component, WebComponentBinding}; -use web_sys::{CustomEvent, Event, HtmlElement, InputEvent, ShadowRoot, window}; +use web_sys::{window, CustomEvent, Event, HtmlElement, InputEvent, ShadowRoot}; use crate::js_lib::LogFailures; @@ -153,7 +153,12 @@ impl WebComponentBinding for NumberSpinner { ) { let nval_el = self.get_input_el(); let name = name.as_string().unwrap(); - debug!(?name, ?old_value, ?new_value, "COUNTS: handling attribute change"); + debug!( + ?name, + ?old_value, + ?new_value, + "COUNTS: handling attribute change" + ); match name.as_str() { "val" => { debug!("COUNTS: got an updated value"); @@ -236,9 +241,15 @@ where create_effect(cx, move || { let new_count = *counter.get(); debug!(new_count, "COUNTS: Updating spinner with new value"); - if let Some(el) = window().unwrap().document().unwrap().get_element_by_id(id.as_str()) { + if let Some(el) = window() + .unwrap() + .document() + .unwrap() + .get_element_by_id(id.as_str()) + { debug!("COUNTS: found element"); - el.set_attribute("val", new_count.to_string().as_str()).unwrap(); + el.set_attribute("val", new_count.to_string().as_str()) + .unwrap(); } }); let id = name.clone(); diff --git a/web/src/components/plan_list.rs b/web/src/components/plan_list.rs index a083cbc..0a6060c 100644 --- a/web/src/components/plan_list.rs +++ b/web/src/components/plan_list.rs @@ -11,8 +11,8 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -use sycamore::prelude::*; use chrono::NaiveDate; +use sycamore::prelude::*; use crate::app_state::{Message, StateHandler}; use tracing::instrument; diff --git a/web/src/components/shopping_list.rs b/web/src/components/shopping_list.rs index b0ea1e8..4248a80 100644 --- a/web/src/components/shopping_list.rs +++ b/web/src/components/shopping_list.rs @@ -194,9 +194,7 @@ 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 = sh.get_selector(cx, |state| { - state.get().use_staples - }); + let show_staples = sh.get_selector(cx, |state| state.get().use_staples); view! {cx, h1 { "Shopping List " } label(for="show_staples_cb") { "Show staples" } diff --git a/web/src/js_lib.rs b/web/src/js_lib.rs index 6acf37a..82596f5 100644 --- a/web/src/js_lib.rs +++ b/web/src/js_lib.rs @@ -16,7 +16,8 @@ use tracing::error; use web_sys::{window, Storage, Window}; pub fn get_storage() -> Storage { - get_window().local_storage() + get_window() + .local_storage() .expect("Failed to get storage") .expect("No storage available") } @@ -26,8 +27,7 @@ pub fn get_ms_timestamp() -> u32 { } pub fn get_window() -> Window { - window() - .expect("No window present") + window().expect("No window present") } pub trait LogFailures { diff --git a/web/src/lib.rs b/web/src/lib.rs index af53b8d..188d5c5 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -15,10 +15,10 @@ mod api; mod app_state; mod components; mod js_lib; +mod linear; mod pages; mod routing; mod web; -mod linear; use sycamore::prelude::*; use wasm_bindgen::prelude::wasm_bindgen; diff --git a/web/src/linear.rs b/web/src/linear.rs index 80c16d9..355910b 100644 --- a/web/src/linear.rs +++ b/web/src/linear.rs @@ -24,7 +24,10 @@ pub struct LinearSignal<'ctx, Payload> { impl<'ctx, Payload> Into> for &'ctx Signal { fn into(self) -> LinearSignal<'ctx, Payload> { - LinearSignal { signal: self, nv: None } + LinearSignal { + signal: self, + nv: None, + } } } diff --git a/web/src/web.rs b/web/src/web.rs index c2b6883..bd40dc7 100644 --- a/web/src/web.rs +++ b/web/src/web.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. use sycamore::{futures::spawn_local_scoped, prelude::*}; -use tracing::{info, debug, instrument}; +use tracing::{debug, info, instrument}; use crate::app_state::Message; use crate::{api, routing::Handler as RouteHandler}; From a32035104176212c6b98f7ffc8ba699e87127636 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sat, 6 Jan 2024 10:25:55 -0500 Subject: [PATCH 13/28] NOTE comment. --- web/src/app_state.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/src/app_state.rs b/web/src/app_state.rs index 88c0244..8770049 100644 --- a/web/src/app_state.rs +++ b/web/src/app_state.rs @@ -174,8 +174,9 @@ impl StateMachine { local_store: &LocalStore, original: &Signal, ) -> Result<(), crate::api::Error> { - // TODO(jwall): We use a linear Signal in here to ensure that we only - // call set on the signal once. + // NOTE(jwall): We use a linear Signal in here to ensure that we only + // call set on the signal once. When the LinearSignal get's dropped it + // will call set on the contained Signal. let mut original: LinearSignal = original.into(); if let Some(state) = local_store.fetch_app_state() { original = original.update(state); From 0ba5f18b22280a10010338103882ba788d287f6f Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Thu, 18 Jan 2024 17:13:17 -0500 Subject: [PATCH 14/28] Display current plan date at the top --- web/src/pages/planning/cook.rs | 4 ++++ web/src/pages/planning/inventory.rs | 4 ++++ web/src/pages/planning/mod.rs | 20 +++++++++++++++----- web/src/pages/planning/plan.rs | 4 ++++ web/src/pages/planning/select.rs | 4 ++++ 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/web/src/pages/planning/cook.rs b/web/src/pages/planning/cook.rs index 2ae3cf9..e213f5b 100644 --- a/web/src/pages/planning/cook.rs +++ b/web/src/pages/planning/cook.rs @@ -18,9 +18,13 @@ use crate::{app_state::StateHandler, components::recipe_list::*}; #[component] pub fn CookPage<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> View { + let current_plan = sh.get_selector(cx, |state| { + state.get().selected_plan_date + }); view! {cx, PlanningPage( selected=Some("Cook".to_owned()), + plan_date = current_plan, ) { RecipeList(sh) } } } diff --git a/web/src/pages/planning/inventory.rs b/web/src/pages/planning/inventory.rs index 39a84ea..67bb43e 100644 --- a/web/src/pages/planning/inventory.rs +++ b/web/src/pages/planning/inventory.rs @@ -18,9 +18,13 @@ use crate::{app_state::StateHandler, components::shopping_list::*}; #[component] pub fn InventoryPage<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> View { + let current_plan = sh.get_selector(cx, |state| { + state.get().selected_plan_date + }); view! {cx, PlanningPage( selected=Some("Inventory".to_owned()), + plan_date = current_plan, ) { ShoppingList(sh) } } } diff --git a/web/src/pages/planning/mod.rs b/web/src/pages/planning/mod.rs index 940e35b..27fe3a1 100644 --- a/web/src/pages/planning/mod.rs +++ b/web/src/pages/planning/mod.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. use crate::components::tabs::*; +use chrono::NaiveDate; use sycamore::prelude::*; pub mod cook; @@ -25,14 +26,19 @@ pub use plan::*; pub use select::*; #[derive(Props)] -pub struct PageState<'a, G: Html> { - pub children: Children<'a, G>, +pub struct PageState<'ctx, G: Html> { + pub children: Children<'ctx, G>, pub selected: Option, + pub plan_date: &'ctx ReadSignal>, } #[component] -pub fn PlanningPage<'a, G: Html>(cx: Scope<'a>, state: PageState<'a, G>) -> View { - let PageState { children, selected } = state; +pub fn PlanningPage<'ctx, G: Html>(cx: Scope<'ctx>, state: PageState<'ctx, G>) -> View { + let PageState { + children, + selected, + plan_date, + } = state; let children = children.call(cx); let planning_tabs: Vec<(String, &'static str)> = vec![ ("/ui/planning/select".to_owned(), "Select"), @@ -45,6 +51,10 @@ pub fn PlanningPage<'a, G: Html>(cx: Scope<'a>, state: PageState<'a, G>) -> View TabbedView( selected=selected, tablist=planning_tabs, - ) { (children) } + ) { div { + "Plan Date: " (plan_date.get().map_or(String::from("Unknown"), |d| format!("{}", d))) + } + (children) + } } } diff --git a/web/src/pages/planning/plan.rs b/web/src/pages/planning/plan.rs index f3f4975..a8e3bba 100644 --- a/web/src/pages/planning/plan.rs +++ b/web/src/pages/planning/plan.rs @@ -18,9 +18,13 @@ use sycamore::prelude::*; #[component] pub fn PlanPage<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> View { + let current_plan = sh.get_selector(cx, |state| { + state.get().selected_plan_date + }); view! {cx, PlanningPage( selected=Some("Plan".to_owned()), + plan_date = current_plan, ) { RecipePlan(sh) } } } diff --git a/web/src/pages/planning/select.rs b/web/src/pages/planning/select.rs index f037003..2057926 100644 --- a/web/src/pages/planning/select.rs +++ b/web/src/pages/planning/select.rs @@ -32,9 +32,13 @@ pub fn SelectPage<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> Vie plans.sort_unstable_by(|d1, d2| d2.cmp(d1)); plans }); + let current_plan = sh.get_selector(cx, |state| { + state.get().selected_plan_date + }); view! {cx, PlanningPage( selected=Some("Select".to_owned()), + plan_date = current_plan.clone(), ) { PlanList(sh=sh, list=plan_dates) button(on:click=move |_| { From b105ce3f4b1a077e77f72102e48249335885c0ad Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sun, 28 Jan 2024 15:09:24 -0500 Subject: [PATCH 15/28] Add some models --- models/planning.d2 | 17 ++++++ models/planning.svg | 125 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 models/planning.d2 create mode 100644 models/planning.svg diff --git a/models/planning.d2 b/models/planning.d2 new file mode 100644 index 0000000..04dafdf --- /dev/null +++ b/models/planning.d2 @@ -0,0 +1,17 @@ +Meal Planning: { + shape: sequence_diagram + user: Cook; client: Kitchen frontend; kitchen: Kitchen backend + + user -> client: Start new meal Plan + client -> kitchen: new plan created + user -> client: Add recipe to meal plan + client -> kitchen: Update meal plan with recipe + client -> client: cache updated meal plan + user -> client: Do inventory + client -> kitchen: Store inventory mutations + client -> client: cache inventory mutations + user -> client: Undo mutation + client -> kitchen: Store inventory mutations + client -> client: cache inventory mutations + user -> user: Cook recipes +} diff --git a/models/planning.svg b/models/planning.svg new file mode 100644 index 0000000..b9984ec --- /dev/null +++ b/models/planning.svg @@ -0,0 +1,125 @@ + + + + + + + + +Meal PlanningCookKitchen frontendKitchen backend Start new meal Plan new plan created Add recipe to meal plan Update meal plan with recipe cache updated meal plan Do inventory Store inventory mutations cache inventory mutations Undo mutation Store inventory mutations cache inventory mutations Cook recipes + + + + + + + + + + + + + + + + + + From 6f7d44ff83e4505db30bed9f13267e8f5bec1288 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sun, 28 Jan 2024 16:56:36 -0500 Subject: [PATCH 16/28] Alloy models for browser_state --- models/browser_state.als | 51 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 models/browser_state.als diff --git a/models/browser_state.als b/models/browser_state.als new file mode 100644 index 0000000..2adf814 --- /dev/null +++ b/models/browser_state.als @@ -0,0 +1,51 @@ +sig Id {} +sig Text {} + +sig Recipe { + , id: one Id + , text: one Text +} + +fact { + no r1, r2: Recipe | (r1.id = r2.id) and (r1.text != r2.text) + no r1, r2: Recipe | (r1 != r2) and (r1.id = r2.id) +} + +sig Ingredient {} +sig Modifier {} +sig Amt {} + +sig ModifiedInventory { + , ingredient: one Ingredient + , modifier: lone Modifier + , amt: one Amt +} + +fact { + no mi1, mi2: ModifiedInventory | mi1 != mi2 && (mi1.ingredient = mi2.ingredient) and (mi1.modifier = mi2.modifier) +} + +sig DeletedInventory { + , ingredient: one Ingredient + , modifier: lone Modifier +} + +fact { + no mi1, mi2: DeletedInventory | mi1 != mi2 && (mi1.ingredient = mi2.ingredient) and (mi1.modifier = mi2.modifier) +} + +sig ExtraItems { + , ingredient: one Ingredient + , amt: one Amt +} + +sig State { + , recipes: some Recipe + , modified: set ModifiedInventory + , deleted: set DeletedInventory + , extras: set ExtraItems +} { + no rs: Recipe | rs not in recipes +} + +run { } for 3 but exactly 2 State, 2 Modifier, exactly 3 ModifiedInventory, exactly 9 Ingredient From 3e675b47f41ceab3bbb6f1b253171ebb454472aa Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Tue, 20 Feb 2024 15:48:51 -0500 Subject: [PATCH 17/28] fix: Unsafe recursive object use --- web/Cargo.toml | 1 + web/src/components/number_field.rs | 45 ++++++++++-------------------- 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/web/Cargo.toml b/web/Cargo.toml index aae28c8..6244a10 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -56,6 +56,7 @@ features = [ "Event", "InputEvent", "CustomEvent", + "CustomEventInit", "EventTarget", "History", "HtmlAnchorElement", diff --git a/web/src/components/number_field.rs b/web/src/components/number_field.rs index 0486afa..15446dc 100644 --- a/web/src/components/number_field.rs +++ b/web/src/components/number_field.rs @@ -14,9 +14,9 @@ use maud::html; use sycamore::prelude::*; use tracing::{debug, error}; -use wasm_bindgen::JsCast; +use wasm_bindgen::{JsCast, JsValue}; use wasm_web_component::{web_component, WebComponentBinding}; -use web_sys::{window, CustomEvent, Event, HtmlElement, InputEvent, ShadowRoot}; +use web_sys::{window, CustomEvent, CustomEventInit, Event, HtmlElement, InputEvent, ShadowRoot}; use crate::js_lib::LogFailures; @@ -135,11 +135,10 @@ impl WebComponentBinding for NumberSpinner { return; } }; + let mut eventDict = CustomEventInit::new(); + eventDict.detail(&JsValue::from_f64(self.value as f64)); element - .set_attribute("val", &format!("{}", self.value)) - .swallow_and_log(); - element - .dispatch_event(&CustomEvent::new("updated").unwrap()) + .dispatch_event(&CustomEvent::new_with_event_init_dict("updated", &eventDict).unwrap()) .unwrap(); debug!("Dispatched updated event"); } @@ -147,9 +146,9 @@ impl WebComponentBinding for NumberSpinner { fn attribute_changed_mut( &mut self, _element: &web_sys::HtmlElement, - name: wasm_bindgen::JsValue, - old_value: wasm_bindgen::JsValue, - new_value: wasm_bindgen::JsValue, + name: JsValue, + old_value: JsValue, + new_value: JsValue, ) { let nval_el = self.get_input_el(); let name = name.as_string().unwrap(); @@ -213,7 +212,7 @@ impl WebComponentBinding for NumberSpinner { #[derive(Props)] pub struct NumberProps<'ctx, F> where - F: Fn(Event), + F: Fn(CustomEvent), { name: String, class: String, @@ -225,7 +224,7 @@ where #[component] pub fn NumberField<'ctx, F, G: Html>(cx: Scope<'ctx>, props: NumberProps<'ctx, F>) -> View where - F: Fn(web_sys::Event) + 'ctx, + F: Fn(CustomEvent) + 'ctx, { let NumberProps { name, @@ -238,27 +237,13 @@ where // TODO(jwall): I'm pretty sure this triggers: https://github.com/sycamore-rs/sycamore/issues/602 // Which means I probably have to wait till v0.9.0 drops or switch to leptos. let id = name.clone(); - create_effect(cx, move || { - let new_count = *counter.get(); - debug!(new_count, "COUNTS: Updating spinner with new value"); - if let Some(el) = window() - .unwrap() - .document() - .unwrap() - .get_element_by_id(id.as_str()) - { - debug!("COUNTS: found element"); - el.set_attribute("val", new_count.to_string().as_str()) - .unwrap(); - } - }); - let id = name.clone(); + let initial_count = *counter.get(); view! {cx, - number-spinner(id=id, class=(class), val=*counter.get(), min=min, on:updated=move |evt: Event| { - let target: HtmlElement = evt.target().unwrap().dyn_into().unwrap(); - let val: f64 = target.get_attribute("val").unwrap().parse().unwrap(); + number-spinner(id=id, class=(class), val=(initial_count), min=min, on:updated=move |evt: Event| { + let event = evt.unchecked_into::(); + let val: f64 = event.detail().as_f64().unwrap(); counter.set(val); - on_change.as_ref().map(|f| f(evt)); + on_change.as_ref().map(|f| f(event)); debug!(counter=%(counter.get_untracked()), "set counter to new value"); }) } From 1b6023a03e2bf6c03e4e5b285bdfa8e2fc8eeb67 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Tue, 20 Feb 2024 17:48:17 -0500 Subject: [PATCH 18/28] maint: having the wasm-pack version in the logs is useful --- nix/kitchenWasm/default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nix/kitchenWasm/default.nix b/nix/kitchenWasm/default.nix index 500712b..8f6df3b 100644 --- a/nix/kitchenWasm/default.nix +++ b/nix/kitchenWasm/default.nix @@ -35,6 +35,7 @@ stdenv.mkDerivation { # TODO(jwall): Build this from the root rather than the src. buildPhase = '' echo building with wasm-pack + wasm-pack --version mkdir -p $out cd web cp -r static $out From 251cbfa5c7e892351ec3828559f2363ca78d51aa Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sun, 10 Mar 2024 15:13:39 -0400 Subject: [PATCH 19/28] build: use rustls --- kitchen/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kitchen/Cargo.toml b/kitchen/Cargo.toml index 99589ec..5833ed0 100644 --- a/kitchen/Cargo.toml +++ b/kitchen/Cargo.toml @@ -51,7 +51,7 @@ features = ["serde", "v4"] [dependencies.axum] version = "0.5.16" -features = ["headers", "http2"] +features = ["headers", "http2", "rustls-tls"] [dependencies.clap] version = "3.2.16" From 3e853f51ebcfb7d9d1588480b39e87ebac9b55b6 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sun, 23 Jun 2024 11:23:57 -0400 Subject: [PATCH 20/28] build: fix rust-tls resolver issues --- flake.lock | 13 ++++++------- flake.nix | 2 +- kitchen/Cargo.toml | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index a36dfa2..9a80f3f 100644 --- a/flake.lock +++ b/flake.lock @@ -99,11 +99,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1679174867, - "narHash": "sha256-fFxb8wN3bjOMvHPr63Iyzo3cuHhQzWW03UkckfTeBWU=", + "lastModified": 1719152388, + "narHash": "sha256-pHg0nzAa2ZM+zFamfsY7ZvVaB19pMr5wl4G5nO0J7eU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f5ec87b82832736f1624874fd34eb60c0b68bdd6", + "rev": "be54c7d931a68ba6a79f097ce979288e90a74288", "type": "github" }, "original": { @@ -124,17 +124,16 @@ }, "rust-overlay": { "inputs": { - "flake-utils": "flake-utils_2", "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1702001829, - "narHash": "sha256-6gEVidNVqzTb06zIy2Gxhz9m6/jXyAgViRxfgEpZkQ8=", + "lastModified": 1718681902, + "narHash": "sha256-E/T7Ge6ayEQe7FVKMJqDBoHyLhRhjc6u9CmU8MyYfy0=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "c2a1dd067a928624c1aab36f976758c0722c79bd", + "rev": "16c8ad83297c278eebe740dea5491c1708960dd1", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index f01beae..dc36109 100644 --- a/flake.nix +++ b/flake.nix @@ -25,7 +25,7 @@ let overlays = [ rust-overlay.overlays.default ]; pkgs = import nixpkgs { inherit system overlays; }; - rust-wasm = pkgs.rust-bin.stable."1.74.1".default.override { + rust-wasm = pkgs.rust-bin.stable."1.77.0".default.override { extensions = [ "rust-src" ]; # Add wasm32 as an extra target besides the native target. targets = [ "wasm32-unknown-unknown" ]; diff --git a/kitchen/Cargo.toml b/kitchen/Cargo.toml index 5833ed0..99589ec 100644 --- a/kitchen/Cargo.toml +++ b/kitchen/Cargo.toml @@ -51,7 +51,7 @@ features = ["serde", "v4"] [dependencies.axum] version = "0.5.16" -features = ["headers", "http2", "rustls-tls"] +features = ["headers", "http2"] [dependencies.clap] version = "3.2.16" From 651f0cb264940c8a46f1284b2fcc79b0d522fd66 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sun, 23 Jun 2024 12:50:24 -0400 Subject: [PATCH 21/28] build: get rid of wasm-pack It get's doing naughty things with network access. --- nix/kitchenWasm/default.nix | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/nix/kitchenWasm/default.nix b/nix/kitchenWasm/default.nix index 8f6df3b..29c2c09 100644 --- a/nix/kitchenWasm/default.nix +++ b/nix/kitchenWasm/default.nix @@ -34,13 +34,20 @@ stdenv.mkDerivation { ''; # TODO(jwall): Build this from the root rather than the src. buildPhase = '' + set -x echo building with wasm-pack wasm-pack --version mkdir -p $out cd web cp -r static $out - RUST_LOG=info wasm-pack build --mode no-install --release --target web --out-dir $out ${features}; + cargo build --lib --release --target wasm32-unknown-unknown --target-dir $out --offline + wasm-bindgen $out/wasm32-unknown-unknown/release/kitchen_wasm.wasm --out-dir $out --typescript --target web + wasm-opt $out/kitchen_wasm_bg.wasm -o $out/kitchen_wasm_bg-opt.wasm -O + rm -f $out/kitchen_wasm_bg.wasm + mv $out/kitchen_wasm_bg-opt.wasm $out/kitchen_wasm_bg.wasm cp -r index.html $out cp -r favicon.ico $out + rm -rf $out/release + rm -rf $out/wasm32-unknown-unknown ''; } From e1735e424363f04998c6e53ca3ace860bb937900 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sun, 30 Jun 2024 20:30:54 -0500 Subject: [PATCH 22/28] maint: use resolver "2" --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 33fc471..4ba40ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ "recipes", "kitchen", "web", "api" ] +resolver = "2" [patch.crates-io] # TODO(jwall): When the fix for RcSignal Binding is released we can drop this patch. From e3c4a016487977ce479528328524e3a3f6500ae8 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sun, 30 Jun 2024 20:31:47 -0500 Subject: [PATCH 23/28] refactor: make the wasm builder a little more configurable. --- flake.nix | 8 ++++++++ nix/kitchenWasm/default.nix | 18 +++++------------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/flake.nix b/flake.nix index dc36109..8884f75 100644 --- a/flake.nix +++ b/flake.nix @@ -45,6 +45,14 @@ wasm-bindgen = pkgs.callPackage wasm-bindgenGen { inherit pkgs; }; kitchenWasm = kitchenWasmGen { inherit pkgs rust-wasm wasm-bindgen version; + lockFile = ./Cargo.lock; + outputHashes = { + # I'm maintaining some patches for these so the lockfile hashes are a little + # incorrect. We override those here. + "wasm-web-component-0.2.0" = "sha256-quuPgzGb2F96blHmD3BAUjsWQYbSyJGZl27PVrwL92k="; + "sycamore-0.8.2" = "sha256-D968+8C5EelGGmot9/LkAlULZOf/Cr+1WYXRCMwb1nQ="; + "sqlx-0.6.2" = "sha256-X/LFvtzRfiOIEZJiVzmFvvULPpjhqvI99pSwH7a//GM="; + }; }; kitchen = (kitchenGen { inherit pkgs version naersk-lib kitchenWasm rust-wasm; diff --git a/nix/kitchenWasm/default.nix b/nix/kitchenWasm/default.nix index 29c2c09..6d0055f 100644 --- a/nix/kitchenWasm/default.nix +++ b/nix/kitchenWasm/default.nix @@ -3,23 +3,18 @@ features ? "", rust-wasm, wasm-bindgen, + lockFile, + outputHashes, }: with pkgs; let pname = "kitchen-wasm"; src = ./../..; - lockFile = ./../../Cargo.lock; # NOTE(jwall): Because we use wasm-pack directly below we need # the cargo dependencies to already be installed. - cargoDeps = (pkgs.rustPlatform.importCargoLock { inherit lockFile; outputHashes = { - # I'm maintaining some patches for these so the lockfile hashes are a little - # incorrect. We override those here. - "wasm-web-component-0.2.0" = "sha256-quuPgzGb2F96blHmD3BAUjsWQYbSyJGZl27PVrwL92k="; - "sycamore-0.8.2" = "sha256-D968+8C5EelGGmot9/LkAlULZOf/Cr+1WYXRCMwb1nQ="; - "sqlx-0.6.2" = "sha256-X/LFvtzRfiOIEZJiVzmFvvULPpjhqvI99pSwH7a//GM="; - }; - }); + cargoDeps = (pkgs.rustPlatform.importCargoLock { inherit lockFile outputHashes; }); in +# TODO(zaphar): I should actually be leveraging naersklib.buildPackage with a postInstall for the optimization and bindgen stdenv.mkDerivation { inherit src pname; version = version; @@ -34,13 +29,10 @@ stdenv.mkDerivation { ''; # TODO(jwall): Build this from the root rather than the src. buildPhase = '' - set -x - echo building with wasm-pack - wasm-pack --version mkdir -p $out cd web cp -r static $out - cargo build --lib --release --target wasm32-unknown-unknown --target-dir $out --offline + cargo build --lib --release --target wasm32-unknown-unknown --target-dir $out ${features} --offline wasm-bindgen $out/wasm32-unknown-unknown/release/kitchen_wasm.wasm --out-dir $out --typescript --target web wasm-opt $out/kitchen_wasm_bg.wasm -o $out/kitchen_wasm_bg-opt.wasm -O rm -f $out/kitchen_wasm_bg.wasm From dac4324c8f01e8c03f2df2b74a34abe3833b7446 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sun, 30 Jun 2024 20:32:14 -0500 Subject: [PATCH 24/28] maint: cleanup some unused nix dependencies --- nix/wasm-bindgen/default.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/nix/wasm-bindgen/default.nix b/nix/wasm-bindgen/default.nix index 9ff0c2c..14b4d18 100644 --- a/nix/wasm-bindgen/default.nix +++ b/nix/wasm-bindgen/default.nix @@ -8,9 +8,7 @@ in , nodejs , pkg-config , openssl -, stdenv , curl -, runCommand }: # This package is special so we don't use the naersk infrastructure to build it. From 9249dca20261144afbb4a26e2e7c0fac54c0f831 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sun, 30 Jun 2024 20:32:38 -0500 Subject: [PATCH 25/28] build: Makefile enhancements --- Makefile | 16 ++++++++++++---- scripts/wasm-build.sh | 9 +++++++++ scripts/wasm-opt.sh | 6 ++++++ 3 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 scripts/wasm-build.sh create mode 100644 scripts/wasm-opt.sh diff --git a/Makefile b/Makefile index cdddd93..41ef01d 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,11 @@ mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) mkfile_dir := $(dir $(mkfile_path)) sqlite_url := sqlite://$(mkfile_dir)/.session_store/store.db +out := dist +project := kitchen + +export out +export kitchen kitchen: wasm kitchen/src/*.rs cd kitchen; cargo build @@ -27,15 +32,18 @@ static-prep: web/index.html web/favicon.ico web/static/*.css cp -r web/favicon.ico web/dist/ cp -r web/static web/dist/ -wasmrelease: wasmrelease-dist static-prep +wasmrelease: wasm-opt static-prep + +wasm-opt: wasmrelease-dist + cd web; sh ../scripts/wasm-opt.sh release wasmrelease-dist: web/src/*.rs web/src/components/*.rs - cd web; wasm-pack build --mode no-install --release --target web --no-typescript --out-name kitchen_wasm --out-dir dist/ + cd web; sh ../scripts/wasm-build.sh release wasm: wasm-dist static-prep wasm-dist: web/src/*.rs web/src/components/*.rs - cd web; wasm-pack build --mode no-install --target web --no-typescript --out-dir dist/ --features debug_logs + cd web; sh ../scripts/wasm-build.sh debug clean: rm -rf web/dist/* @@ -50,5 +58,5 @@ sqlx-add-%: sqlx-revert: cd kitchen; cargo sqlx migrate revert --database-url $(sqlite_url) -sqlx-prepare: +sqlx-prepare: kitchen cd kitchen; cargo sqlx prepare --database-url $(sqlite_url) diff --git a/scripts/wasm-build.sh b/scripts/wasm-build.sh new file mode 100644 index 0000000..49e8356 --- /dev/null +++ b/scripts/wasm-build.sh @@ -0,0 +1,9 @@ +set -x +buildtype=$1; + +if [ -eq ${buildtype} = "release" ]; then + builtype_flag="--release" +fi + +cargo build --lib ${buildtype_flag} --target wasm32-unknown-unknown --target-dir $out --features debug_logs +wasm-bindgen $out/wasm32-unknown-unknown/${buildtype}/kitchen_wasm.wasm --out-dir $out --typescript --target web diff --git a/scripts/wasm-opt.sh b/scripts/wasm-opt.sh new file mode 100644 index 0000000..7cfaa52 --- /dev/null +++ b/scripts/wasm-opt.sh @@ -0,0 +1,6 @@ +set -x +buildtype=$1; + +wasm-opt $out/wasm32-unkown-unkown/${buildtype}/${project}_wasm.wasm --out-dir dist/ -0 +rm -f $out/${project}_wasm_bg.wasm +mv $out/${project}_wasm_bg-opt.wasm dist/${project}_wasm_bg.wasm From ec18d9de9795f5a82efb043c126bd8b5b3da7397 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sun, 30 Jun 2024 20:32:57 -0500 Subject: [PATCH 26/28] feat: recipe schema now has a setting for serving count --- kitchen/migrations/20240701002811_recipe-servings.down.sql | 2 ++ kitchen/migrations/20240701002811_recipe-servings.up.sql | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 kitchen/migrations/20240701002811_recipe-servings.down.sql create mode 100644 kitchen/migrations/20240701002811_recipe-servings.up.sql diff --git a/kitchen/migrations/20240701002811_recipe-servings.down.sql b/kitchen/migrations/20240701002811_recipe-servings.down.sql new file mode 100644 index 0000000..eea316a --- /dev/null +++ b/kitchen/migrations/20240701002811_recipe-servings.down.sql @@ -0,0 +1,2 @@ +-- Add down migration script here +ALTER TABLE recipes DROP COLUMN serving_count; diff --git a/kitchen/migrations/20240701002811_recipe-servings.up.sql b/kitchen/migrations/20240701002811_recipe-servings.up.sql new file mode 100644 index 0000000..e1e3920 --- /dev/null +++ b/kitchen/migrations/20240701002811_recipe-servings.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +ALTER TABLE recipes ADD column serving_count number; From 6bc9f2ea2e2253bec9a2548183592ba0ba7a7d45 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sun, 30 Jun 2024 20:53:35 -0500 Subject: [PATCH 27/28] build: unify make and nix builds --- nix/kitchenWasm/default.nix | 6 ++++-- scripts/wasm-build.sh | 4 ++-- scripts/wasm-opt.sh | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/nix/kitchenWasm/default.nix b/nix/kitchenWasm/default.nix index 6d0055f..7d7c00b 100644 --- a/nix/kitchenWasm/default.nix +++ b/nix/kitchenWasm/default.nix @@ -32,8 +32,10 @@ stdenv.mkDerivation { mkdir -p $out cd web cp -r static $out - cargo build --lib --release --target wasm32-unknown-unknown --target-dir $out ${features} --offline - wasm-bindgen $out/wasm32-unknown-unknown/release/kitchen_wasm.wasm --out-dir $out --typescript --target web + sh ../scripts/wasm-build.sh release + #cargo build --lib --release --target wasm32-unknown-unknown --target-dir $out ${features} --offline + #wasm-bindgen $out/wasm32-unknown-unknown/release/kitchen_wasm.wasm --out-dir $out --typescript --target web + #sh ../scripts/wasm-opt.sh release wasm-opt $out/kitchen_wasm_bg.wasm -o $out/kitchen_wasm_bg-opt.wasm -O rm -f $out/kitchen_wasm_bg.wasm mv $out/kitchen_wasm_bg-opt.wasm $out/kitchen_wasm_bg.wasm diff --git a/scripts/wasm-build.sh b/scripts/wasm-build.sh index 49e8356..d92053d 100644 --- a/scripts/wasm-build.sh +++ b/scripts/wasm-build.sh @@ -1,8 +1,8 @@ set -x buildtype=$1; -if [ -eq ${buildtype} = "release" ]; then - builtype_flag="--release" +if [ ${buildtype} = "release" ]; then + buildtype_flag="--release" fi cargo build --lib ${buildtype_flag} --target wasm32-unknown-unknown --target-dir $out --features debug_logs diff --git a/scripts/wasm-opt.sh b/scripts/wasm-opt.sh index 7cfaa52..f983110 100644 --- a/scripts/wasm-opt.sh +++ b/scripts/wasm-opt.sh @@ -1,6 +1,6 @@ set -x buildtype=$1; -wasm-opt $out/wasm32-unkown-unkown/${buildtype}/${project}_wasm.wasm --out-dir dist/ -0 +wasm-opt $out/wasm32-unknown-unkown/${buildtype}/${project}_wasm.wasm --out-dir dist/ -O rm -f $out/${project}_wasm_bg.wasm mv $out/${project}_wasm_bg-opt.wasm dist/${project}_wasm_bg.wasm From 4ffb4816340b2a1950798a5338204fa64b3dc99f Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 1 Jul 2024 14:46:33 -0500 Subject: [PATCH 28/28] docs: cleanup readme --- readme.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index f4d92c9..8edd299 100644 --- a/readme.md +++ b/readme.md @@ -6,10 +6,6 @@ A web assembly experiment in Meal Planning and Shopping List management. Ensure you have rust installed with support for the web assembly target. You can see instructions here: [Rust wasm book](https://rustwasm.github.io/docs/book/game-of-life/setup.html). -You will also want to have trunk installed. You can see instructions for that here: [trunk](https://trunkrs.dev/) - -Then obtain the source. We do not at this time publish kitchen on [crates.io](https://crates.io/). - ```sh git clone https://github.com/zaphar/kitchen cd kitchen @@ -23,7 +19,7 @@ make release # Hacking on kitchen -If you want to hack on kitchen, then you may find it useful to use trunk in dev mode. The run script will run build the app and run trunk with it watching for changes and reloading on demand in your browser. +The run script will run build the app and run it for you. ```sh ./run.sh @@ -37,4 +33,4 @@ If all of the above looks like too much work, and you already use the nix packag ```sh nix run github:zaphar/kitchen -``` \ No newline at end of file +```