feat(web): wire root layout — bootstrap, guard, Shell, QueryProvider
+layout.ts runs auth.bootstrap() in load() so the shell sees the user synchronously. +layout.svelte installs the TanStack QueryClientProvider, runs the route guard as a \$effect, and swaps Shell vs. bare slot based on auth state.
This commit is contained in:
@@ -1,5 +1,31 @@
|
||||
<script lang="ts">
|
||||
import '../app.css';
|
||||
import { page } from '$app/state';
|
||||
import { goto } from '$app/navigation';
|
||||
import { QueryClientProvider } from '@tanstack/svelte-query';
|
||||
import { queryClient } from '$lib/query/client';
|
||||
import { user } from '$lib/auth/store.svelte';
|
||||
import Shell from '$lib/components/Shell.svelte';
|
||||
|
||||
let { children } = $props<{ children: import('svelte').Snippet }>();
|
||||
|
||||
// Reactive guard: runs every time page.url or user.value changes.
|
||||
$effect(() => {
|
||||
const path = page.url.pathname;
|
||||
const isLogin = path === '/login';
|
||||
if (user.value === null && !isLogin) {
|
||||
const returnTo = path + page.url.search;
|
||||
goto('/login?returnTo=' + encodeURIComponent(returnTo), { replaceState: true });
|
||||
} else if (user.value !== null && isLogin) {
|
||||
goto('/', { replaceState: true });
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<slot />
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{#if user.value !== null && page.url.pathname !== '/login'}
|
||||
<Shell>{@render children()}</Shell>
|
||||
{:else}
|
||||
{@render children()}
|
||||
{/if}
|
||||
</QueryClientProvider>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { LayoutLoad } from './$types';
|
||||
import { bootstrap } from '$lib/auth/store.svelte';
|
||||
|
||||
export const ssr = false; // adapter-static fallback; we're SPA-only
|
||||
export const prerender = false;
|
||||
|
||||
export const load: LayoutLoad = async () => {
|
||||
await bootstrap();
|
||||
return {};
|
||||
};
|
||||
Reference in New Issue
Block a user