Files
cozsweet-frontend-nextjs/src/stores/auth/auth-machine.ts
T
admin b67beaeae6 refactor(auth): remove dead AuthReset event
AuthReset was declared in the AuthEvent union and handled in
auth-machine.idle.on with `assign(() => initialState)`, but
nothing in the codebase actually dispatches it anymore.

History of dispatch sites (now all gone):
- sidebar-screen.tsx previously dispatched AuthReset post-logout
  alongside router.replace(ROUTES.chat) — that was simplified to
  just dispatch AuthLogoutSubmitted, the state machine's
  loggingOut state now does the context reset itself in onDone
  via the same `assign(() => initialState)` action.
- chat-screen / splash / auth screens never dispatched it.
- The /auth screen's pendingRedirect-based redirect loop doesn't
  need it (the redirect is one-shot, not a state that needs
  resetting).

So the event handler is unreachable. Remove the type member
from auth-events.ts and the handler from auth-machine.ts.

No behavior change: the only path that 'reset auth state' was
already covered by loggingOut.onDone.

Verified via `grep -rn 'AuthReset' src/`: only 2 hits remain
after this commit — both are the type declaration and the
machine handler, which is now also gone (zero hits).
2026-06-17 15:13:44 +08:00

307 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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 到状态机 —— 由 <AuthStatusChecker /> 派发
AuthInit: "initializing",
AuthGuestLoginSubmitted: "loadingGuestLogin",
AuthEmailLoginSubmitted: "loadingEmailLogin",
AuthEmailRegisterSubmitted: "loadingEmailRegister",
AuthGoogleLoginSubmitted: "loadingOAuth",
AuthFacebookLoginSubmitted: "loadingOAuth",
AuthAppleLoginSubmitted: {
actions: assign({
errorMessage: "Apple login not implemented",
}),
},
// OAuth 回调后 sync 入口 —— 由 <OAuthSessionSync /> 派发
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 里立即跳 /chatguest 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;