import type { PropsWithChildren } from "react"; import { createContext, useContext, useEffect, useState } from "react"; import type { SessionView } from "@internal-mail/shared"; import { apiClient } from "../api/client"; type SessionContextValue = { session: SessionView; loading: boolean; authBusy: boolean; error: string | null; login: (email: string, password: string, totpCode?: string) => Promise; logout: () => Promise; refresh: () => Promise; }; const anonymousSession: SessionView = { authenticated: false, csrfToken: null, user: null }; const SessionContext = createContext(null); export const SessionProvider = ({ children }: PropsWithChildren) => { const [session, setSession] = useState(anonymousSession); const [loading, setLoading] = useState(true); const [authBusy, setAuthBusy] = useState(false); const [error, setError] = useState(null); const refresh = async () => { try { const nextSession = await apiClient.getSession(); setSession(nextSession); setError(null); } catch (requestError) { setSession(anonymousSession); setError(requestError instanceof Error ? requestError.message : "Unable to load session."); } finally { setLoading(false); } }; useEffect(() => { void refresh(); }, []); const value: SessionContextValue = { session, loading, authBusy, error, login: async (email, password, totpCode) => { try { setAuthBusy(true); const nextSession = await apiClient.login(email, password, totpCode); setSession(nextSession); setError(null); } finally { setAuthBusy(false); } }, logout: async () => { try { setAuthBusy(true); await apiClient.logout(); setSession(anonymousSession); } finally { setAuthBusy(false); } }, refresh }; return {children}; }; export const useSession = (): SessionContextValue => { const value = useContext(SessionContext); if (!value) { throw new Error("useSession must be used inside SessionProvider."); } return value; };