bazzar/web/src/shopping_cart.rs

172 lines
5.0 KiB
Rust
Raw Normal View History

2022-05-15 14:05:26 +02:00
use model::{ProductId, Quantity, QuantityUnit};
use seed::prelude::*;
use serde::{Deserialize, Serialize};
2022-05-19 07:47:47 +02:00
use crate::shared::notification::NotificationMsg;
use crate::{Model, Msg, NetRes};
2022-05-15 14:05:26 +02:00
#[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,
2022-05-19 07:47:47 +02:00
/// Send current non-empty cart to server
Sync,
SyncResult(NetRes<model::api::UpdateCartOutput>),
2022-05-19 14:03:18 +02:00
ChangeNotes(String),
2022-05-15 14:05:26 +02:00
}
2022-05-15 19:33:06 +02:00
impl From<CartMsg> for Msg {
fn from(msg: CartMsg) -> Self {
Msg::Cart(msg)
}
}
pub type Items = indexmap::IndexMap<ProductId, Item>;
2022-05-15 14:05:26 +02:00
2022-05-19 07:47:47 +02:00
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
2022-05-15 14:05:26 +02:00
pub struct Item {
pub product_id: ProductId,
pub quantity: Quantity,
pub quantity_unit: QuantityUnit,
2022-05-15 14:05:26 +02:00
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct ShoppingCart {
pub cart_id: Option<model::ShoppingCartId>,
pub items: Items,
2022-05-19 14:03:18 +02:00
#[serde(default)]
pub checkout_notes: String,
#[serde(skip)]
pub hover: bool,
2022-05-15 14:05:26 +02:00
}
pub fn init(model: &mut Model, _orders: &mut impl Orders<Msg>) {
model.cart = load_local();
}
2022-05-19 07:47:47 +02:00
pub fn update(msg: CartMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
2022-05-15 14:05:26 +02:00
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),
2022-05-15 14:05:26 +02:00
quantity_unit,
product_id,
});
entry.quantity = entry.quantity + quantity;
2022-05-15 14:05:26 +02:00
entry.quantity_unit = quantity_unit;
}
store_local(&model.cart);
2022-05-19 14:03:18 +02:00
sync_cart(model, orders);
2022-05-15 14:05:26 +02:00
}
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);
2022-05-19 14:03:18 +02:00
sync_cart(model, orders);
}
CartMsg::Remove(product_id) => {
model.cart.items.remove(&product_id);
store_local(&model.cart);
2022-05-19 14:03:18 +02:00
sync_cart(model, orders);
}
CartMsg::Hover => {
model.cart.hover = true;
}
CartMsg::Leave => {
model.cart.hover = false;
}
2022-05-19 14:03:18 +02:00
CartMsg::Sync => sync_cart(model, orders),
2022-05-19 07:47:47 +02:00
CartMsg::SyncResult(NetRes::Success(cart)) => {
2022-05-19 14:03:18 +02:00
let len = cart.items.len();
model.cart.items = cart.items.into_iter().fold(
IndexMap::with_capacity(len),
|mut set,
model::api::ShoppingCartItem {
id: _,
product_id,
shopping_cart_id: _,
quantity,
quantity_unit,
}| {
set.insert(
product_id,
Item {
product_id,
quantity,
quantity_unit,
},
);
set
},
);
2022-05-19 07:47:47 +02:00
}
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());
}
2022-05-19 14:03:18 +02:00
CartMsg::ChangeNotes(notes) => {
model.cart.checkout_notes = notes;
store_local(&model.cart);
}
}
}
fn sync_cart(model: &mut Model, orders: &mut impl Orders<Msg>) {
if let Some(access_token) = model.shared.access_token.as_ref().cloned() {
let items: Vec<Item> = 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,
))
});
2022-05-15 14:05:26 +02:00
}
}
fn load_local() -> ShoppingCart {
match LocalStorage::get("ct") {
Ok(cart) => cart,
Err(e) => {
seed::error!("Storage error", e);
ShoppingCart::default()
}
}
2022-05-15 14:05:26 +02:00
}
fn store_local(cart: &ShoppingCart) {
LocalStorage::insert("ct", cart).ok();
}