diff --git a/web/src/lib.rs b/web/src/lib.rs index d855c44..db5b4b8 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -8,6 +8,7 @@ mod model; mod pages; pub mod session; pub mod shared; +pub mod shopping_cart; use seed::empty; use seed::prelude::*; @@ -81,10 +82,12 @@ fn init(url: Url, orders: &mut impl Orders) -> Model { .and_then(|el: web_sys::Element| el.get_attribute("href")), shared: shared::Model::default(), i18n: I18n::load(), + cart: Default::default(), #[cfg(debug_assertions)] debug_modal: false, }; + shopping_cart::init(&mut model, orders); session::init(&mut model, orders); shared::init(&mut model, orders); @@ -101,7 +104,7 @@ fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders) { orders.skip(); } Msg::Shared(msg) => { - shared::update(msg, &mut model.shared, orders); + shared::update(msg, model, orders); } Msg::UrlChanged(subs::UrlChanged(url)) => model.page.page_changed(url, orders), Msg::Public(pages::public::Msg::Listing(msg)) => { diff --git a/web/src/model.rs b/web/src/model.rs index 5f7bb01..d775a58 100644 --- a/web/src/model.rs +++ b/web/src/model.rs @@ -1,6 +1,6 @@ use seed::Url; -use crate::{I18n, Page}; +use crate::{I18n, Page, shopping_cart}; #[derive(Debug)] pub struct Model { @@ -10,6 +10,7 @@ pub struct Model { pub logo: Option, pub shared: crate::shared::Model, pub i18n: I18n, + pub cart: shopping_cart::ShoppingCart, #[cfg(debug_assertions)] pub debug_modal: bool, } diff --git a/web/src/pages.rs b/web/src/pages.rs index b1f4cb6..a158fcb 100644 --- a/web/src/pages.rs +++ b/web/src/pages.rs @@ -13,7 +13,7 @@ pub enum Msg { Public(public::Msg), Admin(admin::Msg), UrlChanged(subs::UrlChanged), - Shared(shared::Msg), + Shared(shared::SharedMsg), Session(crate::session::SessionMsg), #[cfg(debug_assertions)] Debug(crate::debug::DebugMsg), diff --git a/web/src/session.rs b/web/src/session.rs index 37b49e1..f7d518b 100644 --- a/web/src/session.rs +++ b/web/src/session.rs @@ -2,8 +2,9 @@ use chrono::{NaiveDateTime, TimeZone}; use model::{AccessTokenString, RefreshTokenString}; use seed::prelude::*; +use crate::pages::AdminPage; use crate::shared::notification::NotificationMsg; -use crate::{Model, Msg}; +use crate::{pages, Model, Msg, Page, PublicPage}; #[derive(Debug)] pub enum SessionMsg { @@ -29,11 +30,40 @@ pub fn init(model: &mut Model, orders: &mut impl Orders) { .ok() .map(model::RefreshTokenString::new); model.shared.exp = LocalStorage::get::<_, String>("exp").ok().and_then(|s| { - seed::log!("Parsing ", s); chrono::DateTime::parse_from_rfc3339(&s) .ok() .map(|t| t.naive_utc()) - }) + }); + redirect_on_session(model, orders); +} + +pub fn redirect_on_session(model: &Model, orders: &mut impl Orders) { + seed::log!(&model.page, model.shared.me.is_some()); + match &model.page { + Page::Admin(admin) => match admin { + AdminPage::Landing => {} + AdminPage::Dashboard => {} + AdminPage::Products => {} + AdminPage::Product => {} + }, + Page::Public(public) if model.shared.me.is_some() => match public { + PublicPage::SignUp(_) | PublicPage::SignIn(_) => { + let url = model.url.clone().set_path(&[] as &[&str]); + url.go_and_push(); + orders.send_msg(crate::Msg::UrlChanged(subs::UrlChanged(url))); + orders.force_render_now(); + } + _ => {} + }, + Page::Public(public) => match public { + PublicPage::Listing(_) => {} + PublicPage::Product(_) => {} + PublicPage::SignIn(_) => {} + PublicPage::SignUp(_) => {} + PublicPage::ShoppingCart => {} + PublicPage::Checkout => {} + }, + } } pub fn update(msg: SessionMsg, model: &mut Model, orders: &mut impl Orders) { @@ -103,7 +133,7 @@ pub fn update(msg: SessionMsg, model: &mut Model, orders: &mut impl Orders) errors.into_iter().for_each(|msg| { orders .skip() - .send_msg(Msg::Shared(crate::shared::Msg::Notification( + .send_msg(Msg::Shared(crate::shared::SharedMsg::Notification( NotificationMsg::Error(msg), ))); }); @@ -133,7 +163,7 @@ pub fn update(msg: SessionMsg, model: &mut Model, orders: &mut impl Orders) _ => { orders .skip() - .send_msg(Msg::Shared(crate::shared::Msg::Notification( + .send_msg(Msg::Shared(crate::shared::SharedMsg::Notification( NotificationMsg::Error("Request failed".into()), ))); } diff --git a/web/src/shared.rs b/web/src/shared.rs index 227e853..a2a8f97 100644 --- a/web/src/shared.rs +++ b/web/src/shared.rs @@ -1,15 +1,17 @@ use seed::app::Orders; +use crate::session::redirect_on_session; + pub mod notification; pub mod view; #[derive(Debug)] -pub enum Msg { +pub enum SharedMsg { LoadMe, MeLoaded(crate::api::NetRes), SignIn(model::api::SignInInput), SignedIn(crate::api::NetRes), - Notification(crate::shared::notification::NotificationMsg), + Notification(notification::NotificationMsg), } #[derive(Debug, Default)] @@ -22,38 +24,38 @@ pub struct Model { } pub fn init(_model: &mut crate::Model, orders: &mut impl Orders) { - orders.send_msg(crate::Msg::Shared(Msg::LoadMe)); + orders.send_msg(crate::Msg::Shared(SharedMsg::LoadMe)); } -pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders) { +pub fn update(msg: SharedMsg, model: &mut crate::Model, orders: &mut impl Orders) { match msg { - Msg::LoadMe => { - seed::log!("1"); - if let Some(token) = model.access_token.as_ref().cloned() { - seed::log!("2"); + SharedMsg::LoadMe => { + if let Some(token) = model.shared.access_token.as_ref().cloned() { orders.skip().perform_cmd(async move { - seed::log!("3"); - Msg::MeLoaded(crate::api::public::fetch_me(token).await) + crate::Msg::Shared(SharedMsg::MeLoaded( + crate::api::public::fetch_me(token).await, + )) }); } } - Msg::MeLoaded(crate::api::NetRes::Success(account)) => { - model.me = Some(account); + SharedMsg::MeLoaded(crate::api::NetRes::Success(account)) => { + model.shared.me = Some(account); + redirect_on_session(model, orders); } - Msg::MeLoaded(crate::api::NetRes::Error(_error)) => {} - Msg::MeLoaded(crate::api::NetRes::Http(_error)) => {} - Msg::SignIn(input) => { - orders - .skip() - .perform_cmd(async { Msg::SignedIn(crate::api::public::sign_in(input).await) }); + SharedMsg::MeLoaded(crate::api::NetRes::Error(_error)) => {} + SharedMsg::MeLoaded(crate::api::NetRes::Http(_error)) => {} + SharedMsg::SignIn(input) => { + orders.skip().perform_cmd(async { + SharedMsg::SignedIn(crate::api::public::sign_in(input).await) + }); } - Msg::SignedIn(crate::api::NetRes::Success(pair)) => { - handle_auth_pair(pair, model, orders); + SharedMsg::SignedIn(crate::api::NetRes::Success(pair)) => { + handle_auth_pair(pair, &mut model.shared, orders); } - Msg::SignedIn(crate::api::NetRes::Error(_err)) => {} - Msg::SignedIn(crate::api::NetRes::Http(_err)) => {} - Msg::Notification(msg) => { - notification::update(msg, model, orders); + SharedMsg::SignedIn(crate::api::NetRes::Error(_err)) => {} + SharedMsg::SignedIn(crate::api::NetRes::Http(_err)) => {} + SharedMsg::Notification(msg) => { + notification::update(msg, &mut model.shared, orders); } } } diff --git a/web/src/shared/notification.rs b/web/src/shared/notification.rs index 5191e94..811451f 100644 --- a/web/src/shared/notification.rs +++ b/web/src/shared/notification.rs @@ -13,7 +13,7 @@ pub enum NotificationMsg { Clear, } -impl From for shared::Msg { +impl From for shared::SharedMsg { fn from(msg: NotificationMsg) -> Self { Self::Notification(msg) } @@ -152,7 +152,7 @@ pub fn message(id: uuid::Uuid, message: &str, icon: Type) -> Node { ev(Ev::Click, move |ev| { ev.prevent_default(); ev.stop_propagation(); - Msg::Shared(shared::Msg::Notification(NotificationMsg::Close(id))) + Msg::Shared(shared::SharedMsg::Notification(NotificationMsg::Close(id))) }) ] ] diff --git a/web/src/shopping_cart.rs b/web/src/shopping_cart.rs new file mode 100644 index 0000000..a58fc06 --- /dev/null +++ b/web/src/shopping_cart.rs @@ -0,0 +1,68 @@ +use model::{ProductId, Quantity, QuantityUnit}; +use seed::prelude::*; +use serde::{Deserialize, Serialize}; + +use crate::{Model, Msg}; + +#[derive(Debug)] +pub enum CartMsg { + AddItem { + quantity: Quantity, + quantity_unit: QuantityUnit, + product_id: ProductId, + }, + ModifyItem { + quantity: Quantity, + quantity_unit: QuantityUnit, + product_id: ProductId, + }, +} + +pub type Items = indexmap::IndexMap; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Item { + product_id: ProductId, + quantity: Quantity, + quantity_unit: QuantityUnit, +} + +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct ShoppingCart { + pub cart_id: Option, + pub items: Items, +} + +pub fn init(model: &mut Model, _orders: &mut impl Orders) { + model.cart = load_local(); +} + +pub fn update(msg: CartMsg, model: &mut Model, _orders: &mut impl Orders) { + match msg { + CartMsg::AddItem { + quantity, + quantity_unit, + product_id, + } => { + { + let items: &mut Items = &mut model.cart.items; + let entry = items.entry(product_id).or_insert_with(|| Item { + quantity, + quantity_unit, + product_id, + }); + entry.quantity = quantity; + entry.quantity_unit = quantity_unit; + } + store_local(&model.cart); + } + CartMsg::ModifyItem { .. } => {} + } +} + +fn load_local() -> ShoppingCart { + LocalStorage::get("ct").unwrap_or_default() +} +fn store_local(cart: &ShoppingCart) { + LocalStorage::insert("ct", cart).ok(); +}