diff --git a/VERSION b/VERSION
index d8c5ea2..2b7e609 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1231
\ No newline at end of file
+1232
\ No newline at end of file
diff --git a/backend/static/index.html b/backend/static/index.html
index e5e6b49..f047c3f 100644
--- a/backend/static/index.html
+++ b/backend/static/index.html
@@ -86,14 +86,14 @@
Ban Yaro
-
+
-
-
-
-
-
+
+
+
+
+
@@ -612,11 +612,11 @@
-
-
-
-
-
+
+
+
+
+
@@ -626,7 +626,7 @@
-
+
diff --git a/backend/static/js/app.js b/backend/static/js/app.js
index b5c3c8c..8991e1d 100644
--- a/backend/static/js/app.js
+++ b/backend/static/js/app.js
@@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
-const APP_VER = '1231'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
+const APP_VER = '1232'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt
window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator)
window.APP_VERSION = APP_VERSION;
diff --git a/backend/static/landing.html b/backend/static/landing.html
index 05a6a85..86f1ed8 100644
--- a/backend/static/landing.html
+++ b/backend/static/landing.html
@@ -4,7 +4,7 @@
-
+
Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz
diff --git a/backend/static/sw.js b/backend/static/sw.js
index bde574b..9fc4eac 100644
--- a/backend/static/sw.js
+++ b/backend/static/sw.js
@@ -4,7 +4,7 @@
============================================================ */
// ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab
-const VER = '1231';
+const VER = '1232';
const CACHE_VERSION = `by-v${VER}`;
const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
@@ -362,36 +362,52 @@ self.addEventListener('fetch', event => {
return;
}
- // CSS, Core-JS + Seiten-Module: Network-First mit ignoreSearch-Fallback für Offline
+ // CSS, Core-JS + Seiten-Module: Network-First — aber nach 2,5 s ohne Antwort kommt der
+ // CACHE (stale-while-revalidate): bei schwachem Empfang blieb die App sonst SEHR LANGE
+ // weiß, weil der Fetch nicht fehlschlägt, sondern tröpfelt (Gerätetest 2026-06-08).
+ // Das Netz-Ergebnis aktualisiert den Cache im Hintergrund weiter; echte Versions-Updates
+ // zieht der Update-Mechanismus (x-app-version + controllerchange) ohnehin separat.
if (url.pathname.startsWith('/css/') || url.pathname.startsWith('/js/pages/')
|| url.pathname.startsWith('/js/app.js') || url.pathname.startsWith('/js/ui.js')
|| url.pathname.startsWith('/js/api.js') || url.pathname.startsWith('/js/worlds.js')) {
- event.respondWith(
- fetch(event.request)
- .then(response => {
- if (response.ok) {
- const clone = response.clone();
- caches.open(CACHE_STATIC).then(c => c.put(event.request, clone));
- }
- return response;
- })
- .catch(() => caches.match(event.request, { ignoreSearch: true })
- .then(cached => cached || new Response('', { status: 503 })))
- );
+ event.respondWith((async () => {
+ const network = fetch(event.request).then(response => {
+ if (response.ok) {
+ const clone = response.clone();
+ caches.open(CACHE_STATIC).then(c => c.put(event.request, clone));
+ }
+ return response;
+ });
+ const winner = await Promise.race([
+ network.catch(() => null),
+ new Promise(res => setTimeout(() => res(null), 2500)),
+ ]);
+ if (winner) return winner;
+ const cached = await caches.match(event.request, { ignoreSearch: true });
+ if (cached) { network.catch(() => {}); return cached; } // Netz läuft im Hintergrund weiter
+ return network.catch(() => new Response('', { status: 503 }));
+ })());
return;
}
- // Navigation (index.html): immer Network-First
+ // Navigation (index.html): Network-First, nach 2,5 s ohne Antwort die gecachte Shell
+ // (gleicher Schwachempfang-Schutz wie oben; Offline-Fallback bleibt caches.match('/')).
if (event.request.mode === 'navigate') {
- event.respondWith(
- fetch(event.request)
- .then(response => {
- const clone = response.clone();
- caches.open(CACHE_STATIC).then(c => c.put(event.request, clone));
- return response;
- })
- .catch(() => caches.match('/'))
- );
+ event.respondWith((async () => {
+ const network = fetch(event.request).then(response => {
+ const clone = response.clone();
+ caches.open(CACHE_STATIC).then(c => c.put(event.request, clone));
+ return response;
+ });
+ const winner = await Promise.race([
+ network.catch(() => null),
+ new Promise(res => setTimeout(() => res(null), 2500)),
+ ]);
+ if (winner) return winner;
+ const cached = await caches.match('/');
+ if (cached) { network.catch(() => {}); return cached; }
+ return network.catch(() => caches.match('/'));
+ })());
return;
}