Files
erika/lib/realtime/realtime-refresh.ts

64 lines
1.8 KiB
TypeScript

export type RealtimeRefreshFilter =
| { kind: "form"; formId?: string }
| { kind: "uploads" }
| { kind: "profile" }
| { kind: "xp" }
| { kind: "vc"; channelId?: string };
export type RealtimeRefreshEvent =
| { kind: "open" }
| { kind: "form"; formId: string }
| { kind: "upload" }
| { kind: "xp" }
| { kind: "vc"; channelId: string };
export type RealtimeRefreshSubscription = { clean: () => void };
export function matchesRealtimeRefresh(
filter: RealtimeRefreshFilter,
event: Exclude<RealtimeRefreshEvent, { kind: "open" }>,
) {
if (filter.kind === "form") {
return event.kind === "form" && (!filter.formId || event.formId === filter.formId);
}
if (filter.kind === "uploads") return event.kind === "upload";
if (filter.kind === "xp") return event.kind === "xp";
if (filter.kind === "profile") return event.kind === "xp" || event.kind === "vc";
return event.kind === "vc" && (!filter.channelId || event.channelId === filter.channelId);
}
export function startRealtimeRefresh({
filter,
debounceMs,
refresh,
subscribe,
}: {
filter: RealtimeRefreshFilter;
debounceMs: number;
refresh: () => void;
subscribe: (emit: (event: RealtimeRefreshEvent) => void) => RealtimeRefreshSubscription;
}) {
let connectedOnce = false;
let refreshTimer: ReturnType<typeof setTimeout> | undefined;
const scheduleRefresh = () => {
if (refreshTimer) clearTimeout(refreshTimer);
refreshTimer = setTimeout(refresh, debounceMs);
};
const subscription = subscribe((event) => {
if (event.kind === "open") {
if (connectedOnce) scheduleRefresh();
connectedOnce = true;
return;
}
if (matchesRealtimeRefresh(filter, event)) scheduleRefresh();
});
return () => {
if (refreshTimer) clearTimeout(refreshTimer);
subscription.clean();
};
}