"use client"; import { useEffect, useState } from "react"; import type { AutoQueueStatusPayload } from "yt-dlp-transcript-common/views/autoQueueStatus"; import { usePolledPayload } from "../../lib/usePolledPayload"; // THE ONE POLL. Every operations surface — the board and each operation page — // reads the same payload from the same endpoint on the same 3-second cadence, // seeded by SSR so the first paint is not empty. // // ONE PAYLOAD, NOT ONE PER PANEL: the rail's whole purpose is that the lanes // are read TOGETHER, and two polls would let the rail and the lane below it // disagree about the same moment. // // It polls /api/view/autoQueueStatus. The old /api/auto-queue/status path is a // rewrite onto the same handler (next.config.ts) — three specs still address // it there, and that is the rewrite's regression test. export function useOperationsStatus(initial: AutoQueueStatusPayload): { data: AutoQueueStatusPayload; refresh: () => Promise; } { // Seeded by SSR, so the first poll waits one interval (`immediate: false`), // as the hand-rolled loop this replaced did. Always enabled: the board is // live whenever it is open. const { data, refetch } = usePolledPayload( "/api/view/autoQueueStatus", true, 3000, initial, { immediate: false }, ); // Never null: the hook starts from `initial` and never clears it. return { data: data ?? initial, refresh: refetch }; } // "React is live on this subtree" — the same signal `now !== null` gives the // runner sections, for the surfaces that have no clock of their own. // // A TESTING AFFORDANCE, and a deliberate one: a React state update that lands // before hydration is silently discarded — no error, no request, nothing — and // this repo has lost days to that failure mode more than once. Exposing it lets // a test wait for the page rather than race it. export function useHydrated(): boolean { const [hydrated, setHydrated] = useState(false); useEffect(() => setHydrated(true), []); return hydrated; }