This commit is contained in:
2025-03-05 22:50:08 +01:00
parent 196610f8f7
commit 3b232b048d
5 changed files with 131 additions and 63 deletions

View File

@@ -4,27 +4,55 @@ use leptos::prelude::*;
#[component]
#[tracing::instrument(skip_all)]
pub fn Attempt(#[prop(into)] attempt: Signal<Option<models::Attempt>>) -> impl IntoView {
pub fn Attempt(#[prop(into)] date: Signal<chrono::DateTime<chrono::Utc>>, #[prop(into)] attempt: Signal<Option<models::Attempt>>) -> impl IntoView {
tracing::trace!("Enter");
let s = time_ago(date.get());
let text = move || match attempt.get() {
Some(models::Attempt::Attempt) => "Learning experience",
Some(models::Attempt::Send) => "Send",
Some(models::Attempt::Flash) => "Flash",
Some(models::Attempt::Send) => "Send",
Some(models::Attempt::Attempt) => "Learning experience",
None => "No attempt",
};
let text_color = match attempt.get() {
Some(models::Attempt::Flash) => "text-cyan-500",
Some(models::Attempt::Send) => "text-teal-500",
Some(models::Attempt::Attempt) => "text-pink-500",
None => "",
};
let icon = move || match attempt.get() {
Some(models::Attempt::Attempt) => view! { <icons::BoltSlashSolid /> }.into_any(),
Some(models::Attempt::Send) => view! { <icons::PaperAirplaneSolid /> }.into_any(),
Some(models::Attempt::Flash) => view! { <icons::BoltSolid /> }.into_any(),
Some(models::Attempt::Send) => view! { <icons::Trophy /> }.into_any(),
Some(models::Attempt::Attempt) => view! { <icons::ArrowTrendingUp /> }.into_any(),
None => view! { <icons::NoSymbol /> }.into_any(),
};
let classes = format!("flex flex-row gap-3 {}", text_color);
view! {
<div class="flex gap-2 justify-center items-center">
<span>{icon}</span>
<span>{text}</span>
<div class="flex flex-row justify-between my-2">
<div>{s}</div>
<div class=classes>
<span>{text}</span>
<span>{icon}</span>
</div>
</div>
}
}
fn time_ago(dt: chrono::DateTime<chrono::Utc>) -> String {
let now = chrono::Utc::now();
let duration = now.signed_duration_since(dt);
if duration.num_days() == 0 {
"Today".to_string()
} else if duration.num_days() == 1 {
"1 day ago".to_string()
} else {
format!("{} days ago", duration.num_days())
}
}

View File

@@ -20,7 +20,7 @@ pub fn Button(
let icon_view = icon.get().map(|i| {
let icon_view = i.into_view();
let mut classes = "self-center rounded-sm".to_string();
let mut classes = "self-center".to_string();
classes.push(' ');
classes.push_str(margin);
classes.push(' ');
@@ -48,11 +48,7 @@ pub fn Button(
};
view! {
<button
on:click=onclick
type="button"
class="mb-2 me-2 hover:brightness-125 active:brightness-90"
>
<button on:click=onclick type="button" class="hover:brightness-125 active:brightness-90">
<OutlinedBox color highlight>
<div class="flex items-stretch">{icon_view} {separator} {text_view}</div>
</OutlinedBox>

View File

@@ -23,8 +23,8 @@ pub fn OutlinedBox(children: Children, color: Gradient, #[prop(optional)] highli
if highlight() {
let bg = match color {
Gradient::PinkOrange => "bg-pink-900",
Gradient::CyanBlue => "bg-cyan-900",
Gradient::TealLime => "bg-teal-900",
Gradient::CyanBlue => "bg-cyan-800",
Gradient::TealLime => "bg-teal-700",
Gradient::PurplePink => "bg-purple-900",
Gradient::PurpleBlue => "bg-purple-900",
};

View File

@@ -11,7 +11,7 @@ pub fn ProblemInfo(problem: models::Problem) -> impl IntoView {
let method = problem.method;
view! {
<div class="grid grid-rows-none gap-0.5 grid-cols-[auto,1fr]">
<div class="grid grid-rows-none gap-y-1 gap-x-0.5 grid-cols-[auto,1fr]">
<NameValue name="Name:" value=name />
<NameValue name="Method:" value=method.to_string() />
<NameValue name="Set By:" value=set_by />
@@ -23,7 +23,7 @@ pub fn ProblemInfo(problem: models::Problem) -> impl IntoView {
#[tracing::instrument(skip_all)]
fn NameValue(#[prop(into)] name: String, #[prop(into)] value: String) -> impl IntoView {
view! {
<p class="mr-4 text-right text-orange-300">{name}</p>
<p class="font-semibold text-white">{value}</p>
<p class="text-sm font-light mr-4 text-right text-orange-300">{name}</p>
<p class="text-white">{value}</p>
}
}

View File

@@ -151,6 +151,51 @@ pub fn Wall() -> impl IntoView {
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> }
})
};
leptos::view! {
<div class="min-h-screen min-w-screen bg-neutral-950">
<StyledHeader items=Signal::derive(header_items) />
@@ -186,16 +231,38 @@ pub fn Wall() -> impl IntoView {
<div>{grid}</div>
<div class="flex flex-col">
<div class="self-center">
<Button
icon=Icon::ArrowPath
text="Next problem"
onclick=move |_| fn_next_problem(&wall)
/>
<Section title="Filter">{}</Section>
<Separator />
<div class="flex flex-col">
<div class="self-center">
<Button
icon=Icon::ArrowPath
text="Next problem"
onclick=move |_| fn_next_problem(&wall)
/>
</div>
</div>
<Separator />
<Section title="Problem">
<Transition fallback=|| ()>
{move || Suspend::new(async move {
tracing::info!("executing probleminfo suspend");
let problem = problem.await?;
let view = problem
.map(|problem| {
view! { <ProblemInfo problem /> }
});
Ok::<_, ServerFnError>(view)
})}
</Transition>
</Section>
<Separator />
<AttemptRadio
flash=ui_is_flash
send=ui_is_send
@@ -207,42 +274,7 @@ pub fn Wall() -> impl IntoView {
<Separator />
<Transition fallback=|| ()>
{move || Suspend::new(async move {
tracing::info!("executing probleminfo suspend");
let problem = problem.await?;
let problem_view = problem
.map(|problem| view! { <ProblemInfo problem /> });
let view = view! { {problem_view} };
Ok::<_, ServerFnError>(view)
})}
</Transition>
<Separator />
<Suspense fallback=move || {
view! {}
}>
{move || {
let x = 10;
let attempt_suspend = Suspend::new(async move {
let user_interaction = user_interaction.await;
let user_interaction = user_interaction.ok().flatten();
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)
};
view! {
<Attempt attempt=Signal::derive(best_attempt_attempt) />
}
});
attempt_suspend
}}
</Suspense>
<Suspense fallback=move || ()>{foo()}</Suspense>
</div>
</div>
};
@@ -274,7 +306,7 @@ fn AttemptRadio(
tracing::debug!("Enter");
view! {
<div class="flex flex-row justify-evenly md:flex-col 2xl:flex-row">
<div class="gap-2 flex flex-row justify-evenly md:flex-col 2xl:flex-row">
<Button
onclick=onclick_flash
text="Flash"
@@ -362,5 +394,17 @@ fn Hold(hold: models::Hold, role: Signal<Result<Option<HoldRole>, ServerFnError>
#[component]
fn Separator() -> impl IntoView {
view! { <div class="m-2 sm:m-3 md:m-4" /> }
view! { <div class="m-2 sm:m-3 md:m-4 h-4" /> }
}
#[component]
fn Section(children: Children, #[prop(into)] title: MaybeProp<String>) -> impl IntoView {
view! {
<div class="bg-neutral-900 px-5 pt-3 pb-8 rounded-lg">
<div class="py-3 text-lg text-center text-orange-400 border-t border-orange-400">
{move || title.get()}
</div>
{children()}
</div>
}
}