From 968e26cc6aff06f4e226e707d71797520b294655 Mon Sep 17 00:00:00 2001 From: dadaloop82 Date: Mon, 4 May 2026 05:32:57 +0000 Subject: [PATCH] fix(ui): header title always centered, actions to right, real-time update detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CSS header fixes: - .header-content: justify-content:flex-end so .header-actions (camera, Gemini) naturally stays at the right edge as a flex child - .header-title: removed overflow:hidden and text-overflow:ellipsis that were clipping the version number; title stays absolutely centered - Cleaned up unused max-width:none and margin:0 from previous broken attempt Real-time webapp update detection: - Added module-level _loadedVersion captured at page load (version in HTML header) - _checkWebappUpdate() now has two checks: 1. webapp_version from server vs _loadedVersion: if different, the server was updated since this page was loaded β†’ show 'πŸ”„ Nuova versione disponibile' banner 2. GitHub latest release vs _loadedVersion (existing behaviour) Different banner messages: deploy-changed shows simple reload prompt; release-newer shows version + changelog link (same as before) - TTL reduced from 6h to 5 min so updates are detected quickly - _checkWebappUpdate() now also fires on visibilitychange so the user sees the banner as soon as they return to the tab after a deploy --- assets/css/style.css | 6 +--- assets/js/app.js | 74 +++++++++++++++++++++++++++----------------- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/assets/css/style.css b/assets/css/style.css index 7d13e65..13f427f 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -108,10 +108,8 @@ body { position: relative; display: flex; align-items: center; - justify-content: center; + justify-content: flex-end; width: 100%; - max-width: none; - margin: 0; } .header-title { @@ -128,8 +126,6 @@ body { pointer-events: auto; /* prevent title overlapping action buttons on small screens */ max-width: calc(100% - 200px); - overflow: hidden; - text-overflow: ellipsis; } .header-version { diff --git a/assets/js/app.js b/assets/js/app.js index 3c55628..5aa6077 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -76,12 +76,14 @@ function reportError(payload) { } // ── Webapp update notification ─────────────────────────────────────────────── -// Checks the latest GitHub release once per session and shows a text banner -// if the running webapp version is outdated. +// Checks both the deployed webapp version and the latest GitHub release. +// Fires on tab focus and every 5 minutes. +const _loadedVersion = (document.querySelector('.header-version')?.textContent?.trim() || '').replace(/^v/, ''); + function _checkWebappUpdate() { const STORAGE_KEY = '_evershelf_update_checked_at'; // last-checked timestamp const SEEN_KEY = '_evershelf_update_seen_ts'; // published_at of last-dismissed release - const TTL_MS = 6 * 60 * 60 * 1000; // re-check every 6 h (localStorage) + const TTL_MS = 5 * 60 * 1000; // re-check every 5 min const now = Date.now(); const lastCheck = parseInt(localStorage.getItem(STORAGE_KEY) || '0', 10); if (now - lastCheck < TTL_MS) return; @@ -91,19 +93,22 @@ function _checkWebappUpdate() { .then(r => r.ok ? r.json() : null) .then(data => { if (!data) return; - // Release date-based comparison: show banner only if the release is - // newer than the last one the user acknowledged. - const publishedAt = data.published_at || ''; - const seenTs = localStorage.getItem(SEEN_KEY) || ''; - if (!publishedAt || publishedAt === seenTs) return; - - const latestTag = (data.latest_tag || '').replace(/^v/, ''); - const current = (document.querySelector('.header-version')?.textContent?.trim() || '').replace(/^v/, ''); - // If tag looks like a proper semver and they match β†’ no update needed - if (/^\d+\.\d+/.test(latestTag) && current && current === latestTag) return; - - // Show a dismissible banner at the top of the page if (document.getElementById('_evershelf_update_banner')) return; + + // ── Check 1: server has a different version deployed since this page loaded ── + const serverVer = (data.webapp_version || '').replace(/^v/, ''); + const deployChanged = serverVer && _loadedVersion && serverVer !== _loadedVersion; + + // ── Check 2: a newer GitHub release not yet acknowledged ── + const publishedAt = data.published_at || ''; + const seenTs = localStorage.getItem(SEEN_KEY) || ''; + const latestTag = (data.latest_tag || '').replace(/^v/, ''); + const releaseNewer = publishedAt && publishedAt !== seenTs && + /^\d+\.\d+/.test(latestTag) && + _loadedVersion && latestTag !== _loadedVersion; + + if (!deployChanged && !releaseNewer) return; + const banner = document.createElement('div'); banner.id = '_evershelf_update_banner'; banner.style.cssText = [ @@ -114,22 +119,30 @@ function _checkWebappUpdate() { 'border-bottom:2px solid #fbbf24', 'box-shadow:0 2px 8px rgba(0,0,0,.4)', ].join(';'); - const releaseUrl = data.html_url || 'https://github.com/dadaloop82/EverShelf/releases/latest'; - const versionText = /^\d+\.\d+/.test(latestTag) ? ` ${latestTag}` : ''; - banner.innerHTML = - `⬆️ EverShelf${versionText} disponibile. ` + - `novitΓ ` + - `` + + + let msgHtml, dismissAction; + if (deployChanged) { + // Server files updated β€” simple reload prompt + msgHtml = `πŸ”„ Nuova versione dell'app disponibile sul server.`; + dismissAction = () => banner.remove(); // just hide, will reappear next check + } else { + const releaseUrl = data.html_url || 'https://github.com/dadaloop82/EverShelf/releases/latest'; + const versionText = /^\d+\.\d+/.test(latestTag) ? ` v${latestTag}` : ''; + msgHtml = `⬆️ EverShelf${versionText} disponibile. ` + + `novitΓ `; + dismissAction = () => { localStorage.setItem(SEEN_KEY, publishedAt); banner.remove(); }; + } + + banner.innerHTML = msgHtml + + `` + ``; document.body.prepend(banner); - document.getElementById('_evershelf_banner_close').onclick = () => { - localStorage.setItem(SEEN_KEY, publishedAt); // mark as seen - banner.remove(); - }; - // Auto-dismiss after 30 s (without marking as seen, so it reappears next visit) - setTimeout(() => banner.remove(), 30000); + document.getElementById('_evershelf_banner_close').onclick = dismissAction; + // Auto-dismiss after 30 s without marking as seen + setTimeout(() => { if (banner.isConnected) banner.remove(); }, 30000); }) .catch(() => {}); } @@ -11763,7 +11776,10 @@ async function _initApp() { // 3) Aggiorna immediatamente quando la tab torna visibile (es. torni da Bring! app) document.addEventListener('visibilitychange', () => { - if (!document.hidden) refreshCurrentPage(); + if (!document.hidden) { + refreshCurrentPage(); + _checkWebappUpdate(); // also check for app updates when user returns to tab + } }); // 4) Background Bring sync ogni 5 min β€” completamente autonomo, non dipende