From 91624dac2590b57abe860caf8452260e7a1527a2 Mon Sep 17 00:00:00 2001 From: rene Date: Thu, 4 Jun 2026 09:09:07 +0200 Subject: [PATCH] =?UTF-8?q?Fix:=20gelaufene=20km=20bei=20Routen-Navigation?= =?UTF-8?q?=20gehen=20verloren=20wenn=20nicht=20=C3=BCber=20In-App-Zur?= =?UTF-8?q?=C3=BCck=20geschlossen=20wird=20(Angie-Bug)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bisher wurde walked_km NUR in _closeNav (In-App-Zurück-Pfeil) gespeichert. Wer die Navigation anders verlässt (Handy sperren/Home/PWA schließen, oder nur den Dim-Entsperrpfeil + normal schließen), verlor die km. - Fortschritt laufend in localStorage sichern (überlebt App-Kill). - _recordNavWalk() Einmal-Guard, aufgerufen von _closeNav UND pagehide. - _flushPendingNavWalk() trägt beim nächsten App-Start einen nicht gespeicherten Walk nach. - Fehler nicht mehr still verschlucken: bleibt in localStorage → Retry. --- VERSION | 2 +- backend/static/index.html | 24 ++++++------ backend/static/js/app.js | 2 +- backend/static/js/pages/routes.js | 64 ++++++++++++++++++++++++++----- backend/static/landing.html | 2 +- backend/static/sw.js | 2 +- 6 files changed, 70 insertions(+), 26 deletions(-) diff --git a/VERSION b/VERSION index 5ec4258..a899ac6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1159 \ No newline at end of file +1160 \ No newline at end of file diff --git a/backend/static/index.html b/backend/static/index.html index a934541..1689eef 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -86,14 +86,14 @@ Ban Yaro - + - - - - - + + + + + @@ -617,11 +617,11 @@ - - - - - + + + + + @@ -631,7 +631,7 @@ - + diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 7a0fa2b..4e69472 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 = '1159'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '1160'; // ← 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/js/pages/routes.js b/backend/static/js/pages/routes.js index bf3ccaa..1632259 100644 --- a/backend/static/js/pages/routes.js +++ b/backend/static/js/pages/routes.js @@ -61,6 +61,8 @@ window.Page_routes = (() => { let _navPois = []; let _navMaxIdx = 0; let _navWalkMeta = null; // { routeId, totalKm, trackLen } + let _navRecorded = false; // Einmal-Guard pro Navigation + const _PENDING_WALK_KEY = 'by_pending_nav_walk'; // localStorage-Sicherheitsnetz let _navOrientCleanup = null; let _navLastBearing = null; let _navCompassHeading = null; @@ -122,6 +124,7 @@ window.Page_routes = (() => { _render(); UI.loadLeaflet(); // fire & forget — bereit wenn Cards gerendert werden + _flushPendingNavWalk(); // nicht gespeicherten Navigations-Walk nachtragen try { _userPos = await API.getLocation(); } catch {} await _loadData(); @@ -1574,6 +1577,7 @@ window.Page_routes = (() => { if (track.length < 2) return; _navMaxIdx = 0; + _navRecorded = false; _navLastBearing = null; _navWalkMeta = { routeId: route.id, totalKm: route.distanz_km || 0, trackLen: track.length }; @@ -1791,7 +1795,15 @@ window.Page_routes = (() => { }; const _updateStats = (idx, distToRoute, userLat, userLon) => { - if (idx > _navMaxIdx) _navMaxIdx = idx; + if (idx > _navMaxIdx) { + _navMaxIdx = idx; + // Fortschritt persistent sichern — überlebt App-Kill / beliebiges Schließen + if (_navWalkMeta && _navWalkMeta.trackLen > 1) { + const p = Math.round(_navMaxIdx / (_navWalkMeta.trackLen - 1) * 100); + const km = Math.round((_navMaxIdx / (_navWalkMeta.trackLen - 1)) * _navWalkMeta.totalKm * 100) / 100; + try { localStorage.setItem(_PENDING_WALK_KEY, JSON.stringify({ routeId: _navWalkMeta.routeId, walkedKm: km, pct: p, ts: Date.now() })); } catch {} + } + } const pct = Math.round(idx / (track.length - 1) * 100); const rem = _remainingKm(idx); document.getElementById('rk-nav-pct').textContent = pct + '%'; @@ -1827,6 +1839,7 @@ window.Page_routes = (() => { // GPS-Watch await _navAcquireWakeLock(); document.addEventListener('visibilitychange', _navOnVisibility); + window.addEventListener('pagehide', _recordNavWalk); // Schließen/Weg-Navigieren → speichern let _navFirstFix = true; _navWatchId = navigator.geolocation.watchPosition(pos => { const { latitude: lat, longitude: lon } = pos.coords; @@ -1989,23 +2002,54 @@ window.Page_routes = (() => { } } + // Speichert den gelaufenen Walk — egal wie die Navigation verlassen wird + // (In-App-Zurück, pagehide/Schließen). Einmal-Guard; bei Fehler bleibt der + // Eintrag in localStorage und wird beim nächsten App-Start nachgetragen. + function _recordNavWalk() { + if (_navRecorded || !_navWalkMeta || _navWalkMeta.trackLen <= 1) return; + const pct = Math.round(_navMaxIdx / (_navWalkMeta.trackLen - 1) * 100); + if (pct < 50) return; + _navRecorded = true; + const walkedKm = Math.round((_navMaxIdx / (_navWalkMeta.trackLen - 1)) * _navWalkMeta.totalKm * 100) / 100; + API.routes.walked(_navWalkMeta.routeId, walkedKm, pct) + .then(res => { + try { localStorage.removeItem(_PENDING_WALK_KEY); } catch {} + if (res?.new_badges?.length) UI.toast.success(`🏅 Neues Badge: ${res.new_badges[0].name}!`); + }) + .catch(() => {}); // bleibt in localStorage → Nachtrag beim nächsten Start + } + + // Beim App-/Seiten-Start: nicht gespeicherten Walk nachtragen (App gekillt / + // „wie immer" geschlossen, ohne dass ein Speicher-Event lief). + function _flushPendingNavWalk() { + if (_navWatchId !== null) return; // läuft gerade eine Navigation + let p; + try { p = JSON.parse(localStorage.getItem(_PENDING_WALK_KEY) || 'null'); } catch {} + if (!p || (p.pct || 0) < 50) return; + if (Date.now() - (p.ts || 0) > 7 * 24 * 3600 * 1000) { // zu alt → verwerfen + try { localStorage.removeItem(_PENDING_WALK_KEY); } catch {} + return; + } + API.routes.walked(p.routeId, p.walkedKm, p.pct) + .then(res => { + try { localStorage.removeItem(_PENDING_WALK_KEY); } catch {} + UI.toast.success('Gelaufene Tour nachgetragen 🐾'); + if (res?.new_badges?.length) UI.toast.success(`🏅 Neues Badge: ${res.new_badges[0].name}!`); + }) + .catch(() => {}); // bleibt für den nächsten Versuch + } + function _closeNav() { + _recordNavWalk(); // ZUERST speichern (vor jedem Reset) if (_navWatchId !== null) { navigator.geolocation.clearWatch(_navWatchId); _navWatchId = null; } if (_navInactTimer) { clearTimeout(_navInactTimer); _navInactTimer = null; } if (_navWakeLock) { try { _navWakeLock.release(); } catch {} _navWakeLock = null; } document.removeEventListener('visibilitychange', _navOnVisibility); - if (_navWalkMeta && _navWalkMeta.trackLen > 1) { - const pct = Math.round(_navMaxIdx / (_navWalkMeta.trackLen - 1) * 100); - if (pct >= 50) { - const walkedKm = Math.round((_navMaxIdx / (_navWalkMeta.trackLen - 1)) * _navWalkMeta.totalKm * 100) / 100; - API.routes.walked(_navWalkMeta.routeId, walkedKm, pct) - .then(res => { if (res.new_badges?.length) UI.toast.success(`🏅 Neues Badge: ${res.new_badges[0].name}!`); }) - .catch(() => {}); - } - } + window.removeEventListener('pagehide', _recordNavWalk); if (_navOrientCleanup) { _navOrientCleanup(); _navOrientCleanup = null; } _navWalkMeta = null; _navMaxIdx = 0; + _navRecorded = false; _navLastBearing = null; _navCompassHeading = null; _navHeadingSmoothed = null; diff --git a/backend/static/landing.html b/backend/static/landing.html index ca06f56..b1d6c7b 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 31c38a3..733cb37 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 = '1159'; +const VER = '1160'; const CACHE_VERSION = `by-v${VER}`; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten