#![feature(try_trait_v2)] pub mod api; #[cfg(debug_assertions)] mod debug; mod i18n; mod model; mod pages; pub mod session; pub mod shared; pub mod shopping_cart; use seed::empty; use seed::prelude::*; use crate::api::NetRes; use crate::i18n::I18n; use crate::model::Model; use crate::pages::{AdminPage, Msg, Page, PublicPage}; use crate::session::SessionMsg; #[macro_export] macro_rules! fetch_page { (public $model: expr, $page: ident, $ret: expr) => {{ let p = match &mut $model.page { crate::pages::Page::Public(p) => p, _ => return $ret, }; match p { crate::pages::PublicPage::$page(p) => p, _ => return $ret, } }}; (admin $model: expr, $page: ident, $ret: expr) => {{ let p = match &mut $model.page { crate::pages::Page::Admin(p) => p, _ => return $ret, }; match p { crate::pages::AdminPage::$page(p) => p, _ => return $ret, } }}; (public $model: expr, $page: ident) => {{ let p = match &mut $model.page { crate::pages::Page::Public(p) => p, _ => return, }; match p { crate::pages::PublicPage::$page(p) => p, _ => return, } }}; (admin $model: expr, $page: ident) => {{ let p = match &mut $model.page { crate::pages::Page::Admin(p) => p, _ => return, }; match p { crate::pages::AdminPage::$page(p) => p, _ => return, } }}; (public page $page: expr, $page_name: ident) => {{ let p = match $page { crate::pages::Page::Public(p) => p, _ => return, }; match p { crate::pages::PublicPage::$page_name(p) => p, _ => return, } }}; (public page $page: expr, $page_name: ident, $ret: expr) => {{ let p = match $page { crate::pages::Page::Public(p) => p, _ => { *$page = $ret; return; } }; match p { crate::pages::PublicPage::$page_name(p) => p, _ => { *$page = $ret; return; } } }}; (admin page $page: expr, $page_name: ident, $ret: expr) => {{ let p = match $page { crate::pages::Page::Admin(p) => p, _ => { *$page = $ret; return; } }; match p { crate::pages::AdminPage::$page_name(p) => p, _ => { *$page = $ret; return; } } }}; } fn init(url: Url, orders: &mut impl Orders) -> Model { orders .subscribe(Msg::UrlChanged) .perform_cmd(async { Msg::Config(crate::api::public::config().await) }); let mut model = Model { url: url.clone().set_path(&[] as &[&str]), token: LocalStorage::get("auth-token").ok(), page: Page::init(url, orders), logo: seed::document() .query_selector("link[rel=icon]") .ok() .flatten() .and_then(|el: web_sys::Element| el.get_attribute("href")), shared: shared::Model::default(), i18n: I18n::load(), cart: Default::default(), config: model::Config::default(), #[cfg(debug_assertions)] debug_modal: false, }; shopping_cart::init(&mut model, orders); session::init(&mut model, orders); shared::init(&mut model, orders); #[cfg(debug_assertions)] crate::debug::init(&mut model, orders); model } fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders) { #[cfg(debug_assertions)] if !matches!(msg, Msg::Session(SessionMsg::CheckSession)) { seed::log!("msg", msg); } match msg { #[cfg(debug_assertions)] Msg::NoOp => { orders.skip(); } Msg::Config(res) => { if let NetRes::Success(config) = res { model.config = config.into(); } } Msg::Shared(msg) => { shared::update(msg, model, orders); } Msg::UrlChanged(subs::UrlChanged(url)) => model.page.page_changed(url, orders), Msg::Public(pages::public::PublicMsg::Listing(msg)) => { let page = fetch_page!(public model, Listing); pages::public::listing::update(msg, page, &mut orders.proxy(Into::into)); } Msg::Public(pages::public::PublicMsg::Product(msg)) => { let page = fetch_page!(public model, Product); pages::public::product::update(msg, page, &mut orders.proxy(Into::into)) } Msg::Public(pages::public::PublicMsg::SignIn(msg)) => { let page = fetch_page!(public model, SignIn); pages::public::sign_in::update(msg, page, &mut orders.proxy(Into::into)) } Msg::Public(pages::public::PublicMsg::SignUp(msg)) => { let page = fetch_page!(public model, SignUp); pages::public::sign_up::update(msg, page, &mut orders.proxy(Into::into)) } Msg::Public(pages::public::PublicMsg::ShoppingCart(msg)) => { let page = fetch_page!(public model, ShoppingCart); pages::public::shopping_cart::update(msg, page, &mut orders.proxy(Into::into)) } Msg::Public(pages::public::PublicMsg::Checkout(msg)) => { pages::public::checkout::update(msg, model, &mut orders.proxy(Into::into)) } // Admin Msg::Admin(pages::admin::Msg::Landing(msg)) => { let page = fetch_page!(admin model, Landing); pages::admin::landing::update(msg, page, &mut orders.proxy(Into::into)) } Msg::Session(msg) => { session::update(msg, model, orders); } Msg::Cart(msg) => { shopping_cart::update(msg, model, orders); } #[cfg(debug_assertions)] Msg::Debug(msg) => { debug::update(msg, model); } } } fn view(model: &Model) -> Node { let view = match &model.page { Page::Public(PublicPage::Listing(page)) => pages::public::listing::view(model, page), Page::Public(PublicPage::Product(page)) => pages::public::product::view(model, page), Page::Public(PublicPage::SignIn(page)) => pages::public::sign_in::view(model, page), Page::Public(PublicPage::SignUp(page)) => pages::public::sign_up::view(model, page), Page::Public(PublicPage::ShoppingCart(page)) => { pages::public::shopping_cart::view(model, page) } Page::Public(PublicPage::Checkout(page)) => pages::public::checkout::view(model, page), Page::Admin(AdminPage::Landing(page)) => pages::admin::landing::view(model, page), _ => empty![], }; if cfg!(debug_assertions) { use seed::*; div![crate::debug::view(model), view] } else { view } } #[wasm_bindgen(start)] pub fn start() { App::start("main", init, update, view); }