"use client"; import { type Dispatch, type ReactNode, createContext, useContext, useMemo, } from "react"; import { useMachine } from "@xstate/react"; import { chatMachine } from "./chat-machine"; import type { ChatEvent, ChatState as MachineContext } from "./chat-machine"; /** * 对外暴露的 State 形状 * * isGuest 字段已删 —— 由 chat-screen 派生自 `auth.loginStatus === "guest"`。 * 消费方(chat-screen)通过 `useAuthState()` 取 loginStatus,本地派生 isGuest。 */ interface ChatState { messages: MachineContext["messages"]; isReplyingAI: boolean; upgradePromptVisible: boolean; upgradeReason: MachineContext["upgradeReason"]; upgradeHint: MachineContext["upgradeHint"]; upgradeDetail: MachineContext["upgradeDetail"]; isLoadingMore: boolean; hasMore: boolean; historyOffset: number; /** history 初始加载完成标志(local → network → save 跑完) —— UI 可用此显示 loading */ historyLoaded: boolean; unlockHistoryPromptVisible: boolean; lockedHistoryCount: number; isUnlockingHistory: boolean; unlockHistoryError: string | null; } const ChatStateCtx = createContext(null); const ChatDispatchCtx = createContext | null>(null); export interface ChatProviderProps { children: ReactNode; } export function ChatProvider({ children }: ChatProviderProps) { const [state, send] = useMachine(chatMachine, {}); // 映射 XState 状态机快照 → 原 ChatState 形状 const chatState = useMemo( () => ({ messages: state.context.messages, isReplyingAI: state.context.isReplyingAI, upgradePromptVisible: state.context.upgradePromptVisible, upgradeReason: state.context.upgradeReason, upgradeHint: state.context.upgradeHint, upgradeDetail: state.context.upgradeDetail, isLoadingMore: state.context.isLoadingMore, hasMore: state.context.hasMore, historyOffset: state.context.historyOffset, historyLoaded: state.context.historyLoaded, unlockHistoryPromptVisible: state.context.unlockHistoryPromptVisible, lockedHistoryCount: state.context.lockedHistoryCount, isUnlockingHistory: state.context.isUnlockingHistory, unlockHistoryError: state.context.unlockHistoryError, }), [state], ); return ( {children} ); } export function useChatState(): ChatState { const ctx = useContext(ChatStateCtx); if (!ctx) throw new Error("useChatState must be used inside "); return ctx; } export function useChatDispatch(): Dispatch { const ctx = useContext(ChatDispatchCtx); if (!ctx) throw new Error("useChatDispatch must be used inside "); return ctx; }