From 35f4cc54451633d1836a339e5065fae523fab9ff Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Fri, 9 Jan 2026 00:48:07 +0000 Subject: [PATCH 1/2] feat: refresh credit usage after chat completion Invalidate billing status query 3 seconds after chat streaming completes to allow backend billing events to process. This ensures the sidepanel credit usage indicator stays up-to-date during long chat sessions. Fixes #373 Co-authored-by: Anthony --- frontend/src/components/UnifiedChat.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/src/components/UnifiedChat.tsx b/frontend/src/components/UnifiedChat.tsx index 052dc5206..036b4b170 100644 --- a/frontend/src/components/UnifiedChat.tsx +++ b/frontend/src/components/UnifiedChat.tsx @@ -2365,6 +2365,11 @@ export function UnifiedChat() { // Re-enable polling after streaming completes assistantStreamingRef.current = false; setCurrentResponseId(undefined); + + // Invalidate billing status after a delay to allow backend processing + setTimeout(() => { + queryClient.invalidateQueries({ queryKey: ["billingStatus"] }); + }, 3000); } } catch (error) { console.error("Failed to send message:", error); From 4f2da2b2c57c1099e5cee19b900512fb7ce2eb8d Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Fri, 9 Jan 2026 02:41:54 +0000 Subject: [PATCH 2/2] fix: cleanup billing refresh timeout on unmount - Add billingRefreshTimeoutRef to store timeout ID - Clear timeout in useEffect cleanup to prevent memory leaks - Set ref to null after timeout fires Co-authored-by: Anthony --- frontend/src/components/UnifiedChat.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/UnifiedChat.tsx b/frontend/src/components/UnifiedChat.tsx index 036b4b170..d1ce813dc 100644 --- a/frontend/src/components/UnifiedChat.tsx +++ b/frontend/src/components/UnifiedChat.tsx @@ -963,6 +963,7 @@ export function UnifiedChat() { const assistantStreamingRef = useRef(false); const abortControllerRef = useRef(null); + const billingRefreshTimeoutRef = useRef | null>(null); const fileInputRef = useRef(null); const documentInputRef = useRef(null); @@ -992,6 +993,15 @@ export function UnifiedChat() { } }, [input]); + // Cleanup billing refresh timeout on unmount + useEffect(() => { + return () => { + if (billingRefreshTimeoutRef.current) { + clearTimeout(billingRefreshTimeoutRef.current); + } + }; + }, []); + // Auto-focus textbox on desktop (not mobile to avoid keyboard popup interrupting reading) // Focus when: app launches, new chat, conversation loads, or assistant finishes streaming useEffect(() => { @@ -2367,8 +2377,9 @@ export function UnifiedChat() { setCurrentResponseId(undefined); // Invalidate billing status after a delay to allow backend processing - setTimeout(() => { + billingRefreshTimeoutRef.current = setTimeout(() => { queryClient.invalidateQueries({ queryKey: ["billingStatus"] }); + billingRefreshTimeoutRef.current = null; }, 3000); } } catch (error) {