e96d460b8e
- Neue Route /settings/general/desktop (DesktopSettingsPage) - DesktopAppSettings: Version, zwei Primaerknoepfe mit Plattform-Symbol, Dateiname/Groesse je Knopf, Beta-Kanal-Hinweis, vier erklaerende Saetze, Hinweistext wenn keine Pakete hinterlegt sind - Seitenleiste: Eintrag "Desktop-App" unter "Allgemein" - de/en: settings.categoryDesktopApp, settings.desktop.* - umlaut-dictionary.ts: "neuere" zur Allowlist ergaenzt (Rule 1 — Waechter-Test brach an einer bereits korrekten Umlautschreibung, die zufaellig die Buchstabenfolge "ue" enthaelt) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
123 lines
3.9 KiB
TypeScript
123 lines
3.9 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { useLocale, useTranslations } from 'next-intl';
|
|
import { desktopDownloadUrl, type DesktopFileInfo, type DesktopLatestInfo, formatFileSize, loadDesktopLatest } from '@/lib/desktop';
|
|
|
|
const BUTTON_CLASS =
|
|
'inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2.5 text-sm font-medium text-primary-foreground hover:opacity-90';
|
|
|
|
function WindowsIcon() {
|
|
return (
|
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
|
<rect x="1" y="1" width="6" height="6" rx="1" />
|
|
<rect x="9" y="1" width="6" height="6" rx="1" />
|
|
<rect x="1" y="9" width="6" height="6" rx="1" />
|
|
<rect x="9" y="9" width="6" height="6" rx="1" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
function LinuxIcon() {
|
|
return (
|
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" aria-hidden="true">
|
|
<rect x="1" y="2" width="14" height="12" rx="1.5" />
|
|
<path d="M4 6l2.5 2L4 10" strokeLinecap="round" strokeLinejoin="round" />
|
|
<line x1="8" y1="10" x2="11" y2="10" strokeLinecap="round" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
function DownloadButton({
|
|
file,
|
|
label,
|
|
icon,
|
|
testId,
|
|
locale,
|
|
fileInfoText,
|
|
}: {
|
|
file: DesktopFileInfo;
|
|
label: string;
|
|
icon: React.ReactNode;
|
|
testId: string;
|
|
locale: string;
|
|
fileInfoText: (values: { name: string; size: string }) => string;
|
|
}) {
|
|
return (
|
|
<div>
|
|
<a href={desktopDownloadUrl(file)} download data-testid={testId} className={BUTTON_CLASS}>
|
|
{icon}
|
|
{label}
|
|
</a>
|
|
<p className="mt-1 text-xs text-muted-foreground">
|
|
{fileInfoText({ name: file.name, size: formatFileSize(file.size, locale) })}
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Inhalt der Einstellungsseite "Desktop-App" (D-12): vier erklaerende
|
|
* Saetze, dann Version/Downloads oder ein Hinweis, wenn der Server keine
|
|
* Pakete traegt (`loadDesktopLatest()` liefert `null`).
|
|
*/
|
|
export function DesktopAppSettings() {
|
|
const t = useTranslations('settings');
|
|
const locale = useLocale();
|
|
const [info, setInfo] = useState<DesktopLatestInfo | null | undefined>(undefined);
|
|
|
|
useEffect(() => {
|
|
let active = true;
|
|
loadDesktopLatest().then((data) => {
|
|
if (active) setInfo(data);
|
|
});
|
|
return () => {
|
|
active = false;
|
|
};
|
|
}, []);
|
|
|
|
const fileInfoText = (values: { name: string; size: string }) => t('desktop.fileInfo', values);
|
|
|
|
return (
|
|
<div>
|
|
<p className="text-sm text-muted-foreground">{t('desktop.intro')}</p>
|
|
<p className="text-sm text-muted-foreground">{t('desktop.firstStart')}</p>
|
|
<p className="text-sm text-muted-foreground">{t('desktop.tray')}</p>
|
|
<p className="text-sm text-muted-foreground">{t('desktop.update')}</p>
|
|
|
|
{info === null && <p className="mt-4 text-sm text-muted-foreground">{t('desktop.unavailable')}</p>}
|
|
|
|
{info && (
|
|
<>
|
|
<p className="mt-4 text-sm text-foreground">{t('desktop.versionLabel', { version: info.version })}</p>
|
|
{info.channel === 'beta' && (
|
|
<p className="text-sm text-muted-foreground">{t('desktop.channelBeta', { commit: info.commit })}</p>
|
|
)}
|
|
<div className="mt-4 flex flex-wrap gap-4">
|
|
{info.files.windows && (
|
|
<DownloadButton
|
|
file={info.files.windows}
|
|
label={t('desktop.downloadWindows')}
|
|
icon={<WindowsIcon />}
|
|
testId="desktop-download-windows"
|
|
locale={locale}
|
|
fileInfoText={fileInfoText}
|
|
/>
|
|
)}
|
|
{info.files.linux && (
|
|
<DownloadButton
|
|
file={info.files.linux}
|
|
label={t('desktop.downloadLinux')}
|
|
icon={<LinuxIcon />}
|
|
testId="desktop-download-linux"
|
|
locale={locale}
|
|
fileInfoText={fileInfoText}
|
|
/>
|
|
)}
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|