This commit is contained in:
2025-04-01 15:02:49 +02:00
parent 91bea767d0
commit 0a95aca872
5 changed files with 43 additions and 29 deletions

View File

@@ -19,7 +19,7 @@ pub fn Problem(
for row in 0..dim.get().rows {
for col in 0..dim.get().cols {
let hold_position = models::HoldPosition { row, col };
let role = move || problem.get().holds.get(&hold_position).copied();
let role = move || problem.read().pattern.pattern.get(&hold_position).copied();
let role = Signal::derive(role);
let hold = view! { <Hold role /> };
holds.push(hold);

View File

@@ -74,13 +74,12 @@ impl Pattern {
}
impl UserInteraction {
pub(crate) fn new(wall_uid: WallUid, problem_uid: ProblemUid) -> Self {
pub(crate) fn new(wall_uid: WallUid, problem: Problem) -> Self {
Self {
wall_uid,
problem_uid,
problem,
is_favorite: false,
attempted_on: BTreeMap::new(),
is_saved: false,
}
}

View File

@@ -13,6 +13,7 @@ use leptos::prelude::*;
use leptos_router::params::Params;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::ops::Deref;
#[derive(Params, PartialEq, Clone)]
struct RouteParams {
@@ -35,7 +36,7 @@ pub fn Page() -> impl IntoView {
});
let wall = crate::resources::wall_by_uid(wall_uid);
let user_interactions = crate::resources::user_interactions(wall_uid);
let user_interactions = crate::resources::user_interactions_for_wall(wall_uid);
leptos::view! {
<div class="min-h-screen min-w-screen bg-neutral-950">
@@ -79,7 +80,7 @@ fn Controller(
crate::tracing::on_enter!();
// Extract data from URL
let (problem_url, set_problem_url) = leptos_router::hooks::query_signal::<models::Problem>("problem");
let (problem, set_problem) = leptos_router::hooks::query_signal::<models::Problem>("problem");
// Filter
let (filter_holds, set_filter_holds) = signal(BTreeSet::new());
@@ -91,9 +92,8 @@ fn Controller(
// Derive signals
let wall_uid = signals::wall_uid(wall);
let problems = signals::problems(wall);
let user_interaction = signals::user_interaction(user_interactions.into(), problem_url.into());
let filtered_problems = signals::filtered_problems(problems, filter_holds.into());
let user_interaction = signals::user_interaction(user_interactions.into(), problem.into());
let filtered_problems = signals::filtered_problems(wall, filter_holds.into());
let todays_attempt = signals::todays_attempt(user_interaction);
let latest_attempt = signals::latest_attempt(user_interaction);
@@ -107,13 +107,13 @@ fn Controller(
let cb_set_random_problem: Callback<()> = Callback::new(move |_| {
// TODO: remove current problem from population
let population = filtered_problems.read();
let population = population.keys().copied();
let population = population.deref();
use rand::seq::IteratorRandom;
let mut rng = rand::rng();
let problem_uid = population.choose(&mut rng);
let problem = population.iter().choose(&mut rng);
set_problem_url.set(problem_uid);
set_problem.set(problem.cloned());
});
// Callback: On click hold, Add/Remove hold position to problem filter
@@ -127,7 +127,7 @@ fn Controller(
// Set a problem when wall is set (loaded)
Effect::new(move |_prev_value| {
if problem_url.get().is_none() {
if problem.read().is_none() {
tracing::debug!("Setting initial problem");
cb_set_random_problem.run(());
}
@@ -138,14 +138,14 @@ fn Controller(
if let Some(Ok(v)) = upsert_todays_attempt.value().get() {
let v = v.into_inner();
user_interactions.update(|map| {
map.insert(v.problem_uid, v);
map.insert(v.problem.clone(), v);
});
}
});
provide_context(Context {
wall,
problem,
problem: problem.into(),
cb_click_hold,
user_interaction,
latest_attempt,
@@ -302,8 +302,8 @@ fn Filter() -> impl IntoView {
let mut interaction_counters = InteractionCounters::default();
let interaction_counters_view = {
let user_ints = ctx.user_interactions.read();
for problem_uid in ctx.filtered_problems.read().keys() {
if let Some(user_int) = user_ints.get(problem_uid) {
for problem in ctx.filtered_problems.read().iter() {
if let Some(user_int) = user_ints.get(problem) {
match user_int.best_attempt().map(|da| da.attempt) {
Some(models::Attempt::Flash) => interaction_counters.flash += 1,
Some(models::Attempt::Send) => interaction_counters.send += 1,
@@ -555,13 +555,13 @@ mod signals {
}
pub fn user_interaction(
user_interactions: Signal<BTreeMap<models::ProblemUid, models::UserInteraction>>,
problem_uid: Signal<Option<models::ProblemUid>>,
user_interactions: Signal<BTreeMap<models::Problem, models::UserInteraction>>,
problem: Signal<Option<models::Problem>>,
) -> Signal<Option<models::UserInteraction>> {
Signal::derive(move || {
let problem_uid = problem_uid.get()?;
let problem = problem.get()?;
let user_interactions = user_interactions.read();
user_interactions.get(&problem_uid).cloned()
user_interactions.get(&problem).cloned()
})
}

View File

@@ -34,10 +34,14 @@ pub fn user_interaction(wall_uid: Signal<models::WallUid>, problem: Signal<Optio
}
/// Returns all user interactions for a wall
pub fn user_interactions(wall_uid: Signal<models::WallUid>) -> RonResource<BTreeMap<models::ProblemUid, models::UserInteraction>> {
pub fn user_interactions_for_wall(wall_uid: Signal<models::WallUid>) -> RonResource<BTreeMap<models::Problem, models::UserInteraction>> {
Resource::new_with_options(
move || wall_uid.get(),
move |wall_uid| async move { crate::server_functions::get_user_interactions(wall_uid).await.map(RonEncoded::into_inner) },
move |wall_uid| async move {
crate::server_functions::get_user_interactions_for_wall(wall_uid)
.await
.map(RonEncoded::into_inner)
},
false,
)
}

View File

@@ -7,6 +7,7 @@ use derive_more::Error;
use derive_more::From;
use leptos::prelude::*;
use leptos::server;
use redb::ReadableTable;
use server_fn::ServerFnError;
use std::collections::BTreeMap;
use type_toppings::IteratorExt;
@@ -123,7 +124,7 @@ pub(crate) async fn get_user_interaction(
custom = RonEncoded
)]
#[tracing::instrument(err(Debug))]
pub(crate) async fn get_user_interactions(
pub(crate) async fn get_user_interactions_for_wall(
wall_uid: models::WallUid,
) -> Result<RonEncoded<BTreeMap<models::Problem, models::UserInteraction>>, ServerFnError> {
use crate::server::db::Database;
@@ -142,12 +143,22 @@ pub(crate) async fn get_user_interactions(
let user_interactions = db
.read(|txn| {
let user_table = txn.open_table(crate::server::db::current::TABLE_USER)?;
let range = user_table.range((wall_uid, models::ProblemUid::min())..=(wall_uid, models::ProblemUid::max()))?;
let user_interactions = range
let user_interactions = user_table
.iter()?
.filter(|guard| {
guard
.as_ref()
.map(|(key, _val)| {
let (wall_uid, _problem) = key.value();
wall_uid
})
.map(|wall_uid_| wall_uid_ == wall_uid)
.unwrap_or(false)
})
.map(|guard| {
guard.map(|(_key, val)| {
let val = val.value();
(val.problem_uid, val)
(val.problem.clone(), val)
})
})
.collect::<Result<_, _>>()?;
@@ -195,11 +206,11 @@ pub(crate) async fn upsert_todays_attempt(
.write(|txn| {
let mut user_table = txn.open_table(crate::server::db::current::TABLE_USER)?;
let key = (wall_uid, problem);
let key = (wall_uid, problem.clone());
// Pop or default
let mut user_interaction = user_table
.remove(key)?
.remove(&key)?
.map(|guard| guard.value())
.unwrap_or_else(|| models::UserInteraction::new(wall_uid, problem));