"use client"; import { useEffect, useRef, useState } from "react"; import Image from "next/image"; import { useRouter } from "next/navigation"; import { useAuthState } from "@/stores/auth/auth-context"; import type { LoginStatus } from "@/data/dto/auth"; import { AppStorage } from "@/data/storage/app/app_storage"; import { AuthStorage } from "@/data/storage/auth/auth_storage"; import { UserStorage } from "@/data/storage/user/user_storage"; import { useChatDispatch, useChatState } from "@/stores/chat/chat-context"; import { AppEnvUtil, BrowserDetector, todayString, UrlLauncherUtil } from "@/utils"; import { ROUTE_BUILDERS, ROUTES } from "@/router/routes"; import { MobileShell } from "@/app/_components/core"; import { BrowserHintOverlay, ChatArea, ChatHeader, ChatInputBar, ChatQuotaExhaustedBanner, ExternalBrowserDialog, PwaInstallOverlay, } from "./components"; import styles from "./components/chat-screen.module.css"; /** * 派生 isGuest —— 单一来源:`auth.loginStatus` */ function deriveIsGuest(loginStatus: LoginStatus): boolean { return loginStatus === "guest"; } export function ChatScreen() { const state = useChatState(); const chatDispatch = useChatDispatch(); const authState = useAuthState(); const router = useRouter(); const [showExternalBrowserDialog, setShowExternalBrowserDialog] = useState(false); // isGuest 派生(chat-screen 是唯一知道这个值的地方 —— chat 机器无 isGuest 字段) const isGuest = deriveIsGuest(authState.loginStatus); // 消息数量限制由后端统一返回 blocked,前端不再处理本地额度。 const showMessageLimitBanner = state.paywallTriggered && (state.paywallReason === "daily_limit" || state.paywallReason === "total_limit"); const messageLimitTitle = state.paywallReason === "total_limit" ? "The limit for free chat times\nhas been reached" : undefined; const messageLimitCta = isGuest ? "Log in to continue chatting" : undefined; const showPhotoPaywallBanner = state.paywallTriggered && state.paywallReason === "photo_paywall"; const showPrivatePaywallBanner = state.paywallTriggered && state.paywallReason === "private_paywall"; const showInlineUpgradeBanner = showPhotoPaywallBanner || showPrivatePaywallBanner; const inlineUpgradeTitle = showPrivatePaywallBanner ? "Unlock VIP to view private messages" : "Unlock VIP to view Elio's photos"; const inlineUpgradeCta = showPrivatePaywallBanner ? "Activate VIP to view private messages" : "Activate VIP to view photos"; const externalBrowserPromptShownRef = useRef(false); useEffect(() => { if (!authState.hasInitialized || authState.isLoading) return; const isDev = AppEnvUtil.isDevelopment(); const canTriggerPrompt = isDev || (authState.loginStatus === "facebook" && BrowserDetector.isInAppBrowser()); if (!canTriggerPrompt) return; let mounted = true; let timer: number | undefined; const init = async () => { if (externalBrowserPromptShownRef.current) return; const today = todayString(); const canShowResult = await AppStorage.canShowExternalBrowserDialog(today); if (!mounted || !canShowResult.success || !canShowResult.data) return; externalBrowserPromptShownRef.current = true; timer = window.setTimeout(() => { if (!mounted) return; setShowExternalBrowserDialog(true); void AppStorage.recordExternalBrowserDialogShown(today); }, 3000); }; void init(); return () => { mounted = false; if (timer !== undefined) window.clearTimeout(timer); }; }, [ authState.hasInitialized, authState.isLoading, authState.loginStatus, ]); async function handleOpenExternalBrowser(): Promise { setShowExternalBrowserDialog(false); const authStorage = AuthStorage.getInstance(); const userStorage = UserStorage.getInstance(); const [deviceIdR, facebookIdR, avatarUrlR] = await Promise.all([ authStorage.getDeviceId(), authStorage.getFacebookId(), userStorage.getAvatarUrl(), ]); const deviceId = deviceIdR.success ? deviceIdR.data : null; const facebookId = facebookIdR.success ? facebookIdR.data : null; const avatarUrl = avatarUrlR.success ? avatarUrlR.data : null; if (deviceId && facebookId) { UrlLauncherUtil.openUrlWithExternalBrowser( ROUTE_BUILDERS.chatDeviceId(deviceId), { queryParams: { fbid: facebookId, ...(avatarUrl ? { avatarUrl } : {}), }, }, ); return; } UrlLauncherUtil.openUrlWithExternalBrowser(ROUTES.splash); } function handleUnlockPrivateMessage(messageId: string): void { chatDispatch({ type: "ChatUnlockPrivateMessage", messageId }); } function handleMessageLimitUnlock(): void { if (isGuest) { router.push(ROUTES.auth); return; } router.push(`${ROUTES.subscription}?type=vip`); } return (
{/* 背景图(与原 Dart MobileLayout 一致) */}
{/* isGuest 派生自 auth.loginStatus */} {showInlineUpgradeBanner ? ( ) : null} {showMessageLimitBanner ? ( ) : ( )}
{/* 浏览器提示(应用内浏览器检测) */} {/* PWA 安装提示触发器(无可见 UI,3.5s 后弹 dialog) */} setShowExternalBrowserDialog(false)} onConfirm={() => void handleOpenExternalBrowser()} />
); }