744e23fc29
Extract `<Name>Context` interface + `initialContext` const into `<name>-context.ts` and the event union into `<name>-events.ts` for all four state machines (auth, chat, sidebar, user) under src/stores/. - `*-machine.ts` keeps helpers, actors, actions, and the `setup().createMachine()` body; re-exports the types/initial value to preserve its public API. - `*-types.ts` re-exports from the new files for backward compatibility (sidebar keeps enum re-exports, chat keeps GuestChatQuota). - `index.ts` barrels updated to re-export the new files. - Removed unused model imports (AuthMode, AuthPanelMode) from auth-machine.ts; kept LoginType, UiMessage, UserView where still used by actors/helpers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
190 lines
5.9 KiB
TypeScript
190 lines
5.9 KiB
TypeScript
/**
|
||
* Auth 状态机(XState v5)
|
||
*
|
||
* 原始 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()`。
|
||
*/
|
||
import { setup, fromPromise, assign } from "xstate";
|
||
|
||
import type { LoginType } from "@/models/auth/login-type";
|
||
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 type { AuthEvent } from "./auth-events";
|
||
|
||
// 重新导出 Context / Event 类型,保持 machine 文件的公共 API 不变
|
||
export type { AuthContext } from "./auth-context";
|
||
export { initialContext } from "./auth-context";
|
||
export type { AuthEvent } from "./auth-events";
|
||
|
||
// ============================================================
|
||
// Helpers
|
||
// ============================================================
|
||
async function readGuestId(): Promise<string | undefined> {
|
||
const r = await AuthStorage.getInstance().getDeviceId();
|
||
if (r.success && r.data != null) {
|
||
return r.data;
|
||
}
|
||
return undefined;
|
||
}
|
||
|
||
// ============================================================
|
||
// Actors(异步服务)
|
||
// ============================================================
|
||
// 邮箱登录 actor 仅调 authRepository.emailLogin 拿业务 token。
|
||
// 本轮起不再调 /api/auth/marker 写 cookie(统一迁到 NextAuth session
|
||
// cookie 检测,邮箱登录 proxy 行为在后续轮恢复)。
|
||
|
||
const emailLoginActor = fromPromise<LoginType, { email: string; password: string }>(
|
||
async ({ input }) => {
|
||
const guestId = await readGuestId();
|
||
const result = await authRepository.emailLogin({ ...input, guestId });
|
||
if (Result.isErr(result)) throw result.error;
|
||
return "email" as LoginType;
|
||
},
|
||
);
|
||
|
||
const emailRegisterThenLoginActor = fromPromise<
|
||
LoginType,
|
||
{ email: string; password: string; username: string; confirmPassword: string }
|
||
>(async ({ input }) => {
|
||
const guestId = await readGuestId();
|
||
|
||
const registerResult = await authRepository.register({ ...input, guestId });
|
||
if (Result.isErr(registerResult)) throw registerResult.error;
|
||
|
||
// 注册后自动登录(对齐 Dart 行为)
|
||
const loginResult = await authRepository.emailLogin({
|
||
email: input.email,
|
||
password: input.password,
|
||
guestId,
|
||
});
|
||
if (Result.isErr(loginResult)) throw loginResult.error;
|
||
return "email" as LoginType;
|
||
});
|
||
|
||
// ============================================================
|
||
// Machine
|
||
// ============================================================
|
||
export const authMachine = setup({
|
||
types: {
|
||
context: {} as AuthContext,
|
||
events: {} as AuthEvent,
|
||
},
|
||
actors: {
|
||
emailLogin: emailLoginActor,
|
||
emailRegisterThenLogin: emailRegisterThenLoginActor,
|
||
},
|
||
}).createMachine({
|
||
id: "auth",
|
||
initial: "idle",
|
||
context: initialContext,
|
||
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,
|
||
}),
|
||
},
|
||
AuthReset: {
|
||
actions: assign(() => initialContext),
|
||
},
|
||
AuthEmailLoginSubmitted: "loadingEmailLogin",
|
||
AuthEmailRegisterSubmitted: "loadingEmailRegister",
|
||
// 社交登录已迁移到 NextAuth + GoogleLogin / FacebookLogin 类
|
||
// 状态机保留事件以兼容旧调用方(实际不再触发)
|
||
AuthGoogleLoginSubmitted: {},
|
||
AuthFacebookLoginSubmitted: {},
|
||
AuthAppleLoginSubmitted: {
|
||
actions: assign({
|
||
errorMessage: "Apple login not implemented",
|
||
}),
|
||
},
|
||
},
|
||
},
|
||
|
||
loadingEmailLogin: {
|
||
invoke: {
|
||
src: "emailLogin",
|
||
input: ({ event }) => {
|
||
if (event.type !== "AuthEmailLoginSubmitted")
|
||
return { email: "", password: "" };
|
||
return { email: event.email, password: event.password };
|
||
},
|
||
onDone: {
|
||
target: "success",
|
||
actions: assign({
|
||
loginType: ({ event }) => event.output,
|
||
errorMessage: null,
|
||
}),
|
||
},
|
||
onError: {
|
||
target: "idle",
|
||
actions: assign({
|
||
errorMessage: ({ event }) =>
|
||
event.error instanceof Error ? event.error.message : String(event.error),
|
||
}),
|
||
},
|
||
},
|
||
},
|
||
|
||
loadingEmailRegister: {
|
||
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: "success",
|
||
actions: assign({
|
||
loginType: ({ event }) => event.output,
|
||
errorMessage: null,
|
||
}),
|
||
},
|
||
onError: {
|
||
target: "idle",
|
||
actions: assign({
|
||
errorMessage: ({ event }) =>
|
||
event.error instanceof Error ? event.error.message : String(event.error),
|
||
}),
|
||
},
|
||
},
|
||
},
|
||
|
||
success: {
|
||
// 终态:业务层通过 state.matches("success") 判断
|
||
},
|
||
},
|
||
});
|
||
|
||
export type AuthMachine = typeof authMachine;
|