refactor(auth): funnel OAuth signIn into auth state machine

Move new AuthPlatform(provider).signIn() out of UI components and into
the auth state machine. UI now dispatches AuthGoogle/FacebookLoginSubmitted
events; a new oauthSignIn actor inside the machine calls next-auth/react's
signIn(provider). Errors and the OAuth redirect window are exposed via
the existing state.isLoading / state.errorMessage surface, removing the
local busy / error state in auth-facebook-panel.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
kanban
2026-06-11 10:05:14 +08:00
committed by chenhang
parent d4cb40a74b
commit e2a57386e7
6 changed files with 274 additions and 54 deletions
+5 -3
View File
@@ -55,9 +55,11 @@ export function AuthProvider({ children }: AuthProviderProps) {
password: state.context.password,
username: state.context.username,
confirmPassword: state.context.confirmPassword,
// isLoading 仅来自邮箱登录 actor(社交登录改由 NextAuth 处理,UI 自行管 busy 状态
isLoading: state.matches("loadingEmailLogin") ||
state.matches("loadingEmailRegister"),
// isLoading 覆盖邮箱登录 / 邮箱注册 / OAuth 跳转(NextAuth 重定向期间
isLoading:
state.matches("loadingEmailLogin") ||
state.matches("loadingEmailRegister") ||
state.matches("loadingOAuth"),
errorMessage: state.context.errorMessage,
isSuccess: state.matches("success"),
loginType: state.context.loginType,
+4 -3
View File
@@ -3,6 +3,7 @@
*
* 事件名与原 Dart AuthEvent 对齐。
*/
import type { AuthProvider } from "@/lib/auth/auth_platform";
import type { AuthMode } from "@/models/auth/auth-mode";
import type { AuthPanelMode } from "@/models/auth/auth-panel-mode";
@@ -21,7 +22,7 @@ export type AuthEvent =
username: string;
confirmPassword: string;
}
// 社交登录(已迁移到 NextAuth)— 业务层直接 await nextauth.googleLogin() 即可
| { type: "AuthGoogleLoginSubmitted" }
| { type: "AuthFacebookLoginSubmitted" }
// 社交登录 —— 状态机内部调 next-auth/react 的 signIn(provider)
| { type: "AuthGoogleLoginSubmitted"; provider: AuthProvider }
| { type: "AuthFacebookLoginSubmitted"; provider: AuthProvider }
| { type: "AuthAppleLoginSubmitted" };
+40 -7
View File
@@ -3,12 +3,14 @@
*
* 原始 Dart: lib/ui/auth/bloc/auth_bloc.dart + auth_state.dart + auth_event.dart
*
* 本轮迁移:社交登录改由 NextAuth 处理(`@/lib/auth/nextauth`),状态机仅保留
* 邮箱注册/登录 + 通用 reset 事件。社交登录走 `new AuthPlatform("google").signIn()` /
* `new AuthPlatform("facebook").signIn()`
* 本轮迁移:所有认证登录调用(含 OAuth 社交登录)统一收口到状态机内。
* 业务层只派发事件(`AuthEmailLoginSubmitted` / `AuthGoogleLoginSubmitted` / 等),
* 状态机通过 actors 调 `authRepository`(邮箱)或 `next-auth/react.signIn`OAuth
*/
import { setup, fromPromise, assign } from "xstate";
import { signIn } from "next-auth/react";
import type { AuthProvider } from "@/lib/auth/auth_platform";
import type { LoginType } from "@/models/auth/login-type";
import { authRepository } from "@/data/repositories/auth_repository";
import { AuthStorage } from "@/data/storage/auth/auth_storage";
@@ -68,6 +70,13 @@ const emailRegisterThenLoginActor = fromPromise<
return "email" as LoginType;
});
// OAuth 登录 actor:调 next-auth/react 的 signIn(provider) 触发 OAuth 跳转。
// 成功路径下 NextAuth 会重定向离开本应用,actor 通常不会 resolve
// onDone 仍保留以处理 SDK 同步返回(极少见)。
const oauthSignInActor = fromPromise<void, AuthProvider>(async ({ input }) => {
await signIn(input);
});
// ============================================================
// Machine
// ============================================================
@@ -79,6 +88,7 @@ export const authMachine = setup({
actors: {
emailLogin: emailLoginActor,
emailRegisterThenLogin: emailRegisterThenLoginActor,
oauthSignIn: oauthSignInActor,
},
}).createMachine({
id: "auth",
@@ -113,10 +123,8 @@ export const authMachine = setup({
},
AuthEmailLoginSubmitted: "loadingEmailLogin",
AuthEmailRegisterSubmitted: "loadingEmailRegister",
// 社交登录已迁移到 NextAuth + AuthPlatform 类
// 状态机保留事件以兼容旧调用方(实际不再触发)
AuthGoogleLoginSubmitted: {},
AuthFacebookLoginSubmitted: {},
AuthGoogleLoginSubmitted: "loadingOAuth",
AuthFacebookLoginSubmitted: "loadingOAuth",
AuthAppleLoginSubmitted: {
actions: assign({
errorMessage: "Apple login not implemented",
@@ -180,6 +188,31 @@ export const authMachine = setup({
},
},
loadingOAuth: {
entry: assign({ errorMessage: null }),
invoke: {
src: "oauthSignIn",
input: ({ event }) => {
// event 来自 idle 跳转(AuthGoogle/FacebookLoginSubmitted
if (event.type === "AuthGoogleLoginSubmitted") return event.provider;
if (event.type === "AuthFacebookLoginSubmitted") return event.provider;
return "google" as AuthProvider;
},
onDone: {
// 罕见分支:NextAuth 同步 resolve(实际几乎不会发生,
// OAuth 流程会通过重定向离开本应用)。
target: "idle",
},
onError: {
target: "idle",
actions: assign({
errorMessage: ({ event }) =>
event.error instanceof Error ? event.error.message : String(event.error),
}),
},
},
},
success: {
// 终态:业务层通过 state.matches("success") 判断
},