feat: problem filtering
This commit is contained in:
parent
9898af1bf7
commit
83bd8e0e5e
@ -23,7 +23,7 @@ pub fn ProblemInfo(#[prop(into)] problem: Signal<models::Problem>) -> impl IntoV
|
|||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
fn NameValue(#[prop(into)] name: Signal<String>, #[prop(into)] value: Signal<String>) -> impl IntoView {
|
fn NameValue(#[prop(into)] name: Signal<String>, #[prop(into)] value: Signal<String>) -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
<p class="text-sm font-light mr-4 text-right text-orange-300">{name.get()}</p>
|
<p class="font-light mr-4 text-right text-orange-300">{name.get()}</p>
|
||||||
<p class="text-white">{value.get()}</p>
|
<p class="text-white">{value.get()}</p>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,13 +94,6 @@ pub mod v2 {
|
|||||||
pub holds: BTreeMap<v1::HoldPosition, Hold>,
|
pub holds: BTreeMap<v1::HoldPosition, 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 {
|
||||||
|
@ -70,6 +70,7 @@ pub fn Wall() -> impl IntoView {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let wall = crate::resources::wall_by_uid(wall_uid);
|
let wall = crate::resources::wall_by_uid(wall_uid);
|
||||||
|
let problems = crate::resources::problems_for_wall(wall_uid);
|
||||||
|
|
||||||
let header_items = move || HeaderItems {
|
let header_items = move || HeaderItems {
|
||||||
left: vec![],
|
left: vec![],
|
||||||
@ -98,7 +99,8 @@ pub fn Wall() -> impl IntoView {
|
|||||||
{move || Suspend::new(async move {
|
{move || Suspend::new(async move {
|
||||||
tracing::info!("executing main suspend");
|
tracing::info!("executing main suspend");
|
||||||
let wall = wall.await?;
|
let wall = wall.await?;
|
||||||
let v = view! { <WithWall wall /> };
|
let problems = problems.await?;
|
||||||
|
let v = view! { <WithWall wall problems /> };
|
||||||
Ok::<_, ServerFnError>(v)
|
Ok::<_, ServerFnError>(v)
|
||||||
})}
|
})}
|
||||||
</Transition>
|
</Transition>
|
||||||
@ -117,7 +119,7 @@ fn WithProblem(#[prop(into)] problem: Signal<models::Problem>) -> impl IntoView
|
|||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
|
fn WithWall(#[prop(into)] wall: Signal<models::Wall>, #[prop(into)] problems: Signal<Vec<models::Problem>>) -> impl IntoView {
|
||||||
tracing::trace!("Enter");
|
tracing::trace!("Enter");
|
||||||
|
|
||||||
let wall_uid = Signal::derive(move || wall.read().uid);
|
let wall_uid = Signal::derive(move || wall.read().uid);
|
||||||
@ -137,18 +139,36 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let filtered_problems = Memo::new(move |_prev_val| {
|
||||||
|
let filter_holds = filter_holds.get();
|
||||||
|
problems.with(|problems| {
|
||||||
|
problems
|
||||||
|
.iter()
|
||||||
|
.filter(|problem| filter_holds.iter().all(|hold_pos| problem.holds.contains_key(hold_pos)))
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<models::Problem>>()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
let problem = crate::resources::problem_by_uid_optional(wall_uid, problem_uid.into());
|
let problem = crate::resources::problem_by_uid_optional(wall_uid, problem_uid.into());
|
||||||
let user_interaction = crate::resources::user_interaction(wall_uid, problem_uid.into());
|
let user_interaction = crate::resources::user_interaction(wall_uid, problem_uid.into());
|
||||||
|
|
||||||
let fn_next_problem = move |wall: &models::Wall| {
|
let fn_next_problem = move || {
|
||||||
set_problem_uid.set(wall.random_problem());
|
let problems = filtered_problems.read();
|
||||||
|
|
||||||
|
use rand::seq::IteratorRandom;
|
||||||
|
let mut rng = rand::rng();
|
||||||
|
let problem = problems.iter().choose(&mut rng);
|
||||||
|
let problem_uid = problem.map(|p| p.uid);
|
||||||
|
|
||||||
|
set_problem_uid.set(problem_uid);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set a problem when wall is set (loaded)
|
// Set a problem when wall is set (loaded)
|
||||||
Effect::new(move |_prev_value| {
|
Effect::new(move |_prev_value| {
|
||||||
if problem_uid.get().is_none() {
|
if problem_uid.get().is_none() {
|
||||||
tracing::debug!("Setting next problem");
|
tracing::debug!("Setting next problem");
|
||||||
fn_next_problem(&wall.read());
|
fn_next_problem();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -156,6 +176,7 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
|
|||||||
let problem_signal = Signal::derive(move || problem.get().transpose().map(Option::flatten));
|
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
|
||||||
set_filter_holds.update(|set| {
|
set_filter_holds.update(|set| {
|
||||||
if !set.remove(&hold_position) {
|
if !set.remove(&hold_position) {
|
||||||
set.insert(hold_position);
|
set.insert(hold_position);
|
||||||
@ -163,26 +184,18 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let grid = {
|
let grid = view! {
|
||||||
let wall = wall.clone();
|
<Transition fallback=|| ()>
|
||||||
view! {
|
{move || {
|
||||||
<Transition fallback=|| ()>
|
Suspend::new(async move {
|
||||||
{
|
tracing::debug!("executing grid suspend");
|
||||||
let wall = wall.clone();
|
let view = view! {
|
||||||
move || {
|
<Grid wall=wall.get() problem=problem_signal on_click_hold />
|
||||||
let wall = wall.clone();
|
};
|
||||||
Suspend::new(async move {
|
Ok::<_, ServerFnError>(view)
|
||||||
let wall = wall.clone();
|
})
|
||||||
tracing::info!("executing grid suspend");
|
}}
|
||||||
let view = view! {
|
</Transition>
|
||||||
<Grid wall=wall.get() problem=problem_signal on_click_hold />
|
|
||||||
};
|
|
||||||
Ok::<_, ServerFnError>(view)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</Transition>
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let filter = move || {
|
let filter = move || {
|
||||||
@ -205,14 +218,30 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
view! { <div class="grid grid-cols-4 gap-2">{cells}</div> }
|
let problems_counter = {
|
||||||
|
let name = view! { <p class="font-light mr-4 text-right text-orange-300">{"Problems:"}</p> };
|
||||||
|
let value = view! { <p class="text-white">{filtered_problems.read().len()}</p> };
|
||||||
|
view! {
|
||||||
|
<div class="grid grid-rows-none gap-y-1 gap-x-0.5 grid-cols-[auto,1fr]">
|
||||||
|
{name} {value}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let sep = (!cells.is_empty()).then_some(view! { <Separator /> });
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div class="grid grid-cols-5 gap-1">{cells}</div>
|
||||||
|
{sep}
|
||||||
|
{problems_counter}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div class="inline-grid grid-cols-1 gap-8 md:grid-cols-[auto,1fr]">
|
<div class="inline-grid grid-cols-1 gap-8 md:grid-cols-[auto,1fr]">
|
||||||
<div>{grid}</div>
|
<div>{grid}</div>
|
||||||
|
|
||||||
<div class="flex flex-col w-screen-sm">
|
<div class="flex flex-col" style="width:38rem">
|
||||||
<Section title="Filter">{filter}</Section>
|
<Section title="Filter">{filter}</Section>
|
||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
@ -222,7 +251,7 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
|
|||||||
<Button
|
<Button
|
||||||
icon=Icon::ArrowPath
|
icon=Icon::ArrowPath
|
||||||
text="Next problem"
|
text="Next problem"
|
||||||
onclick=move |_| fn_next_problem(&wall.read())
|
onclick=move |_| fn_next_problem()
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user