This commit is contained in:
2025-03-10 22:59:38 +01:00
parent bcfc155266
commit 79ac2ea4ef
5 changed files with 126 additions and 155 deletions

View File

@@ -1,30 +0,0 @@
use leptos::prelude::*;
#[component]
#[tracing::instrument(skip_all)]
pub fn ShowSome<T, C, IV>(#[prop(into)] sig: Signal<Option<T>>, foo: C) -> impl IntoView
where
T: Clone + Send + Sync + 'static,
C: Fn(Signal<T>) -> IV + Sync + Send + 'static,
IV: IntoView,
{
tracing::trace!("Enter");
view! {
// <Show when=move || sig.get().is_some() fallback=|| ()>
// {move || {
// let new = signal()
// sig
// .with(|opt| {
// if let Some(inner) = opt.clone() {
// let new_signal = Signal::derive(move || sig.get().unwrap());
// .into_any()
// } else {
// view! {}.into_any()
// }
// })
// }}
// </Show>
}
}

View File

@@ -20,7 +20,6 @@ pub mod components {
pub mod outlined_box; pub mod outlined_box;
pub mod problem; pub mod problem;
pub mod problem_info; pub mod problem_info;
pub mod show_some;
} }
pub mod gradient; pub mod gradient;

View File

@@ -118,15 +118,13 @@ fn WithProblem(#[prop(into)] problem: Signal<models::Problem>) -> impl IntoView
fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView { fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
tracing::trace!("Enter"); tracing::trace!("Enter");
let (problem_uid, set_problem_uid) = leptos_router::hooks::query_signal::<models::ProblemUid>("problem");
let wall_uid = Signal::derive(move || wall.read().uid); let wall_uid = Signal::derive(move || wall.read().uid);
let (problem_uid, set_problem_uid) = leptos_router::hooks::query_signal::<models::ProblemUid>("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 submit_attempt = ServerAction::<RonEncoded<server_functions::UpsertTodaysAttempt>>::new();
let fn_next_problem = move |wall: &models::Wall| { let fn_next_problem = move |wall: &models::Wall| {
set_problem_uid.set(wall.random_problem()); set_problem_uid.set(wall.random_problem());
}; };
@@ -139,97 +137,6 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
} }
}); });
let (attempt_today, set_attempt_today) = signal(None);
let ui_is_flash = Signal::derive(move || matches!(attempt_today.get(), Some(models::Attempt::Flash)));
let ui_is_send = Signal::derive(move || matches!(attempt_today.get(), Some(models::Attempt::Send)));
let ui_is_attempt = Signal::derive(move || matches!(attempt_today.get(), Some(models::Attempt::Attempt)));
let ui_is_favorite = RwSignal::new(false);
// On reception of user interaction state, set UI signals
Effect::new(move |_prev_value| {
if let Some(user_interaction) = user_interaction.get() {
let user_interaction = user_interaction.ok().flatten();
if let Some(user_interaction) = user_interaction {
ui_is_favorite.set(user_interaction.is_favorite);
} else {
ui_is_favorite.set(false);
}
}
});
// onclick handler helper
let set_or_deselect = |s: WriteSignal<Option<models::Attempt>>, v: models::Attempt| {
s.update(|s| match s {
Some(x) if *x == v => {
*s = None;
}
_ => {
*s = Some(v);
}
});
};
let onclick_flash = move |_| {
set_or_deselect(set_attempt_today, models::Attempt::Flash);
submit_attempt.dispatch(RonEncoded(server_functions::UpsertTodaysAttempt {
wall_uid: wall_uid.get(),
problem_uid: problem.get().uid,
attempt: models::Attempt::Flash,
}));
};
let onclick_send = move |_| {
set_or_deselect(set_attempt_today, models::Attempt::Send);
};
let onclick_attempt = move |_| {
set_or_deselect(set_attempt_today, models::Attempt::Attempt);
};
let foo = move || {
Suspend::new(async move {
let user_interaction = user_interaction.await;
let user_interaction = user_interaction.ok().flatten();
let latest_attempt =
user_interaction.and_then(|x| x.attempted_on.last_key_value().map(|(date, attempt)| (date.clone(), attempt.clone())));
// let latest_attempt_text = move || {
// latest_attempt
// .map(|pair| match pair.1 {
// models::Attempt::Attempt => "Attempt",
// models::Attempt::Send => "Send",
// models::Attempt::Flash => "Flash",
// })
// .unwrap_or("No attempt")
// .to_string()
// };
// let best_attempt = user_interaction.and_then(|x| x.best_attempt());
// let best_attempt_date = move || best_attempt.map(|pair| pair.0);
// let best_attempt_attempt = move || best_attempt.map(|pair| pair.1);
let v = latest_attempt.map(|(date, attempt)| view! { <Attempt date attempt /> });
let placeholder = latest_attempt.is_none().then(|| {
let today = chrono::Utc::now();
view! { <Attempt date=today attempt=None /> }
});
let test = {
let today = chrono::Utc::now();
view! {
<Attempt date=today attempt=None />
<Attempt date=today attempt=models::Attempt::Flash />
<Attempt date=today attempt=models::Attempt::Send />
<Attempt date=today attempt=models::Attempt::Attempt />
}
};
// TODO: loop over attempts in user_interaction
view! { <Section title="History">{test} {placeholder} {v}</Section> }
})
};
// merge outer option (resource hasn't resolved yet) with inner option (there is no problem for the wall) // merge outer option (resource hasn't resolved yet) with inner option (there is no problem for the wall)
let problem_signal = Signal::derive(move || problem.get().transpose().map(Option::flatten)); let problem_signal = Signal::derive(move || problem.get().transpose().map(Option::flatten));
@@ -253,7 +160,7 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
} }
}; };
let v = 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>
@@ -281,7 +188,7 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
let problem = problem.await?; let problem = problem.await?;
let view = problem let view = problem
.map(|problem| { .map(|problem| {
view! { <ProblemInfo problem /> } view! { <WithProblem problem /> }
}); });
Ok::<_, ServerFnError>(view) Ok::<_, ServerFnError>(view)
})} })}
@@ -290,6 +197,117 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
<Separator /> <Separator />
<Suspense fallback=move || ()>
{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! {
<WithUserInteraction wall_uid problem_uid user_interaction />
}
.into_any();
Ok::<_, ServerFnError>(view)
})
}}
</Suspense>
</div>
</div>
}
}
#[component]
#[tracing::instrument(skip_all)]
fn WithUserInteraction(
#[prop(into)] wall_uid: Signal<models::WallUid>,
#[prop(into)] problem_uid: Signal<models::ProblemUid>,
#[prop(into)] user_interaction: Signal<Option<models::UserInteraction>>,
) -> impl IntoView {
tracing::debug!("Enter WithUserInteraction");
let user_interaction_rw = RwSignal::new(None);
Effect::new(move || {
let i = user_interaction.get();
tracing::info!("setting user interaction to parent user interaction value: {i:?}");
user_interaction_rw.set(i);
});
let submit_attempt = ServerAction::<RonEncoded<server_functions::UpsertTodaysAttempt>>::new();
let submit_attempt_value = submit_attempt.value();
Effect::new(move || {
tracing::info!("flaf");
if let Some(Ok(v)) = submit_attempt_value.get() {
tracing::info!("setting user interaction to action return value: {v:?}");
user_interaction_rw.set(Some(v.into_inner()));
}
});
let latest_attempt = move || -> Option<_> {
let i = user_interaction_rw.read();
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_send = RwSignal::new(false);
let ui_is_attempt = RwSignal::new(false);
let ui_is_favorite = RwSignal::new(false);
Effect::new(move || {
let attempt = todays_attempt();
ui_is_flash.set(matches!(attempt, Some(models::Attempt::Flash)));
ui_is_send.set(matches!(attempt, Some(models::Attempt::Send)));
ui_is_attempt.set(matches!(attempt, Some(models::Attempt::Attempt)));
});
let onclick_flash = move |_| {
submit_attempt.dispatch(RonEncoded(server_functions::UpsertTodaysAttempt {
wall_uid: wall_uid.get(),
problem_uid: problem_uid.get(),
attempt: models::Attempt::Flash,
}));
};
let onclick_send = move |_| {
submit_attempt.dispatch(RonEncoded(server_functions::UpsertTodaysAttempt {
wall_uid: wall_uid.get(),
problem_uid: problem_uid.get(),
attempt: models::Attempt::Send,
}));
};
let onclick_attempt = move |_| {
submit_attempt.dispatch(RonEncoded(server_functions::UpsertTodaysAttempt {
wall_uid: wall_uid.get(),
problem_uid: problem_uid.get(),
attempt: models::Attempt::Attempt,
}));
};
// TODO: loop over attempts in user_interaction
let v = move || latest_attempt().map(|(date, attempt)| view! { <Attempt date attempt /> });
let placeholder = move || {
latest_attempt().is_none().then(|| {
let today = chrono::Utc::now();
view! { <Attempt date=today attempt=None /> }
})
};
view! {
<AttemptRadio <AttemptRadio
flash=ui_is_flash flash=ui_is_flash
send=ui_is_send send=ui_is_send
@@ -301,10 +319,8 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
<Separator /> <Separator />
<Suspense fallback=move || ()>{foo()}</Suspense> <Section title="History">{placeholder} {v}</Section>
</div> }
</div>
};
} }
#[component] #[component]

View File

@@ -16,21 +16,6 @@ pub fn wall_by_uid(wall_uid: Signal<models::WallUid>) -> RonResource<models::Wal
) )
} }
pub fn problem_by_uid(wall_uid: Signal<models::WallUid>, problem_uid: Signal<models::ProblemUid>) -> RonResource<models::Problem> {
Resource::new_with_options(
move || (wall_uid.get(), problem_uid.get()),
move |(wall_uid, problem_uid)| async move {
let Some(problem_uid) = problem_uid else {
return Ok(None);
};
crate::server_functions::get_problem_by_uid(wall_uid, problem_uid)
.await
.map(RonEncoded::into_inner)
},
false,
)
}
/// Version of [problem_by_uid] that short circuits if the input problem_uid signal is None. /// Version of [problem_by_uid] that short circuits if the input problem_uid signal is None.
pub fn problem_by_uid_optional( pub fn problem_by_uid_optional(
wall_uid: Signal<models::WallUid>, wall_uid: Signal<models::WallUid>,

View File

@@ -130,6 +130,7 @@
basecamp.mkShell pkgs { basecamp.mkShell pkgs {
rust.enable = true; rust.enable = true;
rust.toolchain.targets = [ "wasm32-unknown-unknown" ]; rust.toolchain.targets = [ "wasm32-unknown-unknown" ];
rust.toolchain.components.rust-analyzer.nightly = true;
packages = [ packages = [
pkgs.bacon pkgs.bacon