refactor(auth): unify Facebook and Google login into AuthPlatform class
Replace the separate FacebookLogin and GoogleLogin classes with a single
AuthPlatform class that takes a provider name ("facebook" | "google") as a
constructor argument. This consolidates duplicate OAuth sign-in logic behind
one entry point while preserving the existing NextAuth flow.
- Add new src/lib/auth/auth_platform.ts exporting AuthPlatform and
AuthProvider type
- Update auth-facebook-panel.tsx and splash-button.tsx to use the new API
- Rename initialContext → initialState in the auth machine for consistency
- Update inline docs and comments to reference AuthPlatform
No behavioral change for end users; both providers still route through
NextAuth's signIn() with the same callbacks and cookies.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
"use client";
|
||||
/**
|
||||
* AuthContext:基于 XState v5 的 React Context Provider
|
||||
*
|
||||
* 业务事件(`Auth*Submitted`)通过 `useMachine(authMachine).send()` 派发。
|
||||
* XState 自动处理异步副作用(invoke + fromPromise actors)。
|
||||
*
|
||||
* 兼容性:保持原 `useAuthState()` / `useAuthDispatch()` API,
|
||||
* 内部将 XState 状态机快照映射回原 `AuthState` 形状。
|
||||
*/
|
||||
import {
|
||||
type Dispatch,
|
||||
type ReactNode,
|
||||
createContext,
|
||||
useContext,
|
||||
useMemo,
|
||||
} from "react";
|
||||
import { useMachine } from "@xstate/react";
|
||||
|
||||
import { authMachine } from "./auth-machine";
|
||||
import type { AuthEvent, AuthState as MachineContext } from "./auth-machine";
|
||||
|
||||
/**
|
||||
* 对外暴露的 State 形状(保持与原 AuthState 兼容)
|
||||
*/
|
||||
interface AuthState {
|
||||
authPanelMode: MachineContext["authPanelMode"];
|
||||
authMode: MachineContext["authMode"];
|
||||
email: string;
|
||||
password: string;
|
||||
username: string;
|
||||
confirmPassword: string;
|
||||
isLoading: boolean;
|
||||
errorMessage: string | null;
|
||||
isSuccess: boolean;
|
||||
loginType: MachineContext["loginType"];
|
||||
}
|
||||
|
||||
const AuthStateCtx = createContext<AuthState | null>(null);
|
||||
const AuthDispatchCtx = createContext<Dispatch<AuthEvent> | null>(null);
|
||||
|
||||
export interface AuthProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function AuthProvider({ children }: AuthProviderProps) {
|
||||
const [state, send] = useMachine(authMachine);
|
||||
|
||||
// 映射 XState 状态机快照 → 原 AuthState 形状
|
||||
const authState = useMemo<AuthState>(
|
||||
() => ({
|
||||
authPanelMode: state.context.authPanelMode,
|
||||
authMode: state.context.authMode,
|
||||
email: state.context.email,
|
||||
password: state.context.password,
|
||||
username: state.context.username,
|
||||
confirmPassword: state.context.confirmPassword,
|
||||
// isLoading 仅来自邮箱登录 actor(社交登录改由 NextAuth 处理,UI 自行管 busy 状态)
|
||||
isLoading: state.matches("loadingEmailLogin") ||
|
||||
state.matches("loadingEmailRegister"),
|
||||
errorMessage: state.context.errorMessage,
|
||||
isSuccess: state.matches("success"),
|
||||
loginType: state.context.loginType,
|
||||
}),
|
||||
[state],
|
||||
);
|
||||
|
||||
return (
|
||||
<AuthStateCtx.Provider value={authState}>
|
||||
<AuthDispatchCtx.Provider value={send}>{children}</AuthDispatchCtx.Provider>
|
||||
</AuthStateCtx.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useAuthState(): AuthState {
|
||||
const ctx = useContext(AuthStateCtx);
|
||||
if (!ctx) throw new Error("useAuthState must be used inside <AuthProvider>");
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export function useAuthDispatch(): Dispatch<AuthEvent> {
|
||||
const ctx = useContext(AuthDispatchCtx);
|
||||
if (!ctx)
|
||||
throw new Error("useAuthDispatch must be used inside <AuthProvider>");
|
||||
return ctx;
|
||||
}
|
||||
Reference in New Issue
Block a user