feat(i18n): translate five more systems, and check for updates more often
CI / Build & test (Node 20) (push) Canceled after 0s
CI / Build & test (Node 24) (push) Canceled after 0s
CI / Build & test (push) Canceled after 0s
CodeQL / Analyze (javascript-typescript) (push) Canceled after 0s
Security / Secret scan (gitleaks) (push) Canceled after 0s
Security / Dependency scan (OSV-Scanner) (push) Canceled after 0s
Security / IaC/config scan (Trivy) (push) Canceled after 0s
Security / License compliance scan (Trivy) (push) Canceled after 0s
OpenSSF Scorecard / Scorecard analysis (push) Canceled after 0s
CI / Build & test (Node 20) (push) Canceled after 0s
CI / Build & test (Node 24) (push) Canceled after 0s
CI / Build & test (push) Canceled after 0s
CodeQL / Analyze (javascript-typescript) (push) Canceled after 0s
Security / Secret scan (gitleaks) (push) Canceled after 0s
Security / Dependency scan (OSV-Scanner) (push) Canceled after 0s
Security / IaC/config scan (Trivy) (push) Canceled after 0s
Security / License compliance scan (Trivy) (push) Canceled after 0s
OpenSSF Scorecard / Scorecard analysis (push) Canceled after 0s
Update cadence: the desktop app checked once ten seconds after launch and
then every four hours. A release published while the app sits open went
unnoticed until the next poll — which is exactly what happened with 1.4.0.
It now polls every 30 minutes and also checks when the window regains focus,
throttled to one check per five minutes so alt-tabbing does not hammer the
feed. `lastCheckAt` is stamped from the `checking-for-update` event rather
than by the callers, so a manual check from the settings panel counts too.
The focus listener is on `app`, not on mainWindow, because the window is
destroyed and recreated when closed to tray.
Translations, five whole systems:
- Settings > Desktop (autostart, updates, instance)
- Settings > Voice: output device, which completes that tab
- Settings > Keybinds, including the action names and conflict prompt
- Settings > Account, the largest — profile, password, detached-instance
notice, danger zone
- Explore spaces
Two sentences wrap a literal that must not be translated (the `input` group
name, the `user@instance` example). Those are split around a placeholder
rather than interpolated as text, so a translation keeps control of the word
order on both sides of it. Keybind action labels gained a `labelKey`
alongside the English `label`, which stays for non-React callers.
Verified: web build (tsc + vite), desktop typecheck, 490 web tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -814,7 +814,14 @@ function initAutoUpdater(): void {
|
||||
|
||||
let updateConfirmed = false;
|
||||
|
||||
// Cadence state. `lastCheckAt` is stamped from the event rather than from
|
||||
// the callers, so a manual "check for updates" from the settings panel
|
||||
// counts too and a focus arriving right after it doesn't check twice.
|
||||
let lastCheckAt = 0;
|
||||
let downloaded = false;
|
||||
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
lastCheckAt = Date.now();
|
||||
recoveryStore.update({ updateState: 'checking', lastCheckResult: null });
|
||||
updateConfirmed = false;
|
||||
});
|
||||
@@ -836,6 +843,7 @@ function initAutoUpdater(): void {
|
||||
});
|
||||
|
||||
autoUpdater.on('update-downloaded', (info: { version: string }) => {
|
||||
downloaded = true;
|
||||
const version = info.version.slice(0, 32);
|
||||
recoveryStore.update({ updateState: 'downloaded', updateVersion: version });
|
||||
mainWindow?.webContents.send('update-downloaded', { version });
|
||||
@@ -875,17 +883,28 @@ function initAutoUpdater(): void {
|
||||
}
|
||||
});
|
||||
|
||||
// Initial check with 10s delay (existing behavior)
|
||||
setTimeout(() => {
|
||||
updateConfirmed = false;
|
||||
autoUpdater.checkForUpdates().catch(() => {});
|
||||
}, 10_000);
|
||||
// A release published while the app sits open used to go unnoticed until
|
||||
// the next poll — the startup check had long since run, and four hours is
|
||||
// a long time to look at a stale version. Poll more often, and also check
|
||||
// when the user comes back to the window, which is exactly when they would
|
||||
// notice an update anyway.
|
||||
const POLL_INTERVAL_MS = 30 * 60 * 1000;
|
||||
const MIN_GAP_MS = 5 * 60 * 1000; // keeps alt-tabbing off the feed
|
||||
|
||||
// Periodic check every 4 hours (existing behavior)
|
||||
setInterval(() => {
|
||||
const maybeCheck = (): void => {
|
||||
if (downloaded) return; // already staged; nothing left to look for
|
||||
if (Date.now() - lastCheckAt < MIN_GAP_MS) return;
|
||||
updateConfirmed = false;
|
||||
autoUpdater.checkForUpdates().catch(() => {});
|
||||
}, 4 * 60 * 60 * 1000);
|
||||
};
|
||||
|
||||
setTimeout(maybeCheck, 10_000);
|
||||
setInterval(maybeCheck, POLL_INTERVAL_MS);
|
||||
|
||||
// App-level rather than mainWindow-level: the window is destroyed and
|
||||
// recreated when closed to tray and reopened, and a listener bound to the
|
||||
// old instance would go quiet after the first close.
|
||||
app.on('browser-window-focus', maybeCheck);
|
||||
} catch {
|
||||
// Auto-updater unavailable — recovery surface still functions, just
|
||||
// without update affordances. autoUpdaterRef stays null; recovery action
|
||||
|
||||
Reference in New Issue
Block a user