"use client"; /** * AuthContext:基于 XState v5 的 React Context Provider * * 业务事件(`Auth*Submitted`)通过 `useMachine(authMachine).send()` 派发。 * XState 自动处理异步副作用(invoke + fromPromise actors)。 * * 兼容性:保持原 `useAuthState()` / `useAuthDispatch()` API, * 内部将 XState 状态机快照映射回原 `AuthState` 形状。 */ import { type Dispatch, type ReactNode, createContext, useContext, useMemo, } from "react"; import { useMachine } from "@xstate/react"; import { authMachine } from "./auth-machine"; import type { AuthEvent, AuthState as MachineContext } from "./auth-machine"; /** * 对外暴露的 State 形状 */ interface AuthState { authPanelMode: MachineContext["authPanelMode"]; authMode: MachineContext["authMode"]; email: string; password: string; username: string; confirmPassword: string; isLoading: boolean; errorMessage: string | null; /** 当前登录状态(未登录 / 游客 / OAuth / 邮箱) */ loginStatus: MachineContext["loginStatus"]; /** 启动时的本地登录态恢复是否已经完成 */ hasInitialized: MachineContext["hasInitialized"]; } const AuthStateCtx = createContext(null); const AuthDispatchCtx = createContext | null>(null); export interface AuthProviderProps { children: ReactNode; } export function AuthProvider({ children }: AuthProviderProps) { const [state, send] = useMachine(authMachine); // 映射 XState 状态机快照 → 原 AuthState 形状 const authState = useMemo( () => ({ authPanelMode: state.context.authPanelMode, authMode: state.context.authMode, email: state.context.email, password: state.context.password, username: state.context.username, confirmPassword: state.context.confirmPassword, // isLoading 覆盖邮箱登录 / 邮箱注册 / OAuth 跳转(NextAuth 重定向期间)/ // OAuth 回调后端 sync / 显式游客登录 isLoading: state.matches("loadingEmailLogin") || state.matches("loadingEmailRegister") || state.matches("loadingOAuth") || state.matches("initializing") || state.matches("loggingOut") || state.matches("syncingGoogleBackend") || state.matches("syncingFacebookBackend") || state.matches("loadingGuestLogin"), errorMessage: state.context.errorMessage, loginStatus: state.context.loginStatus, hasInitialized: state.context.hasInitialized, }), [state], ); return ( {children} ); } export function useAuthState(): AuthState { const ctx = useContext(AuthStateCtx); if (!ctx) throw new Error("useAuthState must be used inside "); return ctx; } export function useAuthDispatch(): Dispatch { const ctx = useContext(AuthDispatchCtx); if (!ctx) throw new Error("useAuthDispatch must be used inside "); return ctx; }