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> {
|
pub fn wall_by_uid(wall_uid: Signal<models::WallUid>) -> RonResource<models::Wall> {
|
||||||
Resource::new_with_options(
|
Resource::new_with_options(
|
||||||
move || wall_uid.get(),
|
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,
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -35,7 +47,7 @@ pub mod resources {
|
|||||||
pub fn problems_for_wall(wall_uid: Signal<models::WallUid>) -> RonResource<Vec<models::Problem>> {
|
pub fn problems_for_wall(wall_uid: Signal<models::WallUid>) -> RonResource<Vec<models::Problem>> {
|
||||||
Resource::new_with_options(
|
Resource::new_with_options(
|
||||||
move || wall_uid.get(),
|
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,
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,13 @@ pub mod v2 {
|
|||||||
pub holds: BTreeMap<v1::HoldPosition, v1::Hold>,
|
pub holds: BTreeMap<v1::HoldPosition, v1::Hold>,
|
||||||
pub problems: BTreeSet<ProblemUid>,
|
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)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct WallDimensions {
|
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::button::Button;
|
||||||
use crate::components::header::HeaderItem;
|
use crate::components::header::HeaderItem;
|
||||||
use crate::components::header::HeaderItems;
|
use crate::components::header::HeaderItems;
|
||||||
@@ -6,7 +8,6 @@ use crate::models;
|
|||||||
use crate::models::HoldRole;
|
use crate::models::HoldRole;
|
||||||
use leptos::Params;
|
use leptos::Params;
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos::reactive::graph::ReactiveNode;
|
|
||||||
use leptos_router::params::Params;
|
use leptos_router::params::Params;
|
||||||
|
|
||||||
#[derive(Params, PartialEq, Clone)]
|
#[derive(Params, PartialEq, Clone)]
|
||||||
@@ -30,6 +31,35 @@ pub fn Wall() -> impl IntoView {
|
|||||||
|
|
||||||
let wall = crate::resources::wall_by_uid(wall_uid);
|
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 {
|
let header_items = move || HeaderItems {
|
||||||
left: vec![],
|
left: vec![],
|
||||||
middle: vec![HeaderItem {
|
middle: vec![HeaderItem {
|
||||||
@@ -57,9 +87,54 @@ pub fn Wall() -> impl IntoView {
|
|||||||
view! { <p>"Loading..."</p> }
|
view! { <p>"Loading..."</p> }
|
||||||
}>
|
}>
|
||||||
{move || Suspend::new(async move {
|
{move || Suspend::new(async move {
|
||||||
let wall: Option<models::Wall> = wall.get().and_then(Result::ok);
|
let wall = wall.await;
|
||||||
wall.map(|wall| {
|
let problem = problem.await;
|
||||||
view! { <Ready wall /> }
|
|
||||||
|
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>
|
</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]
|
#[component]
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
fn Hold(hold: models::Hold, #[prop(into)] role: Signal<Option<HoldRole>>) -> impl leptos::IntoView {
|
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
|
custom = RonEncoded
|
||||||
)]
|
)]
|
||||||
#[tracing::instrument(skip_all, err(Debug))]
|
#[tracing::instrument(skip_all, err(Debug))]
|
||||||
pub(crate) async fn get_problem(
|
pub(crate) async fn get_problem(wall_uid: models::WallUid, problem_uid: models::ProblemUid) -> Result<RonEncoded<models::Problem>, ServerFnError> {
|
||||||
wall_uid: models::WallUid,
|
|
||||||
problem_uid: models::ProblemUid,
|
|
||||||
) -> Result<RonEncoded<Option<models::Problem>>, ServerFnError> {
|
|
||||||
use crate::server::db::Database;
|
use crate::server::db::Database;
|
||||||
|
use crate::server::db::DatabaseOperationError;
|
||||||
tracing::debug!("Enter");
|
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 db = expect_context::<Database>();
|
||||||
let problem = db
|
let problem = db
|
||||||
.read(|txn| {
|
.read(|txn| {
|
||||||
let table = txn.open_table(crate::server::db::current::TABLE_PROBLEMS)?;
|
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)
|
Ok(problem)
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
Reference in New Issue
Block a user