wip
This commit is contained in:
@@ -27,7 +27,19 @@ pub mod resources {
|
||||
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) },
|
||||
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<models::WallUid>, problem_uid: Signal<models::ProblemUid>) -> RonResource<models::Problem> {
|
||||
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<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) },
|
||||
move |wall_uid| async move { crate::server_functions::get_problems_for_wall(wall_uid).await.map(RonEncoded::into_inner) },
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,6 +35,13 @@ pub mod v2 {
|
||||
pub holds: BTreeMap<v1::HoldPosition, v1::Hold>,
|
||||
pub problems: BTreeSet<ProblemUid>,
|
||||
}
|
||||
impl Wall {
|
||||
pub fn random_problem(&self) -> Option<ProblemUid> {
|
||||
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 {
|
||||
|
||||
@@ -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<Option<models::Problem>, 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! { <p>"Loading..."</p> }
|
||||
}>
|
||||
{move || Suspend::new(async move {
|
||||
let wall: Option<models::Wall> = wall.get().and_then(Result::ok);
|
||||
wall.map(|wall| {
|
||||
view! { <Ready wall /> }
|
||||
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! { <Hold role hold=hold.clone() /> };
|
||||
cells.push(cell);
|
||||
}
|
||||
let grid_classes = format!(
|
||||
"grid grid-rows-{} grid-cols-{} gap-3",
|
||||
wall.rows,
|
||||
wall.cols,
|
||||
);
|
||||
view! {
|
||||
<div class="grid grid-cols-[auto,1fr] gap-8">
|
||||
// Render the wall
|
||||
<div
|
||||
style="max-height: 90vh; max-width: 90vh;"
|
||||
class=move || { grid_classes.clone() }
|
||||
>
|
||||
{cells}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
// TODO:
|
||||
// <p>{current_problem.read().as_ref().map(|p| p.name.clone())}</p>
|
||||
// <p>{current_problem.read().as_ref().map(|p| p.set_by.clone())}</p>
|
||||
<div></div>
|
||||
|
||||
<Button
|
||||
onclick=move |_| {
|
||||
set_problem_uid.set(wall.random_problem());
|
||||
}
|
||||
text="➤ Next problem"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
})
|
||||
})}
|
||||
</Suspense>
|
||||
@@ -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::<models::Problem>);
|
||||
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! { <Hold role hold=hold.clone() /> };
|
||||
cells.push(cell);
|
||||
}
|
||||
|
||||
let grid_classes = format!("grid grid-rows-{} grid-cols-{} gap-3", wall.rows, wall.cols);
|
||||
|
||||
tracing::debug!("view");
|
||||
view! {
|
||||
<div class="grid grid-cols-[auto,1fr] gap-8">
|
||||
// Render the wall
|
||||
<div style="max-height: 90vh; max-width: 90vh;" class=move || { grid_classes.clone() }>
|
||||
{cells}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
// TODO:
|
||||
// <p>{current_problem.read().as_ref().map(|p| p.name.clone())}</p>
|
||||
<div>// <p>{current_problem.read().as_ref().map(|p| p.set_by.clone())}</p>
|
||||
</div>
|
||||
|
||||
<Button onclick=move |_| problem_fetcher.mark_dirty() text="➤ Next problem" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn Hold(hold: models::Hold, #[prop(into)] role: Signal<Option<HoldRole>>) -> impl leptos::IntoView {
|
||||
|
||||
@@ -133,18 +133,26 @@ pub(crate) async fn get_problems_for_wall(wall_uid: models::WallUid) -> Result<R
|
||||
custom = RonEncoded
|
||||
)]
|
||||
#[tracing::instrument(skip_all, err(Debug))]
|
||||
pub(crate) async fn get_problem(
|
||||
wall_uid: models::WallUid,
|
||||
problem_uid: models::ProblemUid,
|
||||
) -> Result<RonEncoded<Option<models::Problem>>, ServerFnError> {
|
||||
pub(crate) async fn get_problem(wall_uid: models::WallUid, problem_uid: models::ProblemUid) -> Result<RonEncoded<models::Problem>, 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::<Database>();
|
||||
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?;
|
||||
|
||||
Reference in New Issue
Block a user