diff --git a/VERSION b/VERSION index 0e7f8bf..f704a68 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1203 \ No newline at end of file +1204 \ No newline at end of file diff --git a/backend/static/index.html b/backend/static/index.html index c5f862e..9f95228 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 517c478..1a4ffcf 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 = '1203'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '1204'; // ← 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/map-gl-mini.js b/backend/static/js/map-gl-mini.js index e0608a5..820f846 100644 --- a/backend/static/js/map-gl-mini.js +++ b/backend/static/js/map-gl-mini.js @@ -28,7 +28,8 @@ // Nur fitten wenn Bounds gültig UND der Container eine Größe hat (im Modal // ist er beim Erstellen 0×0 → fitBounds würde NaN werfen; der Re-Fit nach // Modal-Animation greift dann). - if (bb && !isNaN(bb.getWest()) && map.getContainer().clientWidth > 0) { + var _c = map.getContainer(); + if (bb && !isNaN(bb.getWest()) && _c.clientWidth > 0 && _c.clientHeight > 0) { var pad = 30; if (opts && opts.padding) pad = Array.isArray(opts.padding) ? opts.padding[0] : opts.padding; try { map.fitBounds(bb, { padding: pad, maxZoom: opts && opts.maxZoom, duration: 0 }); } catch (e) {} diff --git a/backend/static/js/pages/routes.js b/backend/static/js/pages/routes.js index f25a4bc..3ac5dc6 100644 --- a/backend/static/js/pages/routes.js +++ b/backend/static/js/pages/routes.js @@ -617,8 +617,7 @@ window.Page_routes = (() => { UI.map.circleMarker(lls[0], { radius:7, color:'#22C55E', fillColor:'#22C55E', fillOpacity:1, weight:2 }).addTo(_suggestMap); UI.map.circleMarker(lls.at(-1), { radius:7, color:'#EF4444', fillColor:'#EF4444', fillOpacity:1, weight:2 }).addTo(_suggestMap); _addRouteArrows(_suggestMap, track, '#3b82f6'); - const _sfit = () => { _suggestMap?.invalidateSize(); _suggestMap?.fitBounds(poly.getBounds(), { padding: [16, 16] }); }; - _sfit(); setTimeout(_sfit, 200); setTimeout(_sfit, 500); + _fitRouteMap(_suggestMap, mapEl, () => poly.getBounds()); }; _initMap(); @@ -1713,14 +1712,25 @@ window.Page_routes = (() => { stroke-linejoin="round" stroke-linecap="round"/> - - - - - -
2 Sek. halten
+ + `; document.body.appendChild(ovl); @@ -1936,23 +1946,32 @@ window.Page_routes = (() => { _navResetInactTimer(); const dim = document.getElementById('rk-nav-dim'); + // Entsperren reagiert NUR auf den Fingerabdruck-Knopf (2 Sek. halten) — nicht mehr + // auf das ganze Dim-Overlay. Tippen daneben lässt den Bildschirm bewusst gedimmt. + const navUnlock = document.getElementById('rk-nav-unlock-btn'); let _lpTimer = null; const cancelLp = () => { clearTimeout(_lpTimer); const prog = document.getElementById('rk-nav-dim-prog'); if (prog) { prog.style.transition = 'none'; prog.style.strokeDashoffset = '150.8'; } }; - dim.addEventListener('pointerdown', e => { + navUnlock.addEventListener('pointerdown', e => { e.stopPropagation(); + try { navUnlock.setPointerCapture(e.pointerId); } catch (err) {} const prog = document.getElementById('rk-nav-dim-prog'); if (prog) { prog.style.transition = 'stroke-dashoffset 2s linear'; prog.style.strokeDashoffset = '0'; } _lpTimer = setTimeout(() => { dim.style.display = 'none'; _navDimmed = false; _navResetInactTimer(); }, 2000); }); - dim.addEventListener('pointerup', cancelLp); - dim.addEventListener('pointercancel', cancelLp); - dim.addEventListener('pointerleave', cancelLp); + navUnlock.addEventListener('pointerup', cancelLp); + navUnlock.addEventListener('pointercancel', cancelLp); + // Verlässt der Finger den Knopf während des Haltens → abbrechen (sonst entsperrt + // ein wegrutschender Finger weiter). pointerleave reicht dank setPointerCapture. + navUnlock.addEventListener('pointerleave', cancelLp); + // Sicherheitsnetz: ein Tipp aufs Dim-Overlay (nicht auf den Knopf) tut nichts, + // aber wir schlucken ihn, damit darunterliegende Buttons nicht reagieren. + dim.addEventListener('pointerdown', e => { if (e.target === dim) e.stopPropagation(); }); // Aktions-Buttons document.getElementById('rk-nav-back').addEventListener('click', _closeNav); @@ -2655,6 +2674,23 @@ window.Page_routes = (() => { } } + // Karte robust auf die ganze Route fitten — auch wenn der Container beim Erstellen + // noch 0×0 ist (Modal-Animation / spätes Layout auf iOS). Feste Timeouts greifen dort + // oft zu früh; der ResizeObserver fittet erneut, SOBALD der Container seine endgültige + // Größe hat. Das war der Grund, warum die Detail-/Vorschlag-Karte auf dem Gerät beim + // Start-Zoom (zoom 14, center=Start) hängen blieb statt auf die Route zu zoomen. + function _fitRouteMap(m, el, getBounds, opts) { + opts = opts || { padding: [16, 16] }; + const fit = () => { try { m.invalidateSize(); m.fitBounds(getBounds(), opts); } catch (e) {} }; + fit(); + setTimeout(fit, 150); setTimeout(fit, 400); + if (window.ResizeObserver && el) { + const ro = new ResizeObserver(() => { if (el.clientWidth > 0 && el.clientHeight > 0) fit(); }); + ro.observe(el); + setTimeout(() => { try { ro.disconnect(); } catch (e) {} }, 4000); + } + } + async function _buildDetailMap(el, track) { const lls = track.map(p => [p.lat, p.lon]); const m = await UI.map.create(el, { @@ -2665,11 +2701,7 @@ window.Page_routes = (() => { _addRouteArrows(m, track, '#3b82f6'); UI.map.circleMarker(lls[0], { radius:7, color:'#22C55E', fillColor:'#22C55E', fillOpacity:1 }).addTo(m); UI.map.circleMarker(lls.at(-1), { radius:7, color:'#EF4444', fillColor:'#EF4444', fillOpacity:1 }).addTo(m); - // Mehrfach fitten: beim Erstellen ist der Modal-Container evtl. noch 0×0 → nach - // der Animation (resize) erneut auf die ganze Route zoomen. - const _fit = () => { m.invalidateSize(); m.fitBounds(poly.getBounds(), { padding:[16,16] }); }; - _fit(); - setTimeout(_fit, 200); setTimeout(_fit, 500); + _fitRouteMap(m, el, () => poly.getBounds()); return m; } diff --git a/backend/static/landing.html b/backend/static/landing.html index d6a841b..ef1a7ff 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 d77c914..6ea395b 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 = '1203'; +const VER = '1204'; const CACHE_VERSION = `by-v${VER}`; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten