Skip to content

Commit b8b65ee

Browse files
committed
feat: AI未配置时禁用所有AI功能UI入口 - 新增useAIStatus全局hook(模块级缓存+监听者模式) - 首页/漫画详情/阅读器/小说/推荐页AI按钮条件渲染 - AI设置保存后刷新全局状态缓存
1 parent 303bb30 commit b8b65ee

5 files changed

Lines changed: 32 additions & 17 deletions

File tree

frontend/src/app/novel/[id]/page.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@ import { useTranslation, useLocale } from "@/lib/i18n";
2121
import { useTheme } from "@/lib/theme-context";
2222
import type { ReaderTheme } from "@/components/reader/ReaderToolbar";
2323
import AIChatPanel from "@/components/reader/AIChatPanel";
24+
import { useAIStatus } from "@/hooks/useAIStatus";
2425

2526
export default function NovelReaderPage() {
2627
const params = useParams();
2728
const router = useRouter();
2829
const comicId = params.id as string;
2930
const t = useTranslation();
3031
const { locale } = useLocale();
32+
const { aiConfigured } = useAIStatus();
3133

3234
// Fetch chapters from API
3335
const {
@@ -351,13 +353,15 @@ export default function NovelReaderPage() {
351353
/>
352354

353355
{/* AI Chat Panel */}
354-
<AIChatPanel
355-
comicId={comicId}
356-
locale={locale}
357-
contextText={currentChapterText}
358-
contextLabel={`${apiChapters[currentPage]?.title || `Chapter ${currentPage + 1}`} (${currentPage + 1}/${totalChapters})`}
359-
readerTheme={readerTheme}
360-
/>
356+
{aiConfigured && (
357+
<AIChatPanel
358+
comicId={comicId}
359+
locale={locale}
360+
contextText={currentChapterText}
361+
contextLabel={`${apiChapters[currentPage]?.title || `Chapter ${currentPage + 1}`} (${currentPage + 1}/${totalChapters})`}
362+
readerTheme={readerTheme}
363+
/>
364+
)}
361365

362366
{/* Info Panel (slide-in from right) */}
363367
{showInfoPanel && (

frontend/src/app/reader/[id]/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export default function ReaderPage() {
3737
const { aiConfigured } = useAIStatus();
3838

3939
// Try API first
40+
const {
4041
pages: apiPages,
4142
title: apiTitle,
4243
isNovel,

frontend/src/app/recommendations/page.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Link from "next/link";
55
import Image from "next/image";
66
import { ArrowLeft, Sparkles, RefreshCw, Brain, Loader2 } from "lucide-react";
77
import { useTranslation, useLocale } from "@/lib/i18n";
8+
import { useAIStatus } from "@/hooks/useAIStatus";
89

910
interface RecommendedComic {
1011
id: string;
@@ -21,6 +22,7 @@ interface RecommendedComic {
2122
export default function RecommendationsPage() {
2223
const t = useTranslation();
2324
const locale = useLocale();
25+
const { aiConfigured } = useAIStatus();
2426
const [recommendations, setRecommendations] = useState<RecommendedComic[]>([]);
2527
const [loading, setLoading] = useState(true);
2628
const [aiReasonsLoading, setAiReasonsLoading] = useState(false);
@@ -99,14 +101,16 @@ export default function RecommendationsPage() {
99101
</h1>
100102
</div>
101103
<div className="flex-1" />
102-
<button
103-
onClick={fetchAiReasons}
104-
disabled={aiReasonsLoading || recommendations.length === 0}
105-
className="flex items-center gap-1.5 rounded-lg bg-purple-500/10 px-3 py-1.5 text-sm text-purple-400 transition-colors hover:bg-purple-500/20 disabled:opacity-50"
106-
>
107-
{aiReasonsLoading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Brain className="h-4 w-4" />}
108-
{t.recommend?.aiReasonGenerate || "AI Reasons"}
109-
</button>
104+
{aiConfigured && (
105+
<button
106+
onClick={fetchAiReasons}
107+
disabled={aiReasonsLoading || recommendations.length === 0}
108+
className="flex items-center gap-1.5 rounded-lg bg-purple-500/10 px-3 py-1.5 text-sm text-purple-400 transition-colors hover:bg-purple-500/20 disabled:opacity-50"
109+
>
110+
{aiReasonsLoading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Brain className="h-4 w-4" />}
111+
{t.recommend?.aiReasonGenerate || "AI Reasons"}
112+
</button>
113+
)}
110114
<button
111115
onClick={fetchRecommendations}
112116
disabled={loading}

frontend/src/components/AISettingsPanel.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
XCircle,
1818
} from "lucide-react";
1919
import { useTranslation } from "@/lib/i18n";
20+
import { useAIStatus } from "@/hooks/useAIStatus";
2021

2122
type CloudProvider =
2223
| "openai"
@@ -111,6 +112,7 @@ interface AIUsageStats {
111112

112113
export function AISettingsPanel() {
113114
const t = useTranslation();
115+
const { refreshAIStatus } = useAIStatus();
114116
const [config, setConfig] = useState<AIConfig | null>(null);
115117
const [status, setStatus] = useState<AIStatus | null>(null);
116118
const [saving, setSaving] = useState(false);
@@ -154,6 +156,8 @@ export function AISettingsPanel() {
154156
// Refresh status
155157
const st = await fetch("/api/ai/status").then((r) => r.json());
156158
setStatus(st);
159+
// 刷新全局AI状态缓存
160+
refreshAIStatus();
157161
} finally {
158162
setSaving(false);
159163
}

frontend/src/components/reader/TextReaderView.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useState, useEffect, useRef, useCallback } from "react";
44
import { ChevronLeft, ChevronRight, List, Minus, Plus, Type, Brain, Loader2 } from "lucide-react";
55
import type { ReaderTheme } from "@/components/reader/ReaderToolbar";
66
import { useLocale } from "@/lib/i18n";
7+
import { useAIStatus } from "@/hooks/useAIStatus";
78

89
interface ChapterInfo {
910
index: number;
@@ -38,6 +39,7 @@ export default function TextReaderView({
3839
comicId,
3940
}: TextReaderViewProps) {
4041
const { locale } = useLocale();
42+
const { aiConfigured } = useAIStatus();
4143
const [content, setContent] = useState("");
4244
const [chapterTitle, setChapterTitle] = useState("");
4345
const [isHTML, setIsHTML] = useState(false);
@@ -551,7 +553,7 @@ export default function TextReaderView({
551553
目录 ({chapters.length} 章)
552554
</h3>
553555
<div className="flex items-center gap-2">
554-
{comicId && (
556+
{comicId && aiConfigured && (
555557
<button
556558
onClick={() => setShowSummaries((v) => !v)}
557559
className={`flex items-center gap-1 rounded-lg px-2 py-1 text-[10px] transition-colors ${
@@ -620,7 +622,7 @@ export default function TextReaderView({
620622
)}
621623
</span>
622624
{/* AI 摘要按钮 */}
623-
{showSummaries && comicId && !chapterSummaries[i] && summaryLoadingIdx !== i && (
625+
{showSummaries && comicId && aiConfigured && !chapterSummaries[i] && summaryLoadingIdx !== i && (
624626
<button
625627
onClick={(e) => {
626628
e.stopPropagation();

0 commit comments

Comments
 (0)