|
|
|
|
@@ -118,15 +118,13 @@ fn WithProblem(#[prop(into)] problem: Signal<models::Problem>) -> impl IntoView
|
|
|
|
|
fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
|
|
|
|
|
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 (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 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| {
|
|
|
|
|
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)
|
|
|
|
|
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>{grid}</div>
|
|
|
|
|
|
|
|
|
|
@@ -281,7 +188,7 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
|
|
|
|
|
let problem = problem.await?;
|
|
|
|
|
let view = problem
|
|
|
|
|
.map(|problem| {
|
|
|
|
|
view! { <ProblemInfo problem /> }
|
|
|
|
|
view! { <WithProblem problem /> }
|
|
|
|
|
});
|
|
|
|
|
Ok::<_, ServerFnError>(view)
|
|
|
|
|
})}
|
|
|
|
|
@@ -290,21 +197,130 @@ fn WithWall(#[prop(into)] wall: Signal<models::Wall>) -> impl IntoView {
|
|
|
|
|
|
|
|
|
|
<Separator />
|
|
|
|
|
|
|
|
|
|
<AttemptRadio
|
|
|
|
|
flash=ui_is_flash
|
|
|
|
|
send=ui_is_send
|
|
|
|
|
attempt=ui_is_attempt
|
|
|
|
|
onclick_flash
|
|
|
|
|
onclick_send
|
|
|
|
|
onclick_attempt
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<Separator />
|
|
|
|
|
|
|
|
|
|
<Suspense fallback=move || ()>{foo()}</Suspense>
|
|
|
|
|
<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
|
|
|
|
|
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]
|
|
|
|
|
|