import { useEffect, useState, useCallback } from "react"; import { useTranslation } from "react-i18next"; import { Info, RefreshCw, Download, CheckCircle, AlertCircle, RotateCcw, Loader2, } from "lucide-react"; import { useUpdater } from "../../hooks/useUpdater"; export default function UpdateCard() { const { t, i18n } = useTranslation(); const { state, checkForUpdate, downloadAndInstall, installAndRestart } = useUpdater(); const [releaseNotes, setReleaseNotes] = useState(null); const fetchReleaseNotes = useCallback( (targetVersion: string) => { const file = i18n.language === "fr" ? "/CHANGELOG.fr.md" : "/CHANGELOG.md"; fetch(file) .then((r) => r.text()) .then((text) => { const escaped = targetVersion.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const re = new RegExp( `^## \\[?${escaped}\\]?.*$\\n([\\s\\S]*?)(?=^## |$(?!\\n))`, "m", ); const match = text.match(re); setReleaseNotes(match ? match[1].trim() : null); }) .catch(() => setReleaseNotes(null)); }, [i18n.language], ); useEffect(() => { if (state.status === "available" && state.version) { fetchReleaseNotes(state.version); } }, [state.status, state.version, fetchReleaseNotes]); const progressPercent = state.contentLength && state.contentLength > 0 ? Math.round((state.progress / state.contentLength) * 100) : null; return (

{t("settings.updates.title")}

{state.status === "idle" && ( )} {state.status === "checking" && (
{t("settings.updates.checking")}
)} {state.status === "notEntitled" && (

{t("settings.updates.notEntitled")}

)} {state.status === "upToDate" && (
{t("settings.updates.upToDate")}
)} {state.status === "available" && (

{t("settings.updates.available", { version: state.version })}

{(() => { const notes = releaseNotes || state.body; if (!notes) return null; return (

{t("settings.updates.releaseNotes")}

{notes.split("\n").map((line, i) => { const trimmed = line.trim(); if (!trimmed) return
; if (trimmed.startsWith("### ")) return (

{trimmed.slice(4)}

); if (trimmed.startsWith("## ")) return (

{trimmed.slice(3)}

); if (trimmed.startsWith("- ")) return (

{"• "} {trimmed.slice(2).replace(/\*\*(.+?)\*\*/g, "$1")}

); return

{trimmed}

; })}
); })()}
)} {state.status === "downloading" && (
{t("settings.updates.downloading")} {progressPercent !== null && {progressPercent}%}
)} {state.status === "readyToInstall" && (

{t("settings.updates.readyToInstall")}

)} {state.status === "installing" && (
{t("settings.updates.installing")}
)} {state.status === "error" && (
{t("settings.updates.error")}

{state.error}

)}
); }