Recipe can now be scheduled
This commit is contained in:
parent
ae6da1a5ae
commit
fbef990022
18 changed files with 233 additions and 51 deletions
|
|
@ -18,7 +18,7 @@ struct CalendarStateInternal {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct CalendarState {
|
||||
pub struct CalendarState {
|
||||
internal_state: Rc<RefCell<CalendarStateInternal>>,
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ impl CalendarState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn setup(calendar: Element) {
|
||||
pub fn setup(calendar: Element) -> CalendarState {
|
||||
let prev: Element = calendar.selector(".prev");
|
||||
let next: Element = calendar.selector(".next");
|
||||
|
||||
|
|
@ -98,6 +98,8 @@ pub fn setup(calendar: Element) {
|
|||
}
|
||||
})
|
||||
.forget();
|
||||
|
||||
state
|
||||
}
|
||||
|
||||
const NB_CALENDAR_ROW: u64 = 5;
|
||||
|
|
|
|||
|
|
@ -7,12 +7,26 @@ use crate::{
|
|||
};
|
||||
|
||||
pub async fn show(element_selector: &str) -> bool {
|
||||
show_and_initialize(element_selector, async |_| {}).await
|
||||
show_and_initialize(element_selector, async |_| Some(()))
|
||||
.await
|
||||
.is_some()
|
||||
}
|
||||
|
||||
pub async fn show_and_initialize<T>(element_selector: &str, initializer: T) -> bool
|
||||
pub async fn show_and_initialize<T, U>(element_selector: &str, initializer: T) -> Option<U>
|
||||
where
|
||||
T: AsyncFn(Element),
|
||||
T: AsyncFn(Element) -> U,
|
||||
{
|
||||
show_and_initialize_with_ok(element_selector, initializer, |_, result| result).await
|
||||
}
|
||||
|
||||
pub async fn show_and_initialize_with_ok<T, V, W, U>(
|
||||
element_selector: &str,
|
||||
initializer: T,
|
||||
ok: V,
|
||||
) -> Option<W>
|
||||
where
|
||||
T: AsyncFn(Element) -> U,
|
||||
V: Fn(Element, U) -> W,
|
||||
{
|
||||
let dialog: HtmlDialogElement = by_id("modal-dialog");
|
||||
|
||||
|
|
@ -24,7 +38,7 @@ where
|
|||
let element: Element = selector_and_clone(element_selector);
|
||||
content_element.set_inner_html("");
|
||||
content_element.append_child(&element).unwrap();
|
||||
initializer(element).await;
|
||||
let init_result = initializer(element.clone()).await;
|
||||
|
||||
dialog.show_modal().unwrap();
|
||||
|
||||
|
|
@ -34,8 +48,8 @@ where
|
|||
pin_mut!(click_ok, click_cancel);
|
||||
|
||||
let result = select! {
|
||||
() = click_ok => true,
|
||||
() = click_cancel => false,
|
||||
() = click_ok => Some(ok(element, init_result)),
|
||||
() = click_cancel => None,
|
||||
};
|
||||
|
||||
dialog.close();
|
||||
|
|
|
|||
|
|
@ -19,11 +19,6 @@ use crate::{
|
|||
utils::{by_id, selector, selector_and_clone, SelectorExt},
|
||||
};
|
||||
|
||||
use futures::{
|
||||
future::{FutureExt, Ready},
|
||||
pin_mut, select, Future,
|
||||
};
|
||||
|
||||
pub fn setup_page(recipe_id: i64) -> Result<(), JsValue> {
|
||||
// Title.
|
||||
{
|
||||
|
|
@ -265,6 +260,7 @@ pub fn setup_page(recipe_id: i64) -> Result<(), JsValue> {
|
|||
},
|
||||
)
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
let body = ron_api::Id { id: recipe_id };
|
||||
let _ = request::delete::<(), _>("recipe/remove", body).await;
|
||||
|
|
@ -400,6 +396,7 @@ fn create_group_element(group: &ron_api::Group) -> Element {
|
|||
},
|
||||
)
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
let body = ron_api::Id { id: group_id };
|
||||
let _ = request::delete::<(), _>("recipe/remove_group", body).await;
|
||||
|
|
@ -542,6 +539,7 @@ fn create_step_element(group_element: &Element, step: &ron_api::Step) -> Element
|
|||
},
|
||||
)
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
let body = ron_api::Id { id: step_id };
|
||||
let _ = request::delete::<(), _>("recipe/remove_step", body).await;
|
||||
|
|
@ -696,6 +694,7 @@ fn create_ingredient_element(step_element: &Element, ingredient: &ron_api::Ingre
|
|||
},
|
||||
)
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
let body = ron_api::Id { id: ingredient_id };
|
||||
let _ = request::delete::<(), _>("recipe/remove_ingredient", body).await;
|
||||
|
|
@ -717,7 +716,7 @@ async fn reload_recipes_list(current_recipe_id: i64) {
|
|||
.await
|
||||
{
|
||||
Err(error) => {
|
||||
toast::show(Level::Info, &format!("Internal server error: {}", error));
|
||||
toast::show_message(Level::Info, &format!("Internal server error: {}", error));
|
||||
}
|
||||
Ok(response) => {
|
||||
let list = document().get_element_by_id("recipes-list").unwrap();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use std::future::Future;
|
||||
use std::{cell::RefCell, future::Future, rc::Rc, str::FromStr};
|
||||
|
||||
use common::ron_api;
|
||||
use chrono::Locale;
|
||||
use common::{ron_api, utils::substitute};
|
||||
use gloo::{
|
||||
console::console,
|
||||
console::log,
|
||||
events::EventListener,
|
||||
net::http::Request,
|
||||
utils::{document, window},
|
||||
|
|
@ -24,13 +25,59 @@ pub fn setup_page(recipe_id: i64) -> Result<(), JsValue> {
|
|||
let add_to_planner: Element = selector("#recipe-view .add-to-planner");
|
||||
EventListener::new(&add_to_planner, "click", move |_event| {
|
||||
spawn_local(async move {
|
||||
modal_dialog::show_and_initialize(
|
||||
if let Some((date, servings)) = modal_dialog::show_and_initialize_with_ok(
|
||||
"#hidden-templates .date-and-servings",
|
||||
async |element| {
|
||||
calendar::setup(element.selector(".calendar"));
|
||||
async |element| calendar::setup(element.selector(".calendar")),
|
||||
|element, calendar_state| {
|
||||
let servings_element: HtmlInputElement = element.selector("#input-servings");
|
||||
(
|
||||
calendar_state.get_selected_date().date_naive(),
|
||||
servings_element.value_as_number() as u32,
|
||||
)
|
||||
},
|
||||
)
|
||||
.await;
|
||||
.await
|
||||
{
|
||||
if request::post::<(), _>(
|
||||
"calendar/schedule_recipe",
|
||||
ron_api::ScheduleRecipe {
|
||||
recipe_id,
|
||||
date,
|
||||
servings,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
toast::show_element_and_initialize(
|
||||
Level::Success,
|
||||
"#hidden-templates .calendar-add-to-planner-success",
|
||||
|element| {
|
||||
let title =
|
||||
selector::<Element>("#recipe-view .recipe-title").inner_html();
|
||||
let date_format =
|
||||
selector::<Element>("#hidden-templates .calendar-date-format")
|
||||
.inner_html();
|
||||
let locale = {
|
||||
let lang_and_territory = selector::<Element>("html")
|
||||
.get_attribute("lang")
|
||||
.unwrap()
|
||||
.replace("-", "_");
|
||||
Locale::from_str(&lang_and_territory).unwrap_or_default()
|
||||
};
|
||||
|
||||
element.set_inner_html(&substitute(
|
||||
&element.inner_html(),
|
||||
"{}",
|
||||
&[
|
||||
&title,
|
||||
&date.format_localized(&date_format, locale).to_string(),
|
||||
],
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.forget();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
use common::ron_api;
|
||||
use gloo::net::http::{Request, RequestBuilder};
|
||||
use gloo::{
|
||||
console::log,
|
||||
net::http::{Request, RequestBuilder},
|
||||
};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
|
|
@ -62,19 +65,23 @@ where
|
|||
{
|
||||
match request.send().await {
|
||||
Err(error) => {
|
||||
toast::show(Level::Info, &format!("Internal server error: {}", error));
|
||||
toast::show_message(Level::Info, &format!("Internal server error: {}", error));
|
||||
Err(Error::Gloo(error))
|
||||
}
|
||||
Ok(response) => {
|
||||
if !response.ok() {
|
||||
toast::show(
|
||||
toast::show_message(
|
||||
Level::Info,
|
||||
&format!("HTTP error: {}", response.status_text()),
|
||||
);
|
||||
Err(Error::Http(response.status_text()))
|
||||
} else {
|
||||
// Ok(())
|
||||
Ok(ron::de::from_bytes::<T>(&response.binary().await?)?)
|
||||
let mut r = response.binary().await?;
|
||||
// An empty response is considered to be an unit value.
|
||||
if r.is_empty() {
|
||||
r = b"()".to_vec();
|
||||
}
|
||||
Ok(ron::de::from_bytes::<T>(&r)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
use gloo::{timers::callback::Timeout, utils::document};
|
||||
use web_sys::Element;
|
||||
|
||||
use crate::utils::{by_id, selector_and_clone, SelectorExt};
|
||||
|
||||
pub enum Level {
|
||||
Success,
|
||||
|
|
@ -7,12 +10,36 @@ pub enum Level {
|
|||
Warning,
|
||||
}
|
||||
|
||||
pub fn show(level: Level, message: &str) {
|
||||
const TIME_DISPLAYED: u32 = 10_000; // [ms].
|
||||
|
||||
pub fn show_message(level: Level, message: &str) {
|
||||
let toast_element = document().get_element_by_id("toast").unwrap();
|
||||
toast_element.set_inner_html(message);
|
||||
toast_element.set_class_name("show");
|
||||
|
||||
Timeout::new(4_000, move || {
|
||||
Timeout::new(TIME_DISPLAYED, move || {
|
||||
toast_element.set_class_name("");
|
||||
})
|
||||
.forget();
|
||||
}
|
||||
|
||||
pub fn show_element(level: Level, selector: &str) {
|
||||
show_element_and_initialize(level, selector, |_| {})
|
||||
}
|
||||
|
||||
pub fn show_element_and_initialize<T>(level: Level, selector: &str, initializer: T)
|
||||
where
|
||||
T: Fn(Element),
|
||||
{
|
||||
let toast_element = document().get_element_by_id("toast").unwrap();
|
||||
|
||||
let element: Element = selector_and_clone(selector);
|
||||
toast_element.set_inner_html("");
|
||||
toast_element.append_child(&element).unwrap();
|
||||
initializer(element.clone());
|
||||
toast_element.set_class_name("show");
|
||||
|
||||
Timeout::new(TIME_DISPLAYED, move || {
|
||||
toast_element.set_class_name("");
|
||||
})
|
||||
.forget();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue