diff --git a/crates/ascend/src/lib.rs b/crates/ascend/src/lib.rs index 1148ad6..367b51e 100644 --- a/crates/ascend/src/lib.rs +++ b/crates/ascend/src/lib.rs @@ -27,7 +27,19 @@ pub mod resources { 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) }, + move |wall_uid| async move { crate::server_functions::get_wall(wall_uid).await.map(RonEncoded::into_inner) }, + false, + ) + } + + pub fn problem_by_uid(wall_uid: Signal, problem_uid: Signal) -> RonResource { + Resource::new_with_options( + move || (wall_uid.get(), problem_uid.get()), + move |(wall_uid, problem_uid)| async move { + crate::server_functions::get_problem(wall_uid, problem_uid) + .await + .map(RonEncoded::into_inner) + }, false, ) } @@ -35,7 +47,7 @@ pub mod resources { 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) }, + move |wall_uid| async move { crate::server_functions::get_problems_for_wall(wall_uid).await.map(RonEncoded::into_inner) }, false, ) } diff --git a/crates/ascend/src/models.rs b/crates/ascend/src/models.rs index 4b6e077..7763b8a 100644 --- a/crates/ascend/src/models.rs +++ b/crates/ascend/src/models.rs @@ -35,6 +35,13 @@ pub mod v2 { pub holds: BTreeMap, pub problems: BTreeSet, } + impl Wall { + pub fn random_problem(&self) -> Option { + use rand::seq::IteratorRandom; + let mut rng = rand::rng(); + self.problems.iter().choose(&mut rng).copied() + } + } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct WallDimensions { diff --git a/crates/ascend/src/pages/wall.rs b/crates/ascend/src/pages/wall.rs index ba84109..b72d47b 100644 --- a/crates/ascend/src/pages/wall.rs +++ b/crates/ascend/src/pages/wall.rs @@ -1,3 +1,5 @@ +use crate::codec::ron::Ron; +use crate::codec::ron::RonEncoded; use crate::components::button::Button; use crate::components::header::HeaderItem; use crate::components::header::HeaderItems; @@ -6,7 +8,6 @@ use crate::models; use crate::models::HoldRole; use leptos::Params; use leptos::prelude::*; -use leptos::reactive::graph::ReactiveNode; use leptos_router::params::Params; #[derive(Params, PartialEq, Clone)] @@ -30,6 +31,35 @@ pub fn Wall() -> impl IntoView { let wall = crate::resources::wall_by_uid(wall_uid); + let (problem_uid, set_problem_uid) = signal(None); + + let mut init_problem = false; + Effect::new(move || { + if !init_problem { + if let Some(Ok(wall)) = &*wall.read() { + set_problem_uid.set(wall.random_problem()); + init_problem = true; + } + } + }); + + let problem: Resource, Ron> = Resource::new_with_options( + move || (wall_uid.get(), problem_uid.get()), + move |(wall_uid, problem_uid)| async move { + let Some(problem_uid) = problem_uid else { + return None; + }; + crate::server_functions::get_problem(wall_uid, problem_uid) + .await + .map(RonEncoded::into_inner) + .inspect_err(|err| { + tracing::error!("{err}"); + }) + .ok() + }, + false, + ); + let header_items = move || HeaderItems { left: vec![], middle: vec![HeaderItem { @@ -57,9 +87,54 @@ pub fn Wall() -> impl IntoView { view! {

"Loading..."

} }> {move || Suspend::new(async move { - let wall: Option = wall.get().and_then(Result::ok); - wall.map(|wall| { - view! { } + let wall = wall.await; + let problem = problem.await; + + wall.map(move |wall| { + let mut cells = vec![]; + for (&hold_position, hold) in &wall.holds { + let problem = problem.clone(); + let role = move || { + problem + .clone() + .and_then(|problem| { + problem.holds.get(&hold_position).copied() + }) + }; + let role = Signal::derive(role); + let cell = view! { }; + cells.push(cell); + } + let grid_classes = format!( + "grid grid-rows-{} grid-cols-{} gap-3", + wall.rows, + wall.cols, + ); + view! { +
+ // Render the wall +
+ {cells} +
+ +
+ // TODO: + //

{current_problem.read().as_ref().map(|p| p.name.clone())}

+ //

{current_problem.read().as_ref().map(|p| p.set_by.clone())}

+
+ +
+
+ } }) })} @@ -68,70 +143,6 @@ pub fn Wall() -> impl IntoView { } } -#[component] -#[tracing::instrument(skip_all)] -fn Ready(wall: models::Wall) -> impl leptos::IntoView { - tracing::debug!("ready"); - - let (current_problem, current_problem_writer) = signal(None::); - let problem_fetcher = { - LocalResource::new(move || { - let wall_uid = wall.uid; - let problems = wall.problems.clone(); - - async move { - tracing::info!("Loading random problem"); - - use rand::seq::IteratorRandom; - let mut rng = rand::rng(); - let random_problem = problems.iter().choose(&mut rng); - - let problem = if let Some(random_problem) = random_problem { - crate::server_functions::get_problem(wall_uid, *random_problem) - .await - .expect("cannot get random problem") - .into_inner() - } else { - tracing::info!("Wall has no problems"); - None - }; - - current_problem_writer.set(problem); - } - }) - }; - - let mut cells = vec![]; - for (&hold_position, hold) in &wall.holds { - let role = move || current_problem.get().and_then(|problem| problem.holds.get(&hold_position).copied()); - let role = Signal::derive(role); - - let cell = view! { }; - cells.push(cell); - } - - let grid_classes = format!("grid grid-rows-{} grid-cols-{} gap-3", wall.rows, wall.cols); - - tracing::debug!("view"); - view! { -
- // Render the wall -
- {cells} -
- -
- // TODO: - //

{current_problem.read().as_ref().map(|p| p.name.clone())}

-
//

{current_problem.read().as_ref().map(|p| p.set_by.clone())}

-
- -
-
- } -} - #[component] #[tracing::instrument(skip_all)] fn Hold(hold: models::Hold, #[prop(into)] role: Signal>) -> impl leptos::IntoView { diff --git a/crates/ascend/src/server_functions.rs b/crates/ascend/src/server_functions.rs index 83d9cec..a9940ab 100644 --- a/crates/ascend/src/server_functions.rs +++ b/crates/ascend/src/server_functions.rs @@ -133,18 +133,26 @@ pub(crate) async fn get_problems_for_wall(wall_uid: models::WallUid) -> Result Result>, ServerFnError> { +pub(crate) async fn get_problem(wall_uid: models::WallUid, problem_uid: models::ProblemUid) -> Result, ServerFnError> { use crate::server::db::Database; + use crate::server::db::DatabaseOperationError; tracing::debug!("Enter"); + #[derive(Debug, derive_more::Error, derive_more::Display)] + enum Error { + #[display("Problem not found: {_0:?}")] + NotFound(#[error(not(source))] models::ProblemUid), + } + let db = expect_context::(); let problem = db .read(|txn| { let table = txn.open_table(crate::server::db::current::TABLE_PROBLEMS)?; - let problem = table.get((wall_uid, problem_uid))?.map(|guard| guard.value()); + let problem = table + .get((wall_uid, problem_uid))? + .ok_or(Error::NotFound(problem_uid)) + .map_err(DatabaseOperationError::custom)? + .value(); Ok(problem) }) .await?;