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:
@@ -7,14 +7,16 @@
|
||||
* 视觉规格(与 Dart 对齐):
|
||||
* - Spacer → logo(120h)→ Spacer → Facebook 主按钮 → 链接 → Spacer → 法律
|
||||
* - Facebook 主按钮:46px 白胶囊 + Facebook "f" 图标(#1877f2)+ "Login with Facebook"
|
||||
* - 社交登录走 NextAuth:`new AuthPlatform("facebook").signIn()` / `new AuthPlatform("google").signIn()`
|
||||
* - 社交登录统一收口到 Auth 状态机:派发 `AuthFacebookLoginSubmitted` /
|
||||
* `AuthGoogleLoginSubmitted` 事件,状态机内部调 next-auth/react 的 signIn(provider)
|
||||
* - busy / error 状态来自 `useAuthState()`(与邮箱登录同源)
|
||||
* - "Other Sign-in Options" 链接打开底部弹层
|
||||
* - 弹层 Email 按钮 → 触发 `onSwitchToEmail`(父级派发 `AuthPanelModeChanged`)
|
||||
*/
|
||||
import Image from "next/image";
|
||||
import { useState } from "react";
|
||||
|
||||
import { AuthPlatform } from "@/lib/auth/nextauth";
|
||||
import { useAuthDispatch, useAuthState } from "@/stores/auth/auth-context";
|
||||
|
||||
import { AuthErrorMessage } from "./auth-error-message";
|
||||
import { AuthLegalText } from "./auth-legal-text";
|
||||
@@ -27,31 +29,21 @@ export interface AuthFacebookPanelProps {
|
||||
|
||||
export function AuthFacebookPanel({ onSwitchToEmail }: AuthFacebookPanelProps) {
|
||||
const [showOptions, setShowOptions] = useState(false);
|
||||
const [busy, setBusy] = useState<null | "facebook" | "google">(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const { isLoading, errorMessage } = useAuthState();
|
||||
const authDispatch = useAuthDispatch();
|
||||
|
||||
const handleFacebook = async () => {
|
||||
setBusy("facebook");
|
||||
setError(null);
|
||||
try {
|
||||
await new AuthPlatform("facebook").signIn();
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : "Facebook login failed");
|
||||
} finally {
|
||||
setBusy(null);
|
||||
}
|
||||
const handleFacebook = () => {
|
||||
authDispatch({
|
||||
type: "AuthFacebookLoginSubmitted",
|
||||
provider: "facebook",
|
||||
});
|
||||
};
|
||||
|
||||
const handleGoogle = async () => {
|
||||
setBusy("google");
|
||||
setError(null);
|
||||
try {
|
||||
await new AuthPlatform("google").signIn();
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : "Google login failed");
|
||||
} finally {
|
||||
setBusy(null);
|
||||
}
|
||||
const handleGoogle = () => {
|
||||
authDispatch({
|
||||
type: "AuthGoogleLoginSubmitted",
|
||||
provider: "google",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -70,10 +62,10 @@ export function AuthFacebookPanel({ onSwitchToEmail }: AuthFacebookPanelProps) {
|
||||
<button
|
||||
type="button"
|
||||
className={styles.facebookButton}
|
||||
onClick={() => void handleFacebook()}
|
||||
disabled={busy !== null}
|
||||
onClick={() => handleFacebook()}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{busy === "facebook" ? (
|
||||
{isLoading ? (
|
||||
<span className={styles.spinner} aria-hidden="true" />
|
||||
) : (
|
||||
<span className={styles.facebookIcon} aria-hidden="true">
|
||||
@@ -83,7 +75,7 @@ export function AuthFacebookPanel({ onSwitchToEmail }: AuthFacebookPanelProps) {
|
||||
</span>
|
||||
)}
|
||||
<span className={styles.facebookLabel}>
|
||||
{busy === "facebook" ? "Logging in..." : "Login with Facebook"}
|
||||
{isLoading ? "Logging in..." : "Login with Facebook"}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -97,15 +89,15 @@ export function AuthFacebookPanel({ onSwitchToEmail }: AuthFacebookPanelProps) {
|
||||
|
||||
<div className={styles.spacer} />
|
||||
|
||||
<AuthErrorMessage message={error} />
|
||||
<AuthErrorMessage message={errorMessage} />
|
||||
<AuthLegalText />
|
||||
|
||||
<AuthOtherOptionsDialog
|
||||
open={showOptions}
|
||||
onClose={() => setShowOptions(false)}
|
||||
mode="facebook"
|
||||
onFacebook={() => void handleFacebook()}
|
||||
onGoogle={() => void handleGoogle()}
|
||||
onFacebook={() => handleFacebook()}
|
||||
onGoogle={() => handleGoogle()}
|
||||
onEmail={onSwitchToEmail}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user