fix:splash 界面重定向界面问题
This commit is contained in:
@@ -1,48 +1,63 @@
|
||||
"use client";
|
||||
/**
|
||||
* 认证全屏页面
|
||||
* Auth 屏
|
||||
*
|
||||
* 实现方式与 SplashScreen 同款(MobileShell 顶层 + 内部 .wrapper + 背景组件):
|
||||
* - <MobileShell> 作为 top-level(500px 移动端宽度由 MobileShell 自身约束)
|
||||
* - <AuthBackground> 提供全屏 bg image
|
||||
* - .content 内部承载 AuthPanel(z-index: 2,垂直居中)
|
||||
* - 已登录用户:自动跳 /chat
|
||||
* - 登录成功:通知 ChatBloc + UserBloc
|
||||
* 原始 Dart: lib/ui/auth/auth_screen.dart
|
||||
*/
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
import { useAuthState } from "@/stores/auth/auth-context";
|
||||
import { MobileShell } from "@/app/_components/core/mobile-shell";
|
||||
|
||||
import { useAuthDispatch, useAuthState } from "@/stores/auth/auth-context";
|
||||
import { useChatDispatch } from "@/stores/chat/chat-context";
|
||||
import { useUserDispatch } from "@/stores/user/user-context";
|
||||
import { ROUTES } from "@/router/routes";
|
||||
import { MobileShell } from "@/app/_components/core/mobile-shell";
|
||||
import { AuthPanel } from "@/app/auth/components/auth-panel";
|
||||
import { AuthBackground } from "@/app/auth/components/auth-background";
|
||||
|
||||
import styles from "./auth-screen.module.css";
|
||||
|
||||
import { AuthBackground } from "./auth-background";
|
||||
import { AuthPanel } from "./auth-panel";
|
||||
|
||||
export function AuthScreen() {
|
||||
// ===== 鉴权路由跳转已由 src/router/proxy.ts 集中处理 =====
|
||||
// 本组件只负责:
|
||||
// 1. 渲染登录面板
|
||||
// 2. 登录成功后初始化 chat/user store + navigate 到 /chat(业务层动作)
|
||||
// 1. 渲染登录 UI
|
||||
// 2. 邮箱登录成功 → 初始化 chat/user store + navigate 到 /chat(业务层动作)
|
||||
|
||||
const router = useRouter();
|
||||
const state = useAuthState();
|
||||
const authDispatch = useAuthDispatch();
|
||||
const chatDispatch = useChatDispatch();
|
||||
const userDispatch = useUserDispatch();
|
||||
const wasSuccess = useRef(false);
|
||||
const router = useRouter();
|
||||
|
||||
// 邮箱登录成功 → 通知 Chat + User + 跳转
|
||||
// 用 **pendingRedirect flag**(不是 useRef transition detection)——
|
||||
// re-mount 时 flag 在 auth context 里**持久**,区分"刚按了"和"re-visit"准确无误。
|
||||
useEffect(() => {
|
||||
if (state.isSuccess && !wasSuccess.current) {
|
||||
wasSuccess.current = true;
|
||||
console.log("[auth-screen] useEffect (loginStatus + pendingRedirect)", {
|
||||
pending: state.pendingRedirect,
|
||||
loginStatus: state.loginStatus,
|
||||
willRedirect:
|
||||
state.pendingRedirect && state.loginStatus !== "notLoggedIn",
|
||||
});
|
||||
|
||||
if (state.pendingRedirect && state.loginStatus !== "notLoggedIn") {
|
||||
console.log(
|
||||
"[auth-screen] useEffect → router.replace(/chat) [via pendingRedirect flag]",
|
||||
);
|
||||
authDispatch({ type: "AuthClearPendingRedirect" });
|
||||
chatDispatch({ type: "ChatAuthStatusChanged" });
|
||||
userDispatch({ type: "UserInit" });
|
||||
router.replace(ROUTES.chat);
|
||||
}
|
||||
}, [state.isSuccess, chatDispatch, userDispatch, router]);
|
||||
}, [
|
||||
state.loginStatus,
|
||||
state.pendingRedirect,
|
||||
authDispatch,
|
||||
chatDispatch,
|
||||
userDispatch,
|
||||
router,
|
||||
]);
|
||||
|
||||
return (
|
||||
<MobileShell>
|
||||
|
||||
@@ -5,56 +5,27 @@
|
||||
* 原始 Dart: lib/ui/splash/widgets/splash_button.dart
|
||||
* `Row(children: [Skip 文本, SizedBox 26, 社交登录按钮])`
|
||||
*
|
||||
* 本组件是 splash 鉴权流程的**自治单元**:
|
||||
* - 消费 `useSession()`(next-auth/react)监听 NextAuth session
|
||||
* - 消费 `useAuthState` / `useAuthDispatch`(邮箱登录 + OAuth 流程统一收口到状态机)
|
||||
* - 消费 `useChatDispatch` / `useUserDispatch`(登录成功后初始化)
|
||||
* - 自管路由跳转(已登录 / 登录成功 → /chat)
|
||||
* - **Skip 按钮 = 显式游客登录入口**(派发 `AuthGuestLoginSubmitted`,不是路由跳转)
|
||||
* 本组件是 splash 鉴权流程的**纯 UI 入口**:
|
||||
* - 消费 `useAuthState` / `useAuthDispatch`(仅用于 isLoading 显示 + 派事件)
|
||||
* - **Skip 按钮 = 显式游客登录入口**(派发 `AuthGuestLoginSubmitted`)
|
||||
* - 社交登录按钮仅派发 `AuthFacebookLoginSubmitted` 事件,由状态机负责调 NextAuth
|
||||
*
|
||||
* 跳转时序:
|
||||
* 1. 派发事件(如 `AuthGuestLoginSubmitted`)
|
||||
* 2. auth machine 进 loading state → API 调用 → onDone → loginStatus 更新
|
||||
* 3. useEffect 监听到 isSuccess (= loginStatus !== "notLoggedIn") → 调 chat/user store 初始化 + router.replace("/chat")
|
||||
* **不**做路由跳转 —— 路由逻辑**全部**移到 splash-screen.tsx 统一管理。
|
||||
*
|
||||
* **派事件策略**(**单**事件):
|
||||
* - 派业务事件(`AuthGuestLoginSubmitted` / `AuthFacebookLoginSubmitted`)→ 状态机开始跑
|
||||
* - `pendingRedirect: true` 在**登录成功 onDone** 里**自动**置(auth-machine.ts `onLoginSuccess` helper)
|
||||
* - splash-screen 看到 flag=true + loginStatus 非 notLoggedIn → 跳 /chat
|
||||
*/
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
import { useAuthDispatch, useAuthState } from "@/stores/auth/auth-context";
|
||||
import { useChatDispatch } from "@/stores/chat/chat-context";
|
||||
import { useUserDispatch } from "@/stores/user/user-context";
|
||||
import { ROUTES } from "@/router/routes";
|
||||
import { LoadingIndicator } from "@/app/_components/core/loading-indicator";
|
||||
import styles from "./splash-button.module.css";
|
||||
|
||||
export function SplashButton() {
|
||||
// ===== 鉴权路由跳转已由 src/router/proxy.ts 集中处理 =====
|
||||
// 本组件只负责:
|
||||
// 1. 触发登录动作
|
||||
// 2. 登录成功后初始化 chat/user store + navigate 到 /chat(业务层动作,
|
||||
// 不是鉴权判断;proxy 会让通过因为 cookie 已设)
|
||||
|
||||
const state = useAuthState();
|
||||
const authDispatch = useAuthDispatch();
|
||||
const isLoading = state.isLoading;
|
||||
|
||||
// ===== 跨 store 初始化(登录成功后触发) =====
|
||||
const chatDispatch = useChatDispatch();
|
||||
const userDispatch = useUserDispatch();
|
||||
const router = useRouter();
|
||||
const wasSuccess = useRef(false);
|
||||
|
||||
// 邮箱/OAuth/游客登录成功跳 /chat + 通知 chat/user 初始化
|
||||
useEffect(() => {
|
||||
if (state.isSuccess && !wasSuccess.current) {
|
||||
wasSuccess.current = true;
|
||||
chatDispatch({ type: "ChatAuthStatusChanged" });
|
||||
userDispatch({ type: "UserInit" });
|
||||
router.replace(ROUTES.chat);
|
||||
}
|
||||
}, [state.isSuccess, chatDispatch, userDispatch, router]);
|
||||
|
||||
// Skip = 显式游客登录入口("以游客身份继续")
|
||||
const handleSkip = () => {
|
||||
authDispatch({ type: "AuthGuestLoginSubmitted" });
|
||||
|
||||
@@ -4,6 +4,9 @@ import { useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
import { MobileShell } from "@/app/_components/core/mobile-shell";
|
||||
import { useAuthDispatch, useAuthState } from "@/stores/auth/auth-context";
|
||||
import { useChatDispatch } from "@/stores/chat/chat-context";
|
||||
import { useUserDispatch } from "@/stores/user/user-context";
|
||||
import { AuthStorage } from "@/data/storage/auth/auth_storage";
|
||||
import { ROUTES } from "@/router/routes";
|
||||
|
||||
@@ -15,29 +18,24 @@ import styles from "./splash-screen.module.css";
|
||||
|
||||
export function SplashScreen() {
|
||||
const router = useRouter();
|
||||
const state = useAuthState();
|
||||
const authDispatch = useAuthDispatch();
|
||||
const chatDispatch = useChatDispatch();
|
||||
const userDispatch = useUserDispatch();
|
||||
|
||||
// 启动时检查 localStorage 里的 token:
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// useEffect ① 启动时检查 localStorage 里的 loginToken:
|
||||
// - loginToken(OAuth/email sync 写下的)→ 视为"已登录"跳 /chat
|
||||
// - guestToken(用户**显式**走"游客模式"留下的)→ 同样视为"已登录"跳 /chat
|
||||
// - 都没有 → 留在 splash(**不**自动创建游客账号 —— 见 auth-machine.ts 的 checkAuthStatusActor)
|
||||
//
|
||||
// - **不**查 guestToken —— 游客用户**停留 splash**(按你要求)
|
||||
// - 都没有 → 留 splash
|
||||
// proxy 读不到 localStorage(Edge runtime),所以这条 fallback 必须在 Client 侧。
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
void (async () => {
|
||||
const storage = AuthStorage.getInstance();
|
||||
// 用 getXxxToken() 拿 string | null(不是 hasXxxToken() 拿 boolean)
|
||||
const [loginR, guestR] = await Promise.all([
|
||||
storage.getLoginToken(),
|
||||
storage.getGuestToken(),
|
||||
]);
|
||||
const loginTok = loginR.success ? loginR.data : null;
|
||||
const guestTok = guestR.success ? guestR.data : null;
|
||||
const hasAny =
|
||||
(loginTok != null && loginTok.length > 0) ||
|
||||
(guestTok != null && guestTok.length > 0);
|
||||
|
||||
if (!cancelled && hasAny) {
|
||||
// 用 getLoginToken() 拿 string | null(不是 hasLoginToken() 拿 boolean)
|
||||
const r = await AuthStorage.getInstance().getLoginToken();
|
||||
if (!cancelled && r.success && r.data) {
|
||||
router.replace(ROUTES.chat);
|
||||
}
|
||||
})();
|
||||
@@ -46,6 +44,41 @@ export function SplashScreen() {
|
||||
};
|
||||
}, [router]);
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// useEffect ② 按钮**刚**派了事件(pendingRedirect=true) +
|
||||
// loginStatus 是非 notLoggedIn → 跳 /chat
|
||||
//
|
||||
// **关键**:`pendingRedirect` 是 auth context 里的**持久**状态(不是 ref)——
|
||||
// re-mount 时**不**重置,区分"刚按了"和"历史登录"准确无误。
|
||||
// 跳完调 `AuthClearPendingRedirect` 置 false(一次性信号)。
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
useEffect(() => {
|
||||
// 调试日志(保留,不删 —— 用于排查"re-mount 误跳 /chat"问题)
|
||||
console.log("[splash] useEffect ② (loginStatus + pendingRedirect)", {
|
||||
pending: state.pendingRedirect,
|
||||
loginStatus: state.loginStatus,
|
||||
willRedirect:
|
||||
state.pendingRedirect && state.loginStatus !== "notLoggedIn",
|
||||
});
|
||||
|
||||
if (state.pendingRedirect && state.loginStatus !== "notLoggedIn") {
|
||||
console.log(
|
||||
"[splash] useEffect ② → router.replace(/chat) [via pendingRedirect flag]",
|
||||
);
|
||||
authDispatch({ type: "AuthClearPendingRedirect" });
|
||||
chatDispatch({ type: "ChatAuthStatusChanged" });
|
||||
userDispatch({ type: "UserInit" });
|
||||
router.replace(ROUTES.chat);
|
||||
}
|
||||
}, [
|
||||
state.loginStatus,
|
||||
state.pendingRedirect,
|
||||
authDispatch,
|
||||
chatDispatch,
|
||||
userDispatch,
|
||||
router,
|
||||
]);
|
||||
|
||||
return (
|
||||
<MobileShell background="var(--color-sidebar-background)">
|
||||
<div className={styles.wrapper}>
|
||||
|
||||
Reference in New Issue
Block a user