From 0a3782836c0434aefa866368d8d1b579d41247d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asger=20Juul=20Brunsh=C3=B8j?= Date: Sun, 9 Feb 2025 15:50:51 +0100 Subject: [PATCH] problems --- crates/ascend/src/components/problem.rs | 53 +++++++++++++++ crates/ascend/src/lib.rs | 35 +++++++++- crates/ascend/src/models.rs | 10 +++ crates/ascend/src/pages/edit_wall.rs | 2 - crates/ascend/src/pages/routes.rs | 87 ++++++++++++++++--------- crates/ascend/src/pages/wall.rs | 32 ++++----- 6 files changed, 162 insertions(+), 57 deletions(-) create mode 100644 crates/ascend/src/components/problem.rs diff --git a/crates/ascend/src/components/problem.rs b/crates/ascend/src/components/problem.rs new file mode 100644 index 0000000..6a0e9d5 --- /dev/null +++ b/crates/ascend/src/components/problem.rs @@ -0,0 +1,53 @@ +use crate::models::HoldRole; +use crate::models::{self}; +use leptos::prelude::*; + +#[component] +#[tracing::instrument(skip_all)] +pub fn Problem(#[prop(into)] dim: Signal, #[prop(into)] problem: Signal) -> impl IntoView { + let holds = move || { + let mut holds = vec![]; + for row in 0..dim.get().rows { + for col in 0..dim.get().cols { + let hold_position = models::HoldPosition { row, col }; + let role = move || problem.get().holds.get(&hold_position).copied(); + let role = Signal::derive(role); + let hold = view! { }; + holds.push(hold); + } + } + holds.into_iter().collect_view() + }; + + let grid_classes = move || format!("grid grid-rows-{} grid-cols-{} gap-3", dim.get().rows, dim.get().cols); + + view! { +
+
+ {holds} +
+
+ } +} + +#[component] +#[tracing::instrument(skip_all)] +fn Hold(#[prop(into)] role: Signal>) -> impl IntoView { + let class = move || { + let role_classes = match role.get() { + Some(HoldRole::Start) => Some("outline outline-offset-2 outline-green-500"), + Some(HoldRole::Normal) => Some("outline outline-offset-2 outline-blue-500"), + Some(HoldRole::Zone) => Some("outline outline-offset-2 outline-amber-500"), + Some(HoldRole::End) => Some("outline outline-offset-2 outline-red-500"), + None => Some("brightness-50"), + }; + let mut s = "min-w-2 bg-sky-100 aspect-square rounded".to_string(); + if let Some(c) = role_classes { + s.push(' '); + s.push_str(c); + } + s + }; + + view! {
} +} diff --git a/crates/ascend/src/lib.rs b/crates/ascend/src/lib.rs index f8502ea..1148ad6 100644 --- a/crates/ascend/src/lib.rs +++ b/crates/ascend/src/lib.rs @@ -5,19 +5,48 @@ pub mod pages { pub mod wall; } pub mod components { + pub use button::Button; + pub use header::StyledHeader; + pub use problem::Problem; + pub mod button; pub mod header; + pub mod problem; } +pub mod resources { + use crate::codec::ron::Ron; + use crate::codec::ron::RonEncoded; + use crate::models::{self}; + use leptos::prelude::Get; + use leptos::prelude::Signal; + use leptos::server::Resource; + use server_fn::ServerFnError; + type RonResource = Resource, Ron>; + + pub fn wall_by_uid(wall_uid: Signal) -> RonResource { + Resource::new_with_options( + move || wall_uid.get(), + move |wall_uid: models::WallUid| async move { crate::server_functions::get_wall(wall_uid).await.map(RonEncoded::into_inner) }, + false, + ) + } + + pub fn problems_for_wall(wall_uid: Signal) -> RonResource> { + Resource::new_with_options( + move || wall_uid.get(), + move |wall_uid: models::WallUid| async move { crate::server_functions::get_problems_for_wall(wall_uid).await.map(RonEncoded::into_inner) }, + false, + ) + } +} pub mod codec; - +pub mod models; pub mod server_functions; #[cfg(feature = "ssr")] pub mod server; -pub mod models; - #[cfg(feature = "hydrate")] #[wasm_bindgen::prelude::wasm_bindgen] pub fn hydrate() { diff --git a/crates/ascend/src/models.rs b/crates/ascend/src/models.rs index 569b986..4b6e077 100644 --- a/crates/ascend/src/models.rs +++ b/crates/ascend/src/models.rs @@ -9,6 +9,7 @@ pub use v2::Problem; pub use v2::ProblemUid; pub use v2::Root; pub use v2::Wall; +pub use v2::WallDimensions; pub use v2::WallUid; pub mod v2 { @@ -26,12 +27,21 @@ pub mod v2 { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Wall { pub uid: WallUid, + + // TODO: Replace by walldimensions pub rows: u64, pub cols: u64, + pub holds: BTreeMap, pub problems: BTreeSet, } + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + pub struct WallDimensions { + pub rows: u64, + pub cols: u64, + } + #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, derive_more::FromStr, derive_more::Display)] pub struct WallUid(pub uuid::Uuid); impl WallUid { diff --git a/crates/ascend/src/pages/edit_wall.rs b/crates/ascend/src/pages/edit_wall.rs index fb8f3dc..3727348 100644 --- a/crates/ascend/src/pages/edit_wall.rs +++ b/crates/ascend/src/pages/edit_wall.rs @@ -2,7 +2,6 @@ use crate::codec::ron::Ron; use crate::codec::ron::RonEncoded; use crate::components::header::HeaderItem; use crate::components::header::HeaderItems; -use crate::components::header::StyledHeader; use crate::models; use crate::models::HoldPosition; use crate::models::Wall; @@ -12,7 +11,6 @@ use leptos::prelude::*; use serde::Deserialize; use serde::Serialize; use server_fn::codec::Cbor; -use std::ops::Deref; use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; use web_sys::FileList; diff --git a/crates/ascend/src/pages/routes.rs b/crates/ascend/src/pages/routes.rs index 1e2674e..d2725a7 100644 --- a/crates/ascend/src/pages/routes.rs +++ b/crates/ascend/src/pages/routes.rs @@ -1,5 +1,5 @@ use crate::codec::ron::Ron; -use crate::codec::ron::RonEncoded; +use crate::components; use crate::components::header::HeaderItem; use crate::components::header::HeaderItems; use crate::components::header::StyledHeader; @@ -8,10 +8,6 @@ use crate::models::WallUid; use leptos::Params; use leptos::prelude::*; use leptos_router::params::Params; -use serde::Deserialize; -use serde::Serialize; -use std::collections::BTreeSet; -use std::ops::Deref; #[derive(Params, PartialEq, Clone)] struct RouteParams { @@ -25,20 +21,16 @@ pub fn Routes() -> impl leptos::IntoView { tracing::debug!("Enter"); let params = leptos_router::hooks::use_params::(); - let wall_uid = Signal::derive(move || params.get().map(|p| p.wall_uid.expect("wall_uid param is never None"))); + let wall_uid = Signal::derive(move || { + params + .get() + .expect("gets wall_uid from URL") + .wall_uid + .expect("wall_uid param is never None") + }); - let problems = Resource::>, Ron>::new_with_options( - move || wall_uid.get(), - move |wall_uid: Result| async move { - if let Ok(wall_uid) = wall_uid { - let wall = crate::server_functions::get_problems_for_wall(wall_uid).await.unwrap().into_inner(); - Some(wall) - } else { - None - } - }, - false, - ); + let wall = crate::resources::wall_by_uid(wall_uid); + let problems = crate::resources::problems_for_wall(wall_uid); let header_items = HeaderItems { left: vec![HeaderItem { @@ -52,23 +44,53 @@ pub fn Routes() -> impl leptos::IntoView { right: vec![], }; - leptos::view! { + let suspend = move || { + Suspend::new(async move { + let wall = wall.await; + let problems = problems.await; + + let v = move || -> Result<_, ServerFnError> { + let wall = wall.clone()?; + let problems = problems.clone()?; + + let wall_dimensions = models::WallDimensions { + rows: wall.rows, + cols: wall.cols, + }; + let problems_sample = move || problems.iter().take(10).cloned().collect::>(); + + Ok(view! { +
+ + } + } + /> +
+ }) + }; + + view! { + + {v} + + } + }) + }; + + view! {
- {move || wall_uid.get().map(|wall_uid| view! {})} + {move || view! { }} - - - } - } - /> + "loading"

}> + {suspend}
@@ -77,11 +99,12 @@ pub fn Routes() -> impl leptos::IntoView { #[component] #[tracing::instrument(skip_all)] -fn Problem(problem: models::Problem) -> impl IntoView { +fn Problem(#[prop(into)] dim: Signal, #[prop(into)] problem: Signal) -> impl IntoView { tracing::debug!("Enter"); view! { -

"problem"

+ +

{ move || problem.get().name.clone() }

} } diff --git a/crates/ascend/src/pages/wall.rs b/crates/ascend/src/pages/wall.rs index aebb637..8e400d6 100644 --- a/crates/ascend/src/pages/wall.rs +++ b/crates/ascend/src/pages/wall.rs @@ -1,16 +1,13 @@ -use crate::codec::ron::Ron; use crate::components::button::Button; use crate::components::header::HeaderItem; use crate::components::header::HeaderItems; use crate::components::header::StyledHeader; use crate::models; use crate::models::HoldRole; -use crate::models::WallUid; use leptos::Params; use leptos::prelude::*; use leptos::reactive::graph::ReactiveNode; use leptos_router::params::Params; -use rand::SeedableRng; #[derive(Params, PartialEq, Clone)] struct RouteParams { @@ -19,24 +16,19 @@ struct RouteParams { #[component] #[tracing::instrument(skip_all)] -pub fn Wall() -> impl leptos::IntoView { +pub fn Wall() -> impl IntoView { tracing::debug!("Enter"); let params = leptos_router::hooks::use_params::(); - let wall_uid = move || params.get().map(|p| p.wall_uid.expect("wall_uid param is never None")); + let wall_uid = Signal::derive(move || { + params + .get() + .expect("gets wall_uid from URL") + .wall_uid + .expect("wall_uid param is never None") + }); - let wall = Resource::, Ron>::new_with_options( - move || wall_uid(), - move |wall_uid: Result| async move { - if let Ok(wall_uid) = wall_uid { - let wall = crate::server_functions::get_wall(wall_uid).await.unwrap().into_inner(); - Some(wall) - } else { - None - } - }, - false, - ); + let wall = crate::resources::wall_by_uid(wall_uid); let header_items = move || HeaderItems { left: vec![], @@ -47,11 +39,11 @@ pub fn Wall() -> impl leptos::IntoView { right: vec![ HeaderItem { text: "Routes".to_string(), - link: wall_uid().map(|uid| format!("/wall/{uid}/routes")).ok(), + link: Some(format!("/wall/{}/routes", wall_uid.get())), }, HeaderItem { text: "Holds".to_string(), - link: wall_uid().map(|uid| format!("/wall/{uid}/edit")).ok(), + link: Some(format!("/wall/{}/edit", wall_uid.get())), }, ], }; @@ -65,7 +57,7 @@ pub fn Wall() -> impl leptos::IntoView { view! {

"Loading..."

} }> {move || Suspend::new(async move { - let wall: Option = wall.get().flatten(); + let wall: Option = wall.get().and_then(Result::ok); wall.map(|wall| { view! { } })