import { useQueryClient } from "@tanstack/react-query"; import { toast } from "@unom/ui/toast"; import { Circle, Square } from "lucide-react"; import type { FC } from "react"; import type { StatsStatus } from "@/api/gen/model/statsStatus"; import { getStatsCaptureStatusQueryKey, getStatsRecordingsListQueryKey, useStatsCaptureStart, useStatsCaptureStatus, useStatsCaptureStop, } from "@/api/gen/stats/stats"; import { QueryState } from "@/components/query-state"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { apiErrorMessage } from "@/lib/errors"; import type { Loadable } from "@/lib/query"; import { m } from "@/paraglide/messages"; import { fmtDuration, kindLabel, Stat } from "./helpers"; /** * Container: arm/disarm the capture. Owns the polled status query plus start/stop; stopping also * refreshes the recordings list (owned by the Recordings subsection — invalidated here by key). */ export const CaptureControlSection: FC = () => { const qc = useQueryClient(); const status = useStatsCaptureStatus({ query: { refetchInterval: 2_000 } }); const start = useStatsCaptureStart(); const stop = useStatsCaptureStop(); const refreshStatus = () => qc.invalidateQueries({ queryKey: getStatsCaptureStatusQueryKey() }); // Both paths report failure. A failed STOP is the one that matters: it is "stop & save", so // swallowing the error let a capture the operator had been recording for minutes disappear with // no recording written and nothing on screen to say so. const onStart = () => start.mutate(undefined, { onSuccess: refreshStatus, onError: (e) => toast.error(apiErrorMessage(e) ?? m.stats_start_failed()), }); const onStop = () => stop.mutate(undefined, { onSuccess: () => { refreshStatus(); qc.invalidateQueries({ queryKey: getStatsRecordingsListQueryKey() }); }, onError: (e) => toast.error(apiErrorMessage(e) ?? m.stats_stop_failed()), }); return ( ); }; /** Start/Stop + a Recording/Idle pill with elapsed + sample count. */ export const CaptureControlCard: FC<{ status: Loadable; onStart: () => void; onStop: () => void; isStarting: boolean; isStopping: boolean; }> = ({ status, onStart, onStop, isStarting, isStopping }) => { const s = status.data; const armed = s?.armed ?? false; // Host-measured elapsed (monotonic) — not `Date.now() - started_unix_ms`, which mixes the // browser's clock with the host's and reads wrong (or clamps to 0:00) under any skew. const elapsed = armed && s ? s.elapsed_ms : 0; return ( {m.stats_capture_title()} {armed ? ( {m.stats_recording()} ) : ( {m.stats_idle()} )}

{m.stats_capture_desc()}

{armed && s && (
{s.kind && ( )}
)}
{armed ? ( ) : ( )}
); };