Make State Management ssr safe

This commit is contained in:
Jeremy Wall 2022-08-15 16:58:35 -04:00
parent c9bcfe220e
commit a83b60e016
3 changed files with 63 additions and 33 deletions

View File

@ -21,6 +21,8 @@ pub struct TabState<G: GenericNode> {
#[component(TabbedView<G>)]
pub fn tabbed_view(state: TabState<G>) -> View<G> {
cloned!((state) => view! {
// NOTE(jwall): this needs to be a single node or the Router freaks out.
div {
header(class="no-print") {
nav {
ul {
@ -39,5 +41,6 @@ pub fn tabbed_view(state: TabState<G>) -> View<G> {
main(class=".conatiner-fluid") {
(state.inner)
}
}
})
}

View File

@ -13,9 +13,15 @@
// limitations under the License.
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 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 recipe_store::*;
@ -56,6 +62,7 @@ where
}
}
#[cfg(target_arch = "wasm32")]
fn get_storage(&self) -> Result<Option<Storage>, String> {
window()
.unwrap()
@ -63,9 +70,19 @@ where
.map_err(|e| format!("{:?}", e))
}
#[cfg(not(target_arch = "wasm32"))]
#[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");
// 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 recipes = self
.store
@ -96,10 +113,9 @@ where
Ok(())
}
#[cfg(target_arch = "wasm32")]
#[instrument(skip(self))]
pub fn fetch_categories_from_storage(
&self,
) -> Result<Option<BTreeMap<String, String>>, String> {
fn fetch_categories_from_storage(&self) -> Result<Option<BTreeMap<String, String>>, String> {
let storage = self.get_storage()?.unwrap();
match storage
.get_item("categories")
@ -119,8 +135,9 @@ where
}
}
#[cfg(target_arch = "wasm32")]
#[instrument(skip(self))]
pub fn fetch_recipes_from_storage(
fn fetch_recipes_from_storage(
&self,
) -> Result<(Option<Recipe>, Option<BTreeMap<String, Recipe>>), String> {
let storage = self.get_storage()?.unwrap();
@ -153,19 +170,32 @@ where
}
}
#[cfg(target_arch = "wasm32")]
async fn fetch_recipes(
&self,
) -> Result<(Option<Recipe>, Option<BTreeMap<String, Recipe>>), String> {
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> {
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))]
pub async fn refresh(&mut self) -> Result<(), String> {
self.synchronize().await?;
self.init(true).await?;
debug!("refreshing recipes");
if let (staples, Some(r)) = self.fetch_recipes().await? {
self.set_recipes(r);
@ -182,6 +212,7 @@ where
self.recipes.get().get(idx).map(|r| r.clone())
}
// FIXME(jwall): Stays public
#[instrument(skip(self))]
pub fn get_shopping_list(
&self,
@ -243,7 +274,7 @@ where
.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(
recipes
.iter()

View File

@ -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
// this are somewhat unclear and underdocumented for Sycamore. But basically
// 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! {
PlanPage()
},
@ -50,7 +50,9 @@ fn route_switch<G: Html>(route: ReadSignal<AppRoutes>) -> View<G> {
"Error: " (e)
}
}
})
});
debug!(node=?node, "routing to new page");
node
}
#[cfg(not(target_arch = "wasm32"))]
@ -95,17 +97,11 @@ pub fn ui(route: Option<AppRoutes>) -> View<G> {
children: || {
create_effect(move || {
spawn_local_in_scope({
let mut app_service = app_service.clone();
let app_service = app_service.clone();
async move {
debug!("fetching recipes");
match app_service.fetch_recipes_from_storage() {
Ok((_, Some(recipes))) => {
app_service.set_recipes(recipes);
}
Ok((_, None)) => {
error!("No recipes to find");
}
Err(msg) => error!("Failed to get recipes {}", msg),
if let Err(msg) = app_service.init(false).await {
error!("Failed to get recipes {}", msg)
}
}
});