Show update-available link in the sidebar, not only in Settings

The update card lived inside Settings, easy to miss on an app people
mostly leave on the Dashboard or Timing view. A small link now sits
next to the version number in the sidebar footer, on every view, and
appears automatically from the same startup update check Settings
already used - no new endpoint. Clicking it jumps straight into
Settings with the update ready to run.

Verified with a real headless-browser run against a throwaway git
clone: the link appears on its own without ever opening Settings,
names the real new version, survives navigating to another view, and
clicking it lands on Settings with the update already available there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
larssand
2026-09-10 14:51:46 +02:00
parent a7272d3844
commit 71b3451b13
7 changed files with 53 additions and 3 deletions

View File

@@ -1489,6 +1489,7 @@ const TRANSLATIONS = {
"settings.update_checking": "Kontrollerar om det finns en ny version...",
"settings.update_up_to_date": "Du har den senaste versionen.",
"settings.update_available": "Version {version} finns tillgänglig.",
"settings.update_available_sidebar": "Ny version v{version} - uppdatera",
"settings.update_error": "Kunde inte kontrollera version: {msg}",
"settings.update_check_now": "Kontrollera nu",
"settings.update_run_now": "Uppdatera nu",
@@ -2992,6 +2993,7 @@ const TRANSLATIONS = {
"settings.update_checking": "Checking for a new version...",
"settings.update_up_to_date": "You have the latest version.",
"settings.update_available": "Version {version} is available.",
"settings.update_available_sidebar": "New version v{version} - update",
"settings.update_error": "Could not check for updates: {msg}",
"settings.update_check_now": "Check now",
"settings.update_run_now": "Update now",
@@ -3691,6 +3693,7 @@ async function init() {
updateSyncStatus();
});
startAppVersionPolling();
document.getElementById("appUpdateAvailable")?.addEventListener("click", navigateToSettings);
setInterval(() => refreshUpdateStatus({ render: currentView === "settings" }), 20 * 60 * 1000);
Promise.allSettled([hydrateFromBackend(), loadAmmcConfigFromBackend(), refreshServerBackups({ render: false }), refreshUpdateStatus({ render: false })]).finally(() => {
@@ -4597,10 +4600,29 @@ async function refreshUpdateStatus({ render = true, force = false } = {}) {
}
updateStatusLoaded = true;
updateCheckPending = false;
refreshSidebarUpdateLink();
if (render && currentView === "settings") renderView();
return updateStatus;
}
function refreshSidebarUpdateLink() {
const link = document.getElementById("appUpdateAvailable");
if (!link) return;
if (updateStatus?.available && updateStatus.remoteVersion) {
link.textContent = t("settings.update_available_sidebar", { version: updateStatus.remoteVersion });
link.hidden = false;
} else {
link.hidden = true;
}
}
function navigateToSettings() {
currentView = "settings";
updateViewUrl(currentView);
renderNav();
renderView();
}
async function runUpdateNow() {
const response = await fetch(`${getBackendUrl()}/api/update/run`, { method: "POST" });
const payload = await response.json().catch(() => ({}));