diff --git a/crates/ascend/src/models.rs b/crates/ascend/src/models.rs
index bcebb08..01ab594 100644
--- a/crates/ascend/src/models.rs
+++ b/crates/ascend/src/models.rs
@@ -125,6 +125,12 @@ pub mod v2 {
pub fn create() -> Self {
Self(uuid::Uuid::new_v4())
}
+ pub fn min() -> Self {
+ Self(uuid::Uuid::nil())
+ }
+ pub fn max() -> Self {
+ Self(uuid::Uuid::max())
+ }
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Display)]
diff --git a/crates/ascend/src/pages/wall.rs b/crates/ascend/src/pages/wall.rs
index c8fefb1..b0218d2 100644
--- a/crates/ascend/src/pages/wall.rs
+++ b/crates/ascend/src/pages/wall.rs
@@ -46,6 +46,7 @@ use crate::server_functions;
use leptos::Params;
use leptos::prelude::*;
use leptos_router::params::Params;
+use std::collections::BTreeMap;
use std::collections::BTreeSet;
use web_sys::MouseEvent;
@@ -71,6 +72,7 @@ pub fn Wall() -> impl IntoView {
let wall = crate::resources::wall_by_uid(wall_uid);
let problems = crate::resources::problems_for_wall(wall_uid);
+ let user_interactions = crate::resources::user_interactions(wall_uid);
let header_items = move || HeaderItems {
left: vec![],
@@ -100,7 +102,8 @@ pub fn Wall() -> impl IntoView {
tracing::info!("executing main suspend");
let wall = wall.await?;
let problems = problems.await?;
- let v = view! { };
+ let user_interactions = user_interactions.await?;
+ let v = view! { };
Ok::<_, ServerFnError>(v)
})}
@@ -119,7 +122,11 @@ fn WithProblem(#[prop(into)] problem: Signal) -> impl IntoView
#[component]
#[tracing::instrument(skip_all)]
-fn WithWall(#[prop(into)] wall: Signal, #[prop(into)] problems: Signal>) -> impl IntoView {
+fn WithWall(
+ #[prop(into)] wall: Signal,
+ #[prop(into)] problems: Signal>,
+ #[prop(into)] user_interactions: Signal>,
+) -> impl IntoView {
tracing::trace!("Enter");
let wall_uid = Signal::derive(move || wall.read().uid);
@@ -151,7 +158,13 @@ fn WithWall(#[prop(into)] wall: Signal, #[prop(into)] problems: Si
});
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 = Signal::derive(move || {
+ let Some(problem_uid) = problem_uid.get() else {
+ return None;
+ };
+ let user_interactions = user_interactions.read();
+ user_interactions.get(&problem_uid).cloned()
+ });
let fn_next_problem = move || {
let problems = filtered_problems.read();
@@ -230,10 +243,69 @@ fn WithWall(#[prop(into)] wall: Signal, #[prop(into)] problems: Si
let sep = (!cells.is_empty()).then_some(view! { });
+ #[derive(Default)]
+ struct InteractionCounters {
+ flash: u64,
+ send: u64,
+ attempt: u64,
+ }
+ let mut interaction_counters = InteractionCounters::default();
+ let interaction_counters_view = {
+ let user_ints = user_interactions.read();
+ for problem in filtered_problems.read().iter() {
+ if let Some(user_int) = user_ints.get(&problem.uid) {
+ match user_int.best_attempt() {
+ Some((_, models::Attempt::Flash)) => interaction_counters.flash += 1,
+ Some((_, models::Attempt::Send)) => interaction_counters.send += 1,
+ Some((_, models::Attempt::Attempt)) => interaction_counters.attempt += 1,
+ None => {}
+ }
+ }
+ }
+ let flash = (interaction_counters.flash > 0).then(|| {
+ let class = Gradient::CyanBlue.class_text();
+ view! {
+
+ {interaction_counters.flash}
+
+ }
+ });
+ let send = (interaction_counters.send > 0).then(|| {
+ let class = Gradient::TealLime.class_text();
+ view! {
+
+ {interaction_counters.send}
+
+ }
+ });
+ let attempt = (interaction_counters.attempt > 0).then(|| {
+ let class = Gradient::PinkOrange.class_text();
+ view! {
+
+ {interaction_counters.attempt}
+
+ }
+ });
+
+ if flash.is_some() || send.is_some() || attempt.is_some() {
+ view! {
+ {"("}
+ {flash}
+ {send}
+ {attempt}
+ {")"}
+ }
+ .into_any()
+ } else {
+ ().into_any()
+ }
+ };
+
view! {
{cells}
{sep}
{problems_counter}
+ {interaction_counters_view}
}
};
@@ -274,23 +346,13 @@ fn WithWall(#[prop(into)] wall: Signal, #[prop(into)] problems: Si
-
- {move || {
- Suspend::new(async move {
- tracing::debug!("getting user interaction");
- let user_interaction: Option<_> = user_interaction.await?;
- tracing::debug!("got user interaction");
- let Some(problem_uid) = problem_uid.get() else {
- return Ok(view! {}.into_any());
- };
- let view = view! {
-
- }
- .into_any();
- Ok::<_, ServerFnError>(view)
- })
- }}
-
+ {move || {
+ let Some(problem_uid) = problem_uid.get() else {
+ return view! {}.into_any();
+ };
+ view! { }
+ .into_any()
+ }}
}
@@ -466,7 +528,11 @@ fn Grid(
on_click_hold(hold_position);
}
};
- let cell = view! { };
+ let cell = view! {
+
+
+
+ };
cells.push(cell);
}
let grid_classes = format!("grid grid-rows-{} grid-cols-{} gap-1", wall.rows, wall.cols,);
diff --git a/crates/ascend/src/resources.rs b/crates/ascend/src/resources.rs
index ca4b531..3335072 100644
--- a/crates/ascend/src/resources.rs
+++ b/crates/ascend/src/resources.rs
@@ -5,6 +5,7 @@ use leptos::prelude::Get;
use leptos::prelude::Signal;
use leptos::server::Resource;
use server_fn::ServerFnError;
+use std::collections::BTreeMap;
type RonResource = Resource, Ron>;
@@ -36,6 +37,7 @@ pub fn problem_by_uid_optional(
)
}
+/// Returns all problems for a wall
pub fn problems_for_wall(wall_uid: Signal) -> RonResource> {
Resource::new_with_options(
move || wall_uid.get(),
@@ -44,6 +46,7 @@ pub fn problems_for_wall(wall_uid: Signal) -> RonResource,
problem_uid: Signal