Files
ai-ticket-platform/frontend/src/features/auth/AuthProvider.tsx
2026-07-01 14:16:20 +08:00

93 lines
2.1 KiB
TypeScript

import * as React from 'react'
import * as authApi from './api'
import type { CurrentUser } from './types'
type AuthContextValue = {
user: CurrentUser | null
loading: boolean
authenticated: boolean
login: (username: string, password: string) => Promise<void>
logout: () => Promise<void>
refresh: () => Promise<void>
}
const AuthContext = React.createContext<AuthContextValue | null>(null)
type AuthProviderProps = {
children: React.ReactNode
}
export function AuthProvider({ children }: AuthProviderProps) {
const [user, setUser] = React.useState<CurrentUser | null>(null)
const [loading, setLoading] = React.useState(true)
const refresh = React.useCallback(async () => {
try {
const currentUser = await authApi.getCurrentUser()
setUser(currentUser)
} catch {
setUser(null)
}
}, [])
React.useEffect(() => {
let mounted = true
authApi
.getCurrentUser()
.then((currentUser) => {
if (mounted) {
setUser(currentUser)
}
})
.catch(() => {
if (mounted) {
setUser(null)
}
})
.finally(() => {
if (mounted) {
setLoading(false)
}
})
return () => {
mounted = false
}
}, [])
const handleLogin = React.useCallback(async (username: string, password: string) => {
const currentUser = await authApi.login(username, password)
setUser(currentUser)
}, [])
const handleLogout = React.useCallback(async () => {
await authApi.logout()
setUser(null)
window.location.hash = '/'
}, [])
const value = React.useMemo<AuthContextValue>(
() => ({
user,
loading,
authenticated: Boolean(user),
login: handleLogin,
logout: handleLogout,
refresh
}),
[handleLogin, handleLogout, loading, refresh, user]
)
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
export function useAuth() {
const context = React.useContext(AuthContext)
if (!context) {
throw new Error('useAuth must be used within AuthProvider')
}
return context
}