mirror of
https://github.com/zaphar/kitchen.git
synced 2025-07-22 19:40:14 -04:00
Make State Management ssr safe
This commit is contained in:
parent
c9bcfe220e
commit
a83b60e016
@ -21,23 +21,26 @@ pub struct TabState<G: GenericNode> {
|
|||||||
#[component(TabbedView<G>)]
|
#[component(TabbedView<G>)]
|
||||||
pub fn tabbed_view(state: TabState<G>) -> View<G> {
|
pub fn tabbed_view(state: TabState<G>) -> View<G> {
|
||||||
cloned!((state) => view! {
|
cloned!((state) => view! {
|
||||||
header(class="no-print") {
|
// NOTE(jwall): this needs to be a single node or the Router freaks out.
|
||||||
nav {
|
div {
|
||||||
ul {
|
header(class="no-print") {
|
||||||
li { a(href="/ui/plan", class="no-print") { "Plan" } " > "
|
nav {
|
||||||
|
ul {
|
||||||
|
li { a(href="/ui/plan", class="no-print") { "Plan" } " > "
|
||||||
|
}
|
||||||
|
li { a(href="/ui/inventory", class="no-print") { "Inventory" } " > "
|
||||||
|
}
|
||||||
|
li { a(href="/ui/cook", class="no-print") { "Cook" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
li { a(href="/ui/inventory", class="no-print") { "Inventory" } " > "
|
ul {
|
||||||
|
li { a(href="https://github.com/zaphar/kitchen") { "Github" } }
|
||||||
}
|
}
|
||||||
li { a(href="/ui/cook", class="no-print") { "Cook" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
li { a(href="https://github.com/zaphar/kitchen") { "Github" } }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
main(class=".conatiner-fluid") {
|
||||||
main(class=".conatiner-fluid") {
|
(state.inner)
|
||||||
(state.inner)
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,15 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
use serde_json::{from_str, to_string};
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use serde_json::from_str;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use serde_json::to_string;
|
||||||
use sycamore::{context::use_context, prelude::*};
|
use sycamore::{context::use_context, prelude::*};
|
||||||
use tracing::{debug, error, info, instrument, warn};
|
use tracing::{debug, instrument, warn};
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use tracing::{error, info};
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
use web_sys::{window, Storage};
|
use web_sys::{window, Storage};
|
||||||
|
|
||||||
use recipe_store::*;
|
use recipe_store::*;
|
||||||
@ -56,6 +62,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
fn get_storage(&self) -> Result<Option<Storage>, String> {
|
fn get_storage(&self) -> Result<Option<Storage>, String> {
|
||||||
window()
|
window()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -63,9 +70,19 @@ where
|
|||||||
.map_err(|e| format!("{:?}", e))
|
.map_err(|e| format!("{:?}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
async fn synchronize(&self) -> Result<(), String> {
|
pub async fn init(&self, force: bool) -> Result<(), String> {
|
||||||
|
// TODO(jwall): Allow this to work for the ssr case.
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
#[instrument(skip(self))]
|
||||||
|
pub async fn init(&self, force: bool) -> Result<(), String> {
|
||||||
info!("Synchronizing Recipes");
|
info!("Synchronizing Recipes");
|
||||||
|
// TODO(jwall): Check to see if we already have them in storage
|
||||||
|
// only fetch via http if force is true.
|
||||||
let storage = self.get_storage()?.unwrap();
|
let storage = self.get_storage()?.unwrap();
|
||||||
let recipes = self
|
let recipes = self
|
||||||
.store
|
.store
|
||||||
@ -96,10 +113,9 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub fn fetch_categories_from_storage(
|
fn fetch_categories_from_storage(&self) -> Result<Option<BTreeMap<String, String>>, String> {
|
||||||
&self,
|
|
||||||
) -> Result<Option<BTreeMap<String, String>>, String> {
|
|
||||||
let storage = self.get_storage()?.unwrap();
|
let storage = self.get_storage()?.unwrap();
|
||||||
match storage
|
match storage
|
||||||
.get_item("categories")
|
.get_item("categories")
|
||||||
@ -119,8 +135,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub fn fetch_recipes_from_storage(
|
fn fetch_recipes_from_storage(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<(Option<Recipe>, Option<BTreeMap<String, Recipe>>), String> {
|
) -> Result<(Option<Recipe>, Option<BTreeMap<String, Recipe>>), String> {
|
||||||
let storage = self.get_storage()?.unwrap();
|
let storage = self.get_storage()?.unwrap();
|
||||||
@ -153,19 +170,32 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
async fn fetch_recipes(
|
async fn fetch_recipes(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<(Option<Recipe>, Option<BTreeMap<String, Recipe>>), String> {
|
) -> Result<(Option<Recipe>, Option<BTreeMap<String, Recipe>>), String> {
|
||||||
Ok(self.fetch_recipes_from_storage()?)
|
Ok(self.fetch_recipes_from_storage()?)
|
||||||
}
|
}
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
async fn fetch_recipes(
|
||||||
|
&self,
|
||||||
|
) -> Result<(Option<Recipe>, Option<BTreeMap<String, Recipe>>), String> {
|
||||||
|
Ok((None, None))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
async fn fetch_categories(&self) -> Result<Option<BTreeMap<String, String>>, String> {
|
async fn fetch_categories(&self) -> Result<Option<BTreeMap<String, String>>, String> {
|
||||||
Ok(self.fetch_categories_from_storage()?)
|
Ok(self.fetch_categories_from_storage()?)
|
||||||
}
|
}
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
async fn fetch_categories(&self) -> Result<Option<BTreeMap<String, String>>, String> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(jwall): Stays public
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn refresh(&mut self) -> Result<(), String> {
|
pub async fn refresh(&mut self) -> Result<(), String> {
|
||||||
self.synchronize().await?;
|
self.init(true).await?;
|
||||||
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);
|
||||||
@ -182,6 +212,7 @@ where
|
|||||||
self.recipes.get().get(idx).map(|r| r.clone())
|
self.recipes.get().get(idx).map(|r| r.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME(jwall): Stays public
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub fn get_shopping_list(
|
pub fn get_shopping_list(
|
||||||
&self,
|
&self,
|
||||||
@ -243,7 +274,7 @@ where
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_recipes(&mut self, mut recipes: BTreeMap<String, Recipe>) {
|
pub fn set_recipes(&mut self, recipes: BTreeMap<String, Recipe>) {
|
||||||
self.recipes.set(
|
self.recipes.set(
|
||||||
recipes
|
recipes
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -27,7 +27,7 @@ fn route_switch<G: Html>(route: ReadSignal<AppRoutes>) -> View<G> {
|
|||||||
// NOTE(jwall): This needs to not be a dynamic node. The rules around
|
// NOTE(jwall): This needs to not be a dynamic node. The rules around
|
||||||
// this are somewhat unclear and underdocumented for Sycamore. But basically
|
// this are somewhat unclear and underdocumented for Sycamore. But basically
|
||||||
// avoid conditionals in the `view!` macro calls here.
|
// avoid conditionals in the `view!` macro calls here.
|
||||||
cloned!((route) => match route.get().as_ref() {
|
let node = cloned!((route) => match route.get().as_ref() {
|
||||||
AppRoutes::Plan => view! {
|
AppRoutes::Plan => view! {
|
||||||
PlanPage()
|
PlanPage()
|
||||||
},
|
},
|
||||||
@ -50,7 +50,9 @@ fn route_switch<G: Html>(route: ReadSignal<AppRoutes>) -> View<G> {
|
|||||||
"Error: " (e)
|
"Error: " (e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
debug!(node=?node, "routing to new page");
|
||||||
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@ -95,17 +97,11 @@ pub fn ui(route: Option<AppRoutes>) -> View<G> {
|
|||||||
children: || {
|
children: || {
|
||||||
create_effect(move || {
|
create_effect(move || {
|
||||||
spawn_local_in_scope({
|
spawn_local_in_scope({
|
||||||
let mut app_service = app_service.clone();
|
let app_service = app_service.clone();
|
||||||
async move {
|
async move {
|
||||||
debug!("fetching recipes");
|
debug!("fetching recipes");
|
||||||
match app_service.fetch_recipes_from_storage() {
|
if let Err(msg) = app_service.init(false).await {
|
||||||
Ok((_, Some(recipes))) => {
|
error!("Failed to get recipes {}", msg)
|
||||||
app_service.set_recipes(recipes);
|
|
||||||
}
|
|
||||||
Ok((_, None)) => {
|
|
||||||
error!("No recipes to find");
|
|
||||||
}
|
|
||||||
Err(msg) => error!("Failed to get recipes {}", msg),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user