修复 AI 历史会话删除在 App 内无响应的问题

WebView 不支持 window.confirm,改用 AlertDialog 显示删除确认框。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
allen2fuc
2026-07-06 09:48:21 +08:00
parent 9ed1b6f526
commit 62998de6e5
+61 -22
View File
@@ -8,6 +8,16 @@
============================================================ */ ============================================================ */
import BottomNav from "@/components/BottomNav"; import BottomNav from "@/components/BottomNav";
import ConnectionPlaceholder from "@/components/ConnectionPlaceholder"; import ConnectionPlaceholder from "@/components/ConnectionPlaceholder";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { useDevice } from "@/contexts/DeviceContext"; import { useDevice } from "@/contexts/DeviceContext";
import { import {
AI_SPLIT_RESTORE_PATH_KEY, AI_SPLIT_RESTORE_PATH_KEY,
@@ -211,6 +221,7 @@ export default function AIPage({ variant = "page", onClose }: AIPageProps = {})
const deviceName = deviceState?.device ?? "Luxsin X9"; const deviceName = deviceState?.device ?? "Luxsin X9";
const aiText = useMemo(() => resolveAILocale(language), [language]); const aiText = useMemo(() => resolveAILocale(language), [language]);
const promptText = useMemo(() => resolveLocalePack(language).prompt, [language]);
const restoreDesktopSplit = useCallback(() => { const restoreDesktopSplit = useCallback(() => {
const path = readAndClearAiSplitRestorePath(); const path = readAndClearAiSplitRestorePath();
@@ -223,6 +234,7 @@ export default function AIPage({ variant = "page", onClose }: AIPageProps = {})
const [input, setInput] = useState(""); const [input, setInput] = useState("");
const [sending, setSending] = useState(false); const [sending, setSending] = useState(false);
const [historyOpen, setHistoryOpen] = useState(false); const [historyOpen, setHistoryOpen] = useState(false);
const [pendingDeleteId, setPendingDeleteId] = useState<string | null>(null);
const [chats, setChats] = useState<ChatRead[]>([]); const [chats, setChats] = useState<ChatRead[]>([]);
// 初次加载历史会话期间,避免先闪一下欢迎页再切到消息 // 初次加载历史会话期间,避免先闪一下欢迎页再切到消息
const [initializing, setInitializing] = useState(true); const [initializing, setInitializing] = useState(true);
@@ -382,27 +394,22 @@ export default function AIPage({ variant = "page", onClose }: AIPageProps = {})
} }
}, [aiText, chatId, newChat]); }, [aiText, chatId, newChat]);
// ── 历史面板里删除某条会话 ── // ── 历史面板里删除某条会话(确认框用 AlertDialogWebView 不支持 window.confirm ──
const deleteChatById = useCallback( const confirmDeleteChat = useCallback(async (id: string) => {
async (id: string) => { try {
const ok = window.confirm(aiText.history.deleteConfirm); await clearChat(id);
if (!ok) return; setChats((prev) => prev.filter((c) => c.id !== id));
try { if (chatId === id) {
await clearChat(id); setChatId(null);
setChats((prev) => prev.filter((c) => c.id !== id)); setMessages([]);
if (chatId === id) {
setChatId(null);
setMessages([]);
}
toast.success(aiText.history.deleted);
} catch (e: any) {
toast.error(
formatText(aiText.history.deleteFailed, { message: e?.message ?? e }),
);
} }
}, toast.success(aiText.history.deleted);
[aiText, chatId], } catch (e: any) {
); toast.error(
formatText(aiText.history.deleteFailed, { message: e?.message ?? e }),
);
}
}, [aiText, chatId]);
// ── 发送提问 ── // ── 发送提问 ──
const send = useCallback( const send = useCallback(
@@ -897,10 +904,41 @@ export default function AIPage({ variant = "page", onClose }: AIPageProps = {})
onClose={() => setHistoryOpen(false)} onClose={() => setHistoryOpen(false)}
onSelect={(id) => loadChat(id)} onSelect={(id) => loadChat(id)}
onNew={newChat} onNew={newChat}
onDelete={deleteChatById} onDelete={setPendingDeleteId}
/> />
)} )}
<AlertDialog
open={pendingDeleteId !== null}
onOpenChange={(open) => {
if (!open) setPendingDeleteId(null);
}}
>
<AlertDialogContent className="top-[42%] z-[70] border-white/10 bg-zinc-900 text-white sm:max-w-md">
<AlertDialogHeader>
<AlertDialogTitle className="text-white">
{aiText.history.delete}
</AlertDialogTitle>
<AlertDialogDescription className="text-white/60">
{aiText.history.deleteConfirm}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel className="border-white/20 bg-transparent text-white hover:bg-white/10">
{promptText.cancel}
</AlertDialogCancel>
<AlertDialogAction
className="bg-red-500 text-white hover:bg-red-500/90 focus-visible:ring-red-500"
onClick={() => {
if (pendingDeleteId) void confirmDeleteChat(pendingDeleteId);
}}
>
{promptText.delete}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
{/* ── 对比对话框 ── */} {/* ── 对比对话框 ── */}
{compareMsg && ( {compareMsg && (
<CompareDialog <CompareDialog
@@ -1147,11 +1185,12 @@ function HistoryPanel({
</span> </span>
)} )}
<button <button
type="button"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
onDelete(c.id); onDelete(c.id);
}} }}
className="ml-2 w-8 h-8 rounded-full flex items-center justify-center text-white/40 hover:text-red-400 active:text-red-500 transition-colors" className="ml-2 w-8 h-8 shrink-0 rounded-full flex items-center justify-center text-white/40 hover:text-red-400 active:text-red-500 transition-colors touch-manipulation"
style={{ background: "rgba(44,44,46,0.6)" }} style={{ background: "rgba(44,44,46,0.6)" }}
aria-label={aiText.history.delete} aria-label={aiText.history.delete}
> >