({
updateInfo,
activeDownloads = 0,
onClose,
})
| 435 | |
| 436 | // ── Main component ──────────────────────────────────────────────────────────── |
| 437 | export default function UpdateModal({ |
| 438 | updateInfo, |
| 439 | activeDownloads = 0, |
| 440 | onClose, |
| 441 | }) { |
| 442 | const { latest, current, url, changelog, assets, sourceLabel } = updateInfo; |
| 443 | |
| 444 | const [phase, setPhase] = useState("idle"); // idle | downloading | installing | done | error |
| 445 | const [format, setFormat] = useState(null); // "appimage" | "deb" | "exe" | "dmg" | null |
| 446 | const [progress, setProgress] = useState(0); |
| 447 | const [progressLabel, setProgressLabel] = useState(""); |
| 448 | const [errorMsg, setErrorMsg] = useState(""); |
| 449 | const cancelRef = useRef(false); |
| 450 | |
| 451 | // Detect install format on mount |
| 452 | useEffect(() => { |
| 453 | if (!window.electron?.detectUpdateFormat) return; |
| 454 | let mounted = true; |
| 455 | window.electron.detectUpdateFormat().then((fmt) => { |
| 456 | if (mounted) setFormat(fmt); |
| 457 | }); |
| 458 | return () => { |
| 459 | mounted = false; |
| 460 | }; |
| 461 | }, []); |
| 462 | |
| 463 | // Listen for download progress from main process |
| 464 | useEffect(() => { |
| 465 | if (!window.electron?.onUpdateProgress) return; |
| 466 | const handler = window.electron.onUpdateProgress((data) => { |
| 467 | setProgress(data.percent ?? 0); |
| 468 | setProgressLabel(data.label ?? ""); |
| 469 | }); |
| 470 | return () => window.electron.offUpdateProgress(handler); |
| 471 | }, []); |
| 472 | |
| 473 | const assetUrl = format && assets?.[format]; |
| 474 | const canInstall = |
| 475 | format && assetUrl && activeDownloads === 0 && phase === "idle"; |
| 476 | |
| 477 | const handleInstall = async () => { |
| 478 | if (!canInstall) return; |
| 479 | cancelRef.current = false; |
| 480 | setPhase("downloading"); |
| 481 | setProgress(0); |
| 482 | setProgressLabel("Preparing…"); |
| 483 | |
| 484 | try { |
| 485 | const result = await window.electron.downloadAndInstallUpdate({ |
| 486 | url: assetUrl, |
| 487 | format, |
| 488 | }); |
| 489 | if (cancelRef.current) return; |
| 490 | if (!result.ok) throw new Error(result.error || "Update failed"); |
| 491 | setPhase("installing"); |
| 492 | setProgressLabel("Launching installer…"); |
| 493 | } catch (e) { |
| 494 | if (cancelRef.current) return; |
nothing calls this directly
no test coverage detected