hyperhive/frontend/packages/agent/src/hooks/useTodos.ts
atlas 39b95c2ede treefmt: apply prettier
Pure `nix fmt` output from the commit before this one — no hand edits.
203 files: 52 md, 42 tsx, 32 js, 32 css, 21 ts, 13 html, 8 json, 3 mjs.

Reproduce with `nix develop -c nix fmt` on the parent commit; the result
should be byte-identical to this tree.

None of the 13 `.prettierignore` entries appears here — verified by
intersecting the changed-file list against the ignore file, with a
control proving the intersection finds a match when one exists.
2026-09-02 15:25:07 +02:00

48 lines
1.6 KiB
TypeScript

// useTodos — polls `GET /api/todos` on a light interval, same cadence
// as app.js's `setInterval(refreshTodos, 4000)`. Kept separate from
// useAgentState: todos change asynchronously (matrix syncs, bash task
// starts/completions) independent of the state snapshot, and the old
// page polled them independently too.
import { useEffect, useRef, useState } from "preact/hooks";
import type { TodoRow } from "../types.js";
const POLL_MS = 4000;
export interface UseTodosResult {
todos: TodoRow[];
refresh: () => void;
}
export function useTodos(): UseTodosResult {
const [todos, setTodos] = useState<TodoRow[]>([]);
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const stoppedRef = useRef(false);
async function poll() {
try {
const resp = await fetch("api/todos");
if (!resp.ok) throw new Error(`http ${resp.status}`);
const body = (await resp.json()) as { todos?: TodoRow[] };
if (stoppedRef.current) return;
// Defensive filter: the wire type is the general `LooseEnd` tagged
// enum even though this endpoint only ever emits the `todo` variant
// today — see types.ts's TodoRow comment.
setTodos((body.todos ?? []).filter((t) => t.kind === "todo"));
} catch (err) {
console.warn("todos fetch failed", err);
if (!stoppedRef.current) setTodos([]);
}
}
useEffect(() => {
stoppedRef.current = false;
poll();
timerRef.current = setInterval(poll, POLL_MS);
return () => {
stoppedRef.current = true;
if (timerRef.current) clearInterval(timerRef.current);
};
}, []);
return { todos, refresh: poll };
}