From 9b28404673d343952a301bf95b41a84100f20a8b Mon Sep 17 00:00:00 2001 From: chenhang Date: Mon, 15 Jun 2026 16:24:25 +0800 Subject: [PATCH] refactor(chat): remove QuotaDialog and related state/effects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the QuotaDialog component invocation in chat-screen along with its useState/useEffect wiring, and delete the component files. Update JSDoc in chat-screen and routes to reflect the removed consumer. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/app/chat/components/chat-screen.tsx | 46 +--------- .../chat/components/quota-dialog.module.css | 85 ------------------- src/app/chat/components/quota-dialog.tsx | 80 ----------------- src/router/routes.ts | 2 +- 4 files changed, 2 insertions(+), 211 deletions(-) delete mode 100644 src/app/chat/components/quota-dialog.module.css delete mode 100644 src/app/chat/components/quota-dialog.tsx diff --git a/src/app/chat/components/chat-screen.tsx b/src/app/chat/components/chat-screen.tsx index a2f601ae..346e8ee7 100644 --- a/src/app/chat/components/chat-screen.tsx +++ b/src/app/chat/components/chat-screen.tsx @@ -7,14 +7,12 @@ * 职责(本文件): * - **订阅 auth 状态机**(loginStatus)—— 派生 isGuest / **派鉴权生命周期事件**给 chat 机器 * - 屏幕生命周期事件(init / visible / invisible) - * - 配额告警监听(**仅**游客 —— "非游客无消息限制"业务事实) * - **不**直接管理 WebSocket / **不**直接读 AuthStorage(除 token 取值) * * 子组件职责: * - ChatHeader:顶部栏(游客 banner / 菜单按钮) * - ChatArea:消息列表 * - ChatInputBar:输入栏(文字 + 发送 + More) - * - QuotaDialog:配额告警弹窗(**仅**游客) * - PwaInstallOverlay:PWA 安装提示触发器 * - BrowserHintOverlay:浏览器提示 * @@ -26,14 +24,13 @@ * - `ChatLogout` → 登出(机器自动 cleanup WS actor) * - chat 机器**内部**用 fromCallback actor 完整管理 WS 生命周期 */ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef } from "react"; import Image from "next/image"; import { useAuthState } from "@/stores/auth/auth-context"; import type { LoginStatus } from "@/models/auth/login-status"; import { AuthStorage } from "@/data/storage/auth/auth_storage"; import { useChatDispatch, useChatState } from "@/stores/chat/chat-context"; -import { GuestChatQuota } from "@/stores/chat"; import { MobileShell } from "@/app/_components/core/mobile-shell"; @@ -42,7 +39,6 @@ import { ChatArea } from "./chat-area"; import { ChatHeader } from "./chat-header"; import { ChatInputBar } from "./chat-input-bar"; import { PwaInstallOverlay } from "./pwa-install-overlay"; -import { QuotaDialog } from "./quota-dialog"; import styles from "./chat-screen.module.css"; /** @@ -88,9 +84,6 @@ export function ChatScreen() { // isGuest 派生(chat-screen 是**唯一**知道这个值的地方 —— chat 机器无 isGuest 字段) const isGuest = deriveIsGuest(authState.loginStatus); - const [quotaDialogOpen, setQuotaDialogOpen] = useState(false); - const [quotaExhausted, setQuotaExhausted] = useState(false); - // ───────────────────────────────────────────────────────────── // 屏幕生命周期 + 派初次鉴权生命周期事件 // ───────────────────────────────────────────────────────────── @@ -121,36 +114,6 @@ export function ChatScreen() { dispatchAuthLifecycle(authState.loginStatus, chatDispatch); }, [authState.loginStatus, chatDispatch]); - // ───────────────────────────────────────────────────────────── - // 配额耗尽触发器(**仅**游客 —— "非游客无消息限制") - // ───────────────────────────────────────────────────────────── - const prevTriggerRef = useRef(state.quotaExceededTrigger); - useEffect(() => { - if (!isGuest) return; // ← 防御:非游客 backend 不会发 ChatQuotaExceeded - if ( - state.quotaExceededTrigger !== prevTriggerRef.current && - state.quotaExceededTrigger > 0 - ) { - setQuotaExhausted(true); - setQuotaDialogOpen(true); - } - prevTriggerRef.current = state.quotaExceededTrigger; - }, [state.quotaExceededTrigger, isGuest]); - - // 配额警告(剩余 = 5)—— **仅**游客 - const prevRemainingRef = useRef(state.guestRemainingQuota); - useEffect(() => { - if ( - isGuest && // ← 派生自 auth.loginStatus - state.guestRemainingQuota === GuestChatQuota.warningThreshold && - prevRemainingRef.current !== state.guestRemainingQuota - ) { - setQuotaExhausted(false); - setQuotaDialogOpen(true); - } - prevRemainingRef.current = state.guestRemainingQuota; - }, [state.guestRemainingQuota, isGuest]); - return (
@@ -183,13 +146,6 @@ export function ChatScreen() { {/* PWA 安装提示触发器(无可见 UI,3.5s 后弹 dialog) */} - - {/* 配额告警弹窗(**仅**游客,gate 在 isGuest) */} - setQuotaDialogOpen(false)} - isExhausted={quotaExhausted} - />
); diff --git a/src/app/chat/components/quota-dialog.module.css b/src/app/chat/components/quota-dialog.module.css deleted file mode 100644 index b666afcf..00000000 --- a/src/app/chat/components/quota-dialog.module.css +++ /dev/null @@ -1,85 +0,0 @@ -/* QuotaDialog 配额提示弹窗样式 */ - -.overlay { - position: fixed; - inset: 0; - background: rgba(0, 0, 0, 0.5); - display: flex; - align-items: center; - justify-content: center; - z-index: 50; -} - -.dialog { - width: calc(100% - 40px); - max-width: 360px; - background: var(--color-surface, #1f1a2e); - border-radius: var(--radius-lg, 12px); - padding: var(--spacing-5, 20px); - box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4); - display: flex; - flex-direction: column; - align-items: center; - text-align: center; -} - -.icon { - width: 64px; - height: 64px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-size: 32px; - margin-bottom: var(--spacing-4, 16px); -} - -.iconExhausted { - background: rgba(255, 80, 80, 0.2); - color: #ff5050; -} - -.iconWarning { - background: rgba(248, 77, 150, 0.2); - color: var(--color-accent, #f84d96); -} - -.title { - font-size: var(--font-size-lg, 18px); - font-weight: 600; - margin: 0 0 var(--spacing-2, 8px); - color: var(--color-text-primary, #fff); -} - -.content { - font-size: var(--font-size-sm, 14px); - line-height: 1.5; - color: var(--color-text-secondary, #9e9e9e); - margin: 0 0 var(--spacing-5, 20px); -} - -.actions { - display: flex; - gap: var(--spacing-3, 12px); - width: 100%; -} - -.btn { - flex: 1 1 auto; - height: 40px; - border: 0; - border-radius: var(--radius-md, 8px); - font-size: var(--font-size-base, 14px); - font-weight: 600; - cursor: pointer; -} - -.btnSecondary { - background: transparent; - color: var(--color-text-primary, #fff); -} - -.btnPrimary { - background: var(--color-accent, #f84d96); - color: #fff; -} diff --git a/src/app/chat/components/quota-dialog.tsx b/src/app/chat/components/quota-dialog.tsx deleted file mode 100644 index 37c344fe..00000000 --- a/src/app/chat/components/quota-dialog.tsx +++ /dev/null @@ -1,80 +0,0 @@ -"use client"; -/** - * QuotaDialog 配额提示弹窗 - * - * 原始 Dart: lib/ui/chat/widgets/quota_dialog.dart(130 行) - * - * 两种模式: - * - 警告模式(isExhausted=false):剩余配额 < 5 时,提示用户登录 - * - 耗尽模式(isExhausted=true):配额已用完,强制登录 - */ -import { useRouter } from "next/navigation"; - -import { ROUTES } from "@/router/routes"; - -import styles from "./quota-dialog.module.css"; - -export interface QuotaDialogProps { - open: boolean; - onClose: () => void; - isExhausted: boolean; -} - -export function QuotaDialog({ open, onClose, isExhausted }: QuotaDialogProps) { - const router = useRouter(); - - if (!open) return null; - - const title = isExhausted - ? "Daily quota exhausted" - : "Login for unlimited access!"; - - const content = isExhausted - ? "Login to continue chatting~" - : "Dear, sign up now and all your chats with Elio will be saved forever. In guest mode, your chat history will be deleted when you leave, and Elio will forget you."; - - const handleLogin = () => { - onClose(); - router.push(ROUTES.auth); - }; - - return ( -
-
- - -

- {title} -

-

{content}

- -
- - -
-
-
- ); -} diff --git a/src/router/routes.ts b/src/router/routes.ts index f1f3ad93..668ef8fb 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -10,7 +10,7 @@ * - 动态深链通过 `ROUTE_BUILDERS` 函数生成,避免手拼字符串。 * * 注意:proxy 路由跳转逻辑(2026-06-15 迁出),但本文件的**类型化常量**仍在 - * 被 9 个组件(auth-screen / chat-header / quota-dialog / DeepLinkPersist / + * 被 8 个组件(auth-screen / chat-header / DeepLinkPersist / * error / not-found / page / sidebar-screen / splash-screen)使用 —— **不能**整体注释。 */