feat(desktop): update failures say what happened and what to do
CI / Secret scan (gitleaks) (push) Successful in 15s
CI / Build & Quality Checks (push) Canceled after 53s
CI / Trigger Desktop Build (push) Canceled after 0s
CI / Docker image build & smoke test (push) Canceled after 0s
CI / Playwright smoke (e2e) (push) Canceled after 0s
CI / Secret scan (gitleaks) (push) Successful in 15s
CI / Build & Quality Checks (push) Canceled after 53s
CI / Trigger Desktop Build (push) Canceled after 0s
CI / Docker image build & smoke test (push) Canceled after 0s
CI / Playwright smoke (e2e) (push) Canceled after 0s
A friend's update failed ten times in a row ("Update check failed: error
sending request for url (…nsis.zip)") before the 11th went through, and
he didn't know what to do. The label was also wrong: the check had
worked; the download failed.
- Progress: "Downloading update… 28% (14.3 MB of 49.9 MB)", "The update
server didn't respond. Trying again in 3 s (attempt 2 of 4)…", from the
native `lotus-update-progress` events (cinny-desktop retries itself).
- Failures name the step (check / download / install, from the native
error prefix) in plain language, with Try again and a Download
installer button (Windows: the setup .exe; else the release page), and
the raw error under "Details".
- Installing from the update toast now shows a "Downloading update"
toast, and on failure a sticky "Update didn't install" toast that
retries on click and points at Settings → General → App Updates.
Before, the toast vanished and the failure was only visible in Settings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
23649f1255
commit
568f218fe9
@@ -110,7 +110,8 @@ import {
|
||||
} from '../../../utils/translation/langUtils';
|
||||
import { chromeTranslationEngine } from '../../../utils/translation/chromeEngine';
|
||||
import { SequenceCardStyle } from '../styles.css';
|
||||
import { useTauriUpdater } from '../../../hooks/useTauriUpdater';
|
||||
import { UpdateProgress, useTauriUpdater } from '../../../hooks/useTauriUpdater';
|
||||
import { describeUpdateError, manualDownloadUrl } from '../../../utils/updateErrors';
|
||||
import { isTauri as isTauriEnv, invokeTauri, tauriInvoke } from '../../../hooks/useTauri';
|
||||
import { isSafeGlobalToggleKey } from '../../../hooks/useCallHotkeys';
|
||||
import { customWindowChromeAtom } from '../../../state/customWindowChrome';
|
||||
@@ -2682,6 +2683,22 @@ function Messages() {
|
||||
);
|
||||
}
|
||||
|
||||
const formatMb = (bytes: number): string => `${(bytes / 1_048_576).toFixed(1)} MB`;
|
||||
|
||||
function updateProgressText(progress: UpdateProgress | undefined): string {
|
||||
if (!progress) return 'Checking for the update…';
|
||||
if (progress.phase === 'installing') return 'Installing — Lotus Chat will restart in a moment…';
|
||||
if (progress.phase === 'retrying') {
|
||||
return `The update server didn’t respond. Trying again in ${progress.waitSecs} s (attempt ${progress.attempt + 1} of ${progress.maxAttempts})…`;
|
||||
}
|
||||
const attempt =
|
||||
progress.attempt > 1 ? ` (attempt ${progress.attempt} of ${progress.maxAttempts})` : '';
|
||||
const { downloaded, total } = progress;
|
||||
if (!total) return `Downloading update… ${formatMb(downloaded)}${attempt}`;
|
||||
const pct = Math.min(100, Math.floor((downloaded / total) * 100));
|
||||
return `Downloading update… ${pct}% (${formatMb(downloaded)} of ${formatMb(total)})${attempt}`;
|
||||
}
|
||||
|
||||
function AppUpdates() {
|
||||
const { isTauri, status, check, install } = useTauriUpdater();
|
||||
if (!isTauri) return null;
|
||||
@@ -2694,18 +2711,41 @@ function AppUpdates() {
|
||||
: status.state === 'available'
|
||||
? `Update available: v${status.version}`
|
||||
: status.state === 'installing'
|
||||
? 'Installing update, the app will restart shortly...'
|
||||
? updateProgressText(status.progress)
|
||||
: status.state === 'error'
|
||||
? `Update check failed: ${status.message}`
|
||||
? describeUpdateError(status.phase, status.message)
|
||||
: 'Check for a new version of Lotus Chat.';
|
||||
|
||||
const retry = () => {
|
||||
if (status.state === 'error' && status.phase !== 'check') install();
|
||||
else check();
|
||||
};
|
||||
|
||||
const after =
|
||||
status.state === 'available' ? (
|
||||
<Button size="300" radii="300" onClick={install}>
|
||||
<Button size="300" radii="300" onClick={() => install()}>
|
||||
<Text size="B300">Install & Restart</Text>
|
||||
</Button>
|
||||
) : status.state === 'checking' || status.state === 'installing' ? (
|
||||
<Spinner variant="Secondary" size="200" />
|
||||
) : status.state === 'error' ? (
|
||||
<Box gap="200" wrap="Wrap" justifyContent="End">
|
||||
<Button size="300" radii="300" variant="Secondary" onClick={retry}>
|
||||
<Text size="B300">Try again</Text>
|
||||
</Button>
|
||||
{status.phase !== 'check' && (
|
||||
<Button
|
||||
size="300"
|
||||
radii="300"
|
||||
variant="Secondary"
|
||||
fill="None"
|
||||
outlined
|
||||
onClick={() => window.open(manualDownloadUrl(navigator.userAgent), '_blank')}
|
||||
>
|
||||
<Text size="B300">Download installer</Text>
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
) : (
|
||||
<Button size="300" radii="300" variant="Secondary" onClick={check}>
|
||||
<Text size="B300">Check</Text>
|
||||
@@ -2717,6 +2757,11 @@ function AppUpdates() {
|
||||
<Text size="L400">App Updates</Text>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile title="Check for Updates" description={description} after={after} />
|
||||
{status.state === 'error' && (
|
||||
<Text size="T200" priority="300" style={{ wordBreak: 'break-word' }}>
|
||||
Details: {status.message}
|
||||
</Text>
|
||||
)}
|
||||
</SequenceCard>
|
||||
</Box>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user