191 lines
5.8 KiB
Rust
191 lines
5.8 KiB
Rust
use model::{PaymentMethod, 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<model::api::UpdateCartOutput>),
|
|
NotesChanged(String),
|
|
PaymentChanged(model::PaymentMethod),
|
|
}
|
|
|
|
impl From<CartMsg> for Msg {
|
|
fn from(msg: CartMsg) -> Self {
|
|
Msg::Cart(msg)
|
|
}
|
|
}
|
|
|
|
pub type Items = indexmap::IndexMap<ProductId, Item>;
|
|
|
|
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
|
pub struct Item {
|
|
#[serde(rename = "i")]
|
|
pub product_id: ProductId,
|
|
#[serde(rename = "q")]
|
|
pub quantity: Quantity,
|
|
#[serde(rename = "u")]
|
|
pub quantity_unit: QuantityUnit,
|
|
}
|
|
|
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
|
pub struct ShoppingCart {
|
|
#[serde(rename = "i")]
|
|
pub cart_id: Option<model::ShoppingCartId>,
|
|
#[serde(rename = "is")]
|
|
pub items: Items,
|
|
#[serde(default, rename = "pm")]
|
|
pub payment_method: Option<PaymentMethod>,
|
|
#[serde(default, rename = "cn")]
|
|
pub checkout_notes: String,
|
|
#[serde(skip)]
|
|
pub hover: bool,
|
|
}
|
|
|
|
pub fn init(model: &mut Model, _orders: &mut impl Orders<Msg>) {
|
|
model.cart = load_local();
|
|
}
|
|
|
|
pub fn update(msg: CartMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|
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);
|
|
sync_cart(model, orders);
|
|
}
|
|
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);
|
|
sync_cart(model, orders);
|
|
}
|
|
CartMsg::Remove(product_id) => {
|
|
model.cart.items.remove(&product_id);
|
|
store_local(&model.cart);
|
|
sync_cart(model, orders);
|
|
}
|
|
CartMsg::Hover => {
|
|
model.cart.hover = true;
|
|
}
|
|
CartMsg::Leave => {
|
|
model.cart.hover = false;
|
|
}
|
|
CartMsg::Sync => sync_cart(model, orders),
|
|
CartMsg::SyncResult(NetRes::Success(cart)) => {
|
|
let len = cart.items.len();
|
|
model.cart.cart_id = Some(cart.cart_id);
|
|
model.cart.checkout_notes = cart.checkout_notes;
|
|
model.cart.payment_method = Some(cart.payment_method);
|
|
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
|
|
},
|
|
);
|
|
}
|
|
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());
|
|
}
|
|
CartMsg::NotesChanged(notes) => {
|
|
model.cart.checkout_notes = notes;
|
|
store_local(&model.cart);
|
|
sync_cart(model, orders);
|
|
}
|
|
CartMsg::PaymentChanged(method) => {
|
|
model.cart.payment_method = Some(method);
|
|
store_local(&model.cart);
|
|
sync_cart(model, orders);
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
let notes = model.cart.checkout_notes.clone();
|
|
let payment_method = model.cart.payment_method;
|
|
orders.perform_cmd(async move {
|
|
crate::Msg::from(CartMsg::SyncResult(
|
|
crate::api::public::update_cart(access_token, items, notes, payment_method).await,
|
|
))
|
|
});
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|