Frontend (React 18 + TS + Vite): - Landing: glitching hero, live /api/stats veil ticker, mode cards, featured spirits - Séance: three.js shader ghost (hue/form per entity, mood + audio-reactive), Ouija planchette board spelling utterances, transcript with TTS replay, entity dossier, direct contact streaming, passive/active listening - Modes: Wire Ghost telemetry panel, EVP mic anomaly detection, WebUSB RTL-SDR sweep + waterfall (hardware pass pending), Ouija/Direct Contact - Codex: public registry + entity dossiers, rarity tiers, i18n EN/ES complete - State: VeilSocket (reconnect/backoff), seance reducer, auth context - 72 vitest tests green; served by FastAPI at :7777 Docs: README + as-built plans 3-7
62 lines
1.7 KiB
TypeScript
62 lines
1.7 KiB
TypeScript
// Auth context: who is seated at the table. Wraps the /auth REST endpoints
|
|
// and exposes { user, checking, login, register, logout } to the app.
|
|
|
|
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
|
import type { ReactNode } from 'react'
|
|
import * as api from '../api'
|
|
import type { User } from '../api'
|
|
|
|
export type AuthApi = {
|
|
user: User | null
|
|
checking: boolean
|
|
login: (username: string, password: string) => Promise<User>
|
|
register: (username: string, password: string) => Promise<User>
|
|
logout: () => Promise<void>
|
|
}
|
|
|
|
const AuthContext = createContext<AuthApi | null>(null)
|
|
|
|
export function AuthProvider({ children }: { children: ReactNode }) {
|
|
const [user, setUser] = useState<User | null>(null)
|
|
const [checking, setChecking] = useState(true)
|
|
|
|
useEffect(() => {
|
|
api
|
|
.me()
|
|
.then(setUser)
|
|
.catch(() => setUser(null))
|
|
.finally(() => setChecking(false))
|
|
}, [])
|
|
|
|
const login = useCallback(async (username: string, password: string) => {
|
|
const u = await api.login(username, password)
|
|
setUser(u)
|
|
return u
|
|
}, [])
|
|
|
|
const register = useCallback(
|
|
async (username: string, password: string) => {
|
|
await api.register(username, password)
|
|
return login(username, password)
|
|
},
|
|
[login],
|
|
)
|
|
|
|
const logout = useCallback(async () => {
|
|
await api.logout()
|
|
setUser(null)
|
|
}, [])
|
|
|
|
const value = useMemo<AuthApi>(
|
|
() => ({ user, checking, login, register, logout }),
|
|
[user, checking, login, register, logout],
|
|
)
|
|
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
|
|
}
|
|
|
|
export function useAuth(): AuthApi {
|
|
const ctx = useContext(AuthContext)
|
|
if (!ctx) throw new Error('useAuth must be used inside <AuthProvider>')
|
|
return ctx
|
|
}
|