use model::{ProductId, Quantity, QuantityUnit}; use seed::prelude::*; use serde::{Deserialize, Serialize}; use crate::shared::notification::NotificationMsg; use crate::{Model, Msg, NetRes}; #[derive(Debug)] pub enum CartMsg { AddItem { quantity: Quantity, quantity_unit: QuantityUnit, product_id: ProductId, }, ModifyItem { quantity: Quantity, quantity_unit: QuantityUnit, product_id: ProductId, }, Remove(ProductId), Hover, Leave, /// Send current non-empty cart to server Sync, SyncResult(NetRes), } impl From for Msg { fn from(msg: CartMsg) -> Self { Msg::Cart(msg) } } pub type Items = indexmap::IndexMap; #[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub struct Item { pub product_id: ProductId, pub quantity: Quantity, pub quantity_unit: QuantityUnit, } #[derive(Debug, Default, Serialize, Deserialize)] pub struct ShoppingCart { pub cart_id: Option, pub items: Items, #[serde(skip)] pub hover: bool, } 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: &mut Item = items.entry(product_id).or_insert_with(|| Item { quantity: Quantity::from_u32(0), quantity_unit, product_id, }); entry.quantity = entry.quantity + quantity; entry.quantity_unit = quantity_unit; } store_local(&model.cart); } CartMsg::ModifyItem { product_id, quantity_unit, quantity, } => { if **quantity == 0 { model.cart.items.remove(&product_id); } else { let items: &mut Items = &mut model.cart.items; let entry: &mut Item = 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::Remove(product_id) => { model.cart.items.remove(&product_id); store_local(&model.cart); } CartMsg::Hover => { model.cart.hover = true; } CartMsg::Leave => { model.cart.hover = false; } CartMsg::Sync => { if let Some(access_token) = model.shared.access_token.as_ref().cloned() { let items: Vec = model.cart.items.values().map(Clone::clone).collect(); orders.perform_cmd(async { crate::Msg::from(CartMsg::SyncResult( crate::api::public::update_cart(access_token, items).await, )) }); } } CartMsg::SyncResult(NetRes::Success(cart)) => { // cart.items } CartMsg::SyncResult(NetRes::Error(failure)) => { for msg in failure.errors { orders.send_msg(NotificationMsg::Error(msg).into()); } } CartMsg::SyncResult(NetRes::Http(_cart)) => { orders.send_msg(NotificationMsg::Error("Unable to sync cart".into()).into()); } } } fn load_local() -> ShoppingCart { match LocalStorage::get("ct") { Ok(cart) => cart, Err(e) => { seed::error!("Storage error", e); ShoppingCart::default() } } } fn store_local(cart: &ShoppingCart) { LocalStorage::insert("ct", cart).ok(); }