use chrono::{Datelike, Days, Months, NaiveDate}; use common::ron_api; use gloo::storage::{LocalStorage, Storage}; use ron::ser::{PrettyConfig, to_string_pretty}; use serde::{Deserialize, Serialize}; use thiserror::Error; use crate::{calendar, request}; #[derive(Error, Debug)] pub enum Error { #[error("Request error: {0}")] Request(#[from] request::Error), } type Result = std::result::Result; #[derive(Clone, Copy)] pub struct RecipeScheduler { is_local: bool, } pub enum ScheduleRecipeResult { Ok, RecipeAlreadyScheduledAtThisDate, } impl From for ScheduleRecipeResult { fn from(api_res: ron_api::ScheduleRecipeResult) -> Self { match api_res { ron_api::ScheduleRecipeResult::Ok => Self::Ok, ron_api::ScheduleRecipeResult::RecipeAlreadyScheduledAtThisDate => { Self::RecipeAlreadyScheduledAtThisDate } } } } #[derive(Serialize, Deserialize, Clone, Debug)] struct ScheduledRecipesStore { recipe_id: i64, date: NaiveDate, } fn build_key(year: i32, month: u32) -> String { format!("scheduled_recipes-{}-{}", year, month) } fn load_scheduled_recipes(year: i32, month: u32) -> Vec { LocalStorage::get::>(build_key(year, month)).unwrap_or_default() } fn save_scheduled_recipes(scheduled_recipes: Vec, year: i32, month: u32) { LocalStorage::set(build_key(year, month), scheduled_recipes).unwrap(); } impl RecipeScheduler { pub fn new(is_local: bool) -> Self { Self { is_local } } pub async fn get_scheduled_recipes( &self, start_date: NaiveDate, end_date: NaiveDate, ) -> Result> { if self.is_local { let mut recipe_ids_and_dates = vec![]; let mut current_date = start_date; while current_date <= end_date { current_date = current_date + Months::new(1); for recipe in load_scheduled_recipes(current_date.year(), current_date.month0()) { if recipe.date >= start_date && recipe.date <= end_date { recipe_ids_and_dates.push(recipe); } } } if recipe_ids_and_dates.is_empty() { return Ok(vec![]); } let titles: ron_api::Strings = request::get( "recipe/titles", ron_api::Ids { ids: recipe_ids_and_dates .iter() .map(|r| r.recipe_id) .collect::>(), }, ) .await?; Ok(recipe_ids_and_dates .iter() .zip(titles.strs.into_iter()) .map(|(id_and_date, title)| (id_and_date.date, title, id_and_date.recipe_id)) .collect::>()) } else { let scheduled_recipes: ron_api::ScheduledRecipes = request::get( "calendar/scheduled_recipes", ron_api::DateRange { start_date, end_date, }, ) .await?; Ok(scheduled_recipes.recipes) } } pub async fn shedule_recipe( &self, recipe_id: i64, date: NaiveDate, servings: u32, add_ingredients_to_shopping_list: bool, ) -> Result { if self.is_local { // TODO: use 'add_ingredients_to_shopping_list'. let mut recipe_ids_and_dates = load_scheduled_recipes(date.year(), date.month0()); for recipe in recipe_ids_and_dates.iter() { if recipe.recipe_id == recipe_id && recipe.date == date { return Ok(ScheduleRecipeResult::RecipeAlreadyScheduledAtThisDate); } } recipe_ids_and_dates.push(ScheduledRecipesStore { recipe_id, date }); save_scheduled_recipes(recipe_ids_and_dates, date.year(), date.month0()); Ok(ScheduleRecipeResult::Ok) } else { request::post::( "calendar/schedule_recipe", ron_api::ScheduleRecipe { recipe_id, date, servings, add_ingredients_to_shopping_list, }, ) .await .map_err(Error::from) .map(From::::from) } } // pub async fn remove_scheduled_recipe( // &self, // recipe_id: i64 // ) }