diff --git a/src/components/layout/NotificationCenter.tsx b/src/components/layout/NotificationCenter.tsx index 3ef2c00..c2e7b17 100644 --- a/src/components/layout/NotificationCenter.tsx +++ b/src/components/layout/NotificationCenter.tsx @@ -66,28 +66,41 @@ export default function NotificationCenter() { }, [projectId]); useEffect(() => { + let cancelled = false; let unlisten: (() => void) | null = null; const setup = async () => { - unlisten = await listen("new-notification", (event) => { - const incoming = event.payload.notification; + try { + const cleanup = await listen("new-notification", (event) => { + const incoming = event.payload.notification; - if (projectId && incoming.project_id !== projectId) { + if (projectId && incoming.project_id !== projectId) { + return; + } + + setNotifications((prev) => { + const withoutDuplicate = prev.filter((n) => n.id !== incoming.id); + return [incoming, ...withoutDuplicate]; + }); + + void showSystemNotification(incoming); + }); + + if (cancelled) { + cleanup(); return; } - setNotifications((prev) => { - const withoutDuplicate = prev.filter((n) => n.id !== incoming.id); - return [incoming, ...withoutDuplicate]; - }); - - void showSystemNotification(incoming); - }); + unlisten = cleanup; + } catch (error: unknown) { + console.error("Failed to subscribe to notifications", error); + } }; void setup(); return () => { + cancelled = true; if (unlisten) { unlisten(); } diff --git a/src/components/projects/ProjectLiveAgent.tsx b/src/components/projects/ProjectLiveAgent.tsx index 0d37155..17a86ea 100644 --- a/src/components/projects/ProjectLiveAgent.tsx +++ b/src/components/projects/ProjectLiveAgent.tsx @@ -160,74 +160,93 @@ export default function ProjectLiveAgent() { useEffect(() => { if (!projectId) return; + let cancelled = false; let stop: (() => void) | null = null; - void (async () => { - const [unlistenMessage, unlistenStarted, unlistenChunk, unlistenFinished, unlistenError] = - await Promise.all([ - listen("live-agent-message", (event) => { - const payload = event.payload; - if (payload.project_id !== projectId) return; - if (payload.session_id !== selectedSessionId) return; + const setup = async () => { + try { + const [unlistenMessage, unlistenStarted, unlistenChunk, unlistenFinished, unlistenError] = + await Promise.all([ + listen("live-agent-message", (event) => { + const payload = event.payload; + if (payload.project_id !== projectId) return; + if (payload.session_id !== selectedSessionId) return; - setMessages((prev) => { - const existingIndex = prev.findIndex((msg) => msg.id === payload.message.id); - if (existingIndex === -1) { - return [...prev, payload.message]; + setMessages((prev) => { + const existingIndex = prev.findIndex((msg) => msg.id === payload.message.id); + if (existingIndex === -1) { + return [...prev, payload.message]; + } + + const next = [...prev]; + next[existingIndex] = payload.message; + return next; + }); + + if (payload.message.sender === "agent" && payload.message.content.trim() !== "") { + setStreamingAgentResponse(null); } - - const next = [...prev]; - next[existingIndex] = payload.message; - return next; - }); - - if (payload.message.sender === "agent" && payload.message.content.trim() !== "") { + }), + listen("live-agent-stream-started", (event) => { + const payload = event.payload; + if (payload.project_id !== projectId) return; + if (payload.session_id !== selectedSessionId) return; + setStreamingAgentResponse(""); + }), + listen("live-agent-stream-chunk", (event) => { + const payload = event.payload; + if (payload.project_id !== projectId) return; + if (payload.session_id !== selectedSessionId) return; + setStreamingAgentResponse((prev) => `${prev ?? ""}${payload.chunk}`); + }), + listen("live-agent-stream-finished", (event) => { + const payload = event.payload; + if (payload.project_id !== projectId) return; + if (payload.session_id !== selectedSessionId) return; setStreamingAgentResponse(null); - } - }), - listen("live-agent-stream-started", (event) => { - const payload = event.payload; - if (payload.project_id !== projectId) return; - if (payload.session_id !== selectedSessionId) return; - setStreamingAgentResponse(""); - }), - listen("live-agent-stream-chunk", (event) => { - const payload = event.payload; - if (payload.project_id !== projectId) return; - if (payload.session_id !== selectedSessionId) return; - setStreamingAgentResponse((prev) => `${prev ?? ""}${payload.chunk}`); - }), - listen("live-agent-stream-finished", (event) => { - const payload = event.payload; - if (payload.project_id !== projectId) return; - if (payload.session_id !== selectedSessionId) return; - setStreamingAgentResponse(null); - }), - listen("live-agent-stream-error", (event) => { - const payload = event.payload; - if (payload.project_id !== projectId) return; - if (payload.session_id !== selectedSessionId) return; - setStreamingAgentResponse(null); - setMessages((prev) => - prev.filter((msg) => !(msg.sender === "agent" && msg.content.trim() === "")) - ); - if (payload.error) { - setError(payload.error); - } - }), - ]); + }), + listen("live-agent-stream-error", (event) => { + const payload = event.payload; + if (payload.project_id !== projectId) return; + if (payload.session_id !== selectedSessionId) return; + setStreamingAgentResponse(null); + setMessages((prev) => + prev.filter((msg) => !(msg.sender === "agent" && msg.content.trim() === "")) + ); + if (payload.error) { + setError(payload.error); + } + }), + ]); - stop = () => { - unlistenMessage(); - unlistenStarted(); - unlistenChunk(); - unlistenFinished(); - unlistenError(); - }; - })(); + const cleanup = () => { + unlistenMessage(); + unlistenStarted(); + unlistenChunk(); + unlistenFinished(); + unlistenError(); + }; + + if (cancelled) { + cleanup(); + return; + } + + stop = cleanup; + } catch (err: unknown) { + if (!cancelled) { + setError(getErrorMessage(err)); + } + } + }; + + void setup(); return () => { - if (stop) stop(); + cancelled = true; + if (stop) { + stop(); + } }; }, [projectId, selectedSessionId]); diff --git a/src/components/projects/ProjectTasks.tsx b/src/components/projects/ProjectTasks.tsx index 5f7ddcd..8a61be2 100644 --- a/src/components/projects/ProjectTasks.tsx +++ b/src/components/projects/ProjectTasks.tsx @@ -76,18 +76,31 @@ export default function ProjectTasks() { useEffect(() => { if (!projectId) return; - let stop: (() => void) | null = null; + let cancelled = false; + let unlisten: (() => void) | null = null; - void (async () => { - const unlisten = await listen("agent-task-updated", (event) => { - if (event.payload.project_id !== projectId) return; - void refresh(); + void listen("agent-task-updated", (event) => { + if (event.payload.project_id !== projectId) return; + void refresh(); + }) + .then((cleanup) => { + if (cancelled) { + cleanup(); + return; + } + unlisten = cleanup; + }) + .catch((err: unknown) => { + if (!cancelled) { + setError(getErrorMessage(err)); + } }); - stop = unlisten; - })(); return () => { - if (stop) stop(); + cancelled = true; + if (unlisten) { + unlisten(); + } }; }, [projectId]);