's'
This commit is contained in:
166
context/AuthContext.tsx
Normal file
166
context/AuthContext.tsx
Normal file
@@ -0,0 +1,166 @@
|
||||
import React, {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { Alert } from 'react-native'
|
||||
|
||||
import { clearPbSession, loadPbSession, savePbSession, type StoredPbSession } from '@/lib/authStorage'
|
||||
import {
|
||||
isLiveAllowed,
|
||||
pbAuthRefresh,
|
||||
pbAuthWithPassword,
|
||||
type PbUserRecord,
|
||||
} from '@/lib/pocketbase'
|
||||
|
||||
type Ctx = {
|
||||
session: StoredPbSession | null
|
||||
ready: boolean
|
||||
loginVisible: boolean
|
||||
openLogin: () => void
|
||||
closeLogin: () => void
|
||||
login: (email: string, password: string) => Promise<void>
|
||||
logout: () => Promise<void>
|
||||
refreshSession: () => Promise<boolean>
|
||||
/** 用于远程 API 请求头 */
|
||||
pbToken: string | null
|
||||
/** 开始/重启直播前调用;未登录则弹出登录;无权限则 false */
|
||||
ensureCanControlLive: () => Promise<boolean>
|
||||
}
|
||||
|
||||
const AuthContext = createContext<Ctx | null>(null)
|
||||
|
||||
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
const [session, setSession] = useState<StoredPbSession | null>(null)
|
||||
const [ready, setReady] = useState(false)
|
||||
const [loginVisible, setLoginVisible] = useState(false)
|
||||
const pendingGate = useRef<((ok: boolean) => void) | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
void (async () => {
|
||||
const s = await loadPbSession()
|
||||
if (s) {
|
||||
try {
|
||||
const r = await pbAuthRefresh(s.token)
|
||||
const next: StoredPbSession = { token: r.token, record: r.record }
|
||||
setSession(next)
|
||||
await savePbSession(next)
|
||||
} catch {
|
||||
await clearPbSession()
|
||||
setSession(null)
|
||||
}
|
||||
}
|
||||
setReady(true)
|
||||
})()
|
||||
}, [])
|
||||
|
||||
const openLogin = useCallback(() => setLoginVisible(true), [])
|
||||
const closeLogin = useCallback(() => {
|
||||
setLoginVisible(false)
|
||||
const p = pendingGate.current
|
||||
pendingGate.current = null
|
||||
p?.(false)
|
||||
}, [])
|
||||
|
||||
const login = useCallback(async (email: string, password: string) => {
|
||||
const r = await pbAuthWithPassword(email, password)
|
||||
if (!isLiveAllowed(r.record)) {
|
||||
throw new Error('该账号暂无直播权限,请联系管理员')
|
||||
}
|
||||
const next: StoredPbSession = { token: r.token, record: r.record }
|
||||
setSession(next)
|
||||
await savePbSession(next)
|
||||
setLoginVisible(false)
|
||||
const p = pendingGate.current
|
||||
pendingGate.current = null
|
||||
p?.(true)
|
||||
}, [])
|
||||
|
||||
const logout = useCallback(async () => {
|
||||
await clearPbSession()
|
||||
setSession(null)
|
||||
}, [])
|
||||
|
||||
const refreshSession = useCallback(async () => {
|
||||
if (!session?.token) return false
|
||||
try {
|
||||
const r = await pbAuthRefresh(session.token)
|
||||
const next: StoredPbSession = { token: r.token, record: r.record }
|
||||
setSession(next)
|
||||
await savePbSession(next)
|
||||
return isLiveAllowed(r.record)
|
||||
} catch {
|
||||
await clearPbSession()
|
||||
setSession(null)
|
||||
return false
|
||||
}
|
||||
}, [session?.token])
|
||||
|
||||
const ensureCanControlLive = useCallback(async (): Promise<boolean> => {
|
||||
if (session?.token) {
|
||||
try {
|
||||
const r = await pbAuthRefresh(session.token)
|
||||
const next: StoredPbSession = { token: r.token, record: r.record }
|
||||
setSession(next)
|
||||
await savePbSession(next)
|
||||
if (!isLiveAllowed(r.record)) {
|
||||
Alert.alert('无权限', '账号已被管理员停用远程直播权限。')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} catch {
|
||||
await clearPbSession()
|
||||
setSession(null)
|
||||
}
|
||||
}
|
||||
return new Promise<boolean>((resolve) => {
|
||||
pendingGate.current = (ok) => resolve(ok)
|
||||
setLoginVisible(true)
|
||||
})
|
||||
}, [session?.token])
|
||||
|
||||
const pbToken = session?.token ?? null
|
||||
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
session,
|
||||
ready,
|
||||
loginVisible,
|
||||
openLogin,
|
||||
closeLogin,
|
||||
login,
|
||||
logout,
|
||||
refreshSession,
|
||||
pbToken,
|
||||
ensureCanControlLive,
|
||||
}),
|
||||
[
|
||||
session,
|
||||
ready,
|
||||
loginVisible,
|
||||
openLogin,
|
||||
closeLogin,
|
||||
login,
|
||||
logout,
|
||||
refreshSession,
|
||||
pbToken,
|
||||
ensureCanControlLive,
|
||||
],
|
||||
)
|
||||
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
|
||||
}
|
||||
|
||||
export function useAuth(): Ctx {
|
||||
const c = useContext(AuthContext)
|
||||
if (!c) throw new Error('useAuth outside AuthProvider')
|
||||
return c
|
||||
}
|
||||
|
||||
export function useAuthOptional(): Ctx | null {
|
||||
return useContext(AuthContext)
|
||||
}
|
||||
Reference in New Issue
Block a user