Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/frontend/packages/swarm-ui/src/ui/polled/poller.ts
iris 47c447409b swarm-ui: the refresh picker shows a spinner in place of its clock while its page is fetching
usePolled reports when a request is in flight (through a new onBusy of the
poller) and holds it on for at least 0.4 seconds, so a quick answer still
reads as a refresh. The picker swaps its clock for the spinner, in the same
box so its width stays, and the spinner stands still where motion is off.
Every page with a picker passes it; a page with several polled requests shows
it while any of them runs. The Jobs page fetches through shared components
and has none yet.
2026-11-05 23:09:07 +01:00

61 lines
1.7 KiB
TypeScript

// The request discipline behind a polled page, kept free of Preact so it can
// be tested on its own. `tick` fetches unless a request is already in flight,
// so a slow answer is not overtaken by the next tick. `refetch` drops the
// request in flight and asks again, for a refresh that follows a change.
// `stop` drops the request in flight. An answer from a dropped request never
// reaches `onData` or `onError`, so an older response cannot overwrite a
// newer one or land on a page that is gone. `onBusy` tells when a request goes
// in flight and when none is any more, however it ended, so a page can show
// that it is refreshing.
export interface Poller {
tick(): void;
refetch(): void;
stop(): void;
}
export function createPoller<T>(opts: {
fetch: (signal: AbortSignal) => Promise<T>;
onData: (data: T) => void;
onError: (error: unknown) => void;
onBusy?: (busy: boolean) => void;
}): Poller {
let current: { controller: AbortController } | null = null;
function start() {
const run = { controller: new AbortController() };
current = run;
opts.onBusy?.(true);
opts.fetch(run.controller.signal).then(
(data) => {
if (current !== run) return;
current = null;
opts.onBusy?.(false);
opts.onData(data);
},
(error: unknown) => {
if (current !== run) return;
current = null;
opts.onBusy?.(false);
opts.onError(error);
},
);
}
function stop() {
if (current === null) return;
current.controller.abort();
current = null;
opts.onBusy?.(false);
}
return {
tick() {
if (current === null) start();
},
refetch() {
stop();
start();
},
stop,
};
}