mirror of
https://github.com/zaphar/kitchen.git
synced 2025-07-24 19:59:50 -04:00
Compare commits
5 Commits
aba1e114cf
...
c77fa24515
Author | SHA1 | Date | |
---|---|---|---|
c77fa24515 | |||
6a5046d3c0 | |||
263abda17b | |||
df88c2b7bd | |||
8000b7d317 |
11
flake.nix
11
flake.nix
@ -69,8 +69,15 @@
|
||||
root = ./.;
|
||||
});
|
||||
kitchenWasmDebug = kitchenWasmGen {
|
||||
inherit pkgs rust-wasm wasm-bindgen version;
|
||||
features = "--features debug_logs";
|
||||
inherit pkgs rust-wasm wasm-bindgen version cargo-wasm2map;
|
||||
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=";
|
||||
};
|
||||
#features = "--features debug_logs";
|
||||
};
|
||||
kitchenDebug = (kitchenGen {
|
||||
inherit pkgs version naersk-lib rust-wasm;
|
||||
|
@ -14,6 +14,7 @@
|
||||
use async_std::sync::Arc;
|
||||
use std::collections::BTreeSet;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use std::{collections::BTreeMap, path::Path};
|
||||
|
||||
use argon2::{
|
||||
@ -259,6 +260,7 @@ impl SqliteStore {
|
||||
std::fs::create_dir_all(&path)?;
|
||||
let url = format!("sqlite://{}/store.db", path.as_ref().to_string_lossy());
|
||||
let options = SqliteConnectOptions::from_str(&url)?
|
||||
.busy_timeout(Duration::from_secs(5))
|
||||
.journal_mode(SqliteJournalMode::Wal)
|
||||
.create_if_missing(true);
|
||||
info!(?options, "Connecting to sqlite db");
|
||||
|
@ -34,9 +34,7 @@ stdenv.mkDerivation {
|
||||
cp -r static $out
|
||||
export project=kitchen
|
||||
sh ../scripts/wasm-build.sh release
|
||||
sh ../scripts/wasm-opt.sh release
|
||||
sh ../scripts/wasm-sourcemap.sh
|
||||
rm -f $out/kitchen_wasm_bg.wasm
|
||||
cp -r index.html $out
|
||||
cp -r favicon.ico $out
|
||||
rm -rf $out/release
|
||||
|
@ -101,6 +101,7 @@ impl RecipeEntry {
|
||||
pub struct Recipe {
|
||||
pub title: String,
|
||||
pub desc: Option<String>,
|
||||
pub serving_count: Option<i64>,
|
||||
pub steps: Vec<Step>,
|
||||
}
|
||||
|
||||
@ -110,6 +111,7 @@ impl Recipe {
|
||||
title: title.into(),
|
||||
desc: desc.map(|s| s.into()),
|
||||
steps: Vec::new(),
|
||||
serving_count: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,6 +148,16 @@ impl Recipe {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&RecipeEntry> for Recipe {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: &RecipeEntry) -> Result<Self, Self::Error> {
|
||||
let mut parsed = parse::as_recipe(&value.text)?;
|
||||
parsed.serving_count = value.serving_count.clone();
|
||||
Ok(parsed)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IngredientAccumulator {
|
||||
inner: BTreeMap<IngredientKey, (Ingredient, BTreeSet<String>)>,
|
||||
}
|
||||
|
@ -41,10 +41,10 @@ pub struct AppState {
|
||||
pub recipe_categories: BTreeMap<String, String>,
|
||||
pub extras: Vec<(String, String)>,
|
||||
// FIXME(jwall): This should really be storable I think?
|
||||
#[serde(skip_deserializing,skip_serializing)]
|
||||
#[serde(skip_deserializing, skip_serializing)]
|
||||
pub staples: Option<BTreeSet<Ingredient>>,
|
||||
// FIXME(jwall): This should really be storable I think?
|
||||
#[serde(skip_deserializing,skip_serializing)]
|
||||
#[serde(skip_deserializing, skip_serializing)]
|
||||
pub recipes: BTreeMap<String, Recipe>,
|
||||
pub category_map: BTreeMap<String, String>,
|
||||
pub filtered_ingredients: BTreeSet<IngredientKey>,
|
||||
@ -151,7 +151,7 @@ pub fn parse_recipes(
|
||||
Some(parsed) => {
|
||||
let mut parsed_map = BTreeMap::new();
|
||||
for r in parsed {
|
||||
let recipe = match parse::as_recipe(&r.recipe_text()) {
|
||||
let recipe = match r.try_into() {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
error!("Error parsing recipe {}", e);
|
||||
@ -334,20 +334,16 @@ impl MessageMapper<Message, AppState> for StateMachine {
|
||||
}
|
||||
},
|
||||
Message::SaveRecipe(entry, callback) => {
|
||||
let recipe =
|
||||
parse::as_recipe(entry.recipe_text()).expect("Failed to parse RecipeEntry");
|
||||
original_copy
|
||||
.recipes
|
||||
.insert(entry.recipe_id().to_owned(), recipe);
|
||||
let recipe_id = entry.recipe_id().to_owned();
|
||||
let recipe: Recipe = (&entry).try_into().expect("Failed to parse RecipeEntry");
|
||||
original_copy.recipes.insert(recipe_id.clone(), recipe);
|
||||
if !original_copy.recipe_counts.contains_key(entry.recipe_id()) {
|
||||
original_copy
|
||||
.recipe_counts
|
||||
.insert(entry.recipe_id().to_owned(), 0);
|
||||
original_copy.recipe_counts.insert(recipe_id.clone(), 0);
|
||||
}
|
||||
if let Some(cat) = entry.category().cloned() {
|
||||
original_copy
|
||||
.recipe_categories
|
||||
.entry(entry.recipe_id().to_owned())
|
||||
.entry(recipe_id.clone())
|
||||
.and_modify(|c| *c = cat.clone())
|
||||
.or_insert(cat);
|
||||
}
|
||||
@ -524,9 +520,9 @@ impl MessageMapper<Message, AppState> for StateMachine {
|
||||
spawn_local_scoped(cx, {
|
||||
let local_store = self.local_store.clone();
|
||||
async move {
|
||||
local_store.store_app_state(&original_copy).await;
|
||||
original.set(original_copy);
|
||||
}
|
||||
local_store.store_app_state(&original_copy).await;
|
||||
original.set(original_copy);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,15 @@ pub fn Editor<'ctx, G: Html>(cx: Scope<'ctx>, props: RecipeComponentProps<'ctx>)
|
||||
let store = crate::api::HttpStore::get_from_context(cx);
|
||||
let recipe: &Signal<RecipeEntry> =
|
||||
create_signal(cx, RecipeEntry::new(&recipe_id, String::new()));
|
||||
let text = create_signal(cx, String::new());
|
||||
let text = create_signal(cx, String::from("0"));
|
||||
let serving_count_str = create_signal(cx, String::new());
|
||||
let serving_count = create_memo(cx, || {
|
||||
if let Ok(count) = serving_count_str.get().parse::<i64>() {
|
||||
count
|
||||
} else {
|
||||
0
|
||||
}
|
||||
});
|
||||
let error_text = create_signal(cx, String::from("Parse results..."));
|
||||
let aria_hint = create_signal(cx, "false");
|
||||
let category = create_signal(cx, "Entree".to_owned());
|
||||
@ -83,6 +91,10 @@ pub fn Editor<'ctx, G: Html>(cx: Scope<'ctx>, props: RecipeComponentProps<'ctx>)
|
||||
label(for="recipe_category") { "Category" }
|
||||
input(name="recipe_category", bind:value=category, on:change=move |_| dirty.set(true))
|
||||
}
|
||||
div {
|
||||
label(for="serving_count") { "Serving Count" }
|
||||
input(name="serving_count", bind:value=serving_count_str, on:change=move |_| dirty.set(true))
|
||||
}
|
||||
div {
|
||||
div(class="row-flex") {
|
||||
label(for="recipe_text", class="block align-stretch expand-height") { "Recipe: " }
|
||||
@ -119,7 +131,7 @@ pub fn Editor<'ctx, G: Html>(cx: Scope<'ctx>, props: RecipeComponentProps<'ctx>)
|
||||
id: id.get_untracked().as_ref().clone(),
|
||||
text: text.get_untracked().as_ref().clone(),
|
||||
category,
|
||||
serving_count: None,
|
||||
serving_count: Some(*serving_count.get()),
|
||||
};
|
||||
sh.dispatch(cx, Message::SaveRecipe(recipe_entry, None));
|
||||
dirty.set(false);
|
||||
@ -171,18 +183,22 @@ pub fn Viewer<'ctx, G: Html>(cx: Scope<'ctx>, props: RecipeComponentProps<'ctx>)
|
||||
let recipe_signal = sh.get_selector(cx, move |state| {
|
||||
if let Some(recipe) = state.get().recipes.get(&recipe_id) {
|
||||
let title = recipe.title.clone();
|
||||
let serving_count = recipe.serving_count.clone();
|
||||
let desc = recipe.desc.clone().unwrap_or_else(|| String::new());
|
||||
let steps = recipe.steps.clone();
|
||||
Some((title, desc, steps))
|
||||
Some((title, serving_count, desc, steps))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if let Some((title, desc, steps)) = recipe_signal.get().as_ref().clone() {
|
||||
if let Some((title, serving_count, desc, steps)) = recipe_signal.get().as_ref().clone() {
|
||||
debug!("Viewing recipe.");
|
||||
view.set(view! {cx,
|
||||
div(class="recipe") {
|
||||
h1(class="recipe_title") { (title) }
|
||||
div(class="serving_count") {
|
||||
"Serving Count: " (serving_count.map(|v| format!("{}", v)).unwrap_or_else(|| "Unconfigured".to_owned()))
|
||||
}
|
||||
div(class="recipe_description") {
|
||||
(desc)
|
||||
}
|
||||
|
@ -60,8 +60,14 @@ pub fn CategoryGroup<'ctx, G: Html>(
|
||||
iterable=r,
|
||||
view=move |cx, sig| {
|
||||
let title = create_memo(cx, move || sig.get().1.title.clone());
|
||||
let serving_count = create_memo(cx, move || sig.get().1.serving_count.clone());
|
||||
view! {cx,
|
||||
div(class="cell column-flex justify-end align-stretch") { 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,
|
||||
serving_count=serving_count,
|
||||
) }
|
||||
}
|
||||
},
|
||||
key=|sig| sig.get().0.to_owned(),
|
||||
|
@ -23,6 +23,7 @@ use crate::components::NumberField;
|
||||
pub struct RecipeCheckBoxProps<'ctx> {
|
||||
pub i: String,
|
||||
pub title: &'ctx ReadSignal<String>,
|
||||
pub serving_count: &'ctx ReadSignal<Option<i64>>,
|
||||
pub sh: StateHandler<'ctx>,
|
||||
}
|
||||
|
||||
@ -35,7 +36,7 @@ pub fn RecipeSelection<'ctx, G: Html>(
|
||||
cx: Scope<'ctx>,
|
||||
props: RecipeCheckBoxProps<'ctx>,
|
||||
) -> View<G> {
|
||||
let RecipeCheckBoxProps { i, title, sh } = props;
|
||||
let RecipeCheckBoxProps { i, title, sh, serving_count, } = props;
|
||||
let id = Rc::new(i);
|
||||
let id_for_count = id.clone();
|
||||
// NOTE(jwall): The below get's a little tricky. We need a separate signal to bind for the
|
||||
@ -66,6 +67,9 @@ pub fn RecipeSelection<'ctx, G: Html>(
|
||||
let for_id = name.clone();
|
||||
view! {cx,
|
||||
label(for=for_id, class="flex-item-grow") { a(href=href) { (*title) } }
|
||||
div {
|
||||
"Serves: " (serving_count.get().map(|v| v.to_string()).unwrap_or("Unconfigured".to_owned()))
|
||||
}
|
||||
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 u32));
|
||||
|
Loading…
x
Reference in New Issue
Block a user