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.
48 lines
1.6 KiB
TypeScript
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 };
|
|
}
|