mirror of
https://github.com/zaphar/kitchen.git
synced 2025-07-22 19:40:14 -04:00
Use tracing in our webassembly as well.
This commit is contained in:
parent
cf38056104
commit
bf97f1ed29
13
Cargo.lock
generated
13
Cargo.lock
generated
@ -789,6 +789,8 @@ dependencies = [
|
|||||||
"reqwasm",
|
"reqwasm",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sycamore",
|
"sycamore",
|
||||||
|
"tracing",
|
||||||
|
"tracing-wasm",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
@ -1624,6 +1626,17 @@ dependencies = [
|
|||||||
"tracing-log",
|
"tracing-log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-wasm"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07"
|
||||||
|
dependencies = [
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "try-lock"
|
name = "try-lock"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
@ -18,6 +18,8 @@ reqwasm = "0.5.0"
|
|||||||
# This makes debugging panics more tractable.
|
# This makes debugging panics more tractable.
|
||||||
console_error_panic_hook = "0.1.7"
|
console_error_panic_hook = "0.1.7"
|
||||||
serde_json = "1.0.79"
|
serde_json = "1.0.79"
|
||||||
|
tracing = "0.1.35"
|
||||||
|
tracing-wasm = "0.2.1"
|
||||||
|
|
||||||
[dependencies.wasm-bindgen]
|
[dependencies.wasm-bindgen]
|
||||||
# we need wasm-bindgen v0.2.78 exactly
|
# we need wasm-bindgen v0.2.78 exactly
|
||||||
|
@ -11,11 +11,12 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use crate::console_log;
|
|
||||||
use crate::{components::Recipe, service::AppService};
|
use crate::{components::Recipe, service::AppService};
|
||||||
|
|
||||||
use sycamore::{context::use_context, prelude::*};
|
use sycamore::{context::use_context, prelude::*};
|
||||||
|
use tracing::{debug, instrument};
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
#[component(RecipeList<G>)]
|
#[component(RecipeList<G>)]
|
||||||
pub fn recipe_list() -> View<G> {
|
pub fn recipe_list() -> View<G> {
|
||||||
let app_service = use_context::<AppService>();
|
let app_service = use_context::<AppService>();
|
||||||
@ -26,7 +27,7 @@ pub fn recipe_list() -> View<G> {
|
|||||||
Indexed(IndexedProps{
|
Indexed(IndexedProps{
|
||||||
iterable: menu_list,
|
iterable: menu_list,
|
||||||
template: |(idx, _count)| {
|
template: |(idx, _count)| {
|
||||||
console_log!("Rendering recipe index: {}", idx);
|
debug!(idx=%idx, "Rendering recipe");
|
||||||
let idx = Signal::new(idx);
|
let idx = Signal::new(idx);
|
||||||
view ! {
|
view ! {
|
||||||
Recipe(idx.handle())
|
Recipe(idx.handle())
|
||||||
|
@ -11,17 +11,22 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use crate::console_log;
|
|
||||||
use crate::service::AppService;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use sycamore::{context::use_context, prelude::*};
|
use sycamore::{context::use_context, prelude::*};
|
||||||
|
use tracing::{debug, instrument};
|
||||||
|
|
||||||
|
use crate::service::AppService;
|
||||||
|
|
||||||
pub struct RecipeCheckBoxProps {
|
pub struct RecipeCheckBoxProps {
|
||||||
pub i: usize,
|
pub i: usize,
|
||||||
pub title: ReadSignal<String>,
|
pub title: ReadSignal<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(props), fields(
|
||||||
|
idx=%props.i,
|
||||||
|
title=%props.title.get()
|
||||||
|
))]
|
||||||
#[component(RecipeSelection<G>)]
|
#[component(RecipeSelection<G>)]
|
||||||
pub fn recipe_selection(props: RecipeCheckBoxProps) -> View<G> {
|
pub fn recipe_selection(props: RecipeCheckBoxProps) -> View<G> {
|
||||||
let app_service = use_context::<AppService>();
|
let app_service = use_context::<AppService>();
|
||||||
@ -36,7 +41,7 @@ pub fn recipe_selection(props: RecipeCheckBoxProps) -> View<G> {
|
|||||||
label(for=id_cloned_2) { (props.title.get()) }
|
label(for=id_cloned_2) { (props.title.get()) }
|
||||||
input(type="number", class="item-count-sel", min="0", bind:value=count.clone(), name=format!("recipe_id:{}", i), value=id_as_str.clone(), on:change=move |_| {
|
input(type="number", class="item-count-sel", min="0", bind:value=count.clone(), name=format!("recipe_id:{}", i), value=id_as_str.clone(), on:change=move |_| {
|
||||||
let mut app_service = app_service.clone();
|
let mut app_service = app_service.clone();
|
||||||
console_log!("setting recipe id: {} to count: {}", i, *count.get());
|
debug!(idx=%i, count=*count.get(), "setting recipe count");
|
||||||
app_service.set_recipe_count_by_index(i, count.get().parse().unwrap());
|
app_service.set_recipe_count_by_index(i, count.get().parse().unwrap());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,12 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use crate::console_error;
|
|
||||||
use crate::{components::recipe_selection::*, service::AppService};
|
use crate::{components::recipe_selection::*, service::AppService};
|
||||||
|
|
||||||
use sycamore::{context::use_context, futures::spawn_local_in_scope, prelude::*};
|
use sycamore::{context::use_context, futures::spawn_local_in_scope, prelude::*};
|
||||||
|
use tracing::{error, instrument};
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
#[component(RecipeSelector<G>)]
|
#[component(RecipeSelector<G>)]
|
||||||
pub fn recipe_selector() -> View<G> {
|
pub fn recipe_selector() -> View<G> {
|
||||||
let app_service = use_context::<AppService>();
|
let app_service = use_context::<AppService>();
|
||||||
@ -32,8 +33,8 @@ pub fn recipe_selector() -> View<G> {
|
|||||||
spawn_local_in_scope(cloned!((app_service) => {
|
spawn_local_in_scope(cloned!((app_service) => {
|
||||||
let mut app_service = app_service.clone();
|
let mut app_service = app_service.clone();
|
||||||
async move {
|
async move {
|
||||||
if let Err(e) = app_service.refresh().await {
|
if let Err(err) = app_service.refresh().await {
|
||||||
console_error!("{}", e);
|
error!(?err);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -14,9 +14,10 @@
|
|||||||
use crate::service::AppService;
|
use crate::service::AppService;
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
use crate::console_debug;
|
|
||||||
use sycamore::{context::use_context, prelude::*};
|
use sycamore::{context::use_context, prelude::*};
|
||||||
|
use tracing::{debug, instrument};
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
#[component(ShoppingList<G>)]
|
#[component(ShoppingList<G>)]
|
||||||
pub fn shopping_list() -> View<G> {
|
pub fn shopping_list() -> View<G> {
|
||||||
let app_service = use_context::<AppService>();
|
let app_service = use_context::<AppService>();
|
||||||
@ -27,7 +28,7 @@ pub fn shopping_list() -> View<G> {
|
|||||||
create_effect(cloned!((app_service, ingredients_map) => move || {
|
create_effect(cloned!((app_service, ingredients_map) => move || {
|
||||||
ingredients_map.set(app_service.get_shopping_list());
|
ingredients_map.set(app_service.get_shopping_list());
|
||||||
}));
|
}));
|
||||||
console_debug!("Ingredients map: {:?}", ingredients_map.get_untracked());
|
debug!(ingredients_map=?ingredients_map.get_untracked());
|
||||||
let ingredients = create_memo(cloned!((ingredients_map, filtered_keys) => move || {
|
let ingredients = create_memo(cloned!((ingredients_map, filtered_keys) => move || {
|
||||||
let mut ingredients = Vec::new();
|
let mut ingredients = Vec::new();
|
||||||
// This has the effect of sorting the ingredients by category
|
// This has the effect of sorting the ingredients by category
|
||||||
@ -40,7 +41,7 @@ pub fn shopping_list() -> View<G> {
|
|||||||
}
|
}
|
||||||
ingredients
|
ingredients
|
||||||
}));
|
}));
|
||||||
console_debug!("Ingredients: {:?}", ingredients.get_untracked());
|
debug!(ingredients = ?ingredients.get_untracked());
|
||||||
let table_view = Signal::new(View::empty());
|
let table_view = Signal::new(View::empty());
|
||||||
create_effect(
|
create_effect(
|
||||||
cloned!((table_view, ingredients, filtered_keys, modified_amts, extras) => move || {
|
cloned!((table_view, ingredients, filtered_keys, modified_amts, extras) => move || {
|
||||||
|
@ -16,19 +16,20 @@ mod components;
|
|||||||
mod pages;
|
mod pages;
|
||||||
mod router_integration;
|
mod router_integration;
|
||||||
mod service;
|
mod service;
|
||||||
mod typings;
|
|
||||||
mod web;
|
mod web;
|
||||||
|
|
||||||
use sycamore::prelude::*;
|
use sycamore::prelude::*;
|
||||||
|
#[cfg(feature = "web")]
|
||||||
|
use tracing_wasm;
|
||||||
use wasm_bindgen::prelude::wasm_bindgen;
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
|
|
||||||
use web::UI;
|
use web::UI;
|
||||||
|
|
||||||
#[wasm_bindgen(start)]
|
#[wasm_bindgen(start)]
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
#[cfg(debug_assertions)]
|
if cfg!(feature = "web") {
|
||||||
{
|
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
|
tracing_wasm::set_as_global_default();
|
||||||
}
|
}
|
||||||
sycamore::render(|| view! { UI() });
|
sycamore::render(|| view! { UI() });
|
||||||
}
|
}
|
||||||
|
@ -16,16 +16,15 @@ use std::rc::Rc;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use sycamore::prelude::*;
|
use sycamore::prelude::*;
|
||||||
|
use tracing::{debug, error, instrument};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
use web_sys::Event;
|
use web_sys::Event;
|
||||||
use web_sys::{Element, HtmlAnchorElement};
|
use web_sys::{Element, HtmlAnchorElement};
|
||||||
|
|
||||||
use crate::app_state::AppRoutes;
|
use crate::app_state::AppRoutes;
|
||||||
use crate::console_debug;
|
|
||||||
use crate::console_error;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct BrowserIntegration(Signal<(String, String, String)>);
|
pub struct BrowserIntegration(Signal<(String, String, String)>);
|
||||||
|
|
||||||
impl BrowserIntegration {
|
impl BrowserIntegration {
|
||||||
@ -38,6 +37,7 @@ impl BrowserIntegration {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self))]
|
||||||
pub fn click_handler(&self) -> Box<dyn Fn(web_sys::Event)> {
|
pub fn click_handler(&self) -> Box<dyn Fn(web_sys::Event)> {
|
||||||
let route_signal = self.0.clone();
|
let route_signal = self.0.clone();
|
||||||
Box::new(move |ev| {
|
Box::new(move |ev| {
|
||||||
@ -49,12 +49,12 @@ impl BrowserIntegration {
|
|||||||
.unwrap_throw()
|
.unwrap_throw()
|
||||||
.map(|e| e.unchecked_into::<HtmlAnchorElement>())
|
.map(|e| e.unchecked_into::<HtmlAnchorElement>())
|
||||||
{
|
{
|
||||||
console_debug!("handling navigation event.");
|
debug!("handling navigation event.");
|
||||||
let location = web_sys::window().unwrap_throw().location();
|
let location = web_sys::window().unwrap_throw().location();
|
||||||
|
|
||||||
if tgt.rel() == "external" {
|
if tgt.rel() == "external" {
|
||||||
|
debug!("External Link so ignoring.");
|
||||||
return;
|
return;
|
||||||
console_debug!("External Link so ignoring.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let origin = tgt.origin();
|
let origin = tgt.origin();
|
||||||
@ -67,12 +67,12 @@ impl BrowserIntegration {
|
|||||||
}
|
}
|
||||||
(true, _, false) // different hash
|
(true, _, false) // different hash
|
||||||
| (true, false, _) /* different path */ => {
|
| (true, false, _) /* different path */ => {
|
||||||
console_debug!("different path or hash");
|
debug!("different path or hash");
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
// Signal the pathname change
|
// Signal the pathname change
|
||||||
let path = format!("{}{}{}", &origin, &tgt_pathname, &hash);
|
let path = format!("{}{}{}", &origin, &tgt_pathname, &hash);
|
||||||
console_debug!("new route: ({}, {}, {})", origin, tgt_pathname, hash);
|
debug!("new route: ({}, {}, {})", origin, tgt_pathname, hash);
|
||||||
console_debug!("new path: ({})", &path);
|
debug!("new path: ({})", &path);
|
||||||
route_signal.set((origin, tgt_pathname, hash));
|
route_signal.set((origin, tgt_pathname, hash));
|
||||||
// Update History API.
|
// Update History API.
|
||||||
let window = web_sys::window().unwrap_throw();
|
let window = web_sys::window().unwrap_throw();
|
||||||
@ -88,6 +88,7 @@ impl BrowserIntegration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct RouterProps<R, F, G>
|
pub struct RouterProps<R, F, G>
|
||||||
where
|
where
|
||||||
G: GenericNode,
|
G: GenericNode,
|
||||||
@ -99,13 +100,18 @@ where
|
|||||||
pub browser_integration: BrowserIntegration,
|
pub browser_integration: BrowserIntegration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(fields(?props.route,
|
||||||
|
origin=props.browser_integration.0.get().0,
|
||||||
|
pathn=props.browser_integration.0.get().1,
|
||||||
|
hash=props.browser_integration.0.get().2),
|
||||||
|
skip(props))]
|
||||||
#[component(Router<G>)]
|
#[component(Router<G>)]
|
||||||
pub fn router<R, F>(props: RouterProps<R, F, G>) -> View<G>
|
pub fn router<R, F>(props: RouterProps<R, F, G>) -> View<G>
|
||||||
where
|
where
|
||||||
R: DeriveRoute + NotFound + Clone + Default + Debug + 'static,
|
R: DeriveRoute + NotFound + Clone + Default + Debug + 'static,
|
||||||
F: Fn(ReadSignal<R>) -> View<G> + 'static,
|
F: Fn(ReadSignal<R>) -> View<G> + 'static,
|
||||||
{
|
{
|
||||||
console_debug!("Setting up router");
|
debug!("Setting up router");
|
||||||
let integration = Rc::new(props.browser_integration);
|
let integration = Rc::new(props.browser_integration);
|
||||||
let route_select = Rc::new(props.route_select);
|
let route_select = Rc::new(props.route_select);
|
||||||
|
|
||||||
@ -113,10 +119,10 @@ where
|
|||||||
create_effect(
|
create_effect(
|
||||||
cloned!((view_signal, integration, route_select) => move || {
|
cloned!((view_signal, integration, route_select) => move || {
|
||||||
let path_signal = integration.0.clone();
|
let path_signal = integration.0.clone();
|
||||||
console_debug!("new path: {:?}", path_signal.get());
|
debug!("new path: {:?}", path_signal.get());
|
||||||
let path = path_signal.clone();
|
let path = path_signal.clone();
|
||||||
let route = R::from(path.get().as_ref());
|
let route = R::from(path.get().as_ref());
|
||||||
console_debug!("new route: {:?}", &route);
|
debug!("new route: {:?}", &route);
|
||||||
// TODO(jwall): this is an unnecessary use of signal.
|
// TODO(jwall): this is an unnecessary use of signal.
|
||||||
let view = route_select.as_ref()(Signal::new(route).handle());
|
let view = route_select.as_ref()(Signal::new(route).handle());
|
||||||
register_click_handler(&view, integration.clone());
|
register_click_handler(&view, integration.clone());
|
||||||
@ -131,22 +137,23 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
fn register_click_handler<G>(view: &View<G>, integration: Rc<BrowserIntegration>)
|
fn register_click_handler<G>(view: &View<G>, integration: Rc<BrowserIntegration>)
|
||||||
where
|
where
|
||||||
G: GenericNode<EventType = Event>,
|
G: GenericNode<EventType = Event>,
|
||||||
{
|
{
|
||||||
console_debug!("Registring click handler on node(s)");
|
debug!("Registring click handler on node(s)");
|
||||||
if let Some(node) = view.as_node() {
|
if let Some(node) = view.as_node() {
|
||||||
node.event("click", integration.click_handler());
|
node.event("click", integration.click_handler());
|
||||||
} else if let Some(frag) = view.as_fragment() {
|
} else if let Some(frag) = view.as_fragment() {
|
||||||
console_debug!("Fragment? {:?}", frag);
|
debug!(fragment=?frag);
|
||||||
for n in frag {
|
for n in frag {
|
||||||
register_click_handler(n, integration.clone());
|
register_click_handler(n, integration.clone());
|
||||||
}
|
}
|
||||||
} else if let Some(dyn_node) = view.as_dyn() {
|
} else if let Some(dyn_node) = view.as_dyn() {
|
||||||
console_debug!("Dynamic node? {:?}", dyn_node);
|
debug!(dynamic_node=?dyn_node);
|
||||||
} else {
|
} else {
|
||||||
console_debug!("Unknown node? {:?}", view);
|
debug!(node=?view, "Unknown node");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,8 +172,9 @@ pub trait DeriveRoute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DeriveRoute for AppRoutes {
|
impl DeriveRoute for AppRoutes {
|
||||||
|
#[instrument]
|
||||||
fn from(input: &(String, String, String)) -> AppRoutes {
|
fn from(input: &(String, String, String)) -> AppRoutes {
|
||||||
console_debug!("routing: {input:?}");
|
debug!("routing: {input:?}");
|
||||||
match input.2.as_str() {
|
match input.2.as_str() {
|
||||||
"" => AppRoutes::default(),
|
"" => AppRoutes::default(),
|
||||||
"#plan" => AppRoutes::Plan,
|
"#plan" => AppRoutes::Plan,
|
||||||
@ -183,7 +191,7 @@ impl DeriveRoute for AppRoutes {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console_error!("Path not found: [{:?}]", input);
|
error!("Path not found: [{:?}]", input);
|
||||||
AppRoutes::NotFound
|
AppRoutes::NotFound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
use crate::{console_debug, console_error, console_log};
|
|
||||||
|
|
||||||
use reqwasm::http;
|
use reqwasm::http;
|
||||||
use sycamore::prelude::*;
|
use sycamore::prelude::*;
|
||||||
|
use tracing::{debug, error, info, instrument};
|
||||||
use web_sys::{window, Storage};
|
use web_sys::{window, Storage};
|
||||||
|
|
||||||
use recipes::{parse, Ingredient, IngredientAccumulator, Recipe};
|
use recipes::{parse, Ingredient, IngredientAccumulator, Recipe};
|
||||||
@ -46,6 +45,7 @@ impl AppService {
|
|||||||
.map_err(|e| format!("{:?}", e))
|
.map_err(|e| format!("{:?}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
async fn fetch_recipes_http() -> Result<String, String> {
|
async fn fetch_recipes_http() -> Result<String, String> {
|
||||||
let resp = match http::Request::get("/api/v1/recipes").send().await {
|
let resp = match http::Request::get("/api/v1/recipes").send().await {
|
||||||
Ok(resp) => resp,
|
Ok(resp) => resp,
|
||||||
@ -54,52 +54,55 @@ impl AppService {
|
|||||||
if resp.status() != 200 {
|
if resp.status() != 200 {
|
||||||
return Err(format!("Status: {}", resp.status()));
|
return Err(format!("Status: {}", resp.status()));
|
||||||
} else {
|
} else {
|
||||||
console_debug!("We got a valid response back!");
|
debug!("We got a valid response back!");
|
||||||
return Ok(resp.text().await.map_err(|e| format!("{}", e))?);
|
return Ok(resp.text().await.map_err(|e| format!("{}", e))?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
async fn fetch_categories_http() -> Result<Option<String>, String> {
|
async fn fetch_categories_http() -> Result<Option<String>, String> {
|
||||||
let resp = match http::Request::get("/api/v1/categories").send().await {
|
let resp = match http::Request::get("/api/v1/categories").send().await {
|
||||||
Ok(resp) => resp,
|
Ok(resp) => resp,
|
||||||
Err(e) => return Err(format!("Error: {}", e)),
|
Err(e) => return Err(format!("Error: {}", e)),
|
||||||
};
|
};
|
||||||
if resp.status() == 404 {
|
if resp.status() == 404 {
|
||||||
console_debug!("Categories returned 404");
|
debug!("Categories returned 404");
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
} else if resp.status() != 200 {
|
} else if resp.status() != 200 {
|
||||||
return Err(format!("Status: {}", resp.status()));
|
return Err(format!("Status: {}", resp.status()));
|
||||||
} else {
|
} else {
|
||||||
console_debug!("We got a valid response back!");
|
debug!("We got a valid response back!");
|
||||||
return Ok(Some(resp.text().await.map_err(|e| format!("{}", e))?));
|
return Ok(Some(resp.text().await.map_err(|e| format!("{}", e))?));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
async fn synchronize() -> Result<(), String> {
|
async fn synchronize() -> Result<(), String> {
|
||||||
console_log!("Synchronizing Recipes");
|
info!("Synchronizing Recipes");
|
||||||
let storage = Self::get_storage()?.unwrap();
|
let storage = Self::get_storage()?.unwrap();
|
||||||
let recipes = Self::fetch_recipes_http().await?;
|
let recipes = Self::fetch_recipes_http().await?;
|
||||||
storage
|
storage
|
||||||
.set_item("recipes", &recipes)
|
.set_item("recipes", &recipes)
|
||||||
.map_err(|e| format!("{:?}", e))?;
|
.map_err(|e| format!("{:?}", e))?;
|
||||||
console_log!("Synchronizing categories");
|
info!("Synchronizing categories");
|
||||||
match Self::fetch_categories_http().await {
|
match Self::fetch_categories_http().await {
|
||||||
Ok(Some(categories_content)) => {
|
Ok(Some(categories_content)) => {
|
||||||
console_debug!("categories: {}", categories_content);
|
debug!(categories=?categories_content);
|
||||||
storage
|
storage
|
||||||
.set_item("categories", &categories_content)
|
.set_item("categories", &categories_content)
|
||||||
.map_err(|e| format!("{:?}", e))?;
|
.map_err(|e| format!("{:?}", e))?;
|
||||||
}
|
}
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
console_error!("There is no category file");
|
error!("There is no category file");
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
console_error!("{}", e);
|
error!("{}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
pub fn fetch_categories_from_storage() -> Result<Option<BTreeMap<String, String>>, String> {
|
pub fn fetch_categories_from_storage() -> Result<Option<BTreeMap<String, String>>, String> {
|
||||||
let storage = Self::get_storage()?.unwrap();
|
let storage = Self::get_storage()?.unwrap();
|
||||||
match storage
|
match storage
|
||||||
@ -111,7 +114,7 @@ impl AppService {
|
|||||||
match parse::as_categories(&parsed) {
|
match parse::as_categories(&parsed) {
|
||||||
Ok(categories) => Ok(Some(categories)),
|
Ok(categories) => Ok(Some(categories)),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
console_debug!("Error parsing categories {}", e);
|
debug!("Error parsing categories {}", e);
|
||||||
Err(format!("Error parsing categories {}", e))
|
Err(format!("Error parsing categories {}", e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,6 +123,7 @@ impl AppService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
pub fn fetch_recipes_from_storage(
|
pub fn fetch_recipes_from_storage(
|
||||||
) -> Result<(Option<Recipe>, Option<Vec<(usize, Recipe)>>), String> {
|
) -> Result<(Option<Recipe>, Option<Vec<(usize, Recipe)>>), String> {
|
||||||
let storage = Self::get_storage()?.unwrap();
|
let storage = Self::get_storage()?.unwrap();
|
||||||
@ -136,11 +140,10 @@ impl AppService {
|
|||||||
let recipe = match parse::as_recipe(&r) {
|
let recipe = match parse::as_recipe(&r) {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
console_error!("Error parsing recipe {}", e);
|
error!("Error parsing recipe {}", e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//console_debug!("We parsed a recipe {}", recipe.title);
|
|
||||||
if recipe.title == "Staples" {
|
if recipe.title == "Staples" {
|
||||||
staples = Some(recipe);
|
staples = Some(recipe);
|
||||||
} else {
|
} else {
|
||||||
@ -161,14 +164,15 @@ impl AppService {
|
|||||||
Ok(Self::fetch_categories_from_storage()?)
|
Ok(Self::fetch_categories_from_storage()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self))]
|
||||||
pub async fn refresh(&mut self) -> Result<(), String> {
|
pub async fn refresh(&mut self) -> Result<(), String> {
|
||||||
Self::synchronize().await?;
|
Self::synchronize().await?;
|
||||||
console_debug!("refreshing recipes");
|
debug!("refreshing recipes");
|
||||||
if let (staples, Some(r)) = Self::fetch_recipes().await? {
|
if let (staples, Some(r)) = Self::fetch_recipes().await? {
|
||||||
self.set_recipes(r);
|
self.set_recipes(r);
|
||||||
self.staples.set(staples);
|
self.staples.set(staples);
|
||||||
}
|
}
|
||||||
console_debug!("refreshing categories");
|
debug!("refreshing categories");
|
||||||
if let Some(categories) = Self::fetch_categories().await? {
|
if let Some(categories) = Self::fetch_categories().await? {
|
||||||
self.set_categories(categories);
|
self.set_categories(categories);
|
||||||
}
|
}
|
||||||
@ -179,6 +183,7 @@ impl AppService {
|
|||||||
self.recipes.get().get(idx).map(|(_, r)| r.clone())
|
self.recipes.get().get(idx).map(|(_, r)| r.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self))]
|
||||||
pub fn get_shopping_list(&self) -> BTreeMap<String, Vec<(Ingredient, BTreeSet<String>)>> {
|
pub fn get_shopping_list(&self) -> BTreeMap<String, Vec<(Ingredient, BTreeSet<String>)>> {
|
||||||
let mut acc = IngredientAccumulator::new();
|
let mut acc = IngredientAccumulator::new();
|
||||||
let recipe_counts = self.menu_list.get();
|
let recipe_counts = self.menu_list.get();
|
||||||
@ -205,7 +210,7 @@ impl AppService {
|
|||||||
.or_insert(vec![])
|
.or_insert(vec![])
|
||||||
.push((i.clone(), recipes.clone()));
|
.push((i.clone(), recipes.clone()));
|
||||||
}
|
}
|
||||||
console_debug!("Category map {:?}", self.category_map);
|
debug!(?self.category_map);
|
||||||
// FIXM(jwall): Sort by categories and names.
|
// FIXM(jwall): Sort by categories and names.
|
||||||
groups
|
groups
|
||||||
}
|
}
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
// Copyright 2022 Jeremy Wall
|
|
||||||
//
|
|
||||||
// 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 wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
extern "C" {
|
|
||||||
// Use `js_namespace` here to bind `console.log(..)` instead of just
|
|
||||||
// `log(..)`
|
|
||||||
#[wasm_bindgen(js_namespace = console)]
|
|
||||||
pub fn log(s: &str);
|
|
||||||
#[wasm_bindgen(js_namespace = console)]
|
|
||||||
pub fn debug(s: &str);
|
|
||||||
#[wasm_bindgen(js_namespace = console)]
|
|
||||||
pub fn warn(s: &str);
|
|
||||||
#[wasm_bindgen(js_namespace = console)]
|
|
||||||
pub fn error(s: &str);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! console_log {
|
|
||||||
// Note that this is using the `log` function imported above during
|
|
||||||
// `bare_bones`
|
|
||||||
($($t:tt)*) => {
|
|
||||||
if cfg!(feature="web") {
|
|
||||||
use crate::typings::log;
|
|
||||||
log(&format_args!($($t)*).to_string());
|
|
||||||
} else if cfg!(feature="ssr") {
|
|
||||||
println!($($t)*);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! console_debug {
|
|
||||||
// Note that this is using the `log` function imported above during
|
|
||||||
// `bare_bones`
|
|
||||||
($($t:tt)*) => {{
|
|
||||||
if cfg!(feature="web") {
|
|
||||||
use crate::typings::debug;
|
|
||||||
debug(&format_args!($($t)*).to_string());
|
|
||||||
} else if cfg!(feature="ssr") {
|
|
||||||
print!("DEBUG: ");
|
|
||||||
println!($($t)*);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! console_error {
|
|
||||||
// Note that this is using the `error` function imported above during
|
|
||||||
// `bare_bones`
|
|
||||||
($($t:tt)*) => {{
|
|
||||||
if cfg!(feature="web")
|
|
||||||
{
|
|
||||||
use crate::typings::error;
|
|
||||||
error(&format_args!($($t)*).to_string());
|
|
||||||
} else if cfg!(feature="ssr") {
|
|
||||||
print!("ERROR: ");
|
|
||||||
println!($($t)*);
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! console_warn {
|
|
||||||
// Note that this is using the `warn` function imported above during
|
|
||||||
// `bare_bones`
|
|
||||||
($($t:tt)*) => {{
|
|
||||||
if cfg!("web") {
|
|
||||||
use crate::typings::warn;
|
|
||||||
(warn(&format_args!($($t)*).to_string()))
|
|
||||||
} else if cfg!(feature="ssr") {
|
|
||||||
print!("WARN: ");
|
|
||||||
(println!($($t)*))
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
}
|
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use crate::pages::*;
|
use crate::pages::*;
|
||||||
use crate::{app_state::*, components::*, router_integration::*, service::AppService};
|
use crate::{app_state::*, components::*, router_integration::*, service::AppService};
|
||||||
use crate::{console_error, console_log};
|
use tracing::{error, info, instrument};
|
||||||
|
|
||||||
use sycamore::{
|
use sycamore::{
|
||||||
context::{ContextProvider, ContextProviderProps},
|
context::{ContextProvider, ContextProviderProps},
|
||||||
@ -51,10 +51,11 @@ fn route_switch<G: Html>(route: ReadSignal<AppRoutes>) -> View<G> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
#[component(UI<G>)]
|
#[component(UI<G>)]
|
||||||
pub fn ui() -> View<G> {
|
pub fn ui() -> View<G> {
|
||||||
let app_service = AppService::new();
|
let app_service = AppService::new();
|
||||||
console_log!("Starting UI");
|
info!("Starting UI");
|
||||||
view! {
|
view! {
|
||||||
// NOTE(jwall): Set the app_service in our toplevel scope. Children will be able
|
// NOTE(jwall): Set the app_service in our toplevel scope. Children will be able
|
||||||
// to find the service as long as they are a child of this scope.
|
// to find the service as long as they are a child of this scope.
|
||||||
@ -70,9 +71,9 @@ pub fn ui() -> View<G> {
|
|||||||
app_service.set_recipes(recipes);
|
app_service.set_recipes(recipes);
|
||||||
}
|
}
|
||||||
Ok((_, None)) => {
|
Ok((_, None)) => {
|
||||||
console_error!("No recipes to find");
|
error!("No recipes to find");
|
||||||
}
|
}
|
||||||
Err(msg) => console_error!("Failed to get recipes {}", msg),
|
Err(msg) => error!("Failed to get recipes {}", msg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user