From 0a8091e84307bb8d25d7a23dfcfb9af463c17bd4 Mon Sep 17 00:00:00 2001 From: Matthew Kaminski Date: Thu, 5 Sep 2024 00:59:51 -0400 Subject: [PATCH] Don't load local card info by default, add user page, theme loading --- src/components/header.rs | 11 ++++++++--- src/components/layout.rs | 20 ++++++++++---------- src/global_state.rs | 19 +++++++++++++++---- src/main.rs | 23 ++++++++++++++++++++++- src/models/mod.rs | 2 ++ src/models/theme.rs | 7 +++++++ src/models/user.rs | 9 +++++++++ src/state_enums.rs | 2 ++ src/templates/mod.rs | 1 + src/templates/user/index.rs | 27 +++++++++++++++++++++++++++ src/templates/user/mod.rs | 1 + 11 files changed, 104 insertions(+), 18 deletions(-) create mode 100644 src/models/theme.rs create mode 100644 src/models/user.rs create mode 100644 src/templates/user/index.rs create mode 100644 src/templates/user/mod.rs diff --git a/src/components/header.rs b/src/components/header.rs index f621031..1cad8b8 100644 --- a/src/components/header.rs +++ b/src/components/header.rs @@ -102,13 +102,18 @@ pub fn Header(cx: Scope, props: HeaderProps) -> View { } // Title div (class="navbar-center lg:flex") { - (props.content_state.to_string()) + // (props.content_state.to_string()) } // User buttons div (class="navbar-end") { (match *global_state.auth.state.get() { LoginState::Authenticated => { view! { cx, - button(on:click = handle_log_out, class = "btn btn-primary mr-2") { + a (href = "user") { + button (class = "btn mr-2") { + "Preferences" + } + } + button(on:click = handle_log_out, class = "btn btn-secondary mr-2") { "Log out" } } }, @@ -121,7 +126,7 @@ pub fn Header(cx: Scope, props: HeaderProps) -> View { } } }, LoginState::Unknown => { view! { cx, - div (class = "px-5 py-2.5 me-2 mb-2") { + p { "Loading..." } } }, diff --git a/src/components/layout.rs b/src/components/layout.rs index 37943ce..8a8e9e3 100644 --- a/src/components/layout.rs +++ b/src/components/layout.rs @@ -65,8 +65,9 @@ pub fn Layout<'a, G: Html>( spawn_local_scoped(cx, async move { let global_state = Reactor::::from_cx(cx).get_global_state::(cx); + let local_card_user_pref = (*global_state.user_pref.local_card_data.get()).clone(); let card_table_loaded = (*global_state.constants.is_loaded.get()).clone(); - if !card_table_loaded { + if local_card_user_pref && !card_table_loaded { let client = reqwest::Client::new(); let response = client .get(get_api_path(CARD_INFO).as_str()) @@ -88,12 +89,12 @@ pub fn Layout<'a, G: Html>( // Modals section(class = "flex-2") { - (match (*global_state.constants.card_table.get()).clone() { - Some(card_table) => { view!{ cx, - p { "DONE" } - } }, - None => { view!{ cx, p { "Loading cards" } } }, - }) + // (match (*global_state.constants.card_table.get()).clone() { + // Some(card_table) => { view!{ cx, + // p { "DONE" } + // } }, + // None => { view!{ cx, p { "Loading cards" } } }, + // }) (match *global_state.modals_open.login.get() { OpenState::Open => { @@ -141,12 +142,10 @@ pub fn Layout<'a, G: Html>( main(style = "my-8") { (match content_state { - ContentState::None => view!{ cx, }, - ContentState::Tournaments => view!{ cx, }, ContentState::Inventory => view!{ cx, // Body header div (class = "container mx-auto px-6 py-3") { - nav (class = "sm:flex sm:justify-center sm:items-center mt-4 hidden") { + nav (class = "sm:flex sm:justify-center sm:items-center mt-4") { div (class = "flex flex-col sm:flex-row"){ a(href = "inventory", class = "mt-3 text-gray-600 hover:underline sm:mx-3 sm:mt-0" @@ -163,6 +162,7 @@ pub fn Layout<'a, G: Html>( } } }, + _ => view!{ cx, }, }) } } diff --git a/src/global_state.rs b/src/global_state.rs index cf66121..234f247 100644 --- a/src/global_state.rs +++ b/src/global_state.rs @@ -6,12 +6,14 @@ use serde::{Deserialize, Serialize}; use crate::{ models::{auth::WebAuthInfo, card::CardTable}, state_enums::{LoginState, OpenState}, + DEFAULT_THEME, }; - #[derive(Serialize, Deserialize, ReactiveState, Clone)] #[rx(alias = "AppStateRx")] pub struct AppState { + #[rx(nested)] + pub user_pref: UserPreferences, #[rx(nested)] pub constants: ConstData, #[rx(nested)] @@ -22,6 +24,12 @@ pub struct AppState { pub style: StyleData, } +#[derive(Serialize, Deserialize, ReactiveState, Clone)] +#[rx(alias = "UserPreferencesRx")] +pub struct UserPreferences { + pub local_card_data: bool, +} + #[derive(Serialize, Deserialize, ReactiveState, Clone)] #[rx(alias = "ConstDataRx")] pub struct ConstData { @@ -68,9 +76,12 @@ pub fn get_global_state_creator() -> GlobalStateCreator { #[engine_only_fn] pub async fn get_build_state() -> AppState { AppState { + user_pref: UserPreferences { + local_card_data: false, + }, constants: ConstData { is_loaded: false, - card_table: None + card_table: None, }, auth: AuthData { state: LoginState::Unknown, @@ -86,8 +97,8 @@ pub async fn get_build_state() -> AppState { }, style: StyleData { theme: ThemeData { - current: "luxury".to_owned(), - default: "luxury".to_owned(), + current: DEFAULT_THEME.to_owned(), + default: DEFAULT_THEME.to_owned(), }, }, } diff --git a/src/main.rs b/src/main.rs index 3c10854..f72b1d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,9 @@ mod templates; use perseus::prelude::*; use sycamore::prelude::view; +// TODO -> save theme in where changed +const DEFAULT_THEME: &str = "light"; + cfg_if::cfg_if! { if #[cfg(engine)] { use std::net::SocketAddr; @@ -72,18 +75,36 @@ pub fn main() -> PerseusApp { .global_state_creator(crate::global_state::get_global_state_creator()) .template(crate::templates::index::get_template()) .template(crate::templates::inventory::index::get_template()) + .template(crate::templates::user::index::get_template()) .capsule_ref(&*crate::capsules::login_form::LOGIN_FORM) .capsule_ref(&*crate::capsules::forgot_password_form::FORGOT_PASSWORD_FORM) .capsule_ref(&*crate::capsules::register_form::REGISTER_FORM) .error_views(crate::error_views::get_error_views()) .index_view(|cx| { view! { cx, - html (class = "flex w-full h-full", data-theme = "luxury"){ + html (class = "flex w-full h-full", data-theme = DEFAULT_THEME){ head { meta(charset = "UTF-8") meta(name = "viewport", content = "width=device-width, initial-scale=1.0") // Perseus automatically resolves `/.perseus/static/` URLs to the contents of the `static/` directory at the project root link(rel = "stylesheet", href = ".perseus/static/style.css") + + script { + (format!( + " + function setTheme() {{ + const theme = localStorage.getItem(\"theme\"); + const element = document.documentElement; + if (theme === null) {{ + element.setAttribute(\"data-theme\", \"{}\"); + }} else {{ + element.setAttribute(\"data-theme\", theme); + }} + }} + setTheme(); + " + , DEFAULT_THEME)) + } } body (class = "w-full"){ // Quirk: this creates a wrapper `
` around the root `
` by necessity diff --git a/src/models/mod.rs b/src/models/mod.rs index d9e36fb..197fc0f 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,3 +1,5 @@ pub mod auth; pub mod card; pub mod generic; +pub mod theme; +pub mod user; diff --git a/src/models/theme.rs b/src/models/theme.rs new file mode 100644 index 0000000..4b52094 --- /dev/null +++ b/src/models/theme.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub enum Theme { + Plain, + Purrely, +} diff --git a/src/models/user.rs b/src/models/user.rs new file mode 100644 index 0000000..f63f5e9 --- /dev/null +++ b/src/models/user.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; + +use super::theme::Theme; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct UserPreferences { + pub use_local_card_db: bool, + pub theme: Theme, +} diff --git a/src/state_enums.rs b/src/state_enums.rs index 878d6c4..d607ab1 100644 --- a/src/state_enums.rs +++ b/src/state_enums.rs @@ -14,6 +14,7 @@ pub enum ContentState { None, Inventory, Tournaments, + User, } impl Display for ContentState { @@ -24,6 +25,7 @@ impl Display for ContentState { match self { ContentState::Inventory => "Inventory", ContentState::Tournaments => "Tournament", + ContentState::User => "User", ContentState::None => "", } ) diff --git a/src/templates/mod.rs b/src/templates/mod.rs index 5c32ae4..eed1403 100644 --- a/src/templates/mod.rs +++ b/src/templates/mod.rs @@ -1,5 +1,6 @@ pub mod index; pub mod inventory; +pub mod user; #[cfg(client)] use perseus::utils::get_path_prefix_client; diff --git a/src/templates/user/index.rs b/src/templates/user/index.rs new file mode 100644 index 0000000..b8cce29 --- /dev/null +++ b/src/templates/user/index.rs @@ -0,0 +1,27 @@ +use crate::{components::layout::Layout, state_enums::ContentState}; +use perseus::prelude::*; +use sycamore::prelude::*; + +fn user_index_page(cx: Scope) -> View { + view! { cx, + Layout(content_state = ContentState::User) { + // Anything we put in here will be rendered inside the `
` block of the layout + p { "Hello World!" } + br {} + } + } +} + +#[engine_only_fn] +fn head(cx: Scope) -> View { + view! { cx, + title { "User Page" } + } +} + +pub fn get_template() -> Template { + Template::build("user") + .view(user_index_page) + .head(head) + .build() +} diff --git a/src/templates/user/mod.rs b/src/templates/user/mod.rs new file mode 100644 index 0000000..33edc95 --- /dev/null +++ b/src/templates/user/mod.rs @@ -0,0 +1 @@ +pub mod index;