/** * Auth 状态机(XState v5) */ import { setup, assign } from "xstate"; import type { AuthProvider } from "@/lib/auth/auth_platform"; import { AuthState, initialState } from "./auth-state"; import type { AuthEvent } from "./auth-events"; import { emailLoginActor, emailRegisterThenLoginActor, oauthSignInActor, syncGoogleBackendActor, syncFacebookBackendActor, guestLoginActor, checkAuthStatusActor, logoutActor, } from "./auth-actors"; // 重新导出 State / Event 类型,保持 machine 文件的公共 API 不变 export type { AuthState } from "./auth-state"; export { initialState } from "./auth-state"; export type { AuthEvent } from "./auth-events"; // ============================================================ // Machine // // 设计:所有登录/同步流跑完都回 `idle`(带正确 `loginStatus`)。 // // `pendingRedirect: boolean`("用户显式触发了登录"信号)—— // 在登录成功的 onDone 里自动置 true: // splash / auth screen 看到 `pendingRedirect && loginStatus !== "notLoggedIn"` 跳 /chat // 跳完派 `AuthClearPendingRedirect` 置 false // `initializing` 不设 flag —— 启动 init 是被动读 token,不算"用户意图"。 // ============================================================ export const authMachine = setup({ types: { context: {} as AuthState, events: {} as AuthEvent, }, actors: { emailLogin: emailLoginActor, emailRegisterThenLogin: emailRegisterThenLoginActor, oauthSignIn: oauthSignInActor, syncGoogleBackend: syncGoogleBackendActor, syncFacebookBackend: syncFacebookBackendActor, guestLogin: guestLoginActor, checkAuthStatus: checkAuthStatusActor, logout: logoutActor, }, }).createMachine({ /** @xstate-layout N4IgpgJg5mDOIC5QEMCuAXAFgOgJYQBswBiAQQ0wAVkA7MAgWQHsIwBhTWmCAbQAYAuolAAHJrFzpcTGsJAAPRABYA7NgAc6gEzqAjCvUBOLbvUqlWgDQgAnohWnsAZgCsSpXyeHdTgGy6lXwBfIOs0LDxCEnIsZlYOLkh+ISQQMQkpGTlFBF81Y1MzX3UvdT5NaztcrS1sD10tQ1dvPi0+YNCQcJx8IjIKADEmACcAWzYiZGGkwTl0yWlZVJzzbHMarSUvNpctYsrEXd86pT0SvfULXyUQsIpIvpjMACU4MHRkufEFrOXEXT4fGwvkMLicmxUTT4qj4hgOCC2QKUoL4KkaShc5ycty69160QoAEkaJJPql5pklqAcl4nGtDCCtGDdqotCp4YY9MC0Xw3CpfH5Gi4cd0HgSsABxVBwdAAGSYUFwNAAyqgAEajSToGYpUTfSnZRC0+mM5kQtnwlzlDQuQynEpOJz6BoqEV4qL9LAAUVGyFwBHlipV6s16G1vFm5P1i0NCGNkNNrnN7Ns9gx2D2LhUDjcl0BN06ovxnswPr9BFeitg2uGqo1Wp1XwyMb+caaJuMZtZKaquhBQKMsOhHiafY6dwixaeEqYCqIgaVddD4bJeubv2pRvbCc7Se78P0SjqYLaewuTtObsnHqeA2QAGMwGrZwBrBfB+thxtR9dUhRbukdyZPc0R7RAaiPUEnRKREjH0dQrx6G8KFIEQRHnBVFxDBsI11NJow3f8EVqVRfCzJ14L4Qp4UdI83F0Bkz3BJxtEQsUSxnOcwGVGwaHvJccNXfDf1jeMGV3FlQIPDEj1OQITAYjwijYqdBgfJ9Xx4viBK-XCmx+P8aW3cTgMki1UwQBpHFRB1IQMdRMXHXFr0eCgJjAKZKDAGgICVKBXl86Z7w+SM1wM2MLGwQFopimKAnhLQWOwTFNF2JxAWuDwtBUkkpGQAhcAALz84gIBkMA8BoAA3JgXwqotctwfKir8hAlRq+9kEpZIhIpFtNwRAVkt8JkdCaXxWhMeFNHUNZTEMTwXExaEXF0HKFma4qaCgYgwGGYYRmwdCuoAMxGUZsAajaCq2qA2uqphOu6wReoIwzlGubAFoFUw3BMEpQXhSEXDWMiVDSlkJtdQt7gIJhkF87apRld9SvKyqarqy7YfhxGoGR6t33ujqusWHrQuE8LWytIFwT0fxAW0F14SUXQQe+x0HDZRy7TYuGEb8gm5Uwmhdv2w7jvQM6xmxiJ+bxoWifax7SZkcm8L6wicitXRuQYq1-C2cwXBomovrBX7jDaVonNFeW-LLf1UbKugMdq+qcYF7bHYDEXiZV56BFekTqdaPXxs5PJdGj+EyNqJ0z08Cx6b53GHd9J2RbFg7hiOghTvO2WcHt72M99oN-aesmXopzX3oQK0jyolxrkS05JuzS1bWcW1Am8Jw0SdW3Pbxn3K1was9rR13laxu209L8tx8n4ZK9Vmh1f0g1WwsXWLacEcjAPxuD1tfIB-B8FPBUcFsphuWF6gMewCrGts4l-OpcL+evafsvl5rGvQOwcqYDQsLUMwjcBQGxbmBSyjcvqXAmgxTkAQDCp1-gAeSeNPCqs8PYPywU8IB1cg61zehFUEyUDCmD8M6TQ-IErg2PEPCwB9QQmAwXjbBFB3650ltLC6P9uHEOVlXNWNcNYUJ3gyL6-1NCGAMDTPw0097RVWrvCEAQ2KwF4veQWs4oBEAAEIPjqj5XBbs573F0XxAxXFTH3nMRAEhEiyFSJDmArYUU2Y-VMCCTQugDx0OSheEEnIHKeDvhOHAtj9FI0MSYsx3kIB8LzgXGWoo4n2KMWARxzjXEb0kVvfqRF3BHmaBYVEo4TYWQaMUahJRTAGEaIUHRei-J3kfM+Wq+SUmWPwUXbA2TtpdI0r05JPlCmbx-KAspexnANHIiYTkqIBQHiWdgJZ-jszgnWO0uxoz1I9JfH0ixe0c7pK-pkmxHSjndNfGclxYj14zLCtvMBbJgQtzZA5PQ0cmgHiiVsnkrhYHFE5CEToNAWBwDkN0EpWtEAAFp2aGHRRizF6L1C+HhMi4eLkwCIvrpsowlxsxgjRAEUwqjDDOFRBoxKI1ygIXvj0Rqm0-LEtjAEnudo-DjQ8CNVRQJo43zWQKR0pguGC2lITEW3LWy8sqQKvwQqrAWU5vS1QrNEo6BvgS4uj8fbvkVQNZVvdVXXHaBqqo5E5oAl8GDXkvyZWL39AAvaZqiIWv5SCNVNrT4MWcHQ8iQ5mluqgDwrA3qci+q2P661wq6lh30E68pmh3CbGxGy4Zdz8aJLyZMiAsbEDxqteqg8OhajGE5OYWEA8jDQxiXmw5UAxknKeaW3InI+UJsFYGlNgRkqJVtOUJRE1WUhCAA */ id: "auth", initial: "idle", context: initialState, states: { idle: { on: { AuthPanelModeChanged: { actions: assign({ authPanelMode: ({ event }) => event.mode, errorMessage: null, }), }, AuthModeChanged: { actions: assign({ authMode: ({ event }) => event.mode, errorMessage: null, }), }, AuthFormCleared: { actions: assign({ email: "", password: "", username: "", confirmPassword: "", errorMessage: null, }), }, AuthLogoutSubmitted: "loggingOut", // 启动一次性 init:从 storage 同步 loginStatus 到状态机 —— 由 派发 AuthInit: "initializing", AuthGuestLoginSubmitted: "loadingGuestLogin", AuthEmailLoginSubmitted: "loadingEmailLogin", AuthEmailRegisterSubmitted: "loadingEmailRegister", AuthGoogleLoginSubmitted: "loadingOAuth", AuthFacebookLoginSubmitted: "loadingOAuth", AuthAppleLoginSubmitted: { actions: assign({ errorMessage: "Apple login not implemented", }), }, // OAuth 回调后 sync 入口 —— 由 派发 AuthGoogleSyncSubmitted: "syncingGoogleBackend", AuthFacebookSyncSubmitted: "syncingFacebookBackend", // splash / auth screen 跳完 /chat 后清flag AuthClearPendingRedirect: { actions: assign({ pendingRedirect: false }), }, }, }, initializing: { entry: assign({ errorMessage: null }), invoke: { src: "checkAuthStatus", onDone: { target: "idle", // ← init 完回 idle(从 storage 拿到 loginStatus 后落地) actions: assign({ loginStatus: ({ event }) => event.output, // 不置 pendingRedirect —— 状态查询是被动读 token,不算"用户意图" errorMessage: null, }), }, onError: { target: "idle", actions: assign({ errorMessage: ({ event }) => event.error instanceof Error ? event.error.message : String(event.error), }), }, }, }, loadingGuestLogin: { entry: assign({ errorMessage: null }), invoke: { src: "guestLogin", onDone: { target: "idle", // 内联 assign —— XState v5 type inference 保留 loadingGuestLogin state 类型 actions: assign({ loginStatus: ({ event }) => event.output, // Skip 点击后已在 splash 里立即跳 /chat;guest login 不再依赖 pendingRedirect。 pendingRedirect: false, errorMessage: null, }), }, onError: { target: "idle", actions: assign({ errorMessage: ({ event }) => event.error instanceof Error ? event.error.message : String(event.error), }), }, }, }, loadingEmailLogin: { entry: assign({ errorMessage: null }), invoke: { src: "emailLogin", input: ({ event }) => { if (event.type !== "AuthEmailLoginSubmitted") return { email: "", password: "" }; return { email: event.email, password: event.password }; }, onDone: { target: "idle", actions: assign({ loginStatus: ({ event }) => event.output, pendingRedirect: true, // ← 邮箱登录成功 → 跳 errorMessage: null, }), }, onError: { target: "idle", actions: assign({ errorMessage: ({ event }) => event.error instanceof Error ? event.error.message : String(event.error), }), }, }, }, loadingEmailRegister: { entry: assign({ errorMessage: null }), invoke: { src: "emailRegisterThenLogin", input: ({ event }) => { if (event.type !== "AuthEmailRegisterSubmitted") return { email: "", password: "", username: "", confirmPassword: "" }; return { email: event.email, password: event.password, username: event.username, confirmPassword: event.confirmPassword, }; }, onDone: { target: "idle", actions: assign({ loginStatus: ({ event }) => event.output, pendingRedirect: true, // ← 注册成功 → 跳 errorMessage: null, }), }, onError: { target: "idle", actions: assign({ errorMessage: ({ event }) => event.error instanceof Error ? event.error.message : String(event.error), }), }, }, }, loadingOAuth: { entry: assign({ errorMessage: null }), invoke: { src: "oauthSignIn", input: ({ event }) => { if (event.type === "AuthGoogleLoginSubmitted") return event.provider; if (event.type === "AuthFacebookLoginSubmitted") return event.provider; return "google" as AuthProvider; }, onDone: { target: "idle", }, onError: { target: "idle", actions: assign({ errorMessage: ({ event }) => event.error instanceof Error ? event.error.message : String(event.error), }), }, }, }, loggingOut: { entry: assign({ errorMessage: null, pendingRedirect: false }), invoke: { src: "logout", onDone: { target: "idle", actions: assign(() => initialState), }, onError: { target: "idle", actions: assign(({ event }) => ({ ...initialState, errorMessage: event.error instanceof Error ? event.error.message : String(event.error), })), }, }, }, syncingGoogleBackend: { entry: assign({ errorMessage: null }), invoke: { src: "syncGoogleBackend", input: ({ event }) => { if (event.type !== "AuthGoogleSyncSubmitted") return { idToken: "" }; return { idToken: event.idToken }; }, onDone: { target: "idle", actions: assign({ loginStatus: ({ event }) => event.output, pendingRedirect: true, // ← OAuth sync 成功 → 跳 errorMessage: null, }), }, onError: { target: "idle", actions: assign({ errorMessage: ({ event }) => event.error instanceof Error ? event.error.message : String(event.error), }), }, }, }, syncingFacebookBackend: { entry: assign({ errorMessage: null }), invoke: { src: "syncFacebookBackend", input: ({ event }) => { if (event.type !== "AuthFacebookSyncSubmitted") return { accessToken: "" }; return { accessToken: event.accessToken }; }, onDone: { target: "idle", actions: assign({ loginStatus: ({ event }) => event.output, pendingRedirect: true, // ← OAuth sync 成功 → 跳 errorMessage: null, }), }, onError: { target: "idle", actions: assign({ errorMessage: ({ event }) => event.error instanceof Error ? event.error.message : String(event.error), }), }, }, }, }, }); export type AuthMachine = typeof authMachine;