Fix: gelaufene km bei Routen-Navigation gehen verloren wenn nicht über

In-App-Zurück geschlossen wird (Angie-Bug)

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.
This commit is contained in:
rene 2026-06-04 09:09:07 +02:00
parent 684ffa3b46
commit 91624dac25
6 changed files with 70 additions and 26 deletions

View file

@ -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;

View file

@ -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;