import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { checkForUpdate, IDLE_UPDATE_STATE, installUpdate, type UpdateState, } from '../lib/appUpdates'; import { SparklesIcon, SpinnerIcon, XIcon } from './icons'; // Light-weight update UX: check once on mount, then every hour while the // app is open. When an update exists show a toast with "Install & Restart" // and a dismiss button. Dismiss is session-scoped — on next launch we check // again. export function UpdateToast() { const { t } = useTranslation(['app']); const [state, setState] = useState(IDLE_UPDATE_STATE); const [dismissed, setDismissed] = useState(false); const [installing, setInstalling] = useState(false); const [progressPct, setProgressPct] = useState(null); useEffect(() => { let cancelled = false; const run = async () => { const next = await checkForUpdate(); if (!cancelled) setState(next); }; void run(); const id = window.setInterval(run, 60 * 60 * 1000); return () => { cancelled = true; window.clearInterval(id); }; }, []); if (!state.available || dismissed) return null; const handleInstall = async () => { setInstalling(true); setProgressPct(0); try { await installUpdate((downloaded, total) => { if (total && total > 0) setProgressPct((downloaded / total) * 100); }); } catch (err: unknown) { setState((s) => ({ ...s, error: err instanceof Error ? err.message : 'install failed', })); setInstalling(false); setProgressPct(null); } }; return (

{t('app:update.available', { defaultValue: 'Update verfügbar' })} {state.version ? ' · v' + state.version : ''}

{state.notes && (

{state.notes}

)} {state.error && (

{state.error}

)}
{!installing && ( )}
); }