/** * useAuth — authentication context provider. * * Validates a stored token against /api/auth/me on load and exposes * login/logout backed by the backend auth routes; persists the token in * localStorage. * * Architecture: wraps the whole app in App.jsx; consumed by InfoBar, * LoginButton, LoginModal, and RosterManager. * * Design: one shared context so every auth-gated component reads the same * session state. */ import { createContext, useContext, useState, useEffect, useCallback } from "react"; import { apiUrl } from "../utils/api"; const AuthContext = createContext(null); export function AuthProvider({ children }) { const [token, setToken] = useState(() => localStorage.getItem("valhalla_token")); const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { if (!token) { setLoading(false); return; } fetch(apiUrl("/api/auth/me"), { headers: { Authorization: `Bearer ${token}` }, }) .then((r) => (r.ok ? r.json() : Promise.reject())) .then((u) => setUser(u)) .catch(() => { localStorage.removeItem("valhalla_token"); setToken(null); }) .finally(() => setLoading(false)); }, [token]); const login = useCallback(async (email, password) => { const res = await fetch(apiUrl("/api/auth/login"), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, password }), }); if (!res.ok) { const err = await res.json().catch(() => ({ detail: "Login failed" })); throw new Error(err.detail || "Login failed"); } const data = await res.json(); localStorage.setItem("valhalla_token", data.token); setToken(data.token); setUser(data.user); return data; }, []); const logout = useCallback(async () => { if (token) { await fetch(apiUrl("/api/auth/logout"), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ token }), }).catch(() => {}); } localStorage.removeItem("valhalla_token"); setToken(null); setUser(null); }, [token]); return ( {children} ); } export function useAuth() { const ctx = useContext(AuthContext); if (!ctx) throw new Error("useAuth must be used within AuthProvider"); return ctx; }