Files
OpenFlare/atsf_server/web/components/providers/auth-provider.tsx
T
2026-03-11 13:56:44 +08:00

90 lines
2.0 KiB
TypeScript

'use client';
import {
createContext,
type ReactNode,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react';
import { logout as logoutRequest, getCurrentUser } from '@/features/auth/api/auth';
import type { AuthUser } from '@/types/auth';
interface AuthContextValue {
user: AuthUser | null;
isLoading: boolean;
isAuthenticated: boolean;
refreshUser: () => Promise<AuthUser | null>;
setUser: (user: AuthUser | null) => void;
logout: () => Promise<void>;
}
const AuthContext = createContext<AuthContextValue | null>(null);
interface AuthProviderProps {
children: ReactNode;
}
export function AuthProvider({ children }: AuthProviderProps) {
const [user, setUserState] = useState<AuthUser | null>(null);
const [isLoading, setIsLoading] = useState(true);
const refreshUser = useCallback(async () => {
try {
const nextUser = await getCurrentUser();
setUserState(nextUser);
return nextUser;
} catch {
setUserState(null);
return null;
} finally {
setIsLoading(false);
}
}, []);
useEffect(() => {
void refreshUser();
}, [refreshUser]);
const setUser = useCallback((nextUser: AuthUser | null) => {
setUserState(nextUser);
setIsLoading(false);
}, []);
const logout = useCallback(async () => {
try {
await logoutRequest();
} finally {
setUserState(null);
setIsLoading(false);
}
}, []);
const value = useMemo(
() => ({
user,
isLoading,
isAuthenticated: user !== null,
refreshUser,
setUser,
logout,
}),
[isLoading, logout, refreshUser, setUser, user],
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}