diff --git a/ui/web/src/pages/pending-messages/pending-messages-page.tsx b/ui/web/src/pages/pending-messages/pending-messages-page.tsx index ff1abe2a..fce8ff1e 100644 --- a/ui/web/src/pages/pending-messages/pending-messages-page.tsx +++ b/ui/web/src/pages/pending-messages/pending-messages-page.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import { useTranslation } from "react-i18next"; import { Inbox, RefreshCw, Trash2, Archive, Loader2, Info, ChevronDown, ChevronUp } from "lucide-react"; import { Button } from "@/components/ui/button"; @@ -33,11 +33,30 @@ export function PendingMessagesPage() { const [confirmClear, setConfirmClear] = useState(null); const [actionLoading, setActionLoading] = useState(null); const [compactingKey, setCompactingKey] = useState(null); + const pollRef = useRef | null>(null); + const timeoutRef = useRef | null>(null); useEffect(() => { loadGroups(); }, [loadGroups]); + // Clear spinner as soon as the compacting group's has_summary becomes true + useEffect(() => { + if (!compactingKey) return; + const [channel, historyKey] = compactingKey.split("/"); + const group = groups.find( + (g) => g.channel_name === channel && g.history_key === historyKey, + ); + if (group?.has_summary) { + if (pollRef.current) clearInterval(pollRef.current); + if (timeoutRef.current) clearTimeout(timeoutRef.current); + pollRef.current = null; + timeoutRef.current = null; + setCompactingKey(null); + setActionLoading(null); + } + }, [groups, compactingKey]); + const handleRefresh = () => loadGroups(); const handleCompact = async (e: React.MouseEvent, group: PendingMessageGroup) => { @@ -52,10 +71,16 @@ export function PendingMessagesPage() { return; } // Backend runs LLM in background — poll until done (has_summary changes). - // Compact button is already disabled when has_summary=true. - const poll = setInterval(() => loadGroups(), 5000); + // useEffect above clears spinner when has_summary becomes true. + pollRef.current = setInterval(() => loadGroups(), 5000); setTimeout(() => loadGroups(), 2000); // quick first check - setTimeout(() => { clearInterval(poll); setCompactingKey(null); setActionLoading(null); }, 120_000); + timeoutRef.current = setTimeout(() => { + if (pollRef.current) clearInterval(pollRef.current); + pollRef.current = null; + timeoutRef.current = null; + setCompactingKey(null); + setActionLoading(null); + }, 120_000); }; const handleClear = async (group: PendingMessageGroup) => {