swarm-ui: one polled-resource hook for the refreshing pages
Each polling page repeated its own fetch-and-set code on a fixed timer, so a slow answer could be overtaken by the next tick, an older response could overwrite a newer one, and nothing aborted a request when the page went away. usePolled skips a tick while a request is in flight, aborts on unmount or refresh, ignores answers from dropped requests, and keeps the last good data under an error. The alerts, hives, agents, schedules, builds, review runs and broadcast pages and the subagent list use it.
This commit is contained in:
parent
9338496e24
commit
8917ba27b6
1 changed files with 274 additions and 160 deletions
|
|
@ -5,16 +5,15 @@
|
|||
// rendered as a `Table`.
|
||||
import { useState } from "preact/hooks";
|
||||
import { ApiErrorPanel } from "@hive/shared/api-error-panel.js";
|
||||
import { readApiError, type ProblemDetails } from "@hive/shared/api-error.js";
|
||||
import { Badge, type BadgeTone } from "@hive/shared/badge.js";
|
||||
import { Button } from "../ui/button/Button.js";
|
||||
import { Panel } from "../ui/panel/Panel.js";
|
||||
import { RelativeTime } from "../ui/relative-time/RelativeTime.js";
|
||||
import {
|
||||
RefreshIntervalPicker,
|
||||
useRefreshInterval,
|
||||
type RefreshIntervalMs,
|
||||
} from "../ui/refresh-interval/RefreshInterval.js";
|
||||
import { fetchJson, usePolled } from "../ui/polled/usePolled.js";
|
||||
import { Table, type TableColumn } from "../ui/table/Table.js";
|
||||
import { activeSinceMs, alertKey, listedAlerts, type Alert } from "./alerts.js";
|
||||
|
||||
|
|
@ -73,26 +72,15 @@ const COLUMNS: TableColumn<Alert>[] = [
|
|||
const DEFAULT_INTERVAL_MS: RefreshIntervalMs = 30_000;
|
||||
|
||||
export function AlertsPage() {
|
||||
const [alerts, setAlerts] = useState<Alert[] | null>(null);
|
||||
const [error, setError] = useState<ProblemDetails | null>(null);
|
||||
const [showAll, setShowAll] = useState(false);
|
||||
const [intervalMs, setIntervalMs] =
|
||||
useState<RefreshIntervalMs>(DEFAULT_INTERVAL_MS);
|
||||
const { data: alerts, error } = usePolled(intervalMs, (signal) =>
|
||||
fetchJson<Alert[]>("/api/alerts", signal),
|
||||
);
|
||||
const listed = alerts ? listedAlerts(alerts, showAll) : null;
|
||||
const hidden = alerts && listed ? alerts.length - listed.length : 0;
|
||||
|
||||
useRefreshInterval(intervalMs, () => {
|
||||
(async () => {
|
||||
const r = await fetch("/api/alerts");
|
||||
if (!r.ok) {
|
||||
setError(await readApiError(r));
|
||||
return;
|
||||
}
|
||||
setAlerts((await r.json()) as Alert[]);
|
||||
setError(null);
|
||||
})().catch((e: unknown) => setError({ detail: String(e) }));
|
||||
});
|
||||
|
||||
return (
|
||||
<Panel
|
||||
title="alerts"
|
||||
|
|
|
|||
Loading…
Reference in a new issue