mirror of
https://github.com/zaphar/kitchen.git
synced 2025-07-21 19:29:49 -04:00
Use local storage as a cache
This commit is contained in:
parent
5a99620a0f
commit
2cc4c18238
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1242,9 +1242,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.75"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c059c05b48c5c0067d4b4b2b4f0732dd65feb52daf7e0ea09cd87e7dadc1af79"
|
||||
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
|
||||
dependencies = [
|
||||
"itoa 1.0.1",
|
||||
"ryu",
|
||||
@ -1806,9 +1806,11 @@ dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"recipes",
|
||||
"reqwasm",
|
||||
"serde_json",
|
||||
"sycamore",
|
||||
"sycamore-router",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -11,10 +11,14 @@ reqwasm = "0.4.0"
|
||||
# This makes debugging panics more tractable.
|
||||
console_error_panic_hook = "0.1.7"
|
||||
sycamore-router = "0.7.1"
|
||||
serde_json = "1.0.79"
|
||||
|
||||
[dependencies.wasm-bindgen]
|
||||
version = "0.2.79"
|
||||
#features = [ "console" ]
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
features = [ "Storage", "Window" ]
|
||||
|
||||
[dependencies.sycamore]
|
||||
version = "0.7.1"
|
||||
|
@ -12,15 +12,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
use crate::components::Recipe;
|
||||
use crate::console_log;
|
||||
use crate::service::AppService;
|
||||
use crate::{console_error, console_log};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashSet},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use recipes::{Ingredient, IngredientKey};
|
||||
use sycamore::{context::use_context, prelude::*};
|
||||
use sycamore::{context::use_context, futures::spawn_local_in_scope, prelude::*};
|
||||
|
||||
struct RecipeCheckBoxProps {
|
||||
i: usize,
|
||||
@ -170,10 +170,27 @@ fn recipe_list() -> View<G> {
|
||||
|
||||
#[component(MealPlan<G>)]
|
||||
pub fn meal_plan() -> View<G> {
|
||||
let app_service = use_context::<AppService>();
|
||||
let clicked = Signal::new(false);
|
||||
create_effect(cloned!((clicked, app_service) => move || {
|
||||
clicked.get();
|
||||
spawn_local_in_scope(cloned!((app_service) => {
|
||||
let mut app_service = app_service.clone();
|
||||
async move {
|
||||
if let Err(e) = app_service.refresh_recipes().await {
|
||||
console_error!("{}", e);
|
||||
};
|
||||
}
|
||||
}));
|
||||
}));
|
||||
view! {
|
||||
h1 {
|
||||
"Select your recipes"
|
||||
}
|
||||
input(type="button", value="Refresh Recipes", on:click=move |_| {
|
||||
let toggle = !*clicked.get();
|
||||
clicked.set(toggle);
|
||||
})
|
||||
RecipeSelector()
|
||||
ShoppingList()
|
||||
RecipeList()
|
||||
|
@ -13,10 +13,11 @@
|
||||
// limitations under the License.
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::{console_debug, console_error};
|
||||
use crate::{console_debug, console_error, console_log};
|
||||
|
||||
use reqwasm::http;
|
||||
use reqwasm::http::{self};
|
||||
use sycamore::prelude::*;
|
||||
use web_sys::{window, Storage};
|
||||
|
||||
use recipes::{parse, Ingredient, IngredientAccumulator, IngredientKey, Recipe};
|
||||
|
||||
@ -35,7 +36,14 @@ impl AppService {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn fetch_recipes() -> Result<Vec<(usize, Recipe)>, String> {
|
||||
fn get_storage() -> Result<Option<Storage>, String> {
|
||||
window()
|
||||
.unwrap()
|
||||
.local_storage()
|
||||
.map_err(|e| format!("{:?}", e))
|
||||
}
|
||||
|
||||
async fn fetch_recipes_http() -> Result<String, String> {
|
||||
let resp = match http::Request::get("/api/v1/recipes").send().await {
|
||||
Ok(resp) => resp,
|
||||
Err(e) => return Err(format!("Error: {}", e)),
|
||||
@ -44,26 +52,66 @@ impl AppService {
|
||||
return Err(format!("Status: {}", resp.status()));
|
||||
} else {
|
||||
console_debug!("We got a valid response back!");
|
||||
let recipe_list = match resp.json::<Vec<String>>().await {
|
||||
Ok(recipes) => recipes,
|
||||
Err(e) => return Err(format!("Eror getting recipe list as json {}", e)),
|
||||
};
|
||||
let mut parsed_list = Vec::new();
|
||||
for r in recipe_list {
|
||||
let recipe = match parse::as_recipe(&r) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
console_error!("Error parsing recipe {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
console_debug!("We parsed a recipe {}", recipe.title);
|
||||
parsed_list.push(recipe);
|
||||
}
|
||||
return Ok(parsed_list.drain(0..).enumerate().collect());
|
||||
return Ok(resp.text().await.map_err(|e| format!("{}", e))?);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn synchronize_recipes() -> Result<(), String> {
|
||||
console_log!("Synchronizing Recipes");
|
||||
let storage = Self::get_storage()?.unwrap();
|
||||
let recipes = Self::fetch_recipes_http().await?;
|
||||
storage
|
||||
.set_item("recipes", &recipes)
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fetch_recipes_from_storage() -> Result<Option<Vec<(usize, Recipe)>>, String> {
|
||||
let storage = Self::get_storage()?.unwrap();
|
||||
match storage
|
||||
.get_item("recipes")
|
||||
.map_err(|e| format!("{:?}", e))?
|
||||
{
|
||||
Some(s) => {
|
||||
let parsed =
|
||||
serde_json::from_str::<Vec<String>>(&s).map_err(|e| format!("{}", e))?;
|
||||
let mut parsed_list = Vec::new();
|
||||
for r in parsed {
|
||||
let recipe = match parse::as_recipe(&r) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
console_error!("Error parsing recipe {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
console_debug!("We parsed a recipe {}", recipe.title);
|
||||
parsed_list.push(recipe);
|
||||
}
|
||||
Ok(Some(parsed_list.drain(0..).enumerate().collect()))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn fetch_recipes() -> Result<Option<Vec<(usize, Recipe)>>, String> {
|
||||
if let Some(recipes) = Self::fetch_recipes_from_storage()? {
|
||||
return Ok(Some(recipes));
|
||||
} else {
|
||||
console_debug!("No recipes in cache synchronizing from api");
|
||||
// Try to synchronize first
|
||||
Self::synchronize_recipes().await?;
|
||||
Ok(Self::fetch_recipes_from_storage()?)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn refresh_recipes(&mut self) -> Result<(), String> {
|
||||
Self::synchronize_recipes().await?;
|
||||
if let Some(r) = Self::fetch_recipes().await? {
|
||||
self.set_recipes(r);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_recipe_by_index(&self, idx: usize) -> Option<Signal<Recipe>> {
|
||||
self.recipes.get().get(idx).map(|(_, r)| r.clone())
|
||||
}
|
||||
|
@ -64,9 +64,12 @@ pub fn ui() -> View<G> {
|
||||
let mut app_service = app_service.clone();
|
||||
async move {
|
||||
match AppService::fetch_recipes().await {
|
||||
Ok(recipes) => {
|
||||
Ok(Some(recipes)) => {
|
||||
app_service.set_recipes(recipes);
|
||||
}
|
||||
Ok(None) => {
|
||||
console_error!("No recipes to find");
|
||||
}
|
||||
Err(msg) => console_error!("Failed to get recipes {}", msg),
|
||||
}
|
||||
console_debug!("Determining route.");
|
||||
|
Loading…
x
Reference in New Issue
Block a user