mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-10 22:43:37 +00:00
107 lines
2.6 KiB
TypeScript
107 lines
2.6 KiB
TypeScript
import { useEffect } from "react";
|
|
import { useSWRConfig } from "swr";
|
|
|
|
import {
|
|
subscribeToCrossTabSse,
|
|
type CrossTabSseCoordinator,
|
|
} from "./cross-tab-sse";
|
|
import { runListCacheMatchers } from "./board-cache";
|
|
import { queryKeys } from "./query-keys";
|
|
import {
|
|
createBrowserEventSource,
|
|
subscribeToSharedEventSource,
|
|
type EventPayload,
|
|
type EventSourceLike,
|
|
type MutateFn,
|
|
type SharedEventSubscription,
|
|
} from "./sse";
|
|
|
|
interface BoardEventOptions {
|
|
debounceMs?: number;
|
|
coordinator?: CrossTabSseCoordinator;
|
|
}
|
|
|
|
const BOARD_STATUS_EVENTS = new Set([
|
|
"run.submitted",
|
|
"run.start_requested",
|
|
"run.pending",
|
|
"run.approved",
|
|
"run.denied",
|
|
"run.runnable",
|
|
"run.starting",
|
|
"run.running",
|
|
"run.removing",
|
|
"run.paused",
|
|
"run.unpaused",
|
|
"run.blocked",
|
|
"run.unblocked",
|
|
"run.cancel.requested",
|
|
"run.pause.requested",
|
|
"run.unpause.requested",
|
|
"run.completed",
|
|
"run.failed",
|
|
"run.archived",
|
|
"run.unarchived",
|
|
"run.title.updated",
|
|
"interview.started",
|
|
"interview.completed",
|
|
"interview.timeout",
|
|
"interview.interrupted",
|
|
"pull_request.created",
|
|
"pull_request.linked",
|
|
"pull_request.unlinked",
|
|
]);
|
|
|
|
const subscriptions = new Map<string, SharedEventSubscription>();
|
|
const BOARD_SUBSCRIPTION_KEY = "board";
|
|
|
|
export function shouldRefreshBoardForEvent(event: string) {
|
|
return BOARD_STATUS_EVENTS.has(event);
|
|
}
|
|
|
|
export function subscribeToBoardEvents(
|
|
mutate: MutateFn,
|
|
eventSourceFactory: (url: string) => EventSourceLike = createBrowserEventSource,
|
|
{ debounceMs = 500, coordinator }: BoardEventOptions = {},
|
|
): () => void {
|
|
return subscribeToCrossTabSse<EventPayload>({
|
|
coordinator,
|
|
subscriptionKey: BOARD_SUBSCRIPTION_KEY,
|
|
mutate,
|
|
debounceMs,
|
|
resyncKeys: () => boardRunKeys(),
|
|
resolveInvalidation: boardInvalidation,
|
|
fallbackSubscribe: () =>
|
|
subscribeToSharedEventSource<EventPayload>({
|
|
subscriptions,
|
|
subscriptionKey: BOARD_SUBSCRIPTION_KEY,
|
|
url: queryKeys.system.attachUrl(),
|
|
mutate,
|
|
eventSourceFactory,
|
|
debounceMs,
|
|
resolveInvalidation: boardInvalidation,
|
|
}),
|
|
});
|
|
}
|
|
|
|
function boardInvalidation(payload: EventPayload) {
|
|
return {
|
|
keys: payload.event && shouldRefreshBoardForEvent(payload.event)
|
|
? boardRunKeys()
|
|
: [],
|
|
};
|
|
}
|
|
|
|
function boardRunKeys() {
|
|
return runListCacheMatchers();
|
|
}
|
|
|
|
/**
|
|
* Synchronizes React/SWR with the shared board SSE stream. The subscription is
|
|
* closed before resubscribe and on unmount.
|
|
*/
|
|
export function useBoardEvents() {
|
|
const { mutate } = useSWRConfig();
|
|
|
|
useEffect(() => subscribeToBoardEvents(mutate as MutateFn), [mutate]);
|
|
}
|