diff --git a/src/App.css b/src/App.css index 80bb605..e786887 100644 --- a/src/App.css +++ b/src/App.css @@ -19,7 +19,11 @@ display: flex; align-items: center; justify-content: center; - background: transparent; + background: #eef6ee; +} + +[data-theme="dark"] .page-loader { + background: #0a0a0a; } .page-loader__tree { diff --git a/src/components/ProtectedRoute.tsx b/src/components/ProtectedRoute.tsx index a4219b1..e2cfaa2 100644 --- a/src/components/ProtectedRoute.tsx +++ b/src/components/ProtectedRoute.tsx @@ -1,23 +1,45 @@ -import { type ReactNode } from 'react' +import { type ReactNode, Suspense, useState, useEffect } from 'react' import { Navigate, useLocation } from 'react-router-dom' import { useAuth } from '../contexts/AuthContext' import { PageLoader } from './PageLoader' -type Props = { - children: ReactNode +// Mounts only once Suspense has resolved (chunk is ready). +// Signals the parent to hide the loader and reveal content. +function ContentReady({ onReady }: { onReady: () => void }) { + useEffect(() => { + onReady() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + return null } +type Props = { children: ReactNode } + export function ProtectedRoute({ children }: Props) { const { user, loading } = useAuth() const location = useLocation() - if (loading) { - return - } + // On page refresh: loading starts true → contentReady=false → loader shows throughout. + // On in-app navigation: loading is already false → contentReady=true → no loader shown. + const [contentReady, setContentReady] = useState(() => !loading) - if (!user) { + if (!loading && !user) { return } - return <>{children} + const showLoader = loading || !contentReady + + return ( + <> + {showLoader && } + {!loading && user && ( +
+ + setContentReady(true)} /> + {children} + +
+ )} + + ) }