fix: abort active stream on session switch and warn before closing during streaming

- Add useEffect cleanup to abort in-flight fetch when ChatView unmounts
  (e.g., session switch via key={id} remount). This triggers the existing
  server-side cleanup chain: saves partial message to DB, auto-denies
  pending permissions. Also resets streamingSessionId and
  pendingApprovalSessionId global state to prevent stale UI.

- Add beforeunload handler during streaming to show browser confirmation
  dialog when user attempts to close the window/tab, preventing accidental
  data loss. Listener is automatically removed when streaming ends.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
7418andClaude Opus 4.6 committed 2026-02-25 23:33:51 +08:00
1 parent fa1023c483
commit cf14791de0
1 file changed
+27
+27
View File
@@ -66,6 +66,33 @@ export function ChatView({ sessionId, initialMessages = [], initialHasMore = fal
}, [sessionId]);
const abortControllerRef = useRef<AbortController | null>(null);
// Abort active stream on unmount (e.g., session switch via key={id} remount).
// This triggers the existing server-side cleanup chain:
// fetch abort → request.signal 'abort' → route abortController.abort()
// → collectStreamResponse catch block saves partial message to DB
// → permission-registry abort handler auto-denies pending permissions
useEffect(() => {
return () => {
if (abortControllerRef.current) {
abortControllerRef.current.abort();
abortControllerRef.current = null;
}
setStreamingSessionId('');
setPendingApprovalSessionId('');
};
}, [setStreamingSessionId, setPendingApprovalSessionId]);
// Warn before closing window/tab while streaming to prevent accidental data loss
useEffect(() => {
if (!isStreaming) return;
const handler = (e: BeforeUnloadEvent) => {
e.preventDefault();
e.returnValue = '';
};
window.addEventListener('beforeunload', handler);
return () => window.removeEventListener('beforeunload', handler);
}, [isStreaming]);
// Ref to keep accumulated streaming content in sync regardless of React batching
const accumulatedRef = useRef('');
// Refs to track tool data reliably across closures (state reads can be stale)