diff --git a/src/app/auth/auth-screen.tsx b/src/app/auth/auth-screen.tsx index a16ca88c..2bdd0f2a 100644 --- a/src/app/auth/auth-screen.tsx +++ b/src/app/auth/auth-screen.tsx @@ -17,10 +17,15 @@ import { Logger } from "@/utils"; const log = new Logger("AppAuthAuthScreen"); -export function AuthScreen() { +export interface AuthScreenProps { + redirectTo?: string | null; +} + +export function AuthScreen({ redirectTo = null }: AuthScreenProps) { const state = useAuthState(); const authDispatch = useAuthDispatch(); const router = useRouter(); + const safeRedirectTo = getSafeRedirectPath(redirectTo) ?? ROUTES.chat; // 邮箱登录成功 → 跳转;User hydrate 由根级 监听 loginStatus 变化处理。 // 用 pendingRedirect flag(不是 useRef transition detection)—— @@ -34,13 +39,19 @@ export function AuthScreen() { }); if (state.pendingRedirect && state.loginStatus !== "notLoggedIn") { - log.debug( - "[auth-screen] useEffect → router.replace(/chat) [via pendingRedirect flag]", - ); + log.debug("[auth-screen] useEffect → router.replace [via pendingRedirect flag]", { + redirectTo: safeRedirectTo, + }); authDispatch({ type: "AuthClearPendingRedirect" }); - router.replace(ROUTES.chat); + router.replace(safeRedirectTo); } - }, [state.loginStatus, state.pendingRedirect, authDispatch, router]); + }, [ + state.loginStatus, + state.pendingRedirect, + authDispatch, + router, + safeRedirectTo, + ]); return ( @@ -53,3 +64,9 @@ export function AuthScreen() { ); } + +function getSafeRedirectPath(redirectTo: string | null | undefined): string | null { + if (!redirectTo) return null; + if (!redirectTo.startsWith("/") || redirectTo.startsWith("//")) return null; + return redirectTo; +} diff --git a/src/app/auth/page.tsx b/src/app/auth/page.tsx index a81af6ec..d7f86b80 100644 --- a/src/app/auth/page.tsx +++ b/src/app/auth/page.tsx @@ -1,7 +1,16 @@ -"use client"; - import { AuthScreen } from "@/app/auth/auth-screen"; -export default function AuthPage() { - return ; +interface AuthPageProps { + searchParams: Promise<{ + redirect?: string | string[]; + }>; +} + +export default async function AuthPage({ searchParams }: AuthPageProps) { + const params = await searchParams; + const redirectTo = Array.isArray(params.redirect) + ? params.redirect[0] + : params.redirect; + + return ; } diff --git a/src/app/chat/chat-screen.tsx b/src/app/chat/chat-screen.tsx index 03017e1d..610d798f 100644 --- a/src/app/chat/chat-screen.tsx +++ b/src/app/chat/chat-screen.tsx @@ -133,7 +133,12 @@ export function ChatScreen() { } function handleUnlockImagePaywall(): void { - router.push(`${ROUTES.subscription}?type=vip`); + const subscriptionUrl = ROUTE_BUILDERS.subscription("vip"); + if (isGuest) { + router.push(ROUTE_BUILDERS.authWithRedirect(subscriptionUrl)); + return; + } + router.push(subscriptionUrl); } function handleMessageLimitUnlock(): void { @@ -141,7 +146,7 @@ export function ChatScreen() { router.push(ROUTES.auth); return; } - router.push(`${ROUTES.subscription}?type=vip`); + router.push(ROUTE_BUILDERS.subscription("vip")); } return ( diff --git a/src/router/routes.ts b/src/router/routes.ts index cf14ac3e..d5e631b7 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -30,6 +30,10 @@ export type StaticRoute = (typeof ROUTES)[keyof typeof ROUTES]; export const ROUTE_BUILDERS = { chatDeviceId: (deviceId: string): `/chat/deviceid/${string}` => `/chat/deviceid/${encodeURIComponent(deviceId)}` as const, + subscription: (type: "vip" | "voice"): `/subscription?type=${string}` => + `${ROUTES.subscription}?type=${encodeURIComponent(type)}` as const, + authWithRedirect: (redirectTo: string): `/auth?redirect=${string}` => + `${ROUTES.auth}?redirect=${encodeURIComponent(redirectTo)}` as const, } as const; /** 仅未登录态可见的路由(命中后已登录用户应跳走) */