"use client"; import { useEffect, useRef, useState } from "react"; import "./latest-activity.css"; import { lockBodyScroll } from "../../lib/scrollLock"; type Event = { id: string; service: string; state: string; message: string; started_at?: string; finished_at?: string }; type Operation = { summary?: { title: string; message: string; next: string; action?: string }; id: string; label: string; status: string; events: Event[]; }; export default function LatestActivity({ operation, besideDownload, onDismiss, }: { operation: Operation; besideDownload: boolean; onDismiss: () => void; }) { const dialog = useRef(null); const trigger = useRef(null); const [open, setOpen] = useState(true); useEffect(() => { void operation.id; setOpen(true); }, [operation.id]); // Events are appended in order. Client result events have no timestamp. const latest = operation.events.at(-1); const working = operation.status === "running" || operation.status === "searching"; const message = latest?.message ?? ""; const choosing = /[1-9]\d* releases? (found|shown)/i.test(message); const sent = operation.status === "complete" && /sent|accepted.*release/i.test(message) && /download|release|Sonarr|Radarr/i.test(message); const interrupted = latest?.id === "connection-error"; const status = operation.summary?.title ?? (working ? "Working on it" : choosing ? "Choose a download" : operation.status === "complete" ? "Done" : "Needs your attention"); const currentStep = operation.summary?.message ?? (working ? /send release/i.test(operation.label) ? "Sending your download..." : /search/i.test(operation.label) ? "Looking for a download..." : latest?.service === "Jellyfin" ? "Checking if it is ready to watch..." : latest?.service === "qBittorrent" ? "Checking your download..." : "Checking your request..." : interrupted ? "The connection was lost." : choosing ? "The search is finished. Choose a version to download." : sent ? "Your download has been sent." : operation.status === "error" ? "We could not finish this step." : "This check is finished."); const nextStep = operation.summary?.next ?? (working ? "Please wait. You can close this box while we work." : interrupted ? "Close this box and check the request before trying again." : choosing ? "Close this box to see the available downloads." : sent ? "You can close this box. The request will update when the download starts." : operation.status === "error" ? "Close this box to review the request and its available options." : "Close this box to see the updated request status."); const progress = (
); useEffect(() => { if (!open) return; const element = dialog.current; element?.showModal(); const unlock = lockBodyScroll(); return () => { element?.close(); unlock(); trigger.current?.focus(); }; }, [open]); return (
setOpen(false)} onClose={() => setOpen(false)} >
Request progress

{status}

{working &&

{progress}

{nextStep}

{!working && ( )}
); }