problems
This commit is contained in:
53
crates/ascend/src/components/problem.rs
Normal file
53
crates/ascend/src/components/problem.rs
Normal file
@@ -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<models::WallDimensions>, #[prop(into)] problem: Signal<models::Problem>) -> 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! { <Hold role /> };
|
||||
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! {
|
||||
<div class="grid grid-cols-[auto,1fr] gap-8">
|
||||
<div class=move || { grid_classes.clone() }>
|
||||
{holds}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn Hold(#[prop(into)] role: Signal<Option<HoldRole>>) -> 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! { <div class=class /> }
|
||||
}
|
||||
@@ -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<T> = Resource<Result<T, ServerFnError>, Ron>;
|
||||
|
||||
pub fn wall_by_uid(wall_uid: Signal<models::WallUid>) -> RonResource<models::Wall> {
|
||||
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<models::WallUid>) -> RonResource<Vec<models::Problem>> {
|
||||
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() {
|
||||
|
||||
@@ -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<v1::HoldPosition, v1::Hold>,
|
||||
pub problems: BTreeSet<ProblemUid>,
|
||||
}
|
||||
|
||||
#[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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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::<RouteParams>();
|
||||
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::<Option<Vec<models::Problem>>, Ron>::new_with_options(
|
||||
move || wall_uid.get(),
|
||||
move |wall_uid: Result<WallUid, _>| 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::<Vec<_>>();
|
||||
|
||||
Ok(view! {
|
||||
<div>
|
||||
<For
|
||||
each=problems_sample
|
||||
key=|problem| problem.uid
|
||||
children=move |problem: models::Problem| {
|
||||
view! {
|
||||
<Problem dim=wall_dimensions problem/>
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
})
|
||||
};
|
||||
|
||||
view! {
|
||||
<ErrorBoundary fallback=|errors| "error">
|
||||
{v}
|
||||
</ErrorBoundary>
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
view! {
|
||||
<div class="min-w-screen min-h-screen bg-slate-900">
|
||||
<StyledHeader items=header_items />
|
||||
|
||||
<div class="container mx-auto mt-2">
|
||||
{move || wall_uid.get().map(|wall_uid| view! {<Import wall_uid/>})}
|
||||
{move || view! { <Import wall_uid=wall_uid.get() /> }}
|
||||
|
||||
<Suspense fallback=|| view! {"loading"}>
|
||||
<For
|
||||
each=move || problems.get().flatten().into_iter().flat_map(|v| v.into_iter())
|
||||
key=|problem| problem.uid
|
||||
children=move |problem: models::Problem| {
|
||||
view! {
|
||||
<Problem problem/>
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Suspense fallback=|| view! {<p>"loading"</p>}>
|
||||
{suspend}
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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<models::WallDimensions>, #[prop(into)] problem: Signal<models::Problem>) -> impl IntoView {
|
||||
tracing::debug!("Enter");
|
||||
|
||||
view! {
|
||||
<p>"problem"</p>
|
||||
<components::Problem dim problem />
|
||||
<p>{ move || problem.get().name.clone() }</p>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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::<RouteParams>();
|
||||
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::<Option<models::Wall>, Ron>::new_with_options(
|
||||
move || wall_uid(),
|
||||
move |wall_uid: Result<WallUid, _>| 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! { <p>"Loading..."</p> }
|
||||
}>
|
||||
{move || Suspend::new(async move {
|
||||
let wall: Option<models::Wall> = wall.get().flatten();
|
||||
let wall: Option<models::Wall> = wall.get().and_then(Result::ok);
|
||||
wall.map(|wall| {
|
||||
view! { <Ready wall/> }
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user