Our Personal Data Server from scratch!
tranquil.farm
oauth
atproto
pds
rust
postgresql
objectstorage
fun
1<script lang="ts">
2 import { getAuthState } from '../lib/auth.svelte'
3 import { navigate, routes } from '../lib/router.svelte'
4 import type { Snippet } from 'svelte'
5 import type { Session } from '../lib/types/api'
6 import { createAuthenticatedClient, type AuthenticatedClient } from '../lib/authenticated-client'
7
8 interface Props {
9 children: Snippet<[{ session: Session; client: AuthenticatedClient }]>
10 requireAdmin?: boolean
11 onReady?: (session: Session, client: AuthenticatedClient) => void
12 }
13
14 let { children, requireAdmin = false, onReady }: Props = $props()
15 const auth = $derived(getAuthState())
16 let readyCalled = $state(false)
17
18 $effect(() => {
19 if (auth.kind === 'unauthenticated' || auth.kind === 'error') {
20 navigate(routes.login)
21 }
22 if (requireAdmin && auth.kind === 'authenticated' && !auth.session.isAdmin) {
23 navigate(routes.dashboard)
24 }
25 if (auth.kind === 'authenticated' && onReady && !readyCalled) {
26 readyCalled = true
27 onReady(auth.session, createAuthenticatedClient(auth.session))
28 }
29 })
30</script>
31
32{#if auth.kind === 'authenticated'}
33 {@render children({ session: auth.session, client: createAuthenticatedClient(auth.session) })}
34{:else}
35 <div class="loading-container"><div class="loading-spinner"></div></div>
36{/if}
37
38<style>
39 .loading-container {
40 display: flex;
41 justify-content: center;
42 align-items: center;
43 min-height: 200px;
44 padding: var(--space-7);
45 }
46
47 .loading-spinner {
48 width: 32px;
49 height: 32px;
50 border: 3px solid var(--border-color);
51 border-top-color: var(--accent);
52 border-radius: 50%;
53 animation: spin 0.8s linear infinite;
54 }
55
56 @keyframes spin {
57 to { transform: rotate(360deg); }
58 }
59</style>