mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 14:15:24 +00:00
163 lines
4.6 KiB
TypeScript
163 lines
4.6 KiB
TypeScript
import { useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { toast } from "sonner";
|
|
import { ExternalLink, MonitorDown } from "lucide-react";
|
|
import { Button } from "@/components/button";
|
|
import { SectionCard, SettingRow } from "@/components/section-card";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/select";
|
|
import {
|
|
canConfigureUpdates,
|
|
desktopUpdates,
|
|
useDesktopUpdate,
|
|
type DesktopUpdateMode,
|
|
type DesktopUpdateState,
|
|
} from "@/lib/desktop-updates";
|
|
|
|
const MODES: DesktopUpdateMode[] = ["automatic", "prompt", "never"];
|
|
|
|
function StatusText({ state }: { state: DesktopUpdateState }) {
|
|
const { t } = useTranslation();
|
|
const version = state.version;
|
|
switch (state.status) {
|
|
case "checking":
|
|
return <>{t("desktopUpdates.statusChecking")}</>;
|
|
case "available":
|
|
return <>{t("desktopUpdates.statusAvailable", { version })}</>;
|
|
case "downloading":
|
|
return (
|
|
<>
|
|
{t("desktopUpdates.statusDownloading", {
|
|
version,
|
|
progress: state.progress ?? 0,
|
|
})}
|
|
</>
|
|
);
|
|
case "downloaded":
|
|
return <>{t("desktopUpdates.statusReady", { version })}</>;
|
|
case "up-to-date":
|
|
return <>{t("desktopUpdates.statusUpToDate")}</>;
|
|
case "error":
|
|
return <>{t("desktopUpdates.statusError")}</>;
|
|
default:
|
|
return <>{t("desktopUpdates.statusIdle")}</>;
|
|
}
|
|
}
|
|
|
|
/** Desktop only: how this install updates itself. */
|
|
export function DesktopUpdatesCard() {
|
|
const { t } = useTranslation();
|
|
const state = useDesktopUpdate();
|
|
const [busy, setBusy] = useState(false);
|
|
|
|
if (!canConfigureUpdates(state)) return null;
|
|
const bridge = desktopUpdates();
|
|
|
|
const run = async (action: () => Promise<unknown> | undefined) => {
|
|
setBusy(true);
|
|
try {
|
|
await action();
|
|
} catch {
|
|
toast.error(t("settings.updates.saveFailed"));
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
};
|
|
|
|
const modeLabel = (mode: DesktopUpdateMode) =>
|
|
mode === "automatic"
|
|
? t("desktopUpdates.modeAutomatic")
|
|
: mode === "prompt"
|
|
? t("desktopUpdates.modePrompt")
|
|
: t("desktopUpdates.modeNever");
|
|
|
|
return (
|
|
<SectionCard
|
|
title={t("desktopUpdates.title")}
|
|
icon={<MonitorDown className="size-3.5" />}
|
|
rowId="updates-desktop"
|
|
>
|
|
<SettingRow
|
|
label={t("desktopUpdates.mode")}
|
|
description={
|
|
state.kind === "manual"
|
|
? t("desktopUpdates.manualModeHint")
|
|
: t("desktopUpdates.modeHint")
|
|
}
|
|
>
|
|
<Select
|
|
disabled={busy}
|
|
value={state.mode}
|
|
onValueChange={(next) =>
|
|
void run(() => bridge?.setMode(next as DesktopUpdateMode))
|
|
}
|
|
>
|
|
<SelectTrigger className="h-8 w-40 text-sm">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{MODES.map((mode) => (
|
|
<SelectItem key={mode} value={mode}>
|
|
{modeLabel(mode)}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</SettingRow>
|
|
<SettingRow
|
|
label={t("desktopUpdates.status", { version: state.currentVersion })}
|
|
description={<StatusText state={state} />}
|
|
>
|
|
{state.status === "downloaded" ? (
|
|
<Button
|
|
size="sm"
|
|
className="h-8"
|
|
onClick={() => void bridge?.install()}
|
|
>
|
|
{t("desktopUpdates.restart")}
|
|
</Button>
|
|
) : state.status === "available" && state.kind === "auto" ? (
|
|
<Button
|
|
size="sm"
|
|
className="h-8"
|
|
disabled={busy}
|
|
onClick={() => void run(() => bridge?.download())}
|
|
>
|
|
{t("desktopUpdates.download")}
|
|
</Button>
|
|
) : state.status === "available" && state.releaseUrl ? (
|
|
<Button size="sm" variant="outline" className="h-8 gap-1.5" asChild>
|
|
<a
|
|
href={state.releaseUrl}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
{t("desktopUpdates.download")}
|
|
<ExternalLink className="size-3" />
|
|
</a>
|
|
</Button>
|
|
) : (
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
className="h-8"
|
|
disabled={
|
|
busy ||
|
|
state.status === "checking" ||
|
|
state.status === "downloading"
|
|
}
|
|
onClick={() => void run(() => bridge?.check())}
|
|
>
|
|
{t("desktopUpdates.checkNow")}
|
|
</Button>
|
|
)}
|
|
</SettingRow>
|
|
</SectionCard>
|
|
);
|
|
}
|