feat(App): replace PageLoader with LazyLoader for improved loading experience

This commit is contained in:
Jaime Idolpx 2026-06-11 02:51:35 -04:00
parent fc2ed1c321
commit f1f4c6dc16
2 changed files with 40 additions and 8 deletions

View File

@ -13,6 +13,7 @@ import MediaManager from './components/MediaManager';
import logoSvg from '../imports/logo.svg'; import logoSvg from '../imports/logo.svg';
import { useSettings } from './settings'; import { useSettings } from './settings';
import { WsProvider } from './ws'; import { WsProvider } from './ws';
import { LazyLoader } from './components/ui/lazy-loader';
// Three.js lives only in RealityOverridePage — keep lazy so it doesn't load on startup. // Three.js lives only in RealityOverridePage — keep lazy so it doesn't load on startup.
// CodeMirror/syntax-highlighter/ReactMarkdown live in MediaViewerEditor — lazy-loaded // CodeMirror/syntax-highlighter/ReactMarkdown live in MediaViewerEditor — lazy-loaded
@ -47,13 +48,6 @@ type AppId =
| 'reality-override' | 'reality-override'
| 'reality-override-admin'; | 'reality-override-admin';
function PageLoader() {
return (
<div className="flex items-center justify-center h-full text-neutral-400">
<Loader2 className="w-6 h-6 animate-spin" />
</div>
);
}
export default function App() { export default function App() {
const [currentPage, setCurrentPage] = useState<Page>('status'); const [currentPage, setCurrentPage] = useState<Page>('status');
@ -314,7 +308,7 @@ function AppPage({ title, onBack }: { title: string; onBack: () => void }) {
</header> </header>
<main className="flex-1 overflow-y-auto"> <main className="flex-1 overflow-y-auto">
<Suspense fallback={<PageLoader />}> <Suspense fallback={<LazyLoader />}>
{pages[currentPage]} {pages[currentPage]}
</Suspense> </Suspense>
</main> </main>

View File

@ -0,0 +1,38 @@
import { useEffect, useState } from 'react';
import { Loader2 } from 'lucide-react';
export function LazyLoader() {
const [pct, setPct] = useState(0);
useEffect(() => {
const steps: [number, number][] = [
[80, 30],
[400, 60],
[1200, 80],
[2500, 92],
];
const timers = steps.map(([delay, val]) => setTimeout(() => setPct(val), delay));
return () => timers.forEach(clearTimeout);
}, []);
const duration =
pct <= 30 ? '150ms' :
pct <= 60 ? '500ms' :
pct <= 80 ? '1000ms' : '1500ms';
return (
<div className="fixed inset-0 bg-neutral-950 z-40 flex flex-col items-center justify-center">
<div className="absolute top-0 left-0 right-0 h-[2px] bg-neutral-800 overflow-hidden">
<div
className="h-full bg-blue-500 transition-[width] ease-out"
style={{ width: `${pct}%`, transitionDuration: duration }}
/>
</div>
<div className="flex flex-col items-center gap-3 text-neutral-600">
<Loader2 className="w-5 h-5 animate-spin" />
<span className="text-xs tracking-wide">Loading</span>
</div>
</div>
);
}