wip
This commit is contained in:
@@ -4,27 +4,55 @@ use leptos::prelude::*;
|
|||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
#[tracing::instrument(skip_all)]
|
#[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");
|
tracing::trace!("Enter");
|
||||||
|
|
||||||
|
let s = time_ago(date.get());
|
||||||
|
|
||||||
let text = move || match attempt.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::Flash) => "Flash",
|
||||||
|
Some(models::Attempt::Send) => "Send",
|
||||||
|
Some(models::Attempt::Attempt) => "Learning experience",
|
||||||
None => "No attempt",
|
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() {
|
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::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(),
|
None => view! { <icons::NoSymbol /> }.into_any(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let classes = format!("flex flex-row gap-3 {}", text_color);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div class="flex gap-2 justify-center items-center">
|
<div class="flex flex-row justify-between my-2">
|
||||||
<span>{icon}</span>
|
<div>{s}</div>
|
||||||
<span>{text}</span>
|
|
||||||
|
<div class=classes>
|
||||||
|
<span>{text}</span>
|
||||||
|
<span>{icon}</span>
|
||||||
|
</div>
|
||||||
</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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ pub fn Button(
|
|||||||
|
|
||||||
let icon_view = icon.get().map(|i| {
|
let icon_view = icon.get().map(|i| {
|
||||||
let icon_view = i.into_view();
|
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(' ');
|
||||||
classes.push_str(margin);
|
classes.push_str(margin);
|
||||||
classes.push(' ');
|
classes.push(' ');
|
||||||
@@ -48,11 +48,7 @@ pub fn Button(
|
|||||||
};
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<button
|
<button on:click=onclick type="button" class="hover:brightness-125 active:brightness-90">
|
||||||
on:click=onclick
|
|
||||||
type="button"
|
|
||||||
class="mb-2 me-2 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>
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ pub fn OutlinedBox(children: Children, color: Gradient, #[prop(optional)] highli
|
|||||||
if highlight() {
|
if highlight() {
|
||||||
let bg = match color {
|
let bg = match color {
|
||||||
Gradient::PinkOrange => "bg-pink-900",
|
Gradient::PinkOrange => "bg-pink-900",
|
||||||
Gradient::CyanBlue => "bg-cyan-900",
|
Gradient::CyanBlue => "bg-cyan-800",
|
||||||
Gradient::TealLime => "bg-teal-900",
|
Gradient::TealLime => "bg-teal-700",
|
||||||
Gradient::PurplePink => "bg-purple-900",
|
Gradient::PurplePink => "bg-purple-900",
|
||||||
Gradient::PurpleBlue => "bg-purple-900",
|
Gradient::PurpleBlue => "bg-purple-900",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ pub fn ProblemInfo(problem: models::Problem) -> impl IntoView {
|
|||||||
let method = problem.method;
|
let method = problem.method;
|
||||||
|
|
||||||
view! {
|
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="Name:" value=name />
|
||||||
<NameValue name="Method:" value=method.to_string() />
|
<NameValue name="Method:" value=method.to_string() />
|
||||||
<NameValue name="Set By:" value=set_by />
|
<NameValue name="Set By:" value=set_by />
|
||||||
@@ -23,7 +23,7 @@ pub fn ProblemInfo(problem: models::Problem) -> impl IntoView {
|
|||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
fn NameValue(#[prop(into)] name: String, #[prop(into)] value: String) -> impl IntoView {
|
fn NameValue(#[prop(into)] name: String, #[prop(into)] value: String) -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
<p class="mr-4 text-right text-orange-300">{name}</p>
|
<p class="text-sm font-light mr-4 text-right text-orange-300">{name}</p>
|
||||||
<p class="font-semibold text-white">{value}</p>
|
<p class="text-white">{value}</p>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,6 +151,51 @@ pub fn Wall() -> impl IntoView {
|
|||||||
set_or_deselect(set_attempt_today, models::Attempt::Attempt);
|
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! {
|
leptos::view! {
|
||||||
<div class="min-h-screen min-w-screen bg-neutral-950">
|
<div class="min-h-screen min-w-screen bg-neutral-950">
|
||||||
<StyledHeader items=Signal::derive(header_items) />
|
<StyledHeader items=Signal::derive(header_items) />
|
||||||
@@ -186,16 +231,38 @@ pub fn Wall() -> impl IntoView {
|
|||||||
<div>{grid}</div>
|
<div>{grid}</div>
|
||||||
|
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="self-center">
|
<Section title="Filter">{}</Section>
|
||||||
<Button
|
|
||||||
icon=Icon::ArrowPath
|
<Separator />
|
||||||
text="Next problem"
|
|
||||||
onclick=move |_| fn_next_problem(&wall)
|
<div class="flex flex-col">
|
||||||
/>
|
<div class="self-center">
|
||||||
|
<Button
|
||||||
|
icon=Icon::ArrowPath
|
||||||
|
text="Next problem"
|
||||||
|
onclick=move |_| fn_next_problem(&wall)
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Separator />
|
<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
|
<AttemptRadio
|
||||||
flash=ui_is_flash
|
flash=ui_is_flash
|
||||||
send=ui_is_send
|
send=ui_is_send
|
||||||
@@ -207,42 +274,7 @@ pub fn Wall() -> impl IntoView {
|
|||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
<Transition fallback=|| ()>
|
<Suspense fallback=move || ()>{foo()}</Suspense>
|
||||||
{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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
};
|
};
|
||||||
@@ -274,7 +306,7 @@ fn AttemptRadio(
|
|||||||
tracing::debug!("Enter");
|
tracing::debug!("Enter");
|
||||||
|
|
||||||
view! {
|
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
|
<Button
|
||||||
onclick=onclick_flash
|
onclick=onclick_flash
|
||||||
text="Flash"
|
text="Flash"
|
||||||
@@ -362,5 +394,17 @@ fn Hold(hold: models::Hold, role: Signal<Result<Option<HoldRole>, ServerFnError>
|
|||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
fn Separator() -> impl IntoView {
|
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>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user