diff --git a/recipes/src/parse.rs b/recipes/src/parse.rs index 909e7ef..b8253fd 100644 --- a/recipes/src/parse.rs +++ b/recipes/src/parse.rs @@ -31,6 +31,7 @@ fn format_err(err: Error) -> String { let msg = err.get_msg(); let context = err.get_context(); let (line, column) = (context.line(), context.column()); + // TODO(jwall): It would be nice if we can display out the context line as well here. format!("{} at line {} column {}", msg, line, column) } diff --git a/web/src/components/add_recipe.rs b/web/src/components/add_recipe.rs new file mode 100644 index 0000000..fc49fb7 --- /dev/null +++ b/web/src/components/add_recipe.rs @@ -0,0 +1,92 @@ +// Copyright 2022 Jeremy Wall (Jeremy@marzhilsltudios.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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::{futures::spawn_local_scoped, prelude::*}; +use tracing::{error, info}; + +use recipes::RecipeEntry; + +const STARTER_RECIPE: &'static str = "title: TITLE_PLACEHOLDER + +Description here. + +step: + +1 ingredient + +Instructions here +"; + +#[component] +pub fn AddRecipe(cx: Scope) -> View { + let recipe_title = create_signal(cx, String::new()); + let create_recipe_signal = create_signal(cx, ()); + let dirty = create_signal(cx, false); + + let entry = create_memo(cx, || { + RecipeEntry( + recipe_title + .get() + .as_ref() + .to_lowercase() + .replace(" ", "_") + .replace("\n", ""), + STARTER_RECIPE + .replace("TITLE_PLACEHOLDER", recipe_title.get().as_str()) + .replace("\r", ""), + ) + }); + + create_effect(cx, move || { + create_recipe_signal.track(); + if !*dirty.get_untracked() { + return; + } + spawn_local_scoped(cx, { + let store = crate::api::HttpStore::get_from_context(cx); + async move { + let entry = entry.get_untracked(); + // TODO(jwall): Better error reporting here. + match store.get_recipe_text(entry.recipe_id()).await { + Ok(Some(_)) => { + // TODO(jwall): We should tell the user that this id already exists + info!(recipe_id = entry.recipe_id(), "Recipe already exists"); + return; + } + Ok(None) => { + // noop + } + Err(err) => { + // TODO(jwall): We should tell the user that this is failing + error!(?err) + } + } + store + .save_recipes(vec![entry.as_ref().clone()]) + .await + .expect("Unable to save New Recipe"); + crate::js_lib::navigate_to_path(&format!("/ui/recipe/{}", entry.recipe_id())) + .expect("Unable to navigate to recipe"); + } + }); + }); + view! {cx, + label(for="recipe_title") { "Recipe Title" } + input(bind:value=recipe_title, type="text", name="recipe_title", id="recipe_title", on:change=move |_| { + dirty.set(true); + }) + button(on:click=move |_| { + create_recipe_signal.trigger_subscribers(); + }) { "Create" } + } +} diff --git a/web/src/components/mod.rs b/web/src/components/mod.rs index dd4b51b..b867c53 100644 --- a/web/src/components/mod.rs +++ b/web/src/components/mod.rs @@ -11,6 +11,7 @@ // 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. +pub mod add_recipe; pub mod categories; pub mod header; pub mod recipe; @@ -20,6 +21,7 @@ pub mod recipe_selector; pub mod shopping_list; pub mod tabs; +pub use add_recipe::*; pub use categories::*; pub use header::*; pub use recipe::*; diff --git a/web/src/pages/manage/add_recipe.rs b/web/src/pages/manage/add_recipe.rs index 8949189..7f68088 100644 --- a/web/src/pages/manage/add_recipe.rs +++ b/web/src/pages/manage/add_recipe.rs @@ -11,82 +11,16 @@ // 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::{futures::spawn_local_scoped, prelude::*}; -use tracing::{error, info}; +use super::ManagePage; +use crate::components::add_recipe::AddRecipe; -use recipes::RecipeEntry; - -const STARTER_RECIPE: &'static str = "title: TITLE_PLACEHOLDER - -Description here. - -step: - -1 ingredient - -Instructions here -"; +use sycamore::prelude::*; #[component] pub fn AddRecipePage(cx: Scope) -> View { - let recipe_title = create_signal(cx, String::new()); - let create_recipe_signal = create_signal(cx, ()); - let dirty = create_signal(cx, false); - - let entry = create_memo(cx, || { - RecipeEntry( - recipe_title - .get() - .as_ref() - .to_lowercase() - .replace(" ", "_") - .replace("\n", ""), - STARTER_RECIPE - .replace("TITLE_PLACEHOLDER", recipe_title.get().as_str()) - .replace("\r", ""), - ) - }); - - create_effect(cx, move || { - create_recipe_signal.track(); - if !*dirty.get_untracked() { - return; - } - spawn_local_scoped(cx, { - let store = crate::api::HttpStore::get_from_context(cx); - async move { - let entry = entry.get_untracked(); - // TODO(jwall): Better error reporting here. - match store.get_recipe_text(entry.recipe_id()).await { - Ok(Some(_)) => { - // TODO(jwall): We should tell the user that this id already exists - info!(recipe_id = entry.recipe_id(), "Recipe already exists"); - return; - } - Ok(None) => { - // noop - } - Err(err) => { - // TODO(jwall): We should tell the user that this is failing - error!(?err) - } - } - store - .save_recipes(vec![entry.as_ref().clone()]) - .await - .expect("Unable to save New Recipe"); - crate::js_lib::navigate_to_path(&format!("/ui/recipe/{}", entry.recipe_id())) - .expect("Unable to navigate to recipe"); - } - }); - }); view! {cx, - label(for="recipe_title") { "Recipe Title" } - input(bind:value=recipe_title, type="text", name="recipe_title", id="recipe_title", on:change=move |_| { - dirty.set(true); - }) - button(on:click=move |_| { - create_recipe_signal.trigger_subscribers(); - }) { "Create" } + ManagePage( + selected=Some("New Recipe".to_owned()), + ) { AddRecipe() } } } diff --git a/web/src/pages/manage/categories.rs b/web/src/pages/manage/categories.rs index c95bd37..1ebbc83 100644 --- a/web/src/pages/manage/categories.rs +++ b/web/src/pages/manage/categories.rs @@ -15,9 +15,7 @@ use super::ManagePage; use crate::components::categories::*; use sycamore::prelude::*; -use tracing::instrument; -#[instrument] #[component()] pub fn CategoryPage(cx: Scope) -> View { view! {cx,