49 lines
2.1 KiB
TypeScript
49 lines
2.1 KiB
TypeScript
// R-P8b (RA-C2) — client hook that turns the pipeline-board SSE stream into live
|
|
// board updates. Opens an EventSource on the board's `sseHref`, and on every real
|
|
// `projects.board.changed` change-event revalidates the route so the server
|
|
// rebuilds the KanbanBoardVM (loader/VM boundary intact — no client re-compute).
|
|
// Snapshot-on-connect and the heartbeat are ignored here; only real change-events
|
|
// trigger a revalidation, so there is no polling loop. Route-side wiring keeps the
|
|
// screen component pure (no router hooks in the presentational board).
|
|
|
|
import { useEffect, useRef } from "react";
|
|
import { useRevalidator } from "react-router";
|
|
import { PROJECT_BOARD_SSE_EVENT } from "~/utils/integrations/projects/projectBoardEvents.shared";
|
|
|
|
export type BoardLiveUpdatesOptions = {
|
|
/**
|
|
* The route is already loading/submitting. Its own completion revalidation
|
|
* will include the event, so starting another loader here only duplicates
|
|
* work and can make an optimistic drop stutter.
|
|
*/
|
|
paused?: boolean;
|
|
};
|
|
|
|
export function useBoardLiveUpdates(
|
|
sseHref: string | null | undefined,
|
|
options: BoardLiveUpdatesOptions = {},
|
|
): void {
|
|
const revalidator = useRevalidator();
|
|
// Keep the latest revalidate/state in a ref so the EventSource effect does not
|
|
// re-subscribe on every revalidator tick (which would tear down the stream).
|
|
const revalidateRef = useRef(revalidator.revalidate);
|
|
revalidateRef.current = revalidator.revalidate;
|
|
const stateRef = useRef(revalidator.state);
|
|
stateRef.current = revalidator.state;
|
|
const pausedRef = useRef(Boolean(options.paused));
|
|
pausedRef.current = Boolean(options.paused);
|
|
|
|
useEffect(() => {
|
|
if (!sseHref || typeof window === "undefined" || typeof EventSource === "undefined") return;
|
|
const source = new EventSource(sseHref);
|
|
const handler = () => {
|
|
if (!pausedRef.current && stateRef.current === "idle") revalidateRef.current();
|
|
};
|
|
source.addEventListener(PROJECT_BOARD_SSE_EVENT, handler);
|
|
return () => {
|
|
source.removeEventListener(PROJECT_BOARD_SSE_EVENT, handler);
|
|
source.close();
|
|
};
|
|
}, [sseHref]);
|
|
}
|