bazzar/crates/web/src/shopping_cart.rs

202 lines
6.3 KiB
Rust
Raw Normal View History

2022-11-23 16:31:11 +01:00
use model::v2::ProductVariantId;
2022-05-19 16:13:27 +02:00
use model::{PaymentMethod, ProductId, Quantity, QuantityUnit};
2022-05-15 14:05:26 +02:00
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,
2022-11-23 16:31:11 +01:00
product_variant_id: ProductVariantId,
2022-11-28 17:00:19 +01:00
product_id: ProductId,
2022-05-15 14:05:26 +02:00
},
ModifyItem {
quantity: Quantity,
quantity_unit: QuantityUnit,
2022-11-23 16:31:11 +01:00
product_variant_id: ProductVariantId,
2022-11-28 17:00:19 +01:00
product_id: ProductId,
2022-05-15 14:05:26 +02:00
},
2022-11-23 16:31:11 +01:00
Remove(ProductVariantId),
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 16:13:27 +02:00
NotesChanged(String),
PaymentChanged(model::PaymentMethod),
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)
}
}
2022-11-28 17:00:19 +01:00
pub type Items = indexmap::IndexMap<ProductVariantId, 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 {
2022-11-28 17:00:19 +01:00
#[serde(rename = "v")]
2022-11-23 16:31:11 +01:00
pub product_variant_id: ProductVariantId,
2022-11-28 17:00:19 +01:00
#[serde(rename = "p")]
pub product_id: ProductId,
2022-05-19 16:13:27 +02:00
#[serde(rename = "q")]
pub quantity: Quantity,
2022-05-19 16:13:27 +02:00
#[serde(rename = "u")]
pub quantity_unit: QuantityUnit,
2022-05-15 14:05:26 +02:00
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct ShoppingCart {
2022-05-19 16:13:27 +02:00
#[serde(rename = "i")]
2022-05-15 14:05:26 +02:00
pub cart_id: Option<model::ShoppingCartId>,
2022-05-19 16:13:27 +02:00
#[serde(rename = "is")]
2022-05-15 14:05:26 +02:00
pub items: Items,
2022-05-19 16:13:27 +02:00
#[serde(default, rename = "pm")]
pub payment_method: Option<PaymentMethod>,
#[serde(default, rename = "cn")]
2022-05-19 14:03:18 +02:00
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,
2022-11-23 16:31:11 +01:00
product_variant_id,
2022-11-28 17:00:19 +01:00
product_id,
2022-05-15 14:05:26 +02:00
} => {
{
let items: &mut Items = &mut model.cart.items;
2022-11-23 16:31:11 +01:00
let entry: &mut Item = items.entry(product_variant_id).or_insert_with(|| Item {
quantity: Quantity::from_u32(0),
2022-05-15 14:05:26 +02:00
quantity_unit,
2022-11-23 16:31:11 +01:00
product_variant_id,
2022-11-28 17:00:19 +01:00
product_id,
2022-05-15 14:05:26 +02:00
});
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 {
2022-11-28 17:00:19 +01:00
product_id,
2022-11-23 16:31:11 +01:00
product_variant_id,
quantity_unit,
quantity,
} => {
if **quantity == 0 {
2022-11-23 16:31:11 +01:00
model.cart.items.remove(&product_variant_id);
} else {
let items: &mut Items = &mut model.cart.items;
2022-11-23 16:31:11 +01:00
let entry: &mut Item = items.entry(product_variant_id).or_insert_with(|| Item {
quantity,
quantity_unit,
2022-11-23 16:31:11 +01:00
product_variant_id,
2022-11-28 17:00:19 +01:00
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();
2022-05-19 16:13:27 +02:00
model.cart.cart_id = Some(cart.cart_id);
model.cart.checkout_notes = cart.checkout_notes;
model.cart.payment_method = Some(cart.payment_method);
2022-05-19 14:03:18 +02:00
model.cart.items = cart.items.into_iter().fold(
IndexMap::with_capacity(len),
|mut set,
model::api::ShoppingCartItem {
id: _,
2022-11-23 16:31:11 +01:00
product_variant_id,
2022-11-28 17:00:19 +01:00
product_id,
2022-05-19 14:03:18 +02:00
shopping_cart_id: _,
quantity,
quantity_unit,
}| {
set.insert(
2022-11-23 16:31:11 +01:00
product_variant_id,
2022-05-19 14:03:18 +02:00
Item {
2022-11-23 16:31:11 +01:00
product_variant_id,
2022-11-28 17:00:19 +01:00
product_id,
2022-05-19 14:03:18 +02:00
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 16:13:27 +02:00
CartMsg::NotesChanged(notes) => {
2022-05-19 14:03:18 +02:00
model.cart.checkout_notes = notes;
store_local(&model.cart);
2022-05-19 16:13:27 +02:00
sync_cart(model, orders);
}
CartMsg::PaymentChanged(method) => {
model.cart.payment_method = Some(method);
store_local(&model.cart);
sync_cart(model, orders);
2022-05-19 14:03:18 +02:00
}
}
}
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();
2022-05-19 16:13:27 +02:00
let notes = model.cart.checkout_notes.clone();
let payment_method = model.cart.payment_method;
orders.perform_cmd(async move {
2022-05-19 14:03:18 +02:00
crate::Msg::from(CartMsg::SyncResult(
2022-05-19 16:13:27 +02:00
crate::api::public::update_cart(access_token, items, notes, payment_method).await,
2022-05-19 14:03:18 +02:00
))
});
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();
}