fix(settings): close the two self-update UX gaps from 2026-04-16 VM test
Drive upd-current from the /api/furtka/update/check response so a post-update Check reflects the new installed version without Ctrl+F5, and arm a 45s fallback location.reload on apply-click so the page still comes up on the new version when the mid-apply API restart drops the /update-state.json poll before stage=done is observed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
bf86ffaf4c
commit
a5de3d7622
2 changed files with 11 additions and 3 deletions
|
|
@ -7,10 +7,10 @@ This project uses calendar versioning: `YY.N-stage` (e.g. `26.0-alpha` = 2026, r
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Known UX gaps (to fix in the next release)
|
### Fixed
|
||||||
|
|
||||||
- **Settings page "Installed" field doesn't refresh right after a self-update.** After `/settings` → Update now → success, the browser's `refresh()` against `/status.json` reads a page-load snapshot rather than the new value. A force-reload (Ctrl+F5) shows the correct version. Fix idea: have the update-check endpoint response also drive `upd-current` (we already set `upd-latest` from it).
|
- **Settings page "Installed" field now refreshes after a self-update.** The `/api/furtka/update/check` response already carries `current` — the settings JS now drives `upd-current` from it the same way it drives `upd-latest`, so clicking "Check for updates" after a successful update reflects the new installed version without a force-reload.
|
||||||
- **Auto-reload on update completion is unreliable.** The JS polls `/update-state.json` and calls `location.reload()` 5s after seeing `stage: done`. On the 2026-04-16 VM test the browser never auto-reloaded — user reloaded manually. Probable cause: the API restart mid-apply drops the polling connection between the browser and the page before the done state is observed. Fix idea: fallback `setTimeout(reload, 45_000)` on apply-click regardless of poll outcome.
|
- **Auto-reload on update completion is now reliable.** Clicking "Update now" arms a 45 s fallback `setTimeout(location.reload)` in addition to the existing `/update-state.json` polling loop. If the mid-apply API restart drops the poll connection before `stage: done` is ever observed (as seen on the 2026-04-16 VM test), the fallback still brings the page up on the new version. The fallback is cleared on `done` (5 s reload wins) or `rolled_back` (user needs the error visible).
|
||||||
|
|
||||||
## [26.3-alpha] - 2026-04-16
|
## [26.3-alpha] - 2026-04-16
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
let pollHandle = null;
|
let pollHandle = null;
|
||||||
|
let fallbackReloadHandle = null;
|
||||||
const statusEl = document.getElementById('update-status');
|
const statusEl = document.getElementById('update-status');
|
||||||
const checkBtn = document.getElementById('check-updates-btn');
|
const checkBtn = document.getElementById('check-updates-btn');
|
||||||
const applyBtn = document.getElementById('apply-update-btn');
|
const applyBtn = document.getElementById('apply-update-btn');
|
||||||
|
|
@ -135,6 +136,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
document.getElementById('upd-latest').textContent = data.latest || '—';
|
document.getElementById('upd-latest').textContent = data.latest || '—';
|
||||||
|
document.getElementById('upd-current').textContent = data.current || '—';
|
||||||
if (data.update_available) {
|
if (data.update_available) {
|
||||||
applyBtn.hidden = false;
|
applyBtn.hidden = false;
|
||||||
applyBtn.textContent = `Update to ${data.latest}`;
|
applyBtn.textContent = `Update to ${data.latest}`;
|
||||||
|
|
@ -169,6 +171,10 @@
|
||||||
// Poll /update-state.json (served by Caddy, unaffected by the
|
// Poll /update-state.json (served by Caddy, unaffected by the
|
||||||
// API restart the updater is about to trigger) every 2s.
|
// API restart the updater is about to trigger) every 2s.
|
||||||
pollHandle = setInterval(pollUpdateState, 2000);
|
pollHandle = setInterval(pollUpdateState, 2000);
|
||||||
|
// Fallback: reload regardless of whether polling observes 'done'.
|
||||||
|
// The mid-apply API restart can drop the poll connection before
|
||||||
|
// the terminal state is ever seen by this page.
|
||||||
|
fallbackReloadHandle = setTimeout(() => location.reload(), 45000);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setStatus(`Network error: ${e.message}`, true);
|
setStatus(`Network error: ${e.message}`, true);
|
||||||
applyBtn.disabled = false;
|
applyBtn.disabled = false;
|
||||||
|
|
@ -185,9 +191,11 @@
|
||||||
setStatus(label, s.stage === 'rolled_back');
|
setStatus(label, s.stage === 'rolled_back');
|
||||||
if (s.stage === 'done') {
|
if (s.stage === 'done') {
|
||||||
clearInterval(pollHandle);
|
clearInterval(pollHandle);
|
||||||
|
clearTimeout(fallbackReloadHandle);
|
||||||
setTimeout(() => location.reload(), 5000);
|
setTimeout(() => location.reload(), 5000);
|
||||||
} else if (s.stage === 'rolled_back') {
|
} else if (s.stage === 'rolled_back') {
|
||||||
clearInterval(pollHandle);
|
clearInterval(pollHandle);
|
||||||
|
clearTimeout(fallbackReloadHandle);
|
||||||
if (s.reason) {
|
if (s.reason) {
|
||||||
setStatus(`${label} — ${s.reason}`, true);
|
setStatus(`${label} — ${s.reason}`, true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue