refactor: radio button group
This commit is contained in:
parent
7d95e48941
commit
9b15daaf6d
@ -2,7 +2,6 @@ use super::icons::Icon;
|
|||||||
use crate::components::outlined_box::OutlinedBox;
|
use crate::components::outlined_box::OutlinedBox;
|
||||||
use crate::gradient::Gradient;
|
use crate::gradient::Gradient;
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use web_sys::MouseEvent;
|
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Button(
|
pub fn Button(
|
||||||
@ -13,8 +12,6 @@ pub fn Button(
|
|||||||
#[prop(optional)] color: Gradient,
|
#[prop(optional)] color: Gradient,
|
||||||
|
|
||||||
#[prop(into, optional)] highlight: MaybeProp<bool>,
|
#[prop(into, optional)] highlight: MaybeProp<bool>,
|
||||||
|
|
||||||
onclick: impl FnMut(MouseEvent) + 'static,
|
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
let margin = "mx-2 my-1 sm:mx-5 sm:my-2.5";
|
let margin = "mx-2 my-1 sm:mx-5 sm:my-2.5";
|
||||||
|
|
||||||
@ -48,7 +45,7 @@ pub fn Button(
|
|||||||
};
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<button on:click=onclick type="button" class="hover:brightness-125 active:brightness-90">
|
<button type="button" class="hover:brightness-125 active:brightness-90">
|
||||||
<OutlinedBox color highlight>
|
<OutlinedBox color highlight>
|
||||||
<div class="flex items-stretch">{icon_view} {separator} {text_view}</div>
|
<div class="flex items-stretch">{icon_view} {separator} {text_view}</div>
|
||||||
</OutlinedBox>
|
</OutlinedBox>
|
||||||
@ -65,7 +62,7 @@ mod tests {
|
|||||||
let text = "foo";
|
let text = "foo";
|
||||||
let onclick = |_| {};
|
let onclick = |_| {};
|
||||||
|
|
||||||
view! { <Button text onclick /> };
|
view! { <Button text on:click=onclick /> };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -74,6 +71,6 @@ mod tests {
|
|||||||
let text = "foo";
|
let text = "foo";
|
||||||
let onclick = |_| {};
|
let onclick = |_| {};
|
||||||
|
|
||||||
view! { <Button icon text onclick /> };
|
view! { <Button icon text on:click=onclick /> };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ pub mod v4 {
|
|||||||
|
|
||||||
pub mod v3 {
|
pub mod v3 {
|
||||||
use super::v2;
|
use super::v2;
|
||||||
|
use derive_more::Display;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@ -54,7 +55,7 @@ pub mod v3 {
|
|||||||
pub is_saved: bool,
|
pub is_saved: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Display)]
|
||||||
pub enum Attempt {
|
pub enum Attempt {
|
||||||
/// Tried to climb problem, but was not able to.
|
/// Tried to climb problem, but was not able to.
|
||||||
Attempt,
|
Attempt,
|
||||||
|
@ -4,7 +4,7 @@ use chrono::Utc;
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
impl UserInteraction {
|
impl UserInteraction {
|
||||||
pub fn new(wall_uid: WallUid, problem_uid: ProblemUid) -> Self {
|
pub(crate) fn new(wall_uid: WallUid, problem_uid: ProblemUid) -> Self {
|
||||||
Self {
|
Self {
|
||||||
wall_uid,
|
wall_uid,
|
||||||
problem_uid,
|
problem_uid,
|
||||||
@ -14,11 +14,11 @@ impl UserInteraction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn latest_attempt(&self) -> Option<DatedAttempt> {
|
pub(crate) fn latest_attempt(&self) -> Option<DatedAttempt> {
|
||||||
self.attempted_on.last_key_value().map(Into::into)
|
self.attempted_on.last_key_value().map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn todays_attempt(&self) -> Option<Attempt> {
|
pub(crate) fn todays_attempt(&self) -> Option<Attempt> {
|
||||||
self.latest_attempt()
|
self.latest_attempt()
|
||||||
.filter(|latest_attempt| {
|
.filter(|latest_attempt| {
|
||||||
let today_local_naive = chrono::Local::now().date_naive();
|
let today_local_naive = chrono::Local::now().date_naive();
|
||||||
@ -28,12 +28,12 @@ impl UserInteraction {
|
|||||||
.map(|dated_attempt| dated_attempt.attempt)
|
.map(|dated_attempt| dated_attempt.attempt)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn best_attempt(&self) -> Option<DatedAttempt> {
|
pub(crate) fn best_attempt(&self) -> Option<DatedAttempt> {
|
||||||
self.attempted_on.iter().max_by_key(|(_date_time, attempt)| *attempt).map(Into::into)
|
self.attempted_on.iter().max_by_key(|(_date_time, attempt)| *attempt).map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attempted_on(&self) -> impl IntoIterator<Item = DatedAttempt> {
|
pub(crate) fn attempted_on(&self) -> impl IntoIterator<Item = DatedAttempt> {
|
||||||
self.attempted_on.iter().map(Into::into)
|
self.attempted_on.iter().rev().map(Into::into)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,19 +59,19 @@ impl From<(&DateTime<Utc>, &Attempt)> for DatedAttempt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WallUid {
|
impl WallUid {
|
||||||
pub fn create() -> Self {
|
pub(crate) fn create() -> Self {
|
||||||
Self(uuid::Uuid::new_v4())
|
Self(uuid::Uuid::new_v4())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProblemUid {
|
impl ProblemUid {
|
||||||
pub fn create() -> Self {
|
pub(crate) fn create() -> Self {
|
||||||
Self(uuid::Uuid::new_v4())
|
Self(uuid::Uuid::new_v4())
|
||||||
}
|
}
|
||||||
pub fn min() -> Self {
|
pub(crate) fn min() -> Self {
|
||||||
Self(uuid::Uuid::nil())
|
Self(uuid::Uuid::nil())
|
||||||
}
|
}
|
||||||
pub fn max() -> Self {
|
pub(crate) fn max() -> Self {
|
||||||
Self(uuid::Uuid::max())
|
Self(uuid::Uuid::max())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,7 +87,18 @@ impl Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ImageUid {
|
impl ImageUid {
|
||||||
pub fn create() -> Self {
|
pub(crate) fn create() -> Self {
|
||||||
Self(uuid::Uuid::new_v4())
|
Self(uuid::Uuid::new_v4())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Attempt {
|
||||||
|
pub(crate) fn icon(&self) -> crate::components::icons::Icon {
|
||||||
|
use crate::components::icons::Icon;
|
||||||
|
match self {
|
||||||
|
Attempt::Attempt => Icon::ArrowTrendingUp,
|
||||||
|
Attempt::Send => Icon::Trophy,
|
||||||
|
Attempt::Flash => Icon::BoltSolid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,35 +1,3 @@
|
|||||||
// +--------------- Filter ----------- ↓ -+
|
|
||||||
// | |
|
|
||||||
// | |
|
|
||||||
// | |
|
|
||||||
// | |
|
|
||||||
// | |
|
|
||||||
// | |
|
|
||||||
// | |
|
|
||||||
// +--------------------------------------+
|
|
||||||
|
|
||||||
// +---------------------------+
|
|
||||||
// | Next Problem |
|
|
||||||
// +---------------------------+
|
|
||||||
|
|
||||||
// +--------------- Problem --------------+
|
|
||||||
// | Name: ... |
|
|
||||||
// | Method: ... |
|
|
||||||
// | Set by: ... |
|
|
||||||
// | |
|
|
||||||
// | | Flash | Top | Attempt | |
|
|
||||||
// | |
|
|
||||||
// +--------------------------------------+
|
|
||||||
|
|
||||||
// +---------+ +---------+ +---------+
|
|
||||||
// | Flash | | Send | | Attempt |
|
|
||||||
// +---------+ +---------+ +---------+
|
|
||||||
|
|
||||||
// +---------- <Latest attempt> ----------+
|
|
||||||
// | Today: <Attempt> |
|
|
||||||
// | 14 days ago: <Attempt> |
|
|
||||||
// +--------------------------------------+
|
|
||||||
|
|
||||||
use crate::codec::ron::RonEncoded;
|
use crate::codec::ron::RonEncoded;
|
||||||
use crate::components::OnHoverRed;
|
use crate::components::OnHoverRed;
|
||||||
use crate::components::ProblemInfo;
|
use crate::components::ProblemInfo;
|
||||||
@ -48,7 +16,6 @@ use leptos::prelude::*;
|
|||||||
use leptos_router::params::Params;
|
use leptos_router::params::Params;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use web_sys::MouseEvent;
|
|
||||||
|
|
||||||
#[derive(Params, PartialEq, Clone)]
|
#[derive(Params, PartialEq, Clone)]
|
||||||
struct RouteParams {
|
struct RouteParams {
|
||||||
@ -323,7 +290,7 @@ fn WithWall(
|
|||||||
<Button
|
<Button
|
||||||
icon=Icon::ArrowPath
|
icon=Icon::ArrowPath
|
||||||
text="Next problem"
|
text="Next problem"
|
||||||
onclick=move |_| fn_next_problem()
|
on:click=move |_| fn_next_problem()
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -367,11 +334,13 @@ fn WithUserInteraction(
|
|||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
tracing::debug!("Enter WithUserInteraction");
|
tracing::debug!("Enter WithUserInteraction");
|
||||||
|
|
||||||
let user_interaction_rw = RwSignal::new(None);
|
let parent_user_interaction = user_interaction;
|
||||||
|
|
||||||
|
let user_interaction = RwSignal::new(None);
|
||||||
Effect::new(move || {
|
Effect::new(move || {
|
||||||
let i = user_interaction.get();
|
let i = parent_user_interaction.get();
|
||||||
tracing::info!("setting user interaction to parent user interaction value: {i:?}");
|
tracing::info!("setting user interaction to parent user interaction value: {i:?}");
|
||||||
user_interaction_rw.set(i);
|
user_interaction.set(i);
|
||||||
});
|
});
|
||||||
|
|
||||||
let submit_attempt = ServerAction::<RonEncoded<server_functions::UpsertTodaysAttempt>>::new();
|
let submit_attempt = ServerAction::<RonEncoded<server_functions::UpsertTodaysAttempt>>::new();
|
||||||
@ -380,53 +349,67 @@ fn WithUserInteraction(
|
|||||||
tracing::info!("flaf");
|
tracing::info!("flaf");
|
||||||
if let Some(Ok(v)) = submit_attempt_value.get() {
|
if let Some(Ok(v)) = submit_attempt_value.get() {
|
||||||
tracing::info!("setting user interaction to action return value: {v:?}");
|
tracing::info!("setting user interaction to action return value: {v:?}");
|
||||||
user_interaction_rw.set(Some(v.into_inner()));
|
user_interaction.set(Some(v.into_inner()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let latest_attempt = signals::latest_attempt(user_interaction_rw.into());
|
let todays_attempt = signals::todays_attempt(user_interaction.into());
|
||||||
let todays_attempt = signals::todays_attempt(user_interaction_rw.into());
|
|
||||||
|
|
||||||
let ui_is_flash = RwSignal::new(false);
|
let mut attempt_radio_buttons = vec![];
|
||||||
let ui_is_send = RwSignal::new(false);
|
for variant in [models::Attempt::Flash, models::Attempt::Send, models::Attempt::Attempt] {
|
||||||
let ui_is_attempt = RwSignal::new(false);
|
let ui_toggle = Signal::derive(move || todays_attempt.get() == Some(variant));
|
||||||
let ui_is_favorite = RwSignal::new(false);
|
let onclick = move |_| {
|
||||||
|
let attempt = if ui_toggle.get() { None } else { Some(variant) };
|
||||||
|
submit_attempt.dispatch(RonEncoded(server_functions::UpsertTodaysAttempt {
|
||||||
|
wall_uid: wall_uid.get(),
|
||||||
|
problem_uid: problem_uid.get(),
|
||||||
|
attempt,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
attempt_radio_buttons.push(view! { <AttemptRadioButton on:click=onclick variant selected=ui_toggle /> });
|
||||||
|
}
|
||||||
|
|
||||||
Effect::new(move || {
|
view! {
|
||||||
let attempt = todays_attempt.get();
|
<AttemptRadio>{attempt_radio_buttons}</AttemptRadio>
|
||||||
ui_is_flash.set(matches!(attempt, Some(models::Attempt::Flash)));
|
<Separator />
|
||||||
ui_is_send.set(matches!(attempt, Some(models::Attempt::Send)));
|
<History user_interaction />
|
||||||
ui_is_attempt.set(matches!(attempt, Some(models::Attempt::Attempt)));
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
let onclick_flash = move |_| {
|
#[component]
|
||||||
let attempt = if ui_is_flash.get() { None } else { Some(models::Attempt::Flash) };
|
#[tracing::instrument(skip_all)]
|
||||||
submit_attempt.dispatch(RonEncoded(server_functions::UpsertTodaysAttempt {
|
fn AttemptRadio(children: Children) -> impl IntoView {
|
||||||
wall_uid: wall_uid.get(),
|
tracing::debug!("Enter");
|
||||||
problem_uid: problem_uid.get(),
|
|
||||||
attempt,
|
view! {
|
||||||
}));
|
<div class="gap-2 flex flex-row justify-evenly md:flex-col 2xl:flex-row">{children()}</div>
|
||||||
};
|
}
|
||||||
let onclick_send = move |_| {
|
}
|
||||||
let attempt = if ui_is_send.get() { None } else { Some(models::Attempt::Send) };
|
|
||||||
submit_attempt.dispatch(RonEncoded(server_functions::UpsertTodaysAttempt {
|
#[component]
|
||||||
wall_uid: wall_uid.get(),
|
#[tracing::instrument(skip_all)]
|
||||||
problem_uid: problem_uid.get(),
|
fn AttemptRadioButton(variant: models::Attempt, #[prop(into)] selected: Signal<bool>) -> impl IntoView {
|
||||||
attempt,
|
tracing::debug!("Enter");
|
||||||
}));
|
|
||||||
};
|
let text = variant.to_string();
|
||||||
let onclick_attempt = move |_| {
|
let icon = variant.icon();
|
||||||
let attempt = if ui_is_attempt.get() { None } else { Some(models::Attempt::Attempt) };
|
let color = match variant {
|
||||||
submit_attempt.dispatch(RonEncoded(server_functions::UpsertTodaysAttempt {
|
models::Attempt::Attempt => Gradient::PinkOrange,
|
||||||
wall_uid: wall_uid.get(),
|
models::Attempt::Send => Gradient::TealLime,
|
||||||
problem_uid: problem_uid.get(),
|
models::Attempt::Flash => Gradient::CyanBlue,
|
||||||
attempt,
|
|
||||||
}));
|
|
||||||
};
|
};
|
||||||
|
view! { <Button text icon color highlight=selected /> }
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: loop over attempts in user_interaction
|
#[component]
|
||||||
let v = move || {
|
#[tracing::instrument(skip_all)]
|
||||||
user_interaction_rw
|
fn History(#[prop(into)] user_interaction: Signal<Option<models::UserInteraction>>) -> impl IntoView {
|
||||||
|
tracing::debug!("Enter");
|
||||||
|
|
||||||
|
let latest_attempt = signals::latest_attempt(user_interaction);
|
||||||
|
|
||||||
|
let attempts = move || {
|
||||||
|
user_interaction
|
||||||
.read()
|
.read()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.iter()
|
.iter()
|
||||||
@ -446,61 +429,7 @@ fn WithUserInteraction(
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
view! {
|
view! { <Section title="History">{placeholder} {attempts}</Section> }
|
||||||
<AttemptRadio
|
|
||||||
flash=ui_is_flash
|
|
||||||
send=ui_is_send
|
|
||||||
attempt=ui_is_attempt
|
|
||||||
onclick_flash
|
|
||||||
onclick_send
|
|
||||||
onclick_attempt
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Separator />
|
|
||||||
|
|
||||||
<Section title="History">{placeholder} {v}</Section>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[component]
|
|
||||||
#[tracing::instrument(skip_all)]
|
|
||||||
fn AttemptRadio(
|
|
||||||
#[prop(into)] flash: Signal<bool>,
|
|
||||||
#[prop(into)] send: Signal<bool>,
|
|
||||||
#[prop(into)] attempt: Signal<bool>,
|
|
||||||
onclick_flash: impl FnMut(MouseEvent) + 'static,
|
|
||||||
onclick_send: impl FnMut(MouseEvent) + 'static,
|
|
||||||
onclick_attempt: impl FnMut(MouseEvent) + 'static,
|
|
||||||
) -> impl IntoView {
|
|
||||||
tracing::debug!("Enter");
|
|
||||||
|
|
||||||
view! {
|
|
||||||
<div class="gap-2 flex flex-row justify-evenly md:flex-col 2xl:flex-row">
|
|
||||||
<Button
|
|
||||||
onclick=onclick_flash
|
|
||||||
text="Flash"
|
|
||||||
icon=Icon::BoltSolid
|
|
||||||
color=Gradient::CyanBlue
|
|
||||||
highlight=Signal::derive(move || { flash.get() })
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
onclick=onclick_send
|
|
||||||
text="Send"
|
|
||||||
icon=Icon::Trophy
|
|
||||||
color=Gradient::TealLime
|
|
||||||
highlight=Signal::derive(move || { send.get() })
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
onclick=onclick_attempt
|
|
||||||
text="Attempt"
|
|
||||||
icon=Icon::ArrowTrendingUp
|
|
||||||
color=Gradient::PinkOrange
|
|
||||||
highlight=Signal::derive(move || { attempt.get() })
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user