import { lazy, Suspense, useState } from "react"; import { qk } from "../lib/queryKeys"; import { useTranslation } from "react-i18next"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import Modal, { ModalFooter } from "./ui/Modal"; import { Button } from "./ui/Button"; import { api } from "../lib/api"; import { invalidateDownloads } from "../lib/downloads"; // Lazy: the full profile editor only opens from the dialog's "manage presets" affordance. const ProfileEditor = lazy(() => import("./ProfileEditor")); import { notify } from "../lib/notifications"; import { notifyActionError } from "../lib/actionErrors"; import { navigateTo } from "../lib/nav"; import FilterSelect from "./ui/FilterSelect"; import { Input } from "./ui/Input"; // Preset picker shown when the user hits Download on a card / in the player, or adds a URL from // the Download Center. Enqueues via the API and points the user at the Download Center. export default function DownloadDialog({ source, title, onClose, }: { source: string; title?: string | null; onClose: () => void; }) { const { t } = useTranslation(); const qc = useQueryClient(); const profilesQ = useQuery({ queryKey: qk.downloadProfiles(), queryFn: api.downloadProfiles }); const [profileId, setProfileId] = useState(null); const [name, setName] = useState(""); const [busy, setBusy] = useState(false); const [manage, setManage] = useState(false); const profiles = profilesQ.data ?? []; const chosen = profileId ?? profiles[0]?.id ?? null; const submit = async () => { if (!chosen || busy) return; setBusy(true); try { await api.enqueueDownload({ source, profile_id: chosen, display_name: name.trim() || undefined, }); invalidateDownloads(qc); notify({ level: "success", message: t("downloads.dialog.added"), action: { label: t("downloads.dialog.view"), onClick: () => navigateTo("downloads") }, }); onClose(); } catch (e) { // A quota or validation refusal (422/409) is already in the global error dialog, which the // reporter skips; a 403/404/429 or a lost connection is not, and used to leave the dialog // open with nothing said (E11 S2b). notifyActionError(e, t("errors.writeFailed.enqueueDownload")); setBusy(false); } }; return ( {title &&

{title}

} {/* The user's own list, of any length: a menu, not a segmented bar (owner's pick, 2026-09-19). */} v && setProfileId(Number(v))} options={profiles.map((p) => ({ value: String(p.id), label: p.name }))} /> setName(e.target.value)} placeholder={t("downloads.dialog.namePlaceholder")} /> {manage && ( { setManage(false); profilesQ.refetch(); }} /> )}
); }