refactor
This commit is contained in:
parent
d9406f98d1
commit
f1be2dd735
@ -55,7 +55,7 @@ pub fn Routes() -> impl IntoView {
|
|||||||
rows: wall.rows,
|
rows: wall.rows,
|
||||||
cols: wall.cols,
|
cols: wall.cols,
|
||||||
};
|
};
|
||||||
let problems_sample = move || problems.iter().take(10).cloned().collect::<Vec<_>>();
|
let problems_sample = move || problems.values().take(10).cloned().collect::<Vec<_>>();
|
||||||
|
|
||||||
Ok(view! {
|
Ok(view! {
|
||||||
<div>
|
<div>
|
||||||
|
@ -91,7 +91,7 @@ fn WithProblem(#[prop(into)] problem: Signal<models::Problem>) -> impl IntoView
|
|||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
fn WithWall(
|
fn WithWall(
|
||||||
#[prop(into)] wall: Signal<models::Wall>,
|
#[prop(into)] wall: Signal<models::Wall>,
|
||||||
#[prop(into)] problems: Signal<Vec<models::Problem>>,
|
#[prop(into)] problems: Signal<BTreeMap<models::ProblemUid, models::Problem>>,
|
||||||
#[prop(into)] user_interactions: Signal<BTreeMap<models::ProblemUid, models::UserInteraction>>,
|
#[prop(into)] user_interactions: Signal<BTreeMap<models::ProblemUid, models::UserInteraction>>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
tracing::trace!("Enter");
|
tracing::trace!("Enter");
|
||||||
@ -100,6 +100,9 @@ fn WithWall(
|
|||||||
|
|
||||||
let (problem_uid, set_problem_uid) = leptos_router::hooks::query_signal::<models::ProblemUid>("problem");
|
let (problem_uid, set_problem_uid) = leptos_router::hooks::query_signal::<models::ProblemUid>("problem");
|
||||||
|
|
||||||
|
let problem = signals::problem(problems, problem_uid.into());
|
||||||
|
let user_interaction = signals::user_interaction(user_interactions, problem_uid.into());
|
||||||
|
|
||||||
// Filter
|
// Filter
|
||||||
let (filter_holds, set_filter_holds) = signal(BTreeSet::new());
|
let (filter_holds, set_filter_holds) = signal(BTreeSet::new());
|
||||||
let _filter_add_hold = move |hold_pos: models::HoldPosition| {
|
let _filter_add_hold = move |hold_pos: models::HoldPosition| {
|
||||||
@ -118,22 +121,18 @@ fn WithWall(
|
|||||||
problems.with(|problems| {
|
problems.with(|problems| {
|
||||||
problems
|
problems
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|problem| filter_holds.iter().all(|hold_pos| problem.holds.contains_key(hold_pos)))
|
.filter(|(_, problem)| filter_holds.iter().all(|hold_pos| problem.holds.contains_key(hold_pos)))
|
||||||
.cloned()
|
.map(|(problem_uid, problem)| (*problem_uid, problem.clone()))
|
||||||
.collect::<Vec<models::Problem>>()
|
.collect::<BTreeMap<models::ProblemUid, models::Problem>>()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let problem = crate::resources::problem_by_uid_optional(wall_uid, problem_uid.into());
|
|
||||||
let user_interaction = signals::user_interaction(user_interactions, problem_uid.into());
|
|
||||||
|
|
||||||
let fn_next_problem = move || {
|
let fn_next_problem = move || {
|
||||||
let problems = filtered_problems.read();
|
let problems = filtered_problems.read();
|
||||||
|
|
||||||
use rand::seq::IteratorRandom;
|
use rand::seq::IteratorRandom;
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
let problem = problems.iter().choose(&mut rng);
|
let problem_uid = problems.keys().copied().choose(&mut rng);
|
||||||
let problem_uid = problem.map(|p| p.uid);
|
|
||||||
|
|
||||||
set_problem_uid.set(problem_uid);
|
set_problem_uid.set(problem_uid);
|
||||||
};
|
};
|
||||||
@ -146,9 +145,6 @@ fn WithWall(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// merge outer option (resource hasn't resolved yet) with inner option (there is no problem for the wall)
|
|
||||||
let problem_signal = Signal::derive(move || problem.get().transpose().map(Option::flatten));
|
|
||||||
|
|
||||||
let on_click_hold = move |hold_position: models::HoldPosition| {
|
let on_click_hold = move |hold_position: models::HoldPosition| {
|
||||||
// Add/Remove hold position to problem filter
|
// Add/Remove hold position to problem filter
|
||||||
set_filter_holds.update(|set| {
|
set_filter_holds.update(|set| {
|
||||||
@ -163,9 +159,7 @@ fn WithWall(
|
|||||||
{move || {
|
{move || {
|
||||||
Suspend::new(async move {
|
Suspend::new(async move {
|
||||||
tracing::debug!("executing grid suspend");
|
tracing::debug!("executing grid suspend");
|
||||||
let view = view! {
|
let view = view! { <Grid wall=wall.get() problem on_click_hold /> };
|
||||||
<Grid wall=wall.get() problem=problem_signal on_click_hold />
|
|
||||||
};
|
|
||||||
Ok::<_, ServerFnError>(view)
|
Ok::<_, ServerFnError>(view)
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
@ -213,8 +207,8 @@ fn WithWall(
|
|||||||
let mut interaction_counters = InteractionCounters::default();
|
let mut interaction_counters = InteractionCounters::default();
|
||||||
let interaction_counters_view = {
|
let interaction_counters_view = {
|
||||||
let user_ints = user_interactions.read();
|
let user_ints = user_interactions.read();
|
||||||
for problem in filtered_problems.read().iter() {
|
for problem_uid in filtered_problems.read().keys() {
|
||||||
if let Some(user_int) = user_ints.get(&problem.uid) {
|
if let Some(user_int) = user_ints.get(problem_uid) {
|
||||||
match user_int.best_attempt().map(|da| da.attempt) {
|
match user_int.best_attempt().map(|da| da.attempt) {
|
||||||
Some(models::Attempt::Flash) => interaction_counters.flash += 1,
|
Some(models::Attempt::Flash) => interaction_counters.flash += 1,
|
||||||
Some(models::Attempt::Send) => interaction_counters.send += 1,
|
Some(models::Attempt::Send) => interaction_counters.send += 1,
|
||||||
@ -292,17 +286,7 @@ fn WithWall(
|
|||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
<Section title="Problem">
|
<Section title="Problem">
|
||||||
<Transition fallback=|| ()>
|
{move || problem.get().map(|p| view! { <WithProblem problem=p /> })}
|
||||||
{move || Suspend::new(async move {
|
|
||||||
tracing::info!("executing problem suspend");
|
|
||||||
let problem = problem.await?;
|
|
||||||
let view = problem
|
|
||||||
.map(|problem| {
|
|
||||||
view! { <WithProblem problem /> }
|
|
||||||
});
|
|
||||||
Ok::<_, ServerFnError>(view)
|
|
||||||
})}
|
|
||||||
</Transition>
|
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
@ -429,7 +413,7 @@ fn History(#[prop(into)] user_interaction: Signal<Option<models::UserInteraction
|
|||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
fn Grid(
|
fn Grid(
|
||||||
wall: models::Wall,
|
wall: models::Wall,
|
||||||
#[prop(into)] problem: Signal<Result<Option<models::Problem>, ServerFnError>>,
|
#[prop(into)] problem: Signal<Option<models::Problem>>,
|
||||||
on_click_hold: impl Fn(models::HoldPosition) + 'static,
|
on_click_hold: impl Fn(models::HoldPosition) + 'static,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
tracing::debug!("Enter");
|
tracing::debug!("Enter");
|
||||||
@ -438,8 +422,7 @@ fn Grid(
|
|||||||
|
|
||||||
let mut cells = vec![];
|
let mut cells = vec![];
|
||||||
for (&hold_position, hold) in &wall.holds {
|
for (&hold_position, hold) in &wall.holds {
|
||||||
let role = move || problem.get().map(|o| o.and_then(|p| p.holds.get(&hold_position).copied()));
|
let role = Signal::derive(move || problem.get().and_then(|p| p.holds.get(&hold_position).copied()));
|
||||||
let role = Signal::derive(role);
|
|
||||||
|
|
||||||
let on_click = {
|
let on_click = {
|
||||||
let on_click_hold = std::rc::Rc::clone(&on_click_hold);
|
let on_click_hold = std::rc::Rc::clone(&on_click_hold);
|
||||||
@ -477,14 +460,14 @@ fn Hold(
|
|||||||
|
|
||||||
#[prop(optional)]
|
#[prop(optional)]
|
||||||
#[prop(into)]
|
#[prop(into)]
|
||||||
role: Option<Signal<Result<Option<HoldRole>, ServerFnError>>>,
|
role: Option<Signal<Option<HoldRole>>>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
tracing::trace!("Enter");
|
tracing::trace!("Enter");
|
||||||
|
|
||||||
move || {
|
move || {
|
||||||
let mut class = "bg-sky-100 aspect-square rounded-sm hover:brightness-125".to_string();
|
let mut class = "bg-sky-100 aspect-square rounded-sm hover:brightness-125".to_string();
|
||||||
if let Some(role) = role {
|
if let Some(role) = role {
|
||||||
let role = role.get()?;
|
let role = role.get();
|
||||||
|
|
||||||
let role_classes = match role {
|
let role_classes = match role {
|
||||||
Some(HoldRole::Start) => Some("outline outline-3 outline-green-500"),
|
Some(HoldRole::Start) => Some("outline outline-3 outline-green-500"),
|
||||||
@ -504,8 +487,7 @@ fn Hold(
|
|||||||
view! { <img class="object-cover w-full h-full" srcset=srcset /> }
|
view! { <img class="object-cover w-full h-full" srcset=srcset /> }
|
||||||
});
|
});
|
||||||
|
|
||||||
let view = view! { <div class=class>{img}</div> };
|
view! { <div class=class>{img}</div> }
|
||||||
Ok::<_, ServerFnError>(view)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -549,4 +531,15 @@ mod signals {
|
|||||||
user_interactions.get(&problem_uid).cloned()
|
user_interactions.get(&problem_uid).cloned()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn problem(
|
||||||
|
problems: Signal<BTreeMap<models::ProblemUid, models::Problem>>,
|
||||||
|
problem_uid: Signal<Option<models::ProblemUid>>,
|
||||||
|
) -> Signal<Option<models::Problem>> {
|
||||||
|
Signal::derive(move || {
|
||||||
|
let problem_uid = problem_uid.get()?;
|
||||||
|
let problems = problems.read();
|
||||||
|
problems.get(&problem_uid).cloned()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ pub fn problem_by_uid_optional(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all problems for a wall
|
/// Returns all problems for a wall
|
||||||
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<BTreeMap<models::ProblemUid, models::Problem>> {
|
||||||
Resource::new_with_options(
|
Resource::new_with_options(
|
||||||
move || wall_uid.get(),
|
move || wall_uid.get(),
|
||||||
move |wall_uid| 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) },
|
||||||
|
@ -9,6 +9,7 @@ use leptos::prelude::*;
|
|||||||
use leptos::server;
|
use leptos::server;
|
||||||
use server_fn::ServerFnError;
|
use server_fn::ServerFnError;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use type_toppings::IteratorExt;
|
||||||
|
|
||||||
#[server(
|
#[server(
|
||||||
input = Ron,
|
input = Ron,
|
||||||
@ -76,7 +77,9 @@ pub(crate) async fn get_wall_by_uid(wall_uid: models::WallUid) -> Result<RonEnco
|
|||||||
custom = RonEncoded
|
custom = RonEncoded
|
||||||
)]
|
)]
|
||||||
#[tracing::instrument(err(Debug))]
|
#[tracing::instrument(err(Debug))]
|
||||||
pub(crate) async fn get_problems_for_wall(wall_uid: models::WallUid) -> Result<RonEncoded<Vec<models::Problem>>, ServerFnError> {
|
pub(crate) async fn get_problems_for_wall(
|
||||||
|
wall_uid: models::WallUid,
|
||||||
|
) -> Result<RonEncoded<BTreeMap<models::ProblemUid, models::Problem>>, ServerFnError> {
|
||||||
use crate::server::db::Database;
|
use crate::server::db::Database;
|
||||||
use crate::server::db::DatabaseOperationError;
|
use crate::server::db::DatabaseOperationError;
|
||||||
use leptos::prelude::expect_context;
|
use leptos::prelude::expect_context;
|
||||||
@ -90,7 +93,7 @@ pub(crate) async fn get_problems_for_wall(wall_uid: models::WallUid) -> Result<R
|
|||||||
DatabaseOperation(DatabaseOperationError),
|
DatabaseOperation(DatabaseOperationError),
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn inner(wall_uid: models::WallUid) -> Result<Vec<models::Problem>, Error> {
|
async fn inner(wall_uid: models::WallUid) -> Result<BTreeMap<models::ProblemUid, models::Problem>, Error> {
|
||||||
let db = expect_context::<Database>();
|
let db = expect_context::<Database>();
|
||||||
|
|
||||||
let problems = db
|
let problems = db
|
||||||
@ -109,12 +112,15 @@ pub(crate) async fn get_problems_for_wall(wall_uid: models::WallUid) -> Result<R
|
|||||||
let problems_table = txn.open_table(crate::server::db::current::TABLE_PROBLEMS)?;
|
let problems_table = txn.open_table(crate::server::db::current::TABLE_PROBLEMS)?;
|
||||||
tracing::debug!("opened problems table");
|
tracing::debug!("opened problems table");
|
||||||
|
|
||||||
let mut problems = Vec::new();
|
let problems = wall
|
||||||
for &problem_uid in &wall.problems {
|
.problems
|
||||||
if let Some(problem) = problems_table.get((wall_uid, problem_uid))? {
|
.iter()
|
||||||
problems.push(problem.value());
|
.map(|problem_uid| problems_table.get(&(wall_uid, *problem_uid)))
|
||||||
}
|
.filter_map(|res| res.transpose())
|
||||||
}
|
.map_res(|guard| guard.value())
|
||||||
|
.map_res(|problem| (problem.uid, problem))
|
||||||
|
.collect::<Result<BTreeMap<models::ProblemUid, models::Problem>, _>>()?;
|
||||||
|
|
||||||
Ok(problems)
|
Ok(problems)
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user