41 lines
1.3 KiB
TypeScript
41 lines
1.3 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { BASE } from "../api/client";
|
|
import { joinBackendPath } from "../api/runtime-config";
|
|
import { useSessionStore } from "../store/sessionStore";
|
|
import type { StreamEvent } from "../api/types";
|
|
|
|
export function useSessionStream(sessionId: string | null) {
|
|
const [connected, setConnected] = useState(false);
|
|
const applyEvent = useSessionStore((s) => s.applyEvent);
|
|
|
|
useEffect(() => {
|
|
if (!sessionId) return;
|
|
|
|
const es = new EventSource(joinBackendPath(BASE, `/sessions/${sessionId}/events`));
|
|
es.onopen = () => setConnected(true);
|
|
es.onerror = () => setConnected(false);
|
|
|
|
const handle = (ev: MessageEvent) => {
|
|
try {
|
|
applyEvent(JSON.parse(ev.data) as StreamEvent);
|
|
} catch {
|
|
/* ignore malformed */
|
|
}
|
|
};
|
|
|
|
// onmessage catches unnamed events; addEventListener catches named events
|
|
// (the backend sends event: ui_request, event: text_delta, etc.)
|
|
es.onmessage = handle;
|
|
const namedEvents = ["ui_request", "text_delta", "info", "system_event"] as const;
|
|
for (const name of namedEvents) es.addEventListener(name, handle);
|
|
|
|
return () => {
|
|
for (const name of namedEvents) es.removeEventListener(name, handle);
|
|
es.close();
|
|
setConnected(false);
|
|
};
|
|
}, [sessionId, applyEvent]);
|
|
|
|
return { connected };
|
|
}
|