Files
vibn-frontend/components/project/project-stream-handler.tsx

58 lines
1.7 KiB
TypeScript

"use client";
import { useEffect } from "react";
import { useSWRConfig } from "swr";
/**
* A headless component that maintains a Server-Sent Events (SSE) connection
* to the Postgres database. When the backend agent mutates this project,
* this component receives a ping and aggressively revalidates SWR.
*/
export function ProjectStreamHandler({ projectId }: { projectId: string }) {
const { mutate } = useSWRConfig();
useEffect(() => {
if (!projectId) return;
let eventSource: EventSource | null = null;
let retryTimeout: NodeJS.Timeout;
const connect = () => {
eventSource = new EventSource(`/api/projects/${projectId}/stream`);
eventSource.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
if (data.event === "updated") {
// The database row for this project was mutated!
// Find all SWR cache keys that belong to this project and invalidate them.
// This instantly updates the Plan tab, Task Kanban, and Anatomy overview.
mutate(
(key) => typeof key === "string" && key.includes(projectId),
undefined,
{ revalidate: true }
);
}
} catch (err) {
console.error("[SSE] Parse error", err);
}
};
eventSource.onerror = () => {
eventSource?.close();
// If the connection drops (e.g. server restart or network dip), back off and try again
retryTimeout = setTimeout(connect, 5000);
};
};
connect();
return () => {
eventSource?.close();
clearTimeout(retryTimeout);
};
}, [projectId, mutate]);
return null;
}