diff --git a/src/app/auth/components/auth-facebook-panel.tsx b/src/app/auth/components/auth-facebook-panel.tsx index c3633af2..9d63528a 100644 --- a/src/app/auth/components/auth-facebook-panel.tsx +++ b/src/app/auth/components/auth-facebook-panel.tsx @@ -7,13 +7,13 @@ * 视觉规格(与 Dart 对齐): * - Spacer → logo(120h)→ Spacer → Facebook 主按钮 → 链接 → Spacer → 法律 * - Facebook 主按钮:46px 白胶囊 + Facebook "f" 图标(#1877f2)+ "Login with Facebook" - * - 社交登录走 NextAuth:`new FacebookLogin().signIn()` / `new GoogleLogin().signIn()` + * - 社交登录走 NextAuth:`new AuthPlatform("facebook").signIn()` / `new AuthPlatform("google").signIn()` * - "Other Sign-in Options" 链接打开底部弹层 * - 弹层 Email 按钮 → 触发 `onSwitchToEmail`(父级派发 `AuthPanelModeChanged`) */ import { useState } from "react"; -import { FacebookLogin, GoogleLogin } from "@/lib/auth/nextauth"; +import { AuthPlatform } from "@/lib/auth/nextauth"; import { AuthErrorMessage } from "./auth-error-message"; import { AuthLegalText } from "./auth-legal-text"; @@ -33,7 +33,7 @@ export function AuthFacebookPanel({ onSwitchToEmail }: AuthFacebookPanelProps) { setBusy("facebook"); setError(null); try { - await new FacebookLogin().signIn(); + await new AuthPlatform("facebook").signIn(); } catch (e) { setError(e instanceof Error ? e.message : "Facebook login failed"); } finally { @@ -45,7 +45,7 @@ export function AuthFacebookPanel({ onSwitchToEmail }: AuthFacebookPanelProps) { setBusy("google"); setError(null); try { - await new GoogleLogin().signIn(); + await new AuthPlatform("google").signIn(); } catch (e) { setError(e instanceof Error ? e.message : "Google login failed"); } finally { diff --git a/src/app/splash/components/splash-button.tsx b/src/app/splash/components/splash-button.tsx index 59b86837..5c5965ed 100644 --- a/src/app/splash/components/splash-button.tsx +++ b/src/app/splash/components/splash-button.tsx @@ -11,7 +11,7 @@ * - 消费 `useChatDispatch` / `useUserDispatch`(登录成功后初始化) * - 自管路由跳转(已登录 / 登录成功 → /chat) * - Skip 用 `` 保留 SPA 体验 - * - 社交登录按钮走 `new FacebookLogin().signIn()`(NextAuth 流程) + * - 社交登录按钮走 `new AuthPlatform("facebook").signIn()`(NextAuth 流程) */ import Link from "next/link"; import { useRouter } from "next/navigation"; @@ -20,7 +20,7 @@ import { useEffect, useRef } from "react"; import { useAuthState } from "@/stores/auth/auth-context"; import { useChatDispatch } from "@/stores/chat/chat-context"; import { useUserDispatch } from "@/stores/user/user-context"; -import { FacebookLogin } from "@/lib/auth/nextauth"; +import { AuthPlatform } from "@/lib/auth/nextauth"; import { ROUTES } from "@/router/routes"; import { LoadingIndicator } from "@/app/_components/core/loading-indicator"; import styles from "./splash-button.module.css"; @@ -53,7 +53,7 @@ export function SplashButton() { const handleFacebookLogin = async () => { try { - await new FacebookLogin().signIn(); + await new AuthPlatform("facebook").signIn(); } catch (e) { console.error("[splash-button] Facebook login failed", e); } diff --git a/src/lib/auth/auth_platform.ts b/src/lib/auth/auth_platform.ts new file mode 100644 index 00000000..2c4180ec --- /dev/null +++ b/src/lib/auth/auth_platform.ts @@ -0,0 +1,31 @@ +"use client"; +/** + * AuthPlatform 统一 OAuth 登录入口 + * + * 合并自原 FacebookLogin / GoogleLogin 两个类。 + * 构造时传入 provider 名,调用 signIn() 触发 NextAuth OAuth 流程。 + * + * 用法: + * await new AuthPlatform("facebook").signIn() + * await new AuthPlatform("google").signIn() + * + * NextAuth 流程: + * 1. NextAuth 重定向到 provider OAuth 授权页 + * 2. 用户授权 → 回调 /api/auth/callback/{provider} + * 3. NextAuth 写 next-auth.session-token (httpOnly) cookie + * 4. 重定向到 callbackUrl (默认 /chat) + * + * **不**做任何持久化(不写 unstorage / 不调后端 / 不设自定义 cookie)。 + * 原始 Dart: nextauth-helpers.{facebookLogin, googleLogin}() + */ +import { signIn } from "next-auth/react"; + +export type AuthProvider = "facebook" | "google"; + +export class AuthPlatform { + constructor(private readonly provider: AuthProvider) {} + + async signIn(): Promise { + await signIn(this.provider); + } +} diff --git a/src/lib/auth/facebook_login.ts b/src/lib/auth/facebook_login.ts deleted file mode 100644 index fdc469cd..00000000 --- a/src/lib/auth/facebook_login.ts +++ /dev/null @@ -1,20 +0,0 @@ -"use client"; -/** - * Facebook 登录(NextAuth OAuth 流程触发器) - * - * 调用 `signIn("facebook")` 触发 NextAuth 的 OAuth 流程: - * 1. NextAuth 重定向到 Facebook OAuth 授权页 - * 2. 用户授权 → Facebook 回调 /api/auth/callback/facebook - * 3. NextAuth 写 `next-auth.session-token` (httpOnly) cookie - * 4. 重定向到 callbackUrl (默认 /chat) - * - * **不**做任何持久化(不写 unstorage / 不调后端 / 不设自定义 cookie)。 - * 原始 Dart: nextauth-helpers.facebookLogin() - */ -import { signIn } from "next-auth/react"; - -export class FacebookLogin { - async signIn(): Promise { - await signIn("facebook"); - } -} diff --git a/src/lib/auth/google_login.ts b/src/lib/auth/google_login.ts deleted file mode 100644 index 3eb42a78..00000000 --- a/src/lib/auth/google_login.ts +++ /dev/null @@ -1,20 +0,0 @@ -"use client"; -/** - * Google 登录(NextAuth OAuth 流程触发器) - * - * 调用 `signIn("google")` 触发 NextAuth 的 OAuth 流程: - * 1. NextAuth 重定向到 Google OAuth 授权页 - * 2. 用户授权 → Google 回调 /api/auth/callback/google - * 3. NextAuth 写 `next-auth.session-token` (httpOnly) cookie - * 4. 重定向到 callbackUrl (默认 /chat) - * - * **不**做任何持久化(不写 unstorage / 不调后端 / 不设自定义 cookie)。 - * 原始 Dart: nextauth-helpers.googleLogin() - */ -import { signIn } from "next-auth/react"; - -export class GoogleLogin { - async signIn(): Promise { - await signIn("google"); - } -} diff --git a/src/lib/auth/nextauth.ts b/src/lib/auth/nextauth.ts index ba69020c..42d56640 100644 --- a/src/lib/auth/nextauth.ts +++ b/src/lib/auth/nextauth.ts @@ -2,14 +2,14 @@ * NextAuth v4 配置(单一来源) * * 极简模式:参考 `auth.js` 官方示例的**精神**(不做任何持久化),但保留 v4 语法: - * - 客户端点击登录按钮 → `new GoogleLogin().signIn()` / `new FacebookLogin().signIn()` + * - 客户端点击登录按钮 → `new AuthPlatform("google").signIn()` / `new AuthPlatform("facebook").signIn()` * - NextAuth 重定向到 Google / Facebook OAuth * - OAuth 回调 → NextAuth 写 `next-auth.session-token` (httpOnly) cookie * - 重定向到 callbackUrl (默认 /chat) * - 业务 token 持久化(unstorage / 后端 / 自定义 cookie)**全部不在本轮 scope** * * 导出 `GET` / `POST` 给 `src/app/api/auth/[...nextauth]/route.ts` 引用。 - * 客户端通过 `src/lib/auth/{google_login,facebook_login,logout,auth_gate}.ts` 触发。 + * 客户端通过 `src/lib/auth/{auth_platform,logout}.ts` 触发。 */ import NextAuth from "next-auth"; import Google from "next-auth/providers/google"; @@ -43,8 +43,7 @@ export { handler as GET, handler as POST }; // 每个 class 一个独立文件,详见同目录的子模块。 // 这里 re-export 仅为消费者提供 `@/lib/auth/nextauth` 单点导入。 // 注:上方 `next-auth` 是 server-only,本文件编译时会被 Next.js 切分到 server bundle; -// 客户端 import `GoogleLogin` / `FacebookLogin` / `Logout` 时不会拉入。 +// 客户端 import `AuthPlatform` / `Logout` 时不会拉入。 // ============================================================ -export { GoogleLogin } from "./google_login"; -export { FacebookLogin } from "./facebook_login"; +export { AuthPlatform, type AuthProvider } from "./auth_platform"; export { Logout } from "./logout"; diff --git a/src/stores/auth/auth-context.ts b/src/stores/auth/auth-state.ts similarity index 84% rename from src/stores/auth/auth-context.ts rename to src/stores/auth/auth-state.ts index 4f25bbef..4213206b 100644 --- a/src/stores/auth/auth-context.ts +++ b/src/stores/auth/auth-state.ts @@ -1,11 +1,11 @@ /** - * Auth 状态机:Context 形状 + 初始值 + * Auth 状态机:State 形状 + 初始值 */ import type { AuthMode } from "@/models/auth/auth-mode"; import type { AuthPanelMode } from "@/models/auth/auth-panel-mode"; import type { LoginType } from "@/models/auth/login-type"; -export interface AuthContext { +export interface AuthState { /** 当前面板模式(Facebook / Email) */ authPanelMode: AuthPanelMode; /** 内部登录模式(login / register) */ @@ -18,7 +18,7 @@ export interface AuthContext { loginType: LoginType; } -export const initialContext: AuthContext = { +export const initialState: AuthState = { authPanelMode: "facebook", authMode: "login", email: "", diff --git a/src/stores/auth/auth-types.ts b/src/stores/auth/auth-types.ts index 998f3b13..9a42c424 100644 --- a/src/stores/auth/auth-types.ts +++ b/src/stores/auth/auth-types.ts @@ -7,4 +7,4 @@ * - Context 形状与初始值由 `auth-context.ts` 导出 * - `AuthState` 由 `auth-context.tsx` 导出(保持原 API 兼容) */ -export type { AuthEvent } from "./auth-events"; +export type { AuthEvent } from "./machine/auth-events"; diff --git a/src/stores/auth/index.ts b/src/stores/auth/index.ts index 5dbd5147..baa0c844 100644 --- a/src/stores/auth/index.ts +++ b/src/stores/auth/index.ts @@ -6,7 +6,8 @@ * - 事件联合位于 auth-events.ts * - machine / 类型 / React 入口分别由对应文件导出 */ +export * from "./auth-state"; export * from "./auth-context"; -export * from "./auth-events"; -export * from "./auth-machine"; +export * from "./machine/auth-events"; +export * from "./machine/auth-machine"; export * from "./auth-types"; diff --git a/src/stores/auth/auth-context.tsx b/src/stores/auth/machine/auth-context.tsx similarity index 95% rename from src/stores/auth/auth-context.tsx rename to src/stores/auth/machine/auth-context.tsx index 8fa7c09c..020356fe 100644 --- a/src/stores/auth/auth-context.tsx +++ b/src/stores/auth/machine/auth-context.tsx @@ -18,12 +18,12 @@ import { import { useMachine } from "@xstate/react"; import { authMachine } from "./auth-machine"; -import type { AuthEvent, AuthContext as MachineContext } from "./auth-machine"; +import type { AuthEvent, AuthState as MachineContext } from "./auth-machine"; /** * 对外暴露的 State 形状(保持与原 AuthState 兼容) */ -export interface AuthState { +interface AuthState { authPanelMode: MachineContext["authPanelMode"]; authMode: MachineContext["authMode"]; email: string; diff --git a/src/stores/auth/auth-events.ts b/src/stores/auth/machine/auth-events.ts similarity index 100% rename from src/stores/auth/auth-events.ts rename to src/stores/auth/machine/auth-events.ts diff --git a/src/stores/auth/auth-machine.ts b/src/stores/auth/machine/auth-machine.ts similarity index 91% rename from src/stores/auth/auth-machine.ts rename to src/stores/auth/machine/auth-machine.ts index f692a0b6..1e494795 100644 --- a/src/stores/auth/auth-machine.ts +++ b/src/stores/auth/machine/auth-machine.ts @@ -4,8 +4,8 @@ * 原始 Dart: lib/ui/auth/bloc/auth_bloc.dart + auth_state.dart + auth_event.dart * * 本轮迁移:社交登录改由 NextAuth 处理(`@/lib/auth/nextauth`),状态机仅保留 - * 邮箱注册/登录 + 通用 reset 事件。社交登录走 `new GoogleLogin().signIn()` / - * `new FacebookLogin().signIn()`。 + * 邮箱注册/登录 + 通用 reset 事件。社交登录走 `new AuthPlatform("google").signIn()` / + * `new AuthPlatform("facebook").signIn()`。 */ import { setup, fromPromise, assign } from "xstate"; @@ -14,12 +14,12 @@ import { authRepository } from "@/data/repositories/auth_repository"; import { AuthStorage } from "@/data/storage/auth/auth_storage"; import { Result } from "@/utils/result"; -import { AuthContext, initialContext } from "./auth-context"; +import { AuthState, initialState } from "../auth-state"; import type { AuthEvent } from "./auth-events"; -// 重新导出 Context / Event 类型,保持 machine 文件的公共 API 不变 -export type { AuthContext } from "./auth-context"; -export { initialContext } from "./auth-context"; +// 重新导出 State / Event 类型,保持 machine 文件的公共 API 不变 +export type { AuthState } from "../auth-state"; +export { initialState } from "../auth-state"; export type { AuthEvent } from "./auth-events"; // ============================================================ @@ -73,7 +73,7 @@ const emailRegisterThenLoginActor = fromPromise< // ============================================================ export const authMachine = setup({ types: { - context: {} as AuthContext, + context: {} as AuthState, events: {} as AuthEvent, }, actors: { @@ -83,7 +83,7 @@ export const authMachine = setup({ }).createMachine({ id: "auth", initial: "idle", - context: initialContext, + context: initialState, states: { idle: { on: { @@ -109,11 +109,11 @@ export const authMachine = setup({ }), }, AuthReset: { - actions: assign(() => initialContext), + actions: assign(() => initialState), }, AuthEmailLoginSubmitted: "loadingEmailLogin", AuthEmailRegisterSubmitted: "loadingEmailRegister", - // 社交登录已迁移到 NextAuth + GoogleLogin / FacebookLogin 类 + // 社交登录已迁移到 NextAuth + AuthPlatform 类 // 状态机保留事件以兼容旧调用方(实际不再触发) AuthGoogleLoginSubmitted: {}, AuthFacebookLoginSubmitted: {}, diff --git a/src/stores/chat/chat-context.tsx b/src/stores/chat/chat-context.tsx index df03d662..2efe40ed 100644 --- a/src/stores/chat/chat-context.tsx +++ b/src/stores/chat/chat-context.tsx @@ -20,13 +20,13 @@ import { } from "react"; import { useMachine } from "@xstate/react"; -import { chatMachine } from "./chat-machine"; -import type { ChatEvent, ChatContext as MachineContext } from "./chat-machine"; +import { chatMachine } from "./machine/chat-machine"; +import type { ChatEvent, ChatState as MachineContext } from "./machine/chat-machine"; /** * 对外暴露的 State 形状(保持与原 ChatState 兼容) */ -export interface ChatState { +interface ChatState { messages: MachineContext["messages"]; isReplyingAI: boolean; isGuest: boolean; diff --git a/src/stores/chat/chat-types.ts b/src/stores/chat/chat-types.ts index 7d67d910..b4319e57 100644 --- a/src/stores/chat/chat-types.ts +++ b/src/stores/chat/chat-types.ts @@ -8,5 +8,5 @@ * - `ChatState` 由 `chat-context.tsx` 导出(保持原 API 兼容) * - `GuestChatQuota` 仍由 `chat-machine.ts` 导出(业务常量,与上下文配对紧密) */ -export type { ChatEvent } from "./chat-events"; +export type { ChatEvent } from "./machine/chat-events"; export { GuestChatQuota } from "./chat-machine"; diff --git a/src/stores/chat/index.ts b/src/stores/chat/index.ts index a1b064a2..d9b4c66f 100644 --- a/src/stores/chat/index.ts +++ b/src/stores/chat/index.ts @@ -11,7 +11,8 @@ * 注:chat-side-effects.ts 暂未删除(其中 WebSocket 长生命周期逻辑 * 待下一轮迁移到 fromCallback actor)。 */ +export * from "./machine/chat-state"; export * from "./chat-context"; -export * from "./chat-events"; +export * from "./machine/chat-events"; export * from "./chat-types"; export { chatMachine } from "./chat-machine"; diff --git a/src/stores/chat/chat-events.ts b/src/stores/chat/machine/chat-events.ts similarity index 100% rename from src/stores/chat/chat-events.ts rename to src/stores/chat/machine/chat-events.ts diff --git a/src/stores/chat/chat-machine.ts b/src/stores/chat/machine/chat-machine.ts similarity index 95% rename from src/stores/chat/chat-machine.ts rename to src/stores/chat/machine/chat-machine.ts index 4aef4435..5efae4c3 100644 --- a/src/stores/chat/chat-machine.ts +++ b/src/stores/chat/machine/chat-machine.ts @@ -23,8 +23,8 @@ import { AuthStorage } from "@/data/storage/auth/auth_storage"; import { formatDate } from "@/utils/date"; import { Result } from "@/utils/result"; -import { ChatContext, initialContext } from "./chat-context"; -import type { ChatEvent } from "./chat-events"; +import { ChatState, initialState } from "./chat-state"; +import type { ChatEvent } from "./machine/chat-events"; /** 游客配额阈值(与 Dart `GuestChatQuota` 保持一致) */ export const GuestChatQuota = { @@ -32,10 +32,10 @@ export const GuestChatQuota = { quotaExhausted: 0, } as const; -// 重新导出 Context / Event 类型,保持 machine 文件的公共 API 不变 -export type { ChatContext } from "./chat-context"; -export { initialContext } from "./chat-context"; -export type { ChatEvent } from "./chat-events"; +// 重新导出 State / Event 类型,保持 machine 文件的公共 API 不变 +export type { ChatState } from "./chat-state"; +export { initialState } from "./chat-state"; +export type { ChatEvent } from "./machine/chat-events"; // ============================================================ // Helpers @@ -146,7 +146,7 @@ const loadMoreHistoryActor = fromPromise< // ============================================================ export const chatMachine = setup({ types: { - context: {} as ChatContext, + context: {} as ChatState, events: {} as ChatEvent, }, actors: { @@ -230,7 +230,7 @@ export const chatMachine = setup({ }).createMachine({ id: "chat", initial: "idle", - context: initialContext, + context: initialState, states: { idle: { on: { diff --git a/src/stores/chat/chat-context.ts b/src/stores/chat/machine/chat-state.ts similarity index 83% rename from src/stores/chat/chat-context.ts rename to src/stores/chat/machine/chat-state.ts index ba882afd..ed204a32 100644 --- a/src/stores/chat/chat-context.ts +++ b/src/stores/chat/machine/chat-state.ts @@ -1,11 +1,11 @@ /** - * Chat 状态机:Context 形状 + 初始值 + * Chat 状态机:State 形状 + 初始值 * * 注:GuestChatQuota 仍位于 chat-machine.ts(属于业务常量,与上下文配对紧密)。 */ import type { UiMessage } from "@/models/chat/ui-message"; -export interface ChatContext { +export interface ChatState { messages: UiMessage[]; isReplyingAI: boolean; isGuest: boolean; @@ -17,7 +17,7 @@ export interface ChatContext { historyOffset: number; } -export const initialContext: ChatContext = { +export const initialState: ChatState = { messages: [], isReplyingAI: false, isGuest: true, diff --git a/src/stores/sidebar/index.ts b/src/stores/sidebar/index.ts index 6ef25855..9d43e4b3 100644 --- a/src/stores/sidebar/index.ts +++ b/src/stores/sidebar/index.ts @@ -8,7 +8,8 @@ * - Context 形状与初始值位于 sidebar-context.ts * - 事件联合位于 sidebar-events.ts */ +export * from "./sidebar-state"; export * from "./sidebar-context"; export * from "./sidebar-events"; export * from "./sidebar-types"; -export { sidebarMachine } from "./sidebar-machine"; +export { sidebarMachine } from "./machine/sidebar-machine"; diff --git a/src/stores/sidebar/sidebar-events.ts b/src/stores/sidebar/machine/sidebar-events.ts similarity index 100% rename from src/stores/sidebar/sidebar-events.ts rename to src/stores/sidebar/machine/sidebar-events.ts diff --git a/src/stores/sidebar/sidebar-machine.ts b/src/stores/sidebar/machine/sidebar-machine.ts similarity index 88% rename from src/stores/sidebar/sidebar-machine.ts rename to src/stores/sidebar/machine/sidebar-machine.ts index 21fffd43..f6e747b1 100644 --- a/src/stores/sidebar/sidebar-machine.ts +++ b/src/stores/sidebar/machine/sidebar-machine.ts @@ -11,7 +11,7 @@ */ import { setup, assign } from "xstate"; -import { SidebarContext, initialContext } from "./sidebar-context"; +import { SidebarState, initialState } from "../sidebar-state"; import type { SidebarEvent } from "./sidebar-events"; /** @@ -36,9 +36,9 @@ export const BottomNavItem = { export type BottomNavItem = (typeof BottomNavItem)[keyof typeof BottomNavItem]; -// 重新导出 Context / Event 类型,保持 machine 文件的公共 API 不变 -export type { SidebarContext } from "./sidebar-context"; -export { initialContext } from "./sidebar-context"; +// 重新导出 State / Event 类型,保持 machine 文件的公共 API 不变 +export type { SidebarState } from "../sidebar-state"; +export { initialState } from "../sidebar-state"; export type { SidebarEvent } from "./sidebar-events"; // ============================================================ @@ -46,7 +46,7 @@ export type { SidebarEvent } from "./sidebar-events"; // ============================================================ export const sidebarMachine = setup({ types: { - context: {} as SidebarContext, + context: {} as SidebarState, events: {} as SidebarEvent, }, actions: { @@ -73,7 +73,7 @@ export const sidebarMachine = setup({ }).createMachine({ id: "sidebar", initial: "idle", - context: initialContext, + context: initialState, states: { /** * Sidebar 是纯数据状态(无异步副作用),仅用 idle 状态 diff --git a/src/stores/sidebar/sidebar-context.ts b/src/stores/sidebar/machine/sidebar-state.ts similarity index 65% rename from src/stores/sidebar/sidebar-context.ts rename to src/stores/sidebar/machine/sidebar-state.ts index e70da168..dd53b815 100644 --- a/src/stores/sidebar/sidebar-context.ts +++ b/src/stores/sidebar/machine/sidebar-state.ts @@ -1,18 +1,18 @@ /** - * Sidebar 状态机:Context 形状 + 初始值 + * Sidebar 状态机:State 形状 + 初始值 * * SidebarPage / BottomNavItem 仍由 sidebar-machine.ts 导出(事件/上下文均需引用)。 */ -import { SidebarPage, BottomNavItem } from "./sidebar-machine"; +import { SidebarPage, BottomNavItem } from "./machine/sidebar-machine"; -export interface SidebarContext { +export interface SidebarState { currentPage: SidebarPage; selectedBottomNavItem: BottomNavItem; characterName: string; characterProgress: number; } -export const initialContext: SidebarContext = { +export const initialState: SidebarState = { currentPage: SidebarPage.DefaultView, selectedBottomNavItem: BottomNavItem.None, characterName: "Luna", diff --git a/src/stores/sidebar/sidebar-context.tsx b/src/stores/sidebar/sidebar-context.tsx index f5c04560..1b02bc31 100644 --- a/src/stores/sidebar/sidebar-context.tsx +++ b/src/stores/sidebar/sidebar-context.tsx @@ -17,16 +17,16 @@ import { } from "react"; import { useMachine } from "@xstate/react"; -import { sidebarMachine } from "./sidebar-machine"; +import { sidebarMachine } from "./machine/sidebar-machine"; import type { - SidebarContext as MachineContext, + SidebarState as MachineContext, SidebarEvent, -} from "./sidebar-machine"; +} from "./machine/sidebar-machine"; /** * 对外暴露的 State 形状(保持与原 SidebarState 兼容) */ -export interface SidebarState { +interface SidebarState { currentPage: MachineContext["currentPage"]; selectedBottomNavItem: MachineContext["selectedBottomNavItem"]; characterName: string; diff --git a/src/stores/sidebar/sidebar-types.ts b/src/stores/sidebar/sidebar-types.ts index a81b4c77..b9e00e7a 100644 --- a/src/stores/sidebar/sidebar-types.ts +++ b/src/stores/sidebar/sidebar-types.ts @@ -8,5 +8,5 @@ * - Context 形状由 `sidebar-context.ts` 导出 * - SidebarState 由 `sidebar-context.tsx` 导出(保持原 API 兼容) */ -export { SidebarPage, BottomNavItem } from "./sidebar-machine"; +export { SidebarPage, BottomNavItem } from "./machine/sidebar-machine"; export type { SidebarEvent } from "./sidebar-events"; diff --git a/src/stores/user/index.ts b/src/stores/user/index.ts index 47edfcf8..07faba26 100644 --- a/src/stores/user/index.ts +++ b/src/stores/user/index.ts @@ -8,7 +8,8 @@ * - Context 形状与初始值位于 user-context.ts * - 事件联合位于 user-events.ts */ +export * from "./machine/user-state"; export * from "./user-context"; -export * from "./user-events"; +export * from "./machine/user-events"; export * from "./user-types"; export { userMachine } from "./user-machine"; diff --git a/src/stores/user/user-events.ts b/src/stores/user/machine/user-events.ts similarity index 100% rename from src/stores/user/user-events.ts rename to src/stores/user/machine/user-events.ts diff --git a/src/stores/user/user-machine.ts b/src/stores/user/machine/user-machine.ts similarity index 92% rename from src/stores/user/user-machine.ts rename to src/stores/user/machine/user-machine.ts index c5006026..7b5afade 100644 --- a/src/stores/user/user-machine.ts +++ b/src/stores/user/machine/user-machine.ts @@ -17,13 +17,13 @@ import { authRepository } from "@/data/repositories/auth_repository"; import { UserStorage } from "@/data/storage/user/user_storage"; import { Result } from "@/utils/result"; -import { UserContext, initialContext } from "./user-context"; -import type { UserEvent } from "./user-events"; +import { UserState, initialState } from "./machine/user-state"; +import type { UserEvent } from "./machine/user-events"; -// 重新导出 Context / Event 类型,保持 machine 文件的公共 API 不变 -export type { UserContext } from "./user-context"; -export { initialContext } from "./user-context"; -export type { UserEvent } from "./user-events"; +// 重新导出 State / Event 类型,保持 machine 文件的公共 API 不变 +export type { UserState } from "./machine/user-state"; +export { initialState } from "./machine/user-state"; +export type { UserEvent } from "./machine/user-events"; // ============================================================ // Helpers @@ -102,7 +102,7 @@ const userLogoutActor = fromPromise(async () => { // ============================================================ export const userMachine = setup({ types: { - context: {} as UserContext, + context: {} as UserState, events: {} as UserEvent, }, actors: { @@ -136,7 +136,7 @@ export const userMachine = setup({ }).createMachine({ id: "user", initial: "idle", - context: initialContext, + context: initialState, states: { idle: { on: { diff --git a/src/stores/user/user-context.ts b/src/stores/user/machine/user-state.ts similarity index 70% rename from src/stores/user/user-context.ts rename to src/stores/user/machine/user-state.ts index aeecf8d9..b7a6e3a0 100644 --- a/src/stores/user/user-context.ts +++ b/src/stores/user/machine/user-state.ts @@ -1,9 +1,9 @@ /** - * User 状态机:Context 形状 + 初始值 + * User 状态机:State 形状 + 初始值 */ import type { UserView } from "@/models/user/user"; -export interface UserContext { +export interface UserState { currentUser: UserView | null; pronouns: string; coinBalance: number; @@ -11,7 +11,7 @@ export interface UserContext { avatarUrl: string | null; } -export const initialContext: UserContext = { +export const initialState: UserState = { currentUser: null, pronouns: "He", coinBalance: 45, diff --git a/src/stores/user/user-context.tsx b/src/stores/user/user-context.tsx index db86c1cd..399b1d71 100644 --- a/src/stores/user/user-context.tsx +++ b/src/stores/user/user-context.tsx @@ -18,12 +18,12 @@ import { import { useMachine } from "@xstate/react"; import { userMachine } from "./user-machine"; -import type { UserContext as MachineContext, UserEvent } from "./user-machine"; +import type { UserState as MachineContext, UserEvent } from "./user-machine"; /** * 对外暴露的 State 形状(保持与原 UserState 兼容) */ -export interface UserState { +interface UserState { currentUser: MachineContext["currentUser"]; pronouns: string; coinBalance: number; diff --git a/src/stores/user/user-types.ts b/src/stores/user/user-types.ts index 738d2605..3d4c47b5 100644 --- a/src/stores/user/user-types.ts +++ b/src/stores/user/user-types.ts @@ -7,4 +7,4 @@ * - Context 形状与初始值由 `user-context.ts` 导出 * - `UserState` 由 `user-context.tsx` 导出(保持原 API 兼容) */ -export type { UserEvent } from "./user-events"; +export type { UserEvent } from "./machine/user-events";