use dioxus::prelude::*; use crate::components::sidebar::Sidebar; use crate::infrastructure::auth_check::check_auth; use crate::models::AuthInfo; use crate::Route; /// Application shell layout that wraps all authenticated pages. /// /// Calls [`check_auth`] on mount to fetch the current user's session. /// If unauthenticated, redirects to `/auth`. Otherwise renders the /// sidebar with real user data and the active child route. #[component] pub fn AppShell() -> Element { // use_resource memoises the async call and avoids infinite re-render // loops that use_effect + spawn + signal writes can cause. #[allow(clippy::redundant_closure)] let auth = use_resource(move || check_auth()); // Clone the inner value out of the Signal to avoid holding the // borrow across the rsx! return (Dioxus lifetime constraint). let auth_snapshot: Option> = auth.read().clone(); match auth_snapshot { Some(Ok(info)) if info.authenticated => { rsx! { div { class: "app-shell", Sidebar { email: info.email, name: info.name, avatar_url: info.avatar_url, } main { class: "main-content", Outlet:: {} } } } } Some(Ok(_)) => { // Not authenticated -- redirect to login. let nav = navigator(); nav.push(NavigationTarget::::External("/auth".into())); rsx! { div { class: "app-shell loading", p { "Redirecting to login..." } } } } Some(Err(e)) => { let msg = e.to_string(); rsx! { div { class: "auth-error", p { "Authentication error: {msg}" } a { href: "/auth", "Login" } } } } None => { // Still loading. rsx! { div { class: "app-shell loading", p { "Loading..." } } } } } }