"use client"; import Link from "next/link"; import { useState } from "react"; import { syncAllChannelsAction, type SyncAllResult, } from "../actions"; type Status = | { kind: "idle" } | { kind: "running"; fullSweep: boolean } | { kind: "done"; result: SyncAllResult } | { kind: "error"; message: string }; export function SyncAllChannelsButton() { const [status, setStatus] = useState({ kind: "idle" }); async function handleClick(fullSweep: boolean) { setStatus({ kind: "running", fullSweep }); try { const result = await syncAllChannelsAction({ fullSweep }); setStatus({ kind: "done", result }); } catch (e) { setStatus({ kind: "error", message: (e as Error).message }); } } const running = status.kind === "running"; const sweeping = status.kind === "running" && status.fullSweep; return ( // OUTLINES, not fills. These sweep the whole pool and are reached for // rarely; the page's one primary is "New channel". The result span is a // flex item of this row and wraps to its own line when the header is // narrow — same element, same label, just no longer forcing the cluster // wider than the header.
{/* Forces the deep pass on every channel instead of waiting out each one's cadence — the button to reach for right after upgrading, when nothing has swept yet and no playlist carries deletion flags. */} {status.kind === "done" && ( `${s.slug}: ${s.reason}`) .join("\n") } > Queued {status.result.queued.length} · skipped{" "} {status.result.skipped.length} ·{" "} view jobs )} {status.kind === "error" && ( {status.message} )}
); }