Add card table loading

This commit is contained in:
2024-09-04 01:43:57 -04:00
parent 4674289473
commit 20d7f62f33
10 changed files with 93 additions and 61 deletions

View File

@@ -39,9 +39,9 @@ polars = { version = "0.39.2", default-features = false, features = [
fantoccini = "0.19" fantoccini = "0.19"
[target.'cfg(engine)'.dependencies] [target.'cfg(engine)'.dependencies]
axum = { version = "0.6", features = ["macros"] }
tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread"] } tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread"] }
perseus-axum = { version = "0.4.2" } perseus-axum = { version = "0.4.2" }
axum = "0.6"
futures = "0.3.28" futures = "0.3.28"
sea-orm = { version = "1.0", features = [ sea-orm = { version = "1.0", features = [
"sqlx-postgres", "sqlx-postgres",
@@ -60,3 +60,6 @@ sea-orm = { version = "1.0" }
[lints.rust] [lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(engine)', 'cfg(client)'] } unexpected_cfgs = { level = "warn", check-cfg = ['cfg(engine)', 'cfg(client)'] }
[profile.release]
lto = true

View File

@@ -11,6 +11,14 @@ use crate::{
use perseus::prelude::*; use perseus::prelude::*;
use sycamore::prelude::*; use sycamore::prelude::*;
cfg_if::cfg_if! {
if #[cfg(client)] {
use crate::endpoints::CARD_INFO;
use crate::templates::get_api_path;
use crate::models::card::CardTable;
}
}
#[derive(Prop)] #[derive(Prop)]
pub struct LayoutProps<'a, G: Html> { pub struct LayoutProps<'a, G: Html> {
pub content_state: ContentState, pub content_state: ContentState,
@@ -51,12 +59,42 @@ pub fn Layout<'a, G: Html>(
let content_state_header = content_state.clone(); let content_state_header = content_state.clone();
#[cfg(client)]
{
// TODO -> try to use suspense
spawn_local_scoped(cx, async move {
let global_state = Reactor::<G>::from_cx(cx).get_global_state::<AppStateRx>(cx);
let card_table_loaded = (*global_state.constants.is_loaded.get()).clone();
if !card_table_loaded {
let client = reqwest::Client::new();
let response = client
.get(get_api_path(CARD_INFO).as_str())
.send()
.await
.unwrap();
// TODO add error handling
let response = response.json::<CardTable>().await.unwrap();
global_state.constants.card_table.set(Some(response));
global_state.constants.is_loaded.set(true);
}
});
}
view! { cx, view! { cx,
// Main page header, including login functionality // Main page header, including login functionality
Header(content_state = content_state_header) Header(content_state = content_state_header)
// Modals // Modals
section(class = "flex-2") { 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.modals_open.login.get() { (match *global_state.modals_open.login.get() {
OpenState::Open => { OpenState::Open => {
view! { cx, view! { cx,

View File

@@ -4,3 +4,4 @@ pub const LOGIN: &str = "/api/login";
#[cfg(engine)] #[cfg(engine)]
pub const LOGIN_TEST: &str = "/api/login-test"; pub const LOGIN_TEST: &str = "/api/login-test";
pub const FORGOT_PASSWORD: &str = "/api/forgot-password"; pub const FORGOT_PASSWORD: &str = "/api/forgot-password";
pub const CARD_INFO: &str = "/api/card-info";

View File

@@ -4,13 +4,16 @@ use perseus::{prelude::*, state::GlobalStateCreator};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
models::auth::WebAuthInfo, models::{auth::WebAuthInfo, card::CardTable},
state_enums::{LoginState, OpenState}, state_enums::{LoginState, OpenState},
}; };
#[derive(Serialize, Deserialize, ReactiveState, Clone)] #[derive(Serialize, Deserialize, ReactiveState, Clone)]
#[rx(alias = "AppStateRx")] #[rx(alias = "AppStateRx")]
pub struct AppState { pub struct AppState {
#[rx(nested)]
pub constants: ConstData,
#[rx(nested)] #[rx(nested)]
pub auth: AuthData, pub auth: AuthData,
#[rx(nested)] #[rx(nested)]
@@ -19,6 +22,13 @@ pub struct AppState {
pub style: StyleData, pub style: StyleData,
} }
#[derive(Serialize, Deserialize, ReactiveState, Clone)]
#[rx(alias = "ConstDataRx")]
pub struct ConstData {
pub is_loaded: bool,
pub card_table: Option<CardTable>,
}
#[derive(Serialize, Deserialize, ReactiveState, Clone)] #[derive(Serialize, Deserialize, ReactiveState, Clone)]
#[rx(alias = "AuthDataRx")] #[rx(alias = "AuthDataRx")]
pub struct AuthData { pub struct AuthData {
@@ -58,6 +68,10 @@ pub fn get_global_state_creator() -> GlobalStateCreator {
#[engine_only_fn] #[engine_only_fn]
pub async fn get_build_state() -> AppState { pub async fn get_build_state() -> AppState {
AppState { AppState {
constants: ConstData {
is_loaded: false,
card_table: None
},
auth: AuthData { auth: AuthData {
state: LoginState::Unknown, state: LoginState::Unknown,
pending_username: String::new(), pending_username: String::new(),

View File

@@ -34,6 +34,7 @@ pub enum MonsterAttribute {
Light, Light,
Water, Water,
Wind, Wind,
None,
} }
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
@@ -51,13 +52,14 @@ pub enum TrapType {
Normal, Normal,
Continuous, Continuous,
Counter, Counter,
Unknown,
} }
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub enum CardTypeInfo { pub enum CardTypeInfo {
Monster { Monster {
level: u32, // level/rank/link rating level: Option<u32>, // level/rank/link rating
atk: u32, atk: Option<u32>,
def: Option<u32>, def: Option<u32>,
pendulum_scale: Option<u32>, pendulum_scale: Option<u32>,
attribute: MonsterAttribute, attribute: MonsterAttribute,
@@ -170,16 +172,16 @@ impl CardTable {
"Continuous" => TrapType::Continuous, "Continuous" => TrapType::Continuous,
"Counter" => TrapType::Counter, "Counter" => TrapType::Counter,
"Normal" => TrapType::Normal, "Normal" => TrapType::Normal,
unknown => panic!("Unknown trap type {}", unknown), _ => TrapType::Unknown,
}, },
} }
} else { } else {
CardTypeInfo::Monster { CardTypeInfo::Monster {
level: row[level_idx].try_extract().unwrap(), level: row[level_idx].try_extract().ok(),
atk: row[atk_idx].try_extract().unwrap(), atk: row[atk_idx].try_extract().ok(),
def: row[def_idx].try_extract().ok(), def: row[def_idx].try_extract().ok(),
pendulum_scale: row[pendulum_scale_idx].try_extract().ok(), pendulum_scale: row[pendulum_scale_idx].try_extract().ok(),
attribute: match row[attribute_idx].get_str().unwrap() { attribute: match row[attribute_idx].get_str().unwrap_or("NONE") {
"DARK" => MonsterAttribute::Dark, "DARK" => MonsterAttribute::Dark,
"DIVINE" => MonsterAttribute::Divine, "DIVINE" => MonsterAttribute::Divine,
"EARTH" => MonsterAttribute::Earth, "EARTH" => MonsterAttribute::Earth,
@@ -187,6 +189,7 @@ impl CardTable {
"LIGHT" => MonsterAttribute::Light, "LIGHT" => MonsterAttribute::Light,
"WATER" => MonsterAttribute::Water, "WATER" => MonsterAttribute::Water,
"WIND" => MonsterAttribute::Wind, "WIND" => MonsterAttribute::Wind,
"NONE" => MonsterAttribute::None,
unknown => panic!("Unknown attribute {}", unknown), unknown => panic!("Unknown attribute {}", unknown),
}, },
monster_type: row[monster_type_idx] monster_type: row[monster_type_idx]
@@ -390,18 +393,14 @@ impl CardTable {
"frameType", "frameType",
"ygoprodeck_url", "ygoprodeck_url",
"linkval", "linkval",
"race" "race",
])]) ])])
// Remove link markers, unless it's needed later // Remove link markers, unless it's needed later
.select([col("*").exclude(["linkmarkers"])]) .select([col("*").exclude(["linkmarkers"])])
// TODO add banlist support // TODO add banlist support
.select([col("*").exclude(["banlist_info"])]) .select([col("*").exclude(["banlist_info"])])
// TODO readd // TODO readd
.select([col("*").exclude([ .select([col("*").exclude(["card_sets", "card_images", "card_prices"])])
"card_sets",
"card_images",
"card_prices",
])])
// Filter out "Skill Card" // Filter out "Skill Card"
.filter(col("type").str().contains(lit("Skill Card"), false).not()) .filter(col("type").str().contains(lit("Skill Card"), false).not())
// Filters for testing // Filters for testing

View File

@@ -0,0 +1,18 @@
use std::path::Path;
use lazy_static::lazy_static;
use crate::server::server_state::ServerState;
use axum::{debug_handler, extract::State, http::StatusCode, Json};
use crate::models::card::CardTable;
lazy_static! {
static ref CARD_TABLE: CardTable =
CardTable::new_from_server_json(Path::new("./data/cardinfo.json"));
}
#[debug_handler]
pub async fn get_card_table(State(_): State<ServerState>) -> Result<Json<CardTable>, StatusCode> {
Ok(Json(CARD_TABLE.clone()))
}

View File

@@ -0,0 +1 @@
pub mod card_table;

View File

@@ -1,3 +1,4 @@
pub mod auth; pub mod auth;
pub mod constants;
pub mod routes; pub mod routes;
pub mod server_state; pub mod server_state;

View File

@@ -1,6 +1,6 @@
// (Server only) Routes // (Server only) Routes
use crate::endpoints::{FORGOT_PASSWORD, LOGIN, LOGIN_TEST, REGISTER}; use crate::endpoints::{CARD_INFO, FORGOT_PASSWORD, LOGIN, LOGIN_TEST, REGISTER};
use axum::routing::{post, Router}; use axum::routing::{get, post, Router};
use super::{ use super::{
auth::{ auth::{
@@ -8,6 +8,7 @@ use super::{
login::{post_login_user, post_test_login}, login::{post_login_user, post_test_login},
register::post_register_user, register::post_register_user,
}, },
constants::card_table::get_card_table,
server_state::ServerState, server_state::ServerState,
}; };
@@ -17,5 +18,6 @@ pub fn get_api_router(state: ServerState) -> Router {
.route(LOGIN, post(post_login_user)) .route(LOGIN, post(post_login_user))
.route(LOGIN_TEST, post(post_test_login)) .route(LOGIN_TEST, post(post_test_login))
.route(FORGOT_PASSWORD, post(post_forgot_password)) .route(FORGOT_PASSWORD, post(post_forgot_password))
.route(CARD_INFO, get(get_card_table))
.with_state(state) .with_state(state)
} }

View File

@@ -1,45 +0,0 @@
// Not a page, global state that is shared between all pages
use perseus::{prelude::*, state::GlobalStateCreator};
use serde::{Deserialize, Serialize};
use crate::data::card::CardTable;
cfg_if::cfg_if! {
if #[cfg(engine)] {
use std::thread;
use std::ops::Deref;
use crate::data::store::DATA;
}
}
#[derive(Serialize, Deserialize, ReactiveState, Clone)]
#[rx(alias = "AppStateRx")]
pub struct AppState {
pub card_table: CardTable,
}
pub fn get_global_state_creator() -> GlobalStateCreator {
GlobalStateCreator::new()
.build_state_fn(get_build_state)
.request_state_fn(get_request_state)
}
#[engine_only_fn]
fn get_state() -> AppState {
let card_table = thread::spawn(move || DATA.lock().unwrap().deref().card_table.clone())
.join()
.unwrap();
AppState { card_table }
}
#[engine_only_fn]
pub async fn get_build_state() -> AppState {
get_state()
}
#[engine_only_fn]
pub async fn get_request_state(_req: Request) -> AppState {
get_state()
}