dev: indexeddb indexes on recipe category and serving_count

This commit is contained in:
Jeremy Wall 2024-07-12 17:43:05 -04:00
parent 84cc2a2713
commit 51d165a50b
6 changed files with 56 additions and 41 deletions

View File

@ -99,7 +99,7 @@ impl AsyncFileStore {
let file_name = entry.file_name().to_string_lossy().to_string(); let file_name = entry.file_name().to_string_lossy().to_string();
debug!("adding recipe file {}", file_name); debug!("adding recipe file {}", file_name);
let recipe_contents = read_to_string(entry.path()).await?; let recipe_contents = read_to_string(entry.path()).await?;
entry_vec.push(RecipeEntry(file_name, recipe_contents, None, None)); entry_vec.push(RecipeEntry::new(file_name, recipe_contents));
} else { } else {
warn!( warn!(
file = %entry.path().to_string_lossy(), file = %entry.path().to_string_lossy(),
@ -119,12 +119,12 @@ impl AsyncFileStore {
if recipe_path.exists().await && recipe_path.is_file().await { if recipe_path.exists().await && recipe_path.is_file().await {
debug!("Found recipe file {}", recipe_path.to_string_lossy()); debug!("Found recipe file {}", recipe_path.to_string_lossy());
let recipe_contents = read_to_string(recipe_path).await?; let recipe_contents = read_to_string(recipe_path).await?;
return Ok(Some(RecipeEntry( return Ok(Some(RecipeEntry {
id.as_ref().to_owned(), id: id.as_ref().to_owned(),
recipe_contents, text: recipe_contents,
None, category: None,
None, serving_count: None,
))); }));
} else { } else {
return Ok(None); return Ok(None);
} }

View File

@ -440,12 +440,12 @@ impl APIStore for SqliteStore {
.await? .await?
.iter() .iter()
.map(|row| { .map(|row| {
RecipeEntry( RecipeEntry {
row.recipe_id.clone(), id: row.recipe_id.clone(),
row.recipe_text.clone().unwrap_or_else(|| String::new()), text: row.recipe_text.clone().unwrap_or_else(|| String::new()),
row.category.clone(), category: row.category.clone(),
row.serving_count.clone(), serving_count: row.serving_count.clone(),
) }
}) })
.nth(0); .nth(0);
Ok(entry) Ok(entry)
@ -460,12 +460,12 @@ impl APIStore for SqliteStore {
.await? .await?
.iter() .iter()
.map(|row| { .map(|row| {
RecipeEntry( RecipeEntry {
row.recipe_id.clone(), id: row.recipe_id.clone(),
row.recipe_text.clone().unwrap_or_else(|| String::new()), text: row.recipe_text.clone().unwrap_or_else(|| String::new()),
row.category.clone(), category: row.category.clone(),
row.serving_count.clone(), serving_count: row.serving_count.clone(),
) }
}) })
.collect(); .collect();
Ok(Some(rows)) Ok(Some(rows))

View File

@ -50,39 +50,49 @@ impl Mealplan {
} }
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct RecipeEntry(pub String, pub String, pub Option<String>, pub Option<i64>); pub struct RecipeEntry {
pub id: String,
pub text: String,
pub category: Option<String>,
pub serving_count: Option<i64>,
}
impl RecipeEntry { impl RecipeEntry {
pub fn new<IS: Into<String>, TS: Into<String>>(recipe_id: IS, text: TS) -> Self { pub fn new<IS: Into<String>, TS: Into<String>>(recipe_id: IS, text: TS) -> Self {
Self(recipe_id.into(), text.into(), None, None) Self {
id: recipe_id.into(),
text: text.into(),
category: None,
serving_count: None,
}
} }
pub fn set_recipe_id<S: Into<String>>(&mut self, id: S) { pub fn set_recipe_id<S: Into<String>>(&mut self, id: S) {
self.0 = id.into(); self.id = id.into();
} }
pub fn recipe_id(&self) -> &str { pub fn recipe_id(&self) -> &str {
self.0.as_str() self.id.as_str()
} }
pub fn set_recipe_text<S: Into<String>>(&mut self, text: S) { pub fn set_recipe_text<S: Into<String>>(&mut self, text: S) {
self.1 = text.into(); self.text = text.into();
} }
pub fn recipe_text(&self) -> &str { pub fn recipe_text(&self) -> &str {
self.1.as_str() self.text.as_str()
} }
pub fn set_category<S: Into<String>>(&mut self, cat: S) { pub fn set_category<S: Into<String>>(&mut self, cat: S) {
self.2 = Some(cat.into()); self.category = Some(cat.into());
} }
pub fn category(&self) -> Option<&String> { pub fn category(&self) -> Option<&String> {
self.2.as_ref() self.category.as_ref()
} }
pub fn serving_count(&self) -> Option<i64> { pub fn serving_count(&self) -> Option<i64> {
self.3.clone() self.serving_count.clone()
} }
} }

View File

@ -42,19 +42,19 @@ pub fn AddRecipe<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> View
} else { } else {
Some(category) Some(category)
}; };
RecipeEntry( RecipeEntry {
recipe_title id: recipe_title
.get() .get()
.as_ref() .as_ref()
.to_lowercase() .to_lowercase()
.replace(" ", "_") .replace(" ", "_")
.replace("\n", ""), .replace("\n", ""),
STARTER_RECIPE text: STARTER_RECIPE
.replace("TITLE_PLACEHOLDER", recipe_title.get().as_str()) .replace("TITLE_PLACEHOLDER", recipe_title.get().as_str())
.replace("\r", ""), .replace("\r", ""),
category, category,
None, serving_count: None,
) }
}); });
view! {cx, view! {cx,

View File

@ -115,12 +115,12 @@ pub fn Editor<'ctx, G: Html>(cx: Scope<'ctx>, props: RecipeComponentProps<'ctx>)
} else { } else {
Some(category.as_ref().clone()) Some(category.as_ref().clone())
}; };
let recipe_entry = RecipeEntry( let recipe_entry = RecipeEntry {
id.get_untracked().as_ref().clone(), id: id.get_untracked().as_ref().clone(),
text.get_untracked().as_ref().clone(), text: text.get_untracked().as_ref().clone(),
category, category,
None, serving_count: None,
); };
sh.dispatch(cx, Message::SaveRecipe(recipe_entry, None)); sh.dispatch(cx, Message::SaveRecipe(recipe_entry, None));
dirty.set(false); dirty.set(false);
} }

View File

@ -27,6 +27,8 @@ pub fn get_storage() -> web_sys::Storage {
pub const STATE_STORE_NAME: &'static str = "state-store"; pub const STATE_STORE_NAME: &'static str = "state-store";
pub const RECIPE_STORE_NAME: &'static str = "recipe-store"; pub const RECIPE_STORE_NAME: &'static str = "recipe-store";
pub const SERVING_COUNT_IDX: &'static str = "recipe-serving-count";
pub const CATEGORY_IDX: &'static str = "recipe-category";
pub const DB_VERSION: u32 = 1; pub const DB_VERSION: u32 = 1;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -52,8 +54,11 @@ impl<'name> DBFactory<'name> {
if db.version() == 1 { if db.version() == 1 {
// We use out of line keys for this object store // We use out of line keys for this object store
db.build_object_store(STATE_STORE_NAME).create()?; db.build_object_store(STATE_STORE_NAME).create()?;
db.build_object_store(RECIPE_STORE_NAME).create()?; let recipe_store = db.build_object_store(RECIPE_STORE_NAME).create()?;
// TODO(jwall): Do we need indexes? recipe_store.build_index(CATEGORY_IDX, "category")
.create()?;
recipe_store.build_index(SERVING_COUNT_IDX, "serving_count")
.create()?;
} }
Ok(()) Ok(())
}).await.context(format!("Opening or creating the database {}", self.name))?; }).await.context(format!("Opening or creating the database {}", self.name))?;