refactor
This commit is contained in:
parent
f8aa1e29a2
commit
7d95e48941
@ -1,10 +1,12 @@
|
|||||||
use crate::components::icons;
|
use crate::components::icons;
|
||||||
use crate::models;
|
use crate::models;
|
||||||
|
use chrono::DateTime;
|
||||||
|
use chrono::Utc;
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub fn Attempt(#[prop(into)] date: Signal<chrono::DateTime<chrono::Utc>>, #[prop(into)] attempt: Signal<Option<models::Attempt>>) -> impl IntoView {
|
pub fn Attempt(#[prop(into)] date: Signal<DateTime<Utc>>, #[prop(into)] attempt: Signal<Option<models::Attempt>>) -> impl IntoView {
|
||||||
tracing::trace!("Enter");
|
tracing::trace!("Enter");
|
||||||
|
|
||||||
let s = time_ago(date.get());
|
let s = time_ago(date.get());
|
||||||
|
@ -16,6 +16,21 @@ pub use v2::WallDimensions;
|
|||||||
pub use v2::WallUid;
|
pub use v2::WallUid;
|
||||||
pub use v3::Attempt;
|
pub use v3::Attempt;
|
||||||
pub use v3::UserInteraction;
|
pub use v3::UserInteraction;
|
||||||
|
pub use v4::DatedAttempt;
|
||||||
|
|
||||||
|
mod semantics;
|
||||||
|
|
||||||
|
pub mod v4 {
|
||||||
|
use super::v3;
|
||||||
|
use chrono::DateTime;
|
||||||
|
use chrono::Utc;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct DatedAttempt {
|
||||||
|
pub date_time: DateTime<Utc>,
|
||||||
|
pub attempt: v3::Attempt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub mod v3 {
|
pub mod v3 {
|
||||||
use super::v2;
|
use super::v2;
|
||||||
@ -38,24 +53,6 @@ pub mod v3 {
|
|||||||
/// Added to personal challenges
|
/// Added to personal challenges
|
||||||
pub is_saved: bool,
|
pub is_saved: bool,
|
||||||
}
|
}
|
||||||
impl UserInteraction {
|
|
||||||
pub fn new(wall_uid: v2::WallUid, problem_uid: v2::ProblemUid) -> Self {
|
|
||||||
Self {
|
|
||||||
wall_uid,
|
|
||||||
problem_uid,
|
|
||||||
is_favorite: false,
|
|
||||||
attempted_on: BTreeMap::new(),
|
|
||||||
is_saved: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn best_attempt(&self) -> Option<(chrono::DateTime<chrono::Utc>, Attempt)> {
|
|
||||||
self.attempted_on
|
|
||||||
.iter()
|
|
||||||
.max_by_key(|(_date, attempt)| *attempt)
|
|
||||||
.map(|(date, attempt)| (*date, *attempt))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
|
||||||
pub enum Attempt {
|
pub enum Attempt {
|
||||||
@ -103,11 +100,6 @@ pub mod v2 {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, derive_more::FromStr, derive_more::Display)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, derive_more::FromStr, derive_more::Display)]
|
||||||
pub struct WallUid(pub uuid::Uuid);
|
pub struct WallUid(pub uuid::Uuid);
|
||||||
impl WallUid {
|
|
||||||
pub fn create() -> Self {
|
|
||||||
Self(uuid::Uuid::new_v4())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Problem {
|
pub struct Problem {
|
||||||
@ -121,17 +113,6 @@ pub mod v2 {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, derive_more::FromStr, derive_more::Display)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, derive_more::FromStr, derive_more::Display)]
|
||||||
pub struct ProblemUid(pub uuid::Uuid);
|
pub struct ProblemUid(pub uuid::Uuid);
|
||||||
impl ProblemUid {
|
|
||||||
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)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Display)]
|
||||||
pub enum Method {
|
pub enum Method {
|
||||||
@ -156,15 +137,6 @@ pub mod v2 {
|
|||||||
pub uid: ImageUid,
|
pub uid: ImageUid,
|
||||||
pub resolutions: BTreeMap<ImageResolution, ImageFilename>,
|
pub resolutions: BTreeMap<ImageResolution, ImageFilename>,
|
||||||
}
|
}
|
||||||
impl Image {
|
|
||||||
pub(crate) fn srcset(&self) -> String {
|
|
||||||
self.resolutions
|
|
||||||
.iter()
|
|
||||||
.map(|(res, filename)| format!("/files/holds/{} {}w", filename.filename, res.width))
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(", ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct ImageResolution {
|
pub struct ImageResolution {
|
||||||
@ -179,11 +151,6 @@ pub mod v2 {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, derive_more::FromStr, derive_more::Display)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, derive_more::FromStr, derive_more::Display)]
|
||||||
pub struct ImageUid(pub uuid::Uuid);
|
pub struct ImageUid(pub uuid::Uuid);
|
||||||
impl ImageUid {
|
|
||||||
pub fn create() -> Self {
|
|
||||||
Self(uuid::Uuid::new_v4())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod v1 {
|
pub mod v1 {
|
||||||
|
93
crates/ascend/src/models/semantics.rs
Normal file
93
crates/ascend/src/models/semantics.rs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
use super::*;
|
||||||
|
use chrono::DateTime;
|
||||||
|
use chrono::Utc;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
impl UserInteraction {
|
||||||
|
pub fn new(wall_uid: WallUid, problem_uid: ProblemUid) -> Self {
|
||||||
|
Self {
|
||||||
|
wall_uid,
|
||||||
|
problem_uid,
|
||||||
|
is_favorite: false,
|
||||||
|
attempted_on: BTreeMap::new(),
|
||||||
|
is_saved: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn latest_attempt(&self) -> Option<DatedAttempt> {
|
||||||
|
self.attempted_on.last_key_value().map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn todays_attempt(&self) -> Option<Attempt> {
|
||||||
|
self.latest_attempt()
|
||||||
|
.filter(|latest_attempt| {
|
||||||
|
let today_local_naive = chrono::Local::now().date_naive();
|
||||||
|
let datetime_local_naive = latest_attempt.date_time.with_timezone(&chrono::Local).date_naive();
|
||||||
|
datetime_local_naive == today_local_naive
|
||||||
|
})
|
||||||
|
.map(|dated_attempt| dated_attempt.attempt)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn best_attempt(&self) -> Option<DatedAttempt> {
|
||||||
|
self.attempted_on.iter().max_by_key(|(_date_time, attempt)| *attempt).map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attempted_on(&self) -> impl IntoIterator<Item = DatedAttempt> {
|
||||||
|
self.attempted_on.iter().map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(DateTime<Utc>, Attempt)> for DatedAttempt {
|
||||||
|
fn from(value: (DateTime<Utc>, Attempt)) -> Self {
|
||||||
|
let (date_time, attempt) = value;
|
||||||
|
DatedAttempt { date_time, attempt }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&(DateTime<Utc>, Attempt)> for DatedAttempt {
|
||||||
|
fn from(value: &(DateTime<Utc>, Attempt)) -> Self {
|
||||||
|
let &(date_time, attempt) = value;
|
||||||
|
DatedAttempt { date_time, attempt }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(&DateTime<Utc>, &Attempt)> for DatedAttempt {
|
||||||
|
fn from(value: (&DateTime<Utc>, &Attempt)) -> Self {
|
||||||
|
let (&date_time, &attempt) = value;
|
||||||
|
DatedAttempt { date_time, attempt }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WallUid {
|
||||||
|
pub fn create() -> Self {
|
||||||
|
Self(uuid::Uuid::new_v4())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProblemUid {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Image {
|
||||||
|
pub(crate) fn srcset(&self) -> String {
|
||||||
|
self.resolutions
|
||||||
|
.iter()
|
||||||
|
.map(|(res, filename)| format!("/files/holds/{} {}w", filename.filename, res.width))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageUid {
|
||||||
|
pub fn create() -> Self {
|
||||||
|
Self(uuid::Uuid::new_v4())
|
||||||
|
}
|
||||||
|
}
|
@ -254,10 +254,10 @@ fn WithWall(
|
|||||||
let user_ints = user_interactions.read();
|
let user_ints = user_interactions.read();
|
||||||
for problem in filtered_problems.read().iter() {
|
for problem in filtered_problems.read().iter() {
|
||||||
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() {
|
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,
|
||||||
Some((_, models::Attempt::Attempt)) => interaction_counters.attempt += 1,
|
Some(models::Attempt::Attempt) => interaction_counters.attempt += 1,
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -384,23 +384,8 @@ fn WithUserInteraction(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let latest_attempt = move || -> Option<_> {
|
let latest_attempt = signals::latest_attempt(user_interaction_rw.into());
|
||||||
let i = user_interaction_rw.read();
|
let todays_attempt = signals::todays_attempt(user_interaction_rw.into());
|
||||||
let i = (*i).as_ref();
|
|
||||||
let i = i?;
|
|
||||||
i.attempted_on.last_key_value().map(|(date, attempt)| (date.clone(), attempt.clone()))
|
|
||||||
};
|
|
||||||
|
|
||||||
let todays_attempt = move || -> Option<_> {
|
|
||||||
match latest_attempt() {
|
|
||||||
Some((datetime, attempt)) => {
|
|
||||||
let today_local_naive = chrono::Local::now().date_naive();
|
|
||||||
let datetime_local_naive = datetime.with_timezone(&chrono::Local).date_naive();
|
|
||||||
(datetime_local_naive == today_local_naive).then_some(attempt)
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let ui_is_flash = RwSignal::new(false);
|
let ui_is_flash = RwSignal::new(false);
|
||||||
let ui_is_send = RwSignal::new(false);
|
let ui_is_send = RwSignal::new(false);
|
||||||
@ -408,7 +393,7 @@ fn WithUserInteraction(
|
|||||||
let ui_is_favorite = RwSignal::new(false);
|
let ui_is_favorite = RwSignal::new(false);
|
||||||
|
|
||||||
Effect::new(move || {
|
Effect::new(move || {
|
||||||
let attempt = todays_attempt();
|
let attempt = todays_attempt.get();
|
||||||
ui_is_flash.set(matches!(attempt, Some(models::Attempt::Flash)));
|
ui_is_flash.set(matches!(attempt, Some(models::Attempt::Flash)));
|
||||||
ui_is_send.set(matches!(attempt, Some(models::Attempt::Send)));
|
ui_is_send.set(matches!(attempt, Some(models::Attempt::Send)));
|
||||||
ui_is_attempt.set(matches!(attempt, Some(models::Attempt::Attempt)));
|
ui_is_attempt.set(matches!(attempt, Some(models::Attempt::Attempt)));
|
||||||
@ -440,10 +425,22 @@ fn WithUserInteraction(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO: loop over attempts in user_interaction
|
// TODO: loop over attempts in user_interaction
|
||||||
let v = move || latest_attempt().map(|(date, attempt)| view! { <Attempt date attempt /> });
|
let v = move || {
|
||||||
|
user_interaction_rw
|
||||||
|
.read()
|
||||||
|
.as_ref()
|
||||||
|
.iter()
|
||||||
|
.flat_map(|x| x.attempted_on())
|
||||||
|
.map(|dated_attempt| {
|
||||||
|
let date = dated_attempt.date_time;
|
||||||
|
let attempt = dated_attempt.attempt;
|
||||||
|
view! { <Attempt date attempt /> }
|
||||||
|
})
|
||||||
|
.collect_view()
|
||||||
|
};
|
||||||
|
|
||||||
let placeholder = move || {
|
let placeholder = move || {
|
||||||
latest_attempt().is_none().then(|| {
|
latest_attempt.read().is_none().then(|| {
|
||||||
let today = chrono::Utc::now();
|
let today = chrono::Utc::now();
|
||||||
view! { <Attempt date=today attempt=None /> }
|
view! { <Attempt date=today attempt=None /> }
|
||||||
})
|
})
|
||||||
@ -602,3 +599,16 @@ fn Section(children: Children, #[prop(into)] title: MaybeProp<String>) -> impl I
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod signals {
|
||||||
|
use crate::models;
|
||||||
|
use leptos::prelude::*;
|
||||||
|
|
||||||
|
pub fn latest_attempt(user_interaction: Signal<Option<models::UserInteraction>>) -> Signal<Option<models::DatedAttempt>> {
|
||||||
|
Signal::derive(move || user_interaction.read().as_ref().and_then(models::UserInteraction::latest_attempt))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn todays_attempt(latest_attempt: Signal<Option<models::UserInteraction>>) -> Signal<Option<models::Attempt>> {
|
||||||
|
Signal::derive(move || latest_attempt.read().as_ref().and_then(models::UserInteraction::todays_attempt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user