feat: implement contextual skeletons for login and dashboard
Some checks failed
Build MIPS Binary / build (push) Failing after 1m25s

This commit is contained in:
spinline
2026-02-12 21:55:14 +03:00
parent c8e3caa4fc
commit 45f5d1b678

View File

@@ -1,12 +1,13 @@
use crate::components::layout::protected::Protected; use crate::components::layout::protected::Protected;
use crate::components::ui::skeleton::Skeleton; use crate::components::ui::skeleton::Skeleton;
use crate::components::ui::card::{Card, CardHeader, CardContent};
use crate::components::torrent::table::TorrentTable; use crate::components::torrent::table::TorrentTable;
use crate::components::auth::login::Login; use crate::components::auth::login::Login;
use crate::components::auth::setup::Setup; use crate::components::auth::setup::Setup;
use leptos::prelude::*; use leptos::prelude::*;
use leptos::task::spawn_local; use leptos::task::spawn_local;
use leptos_router::components::{Router, Routes, Route}; use leptos_router::components::{Router, Routes, Route};
use leptos_router::hooks::use_navigate; use leptos_router::hooks::{use_navigate, use_location};
use crate::components::ui::toast::Toaster; use crate::components::ui::toast::Toaster;
use crate::components::hooks::use_theme_mode::ThemeMode; use crate::components::hooks::use_theme_mode::ThemeMode;
@@ -31,7 +32,9 @@ pub fn App() -> impl IntoView {
view! { view! {
<Toaster /> <Toaster />
<InnerApp /> <Router>
<InnerApp />
</Router>
} }
} }
@@ -39,6 +42,7 @@ pub fn App() -> impl IntoView {
fn InnerApp() -> impl IntoView { fn InnerApp() -> impl IntoView {
crate::store::provide_torrent_store(); crate::store::provide_torrent_store();
let store = use_context::<crate::store::TorrentStore>(); let store = use_context::<crate::store::TorrentStore>();
let loc = use_location();
let is_loading = signal(true); let is_loading = signal(true);
let is_authenticated = signal(false); let is_authenticated = signal(false);
@@ -98,98 +102,150 @@ fn InnerApp() -> impl IntoView {
view! { view! {
<div class="relative w-full h-screen" style="height: 100dvh;"> <div class="relative w-full h-screen" style="height: 100dvh;">
<Router> <Routes fallback=|| view! { <div class="p-4">"404 Not Found"</div> }>
<Routes fallback=|| view! { <div class="p-4">"404 Not Found"</div> }> <Route path=leptos_router::path!("/login") view=move || {
<Route path=leptos_router::path!("/login") view=move || { let authenticated = is_authenticated.0.get();
let authenticated = is_authenticated.0.get(); let setup_needed = needs_setup.0.get();
let setup_needed = needs_setup.0.get();
Effect::new(move |_| { Effect::new(move |_| {
if setup_needed { if setup_needed {
let navigate = use_navigate(); let navigate = use_navigate();
navigate("/setup", Default::default());
} else if authenticated {
log::info!("Already authenticated, redirecting to home");
let navigate = use_navigate();
navigate("/", Default::default());
}
});
view! { <Login /> }
} />
<Route path=leptos_router::path!("/setup") view=move || {
Effect::new(move |_| {
if is_authenticated.0.get() {
let navigate = use_navigate();
navigate("/", Default::default());
}
});
view! { <Setup /> }
} />
<Route path=leptos_router::path!("/") view=move || {
let navigate = use_navigate();
Effect::new(move |_| {
if !is_loading.0.get() {
if needs_setup.0.get() {
log::info!("Setup not completed, redirecting to setup");
navigate("/setup", Default::default()); navigate("/setup", Default::default());
} else if authenticated { } else if !is_authenticated.0.get() {
log::info!("Already authenticated, redirecting to home"); log::info!("Not authenticated, redirecting to login");
let navigate = use_navigate(); navigate("/login", Default::default());
navigate("/", Default::default());
} }
}); }
});
view! { <Login /> } view! {
} /> <Show when=move || !is_loading.0.get() fallback=move || {
<Route path=leptos_router::path!("/setup") view=move || { let path = loc.pathname.get();
Effect::new(move |_| { if path == "/login" {
if is_authenticated.0.get() { // Login Skeleton
let navigate = use_navigate(); view! {
navigate("/", Default::default()); <div class="flex items-center justify-center min-h-screen bg-muted/40 px-4">
} <Card class="w-full max-w-sm shadow-lg border-none">
}); <CardHeader class="pb-2 items-center space-y-4">
<Skeleton class="w-12 h-12 rounded-xl" />
view! { <Setup /> } <Skeleton class="h-8 w-32" />
} /> <Skeleton class="h-4 w-48" />
</CardHeader>
<Route path=leptos_router::path!("/") view=move || { <CardContent class="pt-4 space-y-6">
let navigate = use_navigate(); <div class="space-y-2">
Effect::new(move |_| { <Skeleton class="h-4 w-24" />
if !is_loading.0.get() { <Skeleton class="h-10 w-full" />
if needs_setup.0.get() { </div>
log::info!("Setup not completed, redirecting to setup"); <div class="space-y-2">
navigate("/setup", Default::default()); <Skeleton class="h-4 w-24" />
} else if !is_authenticated.0.get() { <Skeleton class="h-10 w-full" />
log::info!("Not authenticated, redirecting to login"); </div>
navigate("/login", Default::default()); <Skeleton class="h-10 w-full rounded-md mt-4" />
} </CardContent>
} </Card>
});
view! {
<Show when=move || !is_loading.0.get() fallback=|| view! {
<div class="flex h-screen bg-background">
// Sidebar skeleton
<div class="w-56 border-r border-border p-4 space-y-4">
<Skeleton class="h-8 w-3/4" />
<div class="space-y-2">
<Skeleton class="h-6 w-full" />
<Skeleton class="h-6 w-full" />
<Skeleton class="h-6 w-4/5" />
<Skeleton class="h-6 w-full" />
<Skeleton class="h-6 w-3/5" />
<Skeleton class="h-6 w-full" />
</div>
</div> </div>
// Main content skeleton }.into_any()
<div class="flex-1 flex flex-col"> } else {
<div class="border-b border-border p-4 flex items-center gap-4"> // Dashboard Skeleton
<Skeleton class="h-8 w-48" /> view! {
<Skeleton class="h-8 w-64" /> <div class="flex h-screen bg-background">
<div class="ml-auto"><Skeleton class="h-8 w-24" /></div> // Sidebar skeleton
</div> <div class="w-56 border-r border-border p-4 space-y-4">
<div class="flex-1 p-4 space-y-3"> <Skeleton class="h-8 w-3/4" />
<Skeleton class="h-10 w-full" /> <div class="space-y-2">
<Skeleton class="h-10 w-full" /> <Skeleton class="h-6 w-full" />
<Skeleton class="h-10 w-full" /> <Skeleton class="h-6 w-full" />
<Skeleton class="h-10 w-full" /> <Skeleton class="h-6 w-4/5" />
<Skeleton class="h-10 w-full" /> <Skeleton class="h-6 w-full" />
<Skeleton class="h-10 w-3/4" /> <Skeleton class="h-6 w-3/5" />
</div> <Skeleton class="h-6 w-full" />
<div class="border-t border-border p-3">
<Skeleton class="h-5 w-96" />
</div>
</div>
</div>
}.into_any()>
<Show when=move || is_authenticated.0.get() fallback=|| ()>
<Protected>
<div class="flex flex-col h-full overflow-hidden">
<div class="flex-1 overflow-hidden">
<TorrentTable />
</div> </div>
</div> </div>
</Protected> // Main content skeleton
</Show> <div class="flex-1 flex flex-col">
<div class="border-b border-border p-4 flex items-center gap-4">
<Skeleton class="h-8 w-48" />
<Skeleton class="h-8 w-64" />
<div class="ml-auto"><Skeleton class="h-8 w-24" /></div>
</div>
<div class="flex-1 p-4 space-y-3">
<Skeleton class="h-10 w-full" />
<Skeleton class="h-10 w-full" />
<Skeleton class="h-10 w-full" />
<Skeleton class="h-10 w-full" />
<Skeleton class="h-10 w-full" />
<Skeleton class="h-10 w-3/4" />
</div>
<div class="border-t border-border p-3">
<Skeleton class="h-5 w-96" />
</div>
</div>
</div>
}.into_any()
}
}>
<Show when=move || is_authenticated.0.get() fallback=|| ()>
<Protected>
<div class="flex flex-col h-full overflow-hidden">
<div class="flex-1 overflow-hidden">
<TorrentTable />
</div>
</div>
</Protected>
</Show> </Show>
}.into_any() </Show>
}/> }.into_any()
}/>
<Route path=leptos_router::path!("/settings") view=move || {
Effect::new(move |_| {
if !is_authenticated.0.get() {
let navigate = use_navigate();
navigate("/login", Default::default());
}
});
view! {
<Show when=move || !is_loading.0.get() fallback=|| ()>
<Show when=move || is_authenticated.0.get() fallback=|| ()>
<Protected>
<div class="p-4">"Settings Page (Coming Soon)"</div>
</Protected>
</Show>
</Show>
}
}/>
</Routes>
</div>
}
}
<Route path=leptos_router::path!("/settings") view=move || { <Route path=leptos_router::path!("/settings") view=move || {
Effect::new(move |_| { Effect::new(move |_| {