"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 形状(保持与原 AuthState 兼容) */ interface AuthState { authPanelMode: MachineContext["authPanelMode"]; authMode: MachineContext["authMode"]; email: string; password: string; username: string; confirmPassword: string; isLoading: boolean; errorMessage: string | null; isSuccess: boolean; loginType: MachineContext["loginType"]; } 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 仅来自邮箱登录 actor(社交登录改由 NextAuth 处理,UI 自行管 busy 状态) isLoading: state.matches("loadingEmailLogin") || state.matches("loadingEmailRegister"), errorMessage: state.context.errorMessage, isSuccess: state.matches("success"), loginType: state.context.loginType, }), [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; }