fix(web): show settings on first load and hoist DemoProvider globally
Bug 1: On a fresh load with no saved config, RootLayout returned `null` while a useEffect-driven `router.navigate()` fired, leaving a blank screen until the user manually refreshed. Move the redirect into the root route's `beforeLoad` so it happens synchronously during route resolution and the settings form renders on first paint. Bug 2: `DemoProvider` was mounted inside `RootLayout` only on the non-settings branch, so any component reading `useDemo()` outside that branch would throw "useDemoContext must be used within DemoProvider". Hoist `<DemoProvider>` to `main.tsx` so the context is available app-wide. Adds vitest + RTL setup with regression tests for both behaviours.
This commit is contained in:
@@ -1,46 +1,43 @@
|
||||
import { createRootRoute, Outlet, useRouter } from "@tanstack/react-router";
|
||||
import { createRootRoute, Outlet, redirect, useRouter } from "@tanstack/react-router";
|
||||
import { useEffect } from "react";
|
||||
import { Sidebar } from "@/components/layout/Sidebar";
|
||||
import { DemoProvider } from "@/context/DemoContext";
|
||||
import { loadConfig } from "@/lib/config";
|
||||
import { applyTheme, getStoredTheme } from "@/lib/theme";
|
||||
|
||||
const SETTINGS_PATH = "/settings";
|
||||
|
||||
function RootLayout() {
|
||||
const config = loadConfig();
|
||||
const router = useRouter();
|
||||
const isSettings = router.state.location.pathname === "/settings";
|
||||
const isSettings = router.state.location.pathname === SETTINGS_PATH;
|
||||
|
||||
useEffect(() => {
|
||||
applyTheme(getStoredTheme());
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!config && !isSettings) {
|
||||
router.navigate({ to: "/settings" as never });
|
||||
}
|
||||
}, [config, isSettings, router]);
|
||||
|
||||
if (isSettings) {
|
||||
return <Outlet />;
|
||||
}
|
||||
|
||||
if (!config) return null;
|
||||
|
||||
return (
|
||||
<DemoProvider>
|
||||
<div
|
||||
className="flex h-screen w-full overflow-hidden"
|
||||
style={{ background: "var(--bg)", position: "relative", zIndex: 1 }}
|
||||
>
|
||||
<Sidebar />
|
||||
<main className="flex-1 overflow-auto" style={{ position: "relative", zIndex: 1 }}>
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
</DemoProvider>
|
||||
<div
|
||||
className="flex h-screen w-full overflow-hidden"
|
||||
style={{ background: "var(--bg)", position: "relative", zIndex: 1 }}
|
||||
>
|
||||
<Sidebar />
|
||||
<main className="flex-1 overflow-auto" style={{ position: "relative", zIndex: 1 }}>
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const Route = createRootRoute({
|
||||
beforeLoad: ({ location }) => {
|
||||
// Redirect to settings synchronously when no config is present, so the
|
||||
// first paint already shows the settings form instead of a blank screen.
|
||||
if (location.pathname !== SETTINGS_PATH && !loadConfig()) {
|
||||
throw redirect({ to: SETTINGS_PATH as never });
|
||||
}
|
||||
},
|
||||
component: RootLayout,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user