Feature: Routenaufzeichnung übersteht App-Updates (Guard + Persistenz)
Stufe 1 (Guard): Während aktiver Aufzeichnung wird der SW-/Force-Update-Reload aufgeschoben (window._byRecording → boot.js/_bySwReload + app.js force-update); nach Stop/Speichern via window._byReloadIfPending() nachgeholt. Stufe 2 (Persistenz): Track wird gedrosselt nach localStorage (RecStore) gesichert und beim nächsten Öffnen der Karten-/Routen-Seite als 'Aufzeichnung fortsetzen?' angeboten (Resume seedet Track+km+Startzeit). Schützt auch bei Crash/OS-Kill/ manuellem Reload. Greift in map.js UND routes.js. SW v1167
This commit is contained in:
parent
ddfb9474ef
commit
78866206b4
8 changed files with 174 additions and 40 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
1166
|
||||
1167
|
||||
|
|
@ -86,14 +86,14 @@
|
|||
<title>Ban Yaro</title>
|
||||
|
||||
<!-- Theme + theme-color Statusleiste vor CSS setzen -->
|
||||
<script src="/js/boot-early.js?v=1166"></script>
|
||||
<script src="/js/boot-early.js?v=1167"></script>
|
||||
|
||||
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
|
||||
<link rel="stylesheet" href="/css/design-system.css?v=1166">
|
||||
<link rel="stylesheet" href="/css/layout.css?v=1166">
|
||||
<link rel="stylesheet" href="/css/components.css?v=1166">
|
||||
<link rel="stylesheet" href="/css/utilities.css?v=1166">
|
||||
<link rel="stylesheet" href="/css/lists.css?v=1166">
|
||||
<link rel="stylesheet" href="/css/design-system.css?v=1167">
|
||||
<link rel="stylesheet" href="/css/layout.css?v=1167">
|
||||
<link rel="stylesheet" href="/css/components.css?v=1167">
|
||||
<link rel="stylesheet" href="/css/utilities.css?v=1167">
|
||||
<link rel="stylesheet" href="/css/lists.css?v=1167">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
|
@ -617,11 +617,11 @@
|
|||
<div id="modal-container"></div>
|
||||
|
||||
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
|
||||
<script src="/js/api.js?v=1166"></script>
|
||||
<script src="/js/ui.js?v=1166"></script>
|
||||
<script src="/js/app.js?v=1166"></script>
|
||||
<script src="/js/worlds.js?v=1166"></script>
|
||||
<script src="/js/offline-indicator.js?v=1166"></script>
|
||||
<script src="/js/api.js?v=1167"></script>
|
||||
<script src="/js/ui.js?v=1167"></script>
|
||||
<script src="/js/app.js?v=1167"></script>
|
||||
<script src="/js/worlds.js?v=1167"></script>
|
||||
<script src="/js/offline-indicator.js?v=1167"></script>
|
||||
|
||||
<!-- Feature-Seiten werden lazy geladen -->
|
||||
|
||||
|
|
@ -631,7 +631,7 @@
|
|||
|
||||
|
||||
<!-- Boot: Offline-Banner + SW-Registration (extrahiert für CSP) -->
|
||||
<script src="/js/boot.js?v=1166"></script>
|
||||
<script src="/js/boot.js?v=1167"></script>
|
||||
|
||||
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Router, State-Management, Navigation, Initialisierung.
|
||||
============================================================ */
|
||||
|
||||
const APP_VER = '1166'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VER = '1167'; // ← 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;
|
||||
|
|
@ -137,7 +137,8 @@ const App = (() => {
|
|||
let lastForce = 0;
|
||||
try { lastForce = parseInt(localStorage.getItem('by_last_force_update') || '0', 10); } catch {}
|
||||
const cooldownActive = (Date.now() - lastForce) < 10 * 60 * 1000;
|
||||
if (!modalOpen && !cooldownActive) {
|
||||
// Während einer laufenden Aufzeichnung NIE force-updaten (Datenverlust).
|
||||
if (!modalOpen && !cooldownActive && !window._byRecording) {
|
||||
window._byUpdatePending = false;
|
||||
sessionStorage.setItem('by_updated_to', window._byNewVersion || '');
|
||||
sessionStorage.setItem('by_update_target', pageId);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,38 @@
|
|||
_updateBanner();
|
||||
})();
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// Aufzeichnungs-Speicher (Sicherheitsnetz gegen Datenverlust bei Reload/Crash)
|
||||
// ----------------------------------------------------------
|
||||
window.RecStore = {
|
||||
save: function(s) { try { localStorage.setItem('by_active_recording', JSON.stringify(Object.assign({ ts: Date.now() }, s))); } catch (e) {} },
|
||||
load: function() { try { return JSON.parse(localStorage.getItem('by_active_recording') || 'null'); } catch (e) { return null; } },
|
||||
clear: function() { try { localStorage.removeItem('by_active_recording'); } catch (e) {} },
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// SW-Reload — wird während einer laufenden Routen-Aufzeichnung AUFGESCHOBEN,
|
||||
// damit der nur im RAM gehaltene Track nicht verloren geht. Sobald die
|
||||
// Aufzeichnung beendet ist, holt window._byReloadIfPending() den Reload nach.
|
||||
// ----------------------------------------------------------
|
||||
window._byReloadIfPending = function() {
|
||||
if (window._byReloadPending) {
|
||||
window._byReloadPending = false;
|
||||
window.location.replace('/?_t=' + Date.now());
|
||||
}
|
||||
};
|
||||
function _bySwReload() {
|
||||
if (sessionStorage.getItem('by_skip_sw_reload')) {
|
||||
sessionStorage.removeItem('by_skip_sw_reload'); // einmalig konsumieren
|
||||
return;
|
||||
}
|
||||
if (window._byRecording) { // Aufzeichnung läuft → Reload aufschieben
|
||||
window._byReloadPending = true;
|
||||
return;
|
||||
}
|
||||
window.location.replace('/?_t=' + Date.now());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// Service Worker Registration + Update-Flow
|
||||
// ----------------------------------------------------------
|
||||
|
|
@ -56,13 +88,7 @@ if ('serviceWorker' in navigator) {
|
|||
function _watchSW(sw) {
|
||||
if (!sw) return;
|
||||
sw.addEventListener('statechange', function() {
|
||||
if (sw.state === 'activated') {
|
||||
if (sessionStorage.getItem('by_skip_sw_reload')) {
|
||||
sessionStorage.removeItem('by_skip_sw_reload'); // einmalig konsumieren
|
||||
return;
|
||||
}
|
||||
window.location.replace('/?_t=' + Date.now());
|
||||
}
|
||||
if (sw.state === 'activated') _bySwReload();
|
||||
});
|
||||
}
|
||||
reg.addEventListener('updatefound', function() { _watchSW(reg.installing); });
|
||||
|
|
@ -83,11 +109,7 @@ if ('serviceWorker' in navigator) {
|
|||
// NICHT registrieren wenn diese Seite selbst durch SW-Reload entstand
|
||||
if (!window._BY_SW_RELOAD) {
|
||||
navigator.serviceWorker.addEventListener('controllerchange', function() {
|
||||
if (sessionStorage.getItem('by_skip_sw_reload')) {
|
||||
sessionStorage.removeItem('by_skip_sw_reload');
|
||||
return;
|
||||
}
|
||||
window.location.replace('/?_t=' + Date.now());
|
||||
_bySwReload();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -156,6 +156,7 @@ window.Page_map = (() => {
|
|||
_initMap(); // sofort mit Deutschland-Mitte starten
|
||||
_startLocationTracking();
|
||||
_loadAll();
|
||||
_offerResume(); // unterbrochene Aufzeichnung anbieten
|
||||
// Standort im Hintergrund holen — bei Erfolg zur Position fliegen
|
||||
API.getLocation().then(pos => {
|
||||
_userPos = pos;
|
||||
|
|
@ -1726,7 +1727,45 @@ window.Page_map = (() => {
|
|||
else _stopRecording();
|
||||
}
|
||||
|
||||
async function _startRecording() {
|
||||
// Aufzeichnung gedrosselt nach localStorage sichern (Sicherheitsnetz gegen
|
||||
// Datenverlust bei Reload/Crash). force=true schreibt sofort.
|
||||
let _recPersistAt = 0;
|
||||
function _persistRec(force) {
|
||||
const now = Date.now();
|
||||
if (!force && now - _recPersistAt < 8000) return;
|
||||
_recPersistAt = now;
|
||||
window.RecStore?.save({ source: 'map', track: _recTrack, distKm: _recDistKm, startTime: _recStartTime });
|
||||
}
|
||||
|
||||
// Aufzeichnung endgültig abgeschlossen (gespeichert/verworfen): Speicher
|
||||
// leeren, Guard lösen und einen ggf. aufgeschobenen Update-Reload nachholen.
|
||||
function _recDone() {
|
||||
window.RecStore?.clear();
|
||||
window._byRecording = false;
|
||||
window._byReloadIfPending?.();
|
||||
}
|
||||
|
||||
// Unterbrochene Aufzeichnung (Reload/Crash/Update) zum Fortsetzen anbieten.
|
||||
let _resumeOffered = false;
|
||||
async function _offerResume() {
|
||||
if (_recActive || _resumeOffered) return;
|
||||
const saved = window.RecStore?.load();
|
||||
if (!saved || saved.source !== 'map' || !Array.isArray(saved.track) || saved.track.length < 2) return;
|
||||
if (Date.now() - (saved.ts || 0) > 6 * 3600 * 1000) { window.RecStore?.clear(); return; } // > 6h alt
|
||||
_resumeOffered = true;
|
||||
const km = (saved.distKm || 0).toFixed(2);
|
||||
const ok = await UI.modal.confirm({
|
||||
title: 'Aufzeichnung fortsetzen?',
|
||||
message: `Eine unterbrochene Aufzeichnung wurde gefunden (${km} km, ${saved.track.length} Punkte). Möchtest du sie fortsetzen?`,
|
||||
confirmText: 'Fortsetzen',
|
||||
cancelText: 'Später',
|
||||
});
|
||||
// Nur explizites Fortsetzen resumt; sonst Track behalten (erneut anbieten /
|
||||
// Staleness räumt nach 6h auf) — kein versehentlicher Datenverlust.
|
||||
if (ok) _startRecording(saved);
|
||||
}
|
||||
|
||||
async function _startRecording(resume) {
|
||||
if (!_appState.user) {
|
||||
UI.toast.warning('Bitte zuerst anmelden.');
|
||||
App.navigate('settings');
|
||||
|
|
@ -1736,11 +1775,12 @@ window.Page_map = (() => {
|
|||
UI.toast.error('GPS nicht verfügbar.');
|
||||
return;
|
||||
}
|
||||
window._byRecording = true; // Guard: Update-Reload wird aufgeschoben
|
||||
_recActive = true;
|
||||
_recPaused = false;
|
||||
_recTrack = [];
|
||||
_recDistKm = 0;
|
||||
_recStartTime = Date.now();
|
||||
_recTrack = (resume && Array.isArray(resume.track)) ? resume.track.slice() : [];
|
||||
_recDistKm = resume?.distKm || 0;
|
||||
_recStartTime = resume?.startTime || Date.now();
|
||||
|
||||
// FAB umschalten
|
||||
const btn = document.getElementById('map-rec-btn');
|
||||
|
|
@ -1775,13 +1815,24 @@ window.Page_map = (() => {
|
|||
_recDistKm += d / 1000;
|
||||
}
|
||||
_recTrack.push({ lat, lon });
|
||||
_persistRec();
|
||||
_updateRecMap(lat, lon);
|
||||
_updateRecStatus();
|
||||
},
|
||||
() => {},
|
||||
{ enableHighAccuracy: true, maximumAge: 0, timeout: 10000 }
|
||||
);
|
||||
UI.toast.success('Aufzeichnung gestartet — los geht\'s!');
|
||||
|
||||
// Fortgesetzte Aufzeichnung: bestehenden Track sofort einzeichnen
|
||||
if (resume && _recTrack.length && _map && window.L) {
|
||||
_recPolyline = L.polyline(_recTrack.map(p => [p.lat, p.lon]), { color: '#EF4444', weight: 5, opacity: 0.9 }).addTo(_map);
|
||||
const last = _recTrack[_recTrack.length - 1];
|
||||
_recMarker = L.circleMarker([last.lat, last.lon], { radius: 8, color: '#EF4444', fillColor: '#fff', fillOpacity: 1, weight: 3 }).addTo(_map);
|
||||
_map.panTo([last.lat, last.lon]);
|
||||
_updateRecStatus();
|
||||
}
|
||||
_persistRec(true);
|
||||
UI.toast.success(resume ? 'Aufzeichnung fortgesetzt.' : 'Aufzeichnung gestartet — los geht\'s!');
|
||||
|
||||
// Pocket-Modus aktivieren wenn in Einstellungen eingeschaltet
|
||||
if (localStorage.getItem('by_pocket_mode') === 'true') {
|
||||
|
|
@ -1882,9 +1933,13 @@ window.Page_map = (() => {
|
|||
UI.toast.warning('Zu wenige GPS-Punkte — bitte etwas länger laufen.');
|
||||
if (_recPolyline) { _recPolyline.remove(); _recPolyline = null; }
|
||||
if (_recMarker) { _recMarker.remove(); _recMarker = null; }
|
||||
_recDone();
|
||||
return;
|
||||
}
|
||||
|
||||
// Guard bleibt aktiv bis gespeichert/verworfen — der Track liegt jetzt im
|
||||
// Save-Modal UND (als Netz) in RecStore.
|
||||
_persistRec(true);
|
||||
const dauMin = Math.max(1, Math.floor((Date.now() - _recStartTime) / 1000 / 60));
|
||||
_showRecSaveModal(_recTrack, _recDistKm, dauMin);
|
||||
}
|
||||
|
|
@ -2013,6 +2068,7 @@ window.Page_map = (() => {
|
|||
UI.modal.close();
|
||||
if (_recPolyline) { _recPolyline.remove(); _recPolyline = null; }
|
||||
if (_recMarker) { _recMarker.remove(); _recMarker = null; }
|
||||
_recDone();
|
||||
});
|
||||
|
||||
// Hund-Checkbox Toggle-Styling
|
||||
|
|
@ -2050,6 +2106,7 @@ window.Page_map = (() => {
|
|||
UI.modal.close();
|
||||
if (_recPolyline) { _recPolyline.remove(); _recPolyline = null; }
|
||||
if (_recMarker) { _recMarker.remove(); _recMarker = null; }
|
||||
_recDone();
|
||||
if (saved.is_valid === false) {
|
||||
UI.toast.warning(`Route „${saved.name}" gespeichert — wird nicht für Statistiken gewertet (Geschwindigkeit zu hoch).`);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ window.Page_routes = (() => {
|
|||
_flushPendingNavWalk(); // nicht gespeicherten Navigations-Walk nachtragen
|
||||
try { _userPos = await API.getLocation(); } catch {}
|
||||
await _loadData();
|
||||
_offerResume(); // unterbrochene Aufzeichnung anbieten
|
||||
|
||||
// Vorschlag sofort rendern (Leaflet war noch nicht bereit bei _render)
|
||||
if (params._suggestResult) {
|
||||
|
|
@ -659,6 +660,26 @@ window.Page_routes = (() => {
|
|||
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
||||
}
|
||||
|
||||
// Unterbrochene Aufzeichnung (Reload/Crash/Update) zum Fortsetzen anbieten.
|
||||
let _resumeOffered = false;
|
||||
async function _offerResume() {
|
||||
if (_recActive || _resumeOffered || _recOvl) return;
|
||||
const saved = window.RecStore?.load();
|
||||
if (!saved || saved.source !== 'routes' || !Array.isArray(saved.track) || saved.track.length < 2) return;
|
||||
if (Date.now() - (saved.ts || 0) > 6 * 3600 * 1000) { window.RecStore?.clear(); return; }
|
||||
_resumeOffered = true;
|
||||
const km = (saved.distKm || 0).toFixed(2);
|
||||
const ok = await UI.modal.confirm({
|
||||
title: 'Aufzeichnung fortsetzen?',
|
||||
message: `Eine unterbrochene Aufzeichnung wurde gefunden (${km} km, ${saved.track.length} Punkte). Möchtest du sie fortsetzen?`,
|
||||
confirmText: 'Fortsetzen',
|
||||
cancelText: 'Später',
|
||||
});
|
||||
if (!ok) return; // Track bleibt erhalten (erneut anbieten / Staleness räumt auf)
|
||||
await _openRecOvl();
|
||||
await _startRecInOvl(saved);
|
||||
}
|
||||
|
||||
async function _openRecOvl() {
|
||||
if (!_appState.user) { UI.toast.warning('Bitte anmelden.'); return; }
|
||||
if (_recOvl) return;
|
||||
|
|
@ -752,10 +773,30 @@ window.Page_routes = (() => {
|
|||
} catch {}
|
||||
}
|
||||
|
||||
async function _startRecInOvl() {
|
||||
// Aufzeichnung gedrosselt sichern (Sicherheitsnetz gegen Datenverlust).
|
||||
let _recPersistAt = 0;
|
||||
function _persistRec(force) {
|
||||
const now = Date.now();
|
||||
if (!force && now - _recPersistAt < 8000) return;
|
||||
_recPersistAt = now;
|
||||
window.RecStore?.save({ source: 'routes', track: _recTrack, distKm: _recDistKm, startTime: _recStartTime });
|
||||
}
|
||||
function _recDone() {
|
||||
window.RecStore?.clear();
|
||||
window._byRecording = false;
|
||||
window._byReloadIfPending?.();
|
||||
}
|
||||
|
||||
async function _startRecInOvl(resume) {
|
||||
if (!navigator.geolocation) { UI.toast.error('GPS nicht verfügbar.'); return; }
|
||||
window._byRecording = true; // Guard: Update-Reload wird aufgeschoben
|
||||
_recActive = true;
|
||||
_recTrack = []; _recDistKm = 0; _recStartTime = Date.now();
|
||||
if (resume && Array.isArray(resume.track) && resume.track.length) {
|
||||
_recTrack = resume.track.slice(); _recDistKm = resume.distKm || 0;
|
||||
_recStartTime = resume.startTime || Date.now();
|
||||
} else {
|
||||
_recTrack = []; _recDistKm = 0; _recStartTime = Date.now();
|
||||
}
|
||||
|
||||
// iOS-Hinweis: Display muss wach bleiben
|
||||
if (/iPad|iPhone|iPod/.test(navigator.userAgent)) {
|
||||
|
|
@ -816,8 +857,16 @@ window.Page_routes = (() => {
|
|||
document.getElementById('rk-rec-stats-bar').style.display = '';
|
||||
|
||||
if (_recMap && window.L) {
|
||||
_recPolyline = L.polyline([], { color: '#ef4444', weight: 5, opacity: 0.9 }).addTo(_recMap);
|
||||
// Bei Fortsetzung den bestehenden Track sofort einzeichnen
|
||||
const seed = (resume && _recTrack.length) ? _recTrack.map(p => [p.lat, p.lon]) : [];
|
||||
_recPolyline = L.polyline(seed, { color: '#ef4444', weight: 5, opacity: 0.9 }).addTo(_recMap);
|
||||
if (seed.length) {
|
||||
const last = seed[seed.length - 1];
|
||||
_recLocMarker?.setLatLng(last);
|
||||
_recMap.setView(last, 16);
|
||||
}
|
||||
}
|
||||
if (resume) { _updateRecStats(); _persistRec(true); }
|
||||
|
||||
await _recAcquireWakeLock();
|
||||
document.addEventListener('visibilitychange', _recOnVisibility);
|
||||
|
|
@ -832,6 +881,7 @@ window.Page_routes = (() => {
|
|||
_recDistKm += d;
|
||||
}
|
||||
_recTrack.push({ lat, lon, ...(alt !== null ? { alt: Math.round(alt) } : {}) });
|
||||
_persistRec();
|
||||
_recPolyline?.addLatLng([lat, lon]);
|
||||
_recLocMarker?.setLatLng([lat, lon]);
|
||||
if (_recTrack.length === 1) _recMap?.setView([lat, lon], 16);
|
||||
|
|
@ -940,12 +990,14 @@ window.Page_routes = (() => {
|
|||
_recOvl?.removeEventListener('touchstart', _onRecOvlTouch);
|
||||
_recOvl?.removeEventListener('pointerdown', _onRecOvlTouch);
|
||||
|
||||
if (!save) { _closeRecOvlClean(); return; }
|
||||
if (!save) { _closeRecOvlClean(); _recDone(); return; }
|
||||
|
||||
const track = [..._recTrack], distKm = _recDistKm;
|
||||
const dauMin = Math.round((Date.now() - _recStartTime) / 60000);
|
||||
_persistRec(true); // finalen Stand sichern, bevor _recTrack zurückgesetzt wird
|
||||
_closeRecOvlClean();
|
||||
if (track.length < 2) { UI.toast.warning('Zu wenige GPS-Punkte zum Speichern.'); return; }
|
||||
if (track.length < 2) { UI.toast.warning('Zu wenige GPS-Punkte zum Speichern.'); _recDone(); return; }
|
||||
// Guard bleibt aktiv bis im Save-Modal gespeichert/verworfen wird.
|
||||
_showRecSaveModal(track, distKm, dauMin);
|
||||
}
|
||||
|
||||
|
|
@ -1048,7 +1100,7 @@ window.Page_routes = (() => {
|
|||
document.getElementById('rk-rms-paw-val').value = btn.dataset.val;
|
||||
});
|
||||
|
||||
document.getElementById('rk-rms-discard')?.addEventListener('click', () => UI.modal.close());
|
||||
document.getElementById('rk-rms-discard')?.addEventListener('click', () => { UI.modal.close(); _recDone(); });
|
||||
|
||||
document.getElementById('rk-rms-form')?.addEventListener('submit', async e => {
|
||||
e.preventDefault();
|
||||
|
|
@ -1072,12 +1124,14 @@ window.Page_routes = (() => {
|
|||
if (!navigator.onLine) {
|
||||
_addPending(payload);
|
||||
UI.modal.close();
|
||||
_recDone();
|
||||
UI.toast.success(`Route offline gespeichert — wird synchronisiert sobald Verbindung besteht.`);
|
||||
_loadData();
|
||||
return;
|
||||
}
|
||||
const saved = await API.routes.create(payload);
|
||||
UI.modal.close();
|
||||
_recDone();
|
||||
UI.toast.success(`Route „${saved.name}" gespeichert!`);
|
||||
_loadData();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<script src="/js/landing-init.js?v=1166"></script>
|
||||
<script src="/js/landing-init.js?v=1167"></script>
|
||||
<title>Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz</title>
|
||||
<meta name="description" content="Ban Yaro: Die kostenlose All-in-One Hunde-App für DACH. Tagebuch, Giftköder-Alarm, Training mit KI, Forum, Wurfbörse, Stammbaum, Inzucht-Check — DSGVO-konform, offline-fähig, ohne App Store.">
|
||||
<meta name="keywords" content="Hunde App, Hunde Community, Wurfbörse, Züchter, Welpen kaufen, Stammbaum Hund, Inzuchtkoeffizient, Hundezucht, Impfpass Hund, Giftköder Alarm, Gassi Community, Hundetraining App, Hunde Forum, Hunde KI, Hundefilm Datenbank, Welpen Marktplatz">
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
============================================================ */
|
||||
|
||||
// ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab
|
||||
const VER = '1166';
|
||||
const VER = '1167';
|
||||
const CACHE_VERSION = `by-v${VER}`;
|
||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue