93 lines
2.1 KiB
TypeScript
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
|
|
}
|