fix: Update-Hinweis vergleicht gegen den laufenden Build (D45)
Der Hinweis erschien oft für genau die Fassung, die gerade geladen war. Zwei Fehler mit derselben Wurzel — die Prüfung verglich nie mit dem laufenden Stand: - Relais statt Vergleich: der abgerufene Inhalts-Hash wurde gegen den ZULETZT ABGERUFENEN gehalten (localStorage), nicht gegen den laufenden. Beim Deploy antworten CDN-Knoten unterschiedlich; jeder Wechsel schlug an, und der Schlüssel wird von allen Tabs geteilt. - Klebendes Flag: `werkbaum-update-available` entfernte nur „Jetzt laden". Nach F5 oder „Später" blieb es stehen, und der Load-Check zeigte das Banner bei jedem weiteren Laden erneut. Verglichen wird jetzt der Commit aus dem Footer-Versionslink der laufenden Seite gegen den der abgerufenen HTML (D16 spritzt ihn in beiden Deploy-Wegen ein). Kein localStorage dazwischen; beim Laden wird nichts gemeldet, die Prüfung zwei Sekunden später holt es nach. Eine spätere „aktuell"-Meldung sammelt Banner und Footer-Symbol wieder ein. Rückfall auf den Inhalts-Hash für Builds ohne Marker (Dev-Server), Vergleichsstand dort der erste Abruf der Seiten-Sitzung. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
579dfbf789
commit
f1ac57c931
+72
-28
@@ -3321,7 +3321,35 @@ mountBuildBadge();
|
||||
ALLEN Builds aktiv. */
|
||||
const isProdBuild = import.meta.env.VITE_BUILD_BADGE === 'none';
|
||||
|
||||
/* Kompakter, deterministischer Hash (cyrb53) über den GESAMTEN HTML-Text.
|
||||
/* Beides bewusst NUR im Speicher, nicht im localStorage (D45): Ein gemerktes
|
||||
„Update verfügbar" überlebte das Neuladen, das es gerade eingespielt hat —
|
||||
und meldete dieselbe Fassung wieder und wieder. Nach dem Laden ist nichts
|
||||
bekannt; was noch gilt, findet die nächste Prüfung zwei Sekunden später. */
|
||||
let updateAvailable = false;
|
||||
let baselineHash = null;
|
||||
|
||||
/* Verglichen wird gegen den LAUFENDEN Build, nicht gegen einen gemerkten Abruf.
|
||||
Beide Deploy-Wege spritzen den Commit in den Footer-Versionslink (D16); die
|
||||
laufende Seite trägt ihn also selbst, und die abgerufene Seite auch. Zwei
|
||||
Werte, die im selben Moment vorliegen — kein localStorage dazwischen, damit
|
||||
nichts hängenbleiben und nichts zwischen Tabs durcheinandergeraten kann.
|
||||
Siehe D45. */
|
||||
function buildIdFromHtml(html){
|
||||
const m = html.match(/<a class="ver"[^>]*href="[^"]*\/commit\/([0-9a-f]{7,40})/);
|
||||
return m ? m[1] : null;
|
||||
}
|
||||
let runningBuild;
|
||||
function runningBuildId(){
|
||||
if(runningBuild === undefined){
|
||||
const el = document.querySelector('.site-footer .ver');
|
||||
const m = el && (el.getAttribute('href') || '').match(/\/commit\/([0-9a-f]{7,40})\b/);
|
||||
runningBuild = m ? m[1] : null; /* Platzhalter „…/commit/main" ⇒ null */
|
||||
}
|
||||
return runningBuild;
|
||||
}
|
||||
|
||||
/* Rückfall für Builds ohne eingespritzten Commit (Dev-Server, `file://`):
|
||||
Kompakter, deterministischer Hash (cyrb53) über den GESAMTEN HTML-Text.
|
||||
Wichtig: Der frühere Ansatz „erste 300 + letzte 300 Zeichen" verfehlte JEDE
|
||||
Änderung — die Seite ist eine self-contained Datei (D19), der komplette
|
||||
App-Code UND die Footer-Version liegen als inline-Bundle in der MITTE, also
|
||||
@@ -3349,25 +3377,44 @@ async function checkForUpdates(){
|
||||
const resp = await fetch(bust.href, { cache: 'no-store' });
|
||||
if(!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
||||
|
||||
/* Voller Content-Hash ist die alleinige Wahrheit. ETag/Last-Modified werden
|
||||
bewusst NICHT mehr herangezogen: GitHub Pages liefert je Cache-Knoten
|
||||
unterschiedliche ETags für identischen Inhalt und löste damit die
|
||||
irreführende Meldung „Metadaten geändert, aber Inhalt gleich" aus. */
|
||||
/* ETag/Last-Modified werden bewusst NICHT herangezogen: GitHub Pages liefert
|
||||
je Cache-Knoten unterschiedliche ETags für identischen Inhalt und löste
|
||||
damit die irreführende Meldung „Metadaten geändert, aber Inhalt gleich" aus. */
|
||||
const html = await resp.text();
|
||||
const hash = hashContent(html);
|
||||
const stored = localStorage.getItem('werkbaum-html-hash');
|
||||
const laufend = runningBuildId(), geliefert = buildIdFromHtml(html);
|
||||
let neu;
|
||||
|
||||
if(!stored){
|
||||
logUpdate('✓ Erste Prüfung – Hash gespeichert');
|
||||
} else if(hash === stored){
|
||||
logUpdate('✓ Alles aktuell');
|
||||
if(laufend && geliefert){
|
||||
neu = geliefert !== laufend;
|
||||
logUpdate(neu ? '✅ Neuer Build ' + geliefert.slice(0, 7) : '✓ Alles aktuell');
|
||||
} else {
|
||||
localStorage.setItem('werkbaum-update-available', 'true');
|
||||
logUpdate('✅ NEUE VERSION ERKANNT!');
|
||||
if(!document.hidden) checkAndShowUpdateNotification();
|
||||
/* Ohne Marker bleibt nur der Inhalts-Hash. Vergleichsstand ist der ERSTE
|
||||
Abruf dieser Seiten-Sitzung und bleibt es — er im localStorage
|
||||
nachgeführt hieße: ein einzelner Abruf gegen einen veralteten
|
||||
CDN-Knoten setzt den Stand um, und der nächste meldet fälschlich neu. */
|
||||
const hash = hashContent(html);
|
||||
if(baselineHash === null){
|
||||
baselineHash = hash;
|
||||
neu = false;
|
||||
logUpdate('✓ Erste Prüfung – Vergleichsstand gesetzt');
|
||||
} else {
|
||||
neu = hash !== baselineHash;
|
||||
logUpdate(neu ? '✅ NEUE VERSION ERKANNT!' : '✓ Alles aktuell');
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem('werkbaum-html-hash', hash);
|
||||
if(neu && !updateAvailable){
|
||||
updateAvailable = true;
|
||||
if(!document.hidden) checkAndShowUpdateNotification();
|
||||
else updateFooterUpdateIcon();
|
||||
} else if(!neu && updateAvailable){
|
||||
/* Zurückgenommen (Rollback, oder der Abruf lief vorher gegen einen
|
||||
veralteten Knoten): Meldung wieder einsammeln, statt sie stehenzulassen. */
|
||||
updateAvailable = false;
|
||||
const notif = document.getElementById('updateNotification');
|
||||
if(notif) notif.remove();
|
||||
updateFooterUpdateIcon();
|
||||
}
|
||||
} catch(err) {
|
||||
const msg = err.message || err.toString();
|
||||
if(msg.includes('Failed to fetch')) {
|
||||
@@ -3457,16 +3504,12 @@ document.addEventListener('visibilitychange', () => {
|
||||
if(!document.hidden){
|
||||
checkForUpdates();
|
||||
showUpdateDebug();
|
||||
if(localStorage.getItem('werkbaum-update-available')){
|
||||
checkAndShowUpdateNotification();
|
||||
}
|
||||
if(updateAvailable) checkAndShowUpdateNotification();
|
||||
}
|
||||
});
|
||||
|
||||
/* Prüfe beim Laden, falls Update bereits verfügbar */
|
||||
if(!document.hidden && localStorage.getItem('werkbaum-update-available')){
|
||||
checkAndShowUpdateNotification();
|
||||
}
|
||||
/* Beim Laden wird NICHT gemeldet: Was die Seite gerade geladen hat, IST der
|
||||
aktuelle Stand, bis eine Prüfung etwas anderes zeigt (D45). */
|
||||
|
||||
function checkAndShowUpdateNotification(){
|
||||
const existingNotif = document.getElementById('updateNotification');
|
||||
@@ -3519,7 +3562,6 @@ function checkAndShowUpdateNotification(){
|
||||
document.body.insertBefore(notif, document.body.firstChild);
|
||||
|
||||
notif.querySelector('.updateBtn').addEventListener('click', () => {
|
||||
localStorage.removeItem('werkbaum-update-available');
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
@@ -3539,8 +3581,10 @@ function resetToDefaults(){
|
||||
|
||||
if(!confirmed) return;
|
||||
|
||||
/* Nicht-Dokument-Zustand auf Defaults (UI, Sprache, Update-Flags) — die
|
||||
Dokumentenliste (werkbaum-docs) bleibt erhalten (D22). */
|
||||
/* Nicht-Dokument-Zustand auf Defaults (UI, Sprache, Update-Log) — die
|
||||
Dokumentenliste (werkbaum-docs) bleibt erhalten (D22). Die beiden
|
||||
Update-Schlüssel schreibt niemand mehr (D45); sie werden nur noch
|
||||
aufgeräumt, falls sie aus einer früheren Fassung herumliegen. */
|
||||
['werkbaum-ui','werkbaum-lang','werkbaum-html-hash','werkbaum-update-available','werkbaum-update-log']
|
||||
.forEach(k => { try{ localStorage.removeItem(k); }catch(_){} });
|
||||
|
||||
@@ -3572,7 +3616,7 @@ function resetToDefaults(){
|
||||
/* Update-Benachrichtigung-Symbol im Footer (rechts neben Version) */
|
||||
function updateFooterUpdateIcon(){
|
||||
let icon = document.getElementById('footerUpdateIcon');
|
||||
const hasUpdate = localStorage.getItem('werkbaum-update-available');
|
||||
const hasUpdate = updateAvailable;
|
||||
|
||||
if(hasUpdate && !icon){
|
||||
const footer = document.querySelector('.site-footer');
|
||||
@@ -3609,8 +3653,8 @@ function updateFooterUpdateIcon(){
|
||||
}
|
||||
}
|
||||
|
||||
/* Icon beim Laden und bei Update-Erkennung aktualisieren */
|
||||
document.addEventListener('DOMContentLoaded', updateFooterUpdateIcon);
|
||||
/* Icon bei Update-Erkennung aktualisieren. Beim Laden gibt es nichts zu zeigen —
|
||||
der Zustand lebt nur in dieser Seiten-Sitzung (D45). */
|
||||
const originalCheckAndShowUpdateNotification = checkAndShowUpdateNotification;
|
||||
checkAndShowUpdateNotification = function(){
|
||||
originalCheckAndShowUpdateNotification.call(this);
|
||||
|
||||
Reference in New Issue
Block a user