diff --git a/VERSION b/VERSION index 96a2d52..c63ea9e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1237 \ No newline at end of file +1238 \ No newline at end of file diff --git a/backend/static/index.html b/backend/static/index.html index fb7fa45..2731405 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 37a62d1..6ad2b32 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 = '1237'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '1238'; // ← 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 5afd176..695d175 100644 --- a/backend/static/js/map-gl-mini.js +++ b/backend/static/js/map-gl-mini.js @@ -190,6 +190,13 @@ if (this._map && this._map.getSource(this._id)) this._map.getSource(this._id).setData(this._geo()); return this; }, + // Leaflet-kompatibel: Punkt anhängen (Live-Track bei Routen-Aufzeichnung). + // FEHLTE bis 2026-06-08 → TypeError im Rec-Overlay = Strecke blieb unsichtbar. + addLatLng: function (ll) { + this._latlngs.push(ll); + if (this._map && this._map.getSource(this._id)) this._map.getSource(this._id).setData(this._geo()); + return this; + }, // Leaflet-kompatibel: Array von {lat,lng} (für fitBounds-Sammlung). getLatLngs: function () { return this._latlngs.map(function (p) { return (p && p.lat != null) ? { lat: p.lat, lng: p.lng } : { lat: p[0], lng: p[1] }; }); }, getBounds: function () { return { _coords: this._latlngs.map(function (p) { return (p && p.lat != null) ? [p.lat, p.lng] : p; }) }; }, diff --git a/backend/static/js/pages/map.js b/backend/static/js/pages/map.js index 6fd35bb..081d80e 100644 --- a/backend/static/js/pages/map.js +++ b/backend/static/js/pages/map.js @@ -17,6 +17,7 @@ window.Page_map = (() => { let _tileLayer = null; let _usingVector = false; // true wenn Vektor-Basemap (PMTiles) statt OSM-Raster let _engineGL = false; // true wenn MapLibre GL statt Leaflet (zentrale Karte) + let _followGps = false; // Karte folgt dem Standort (Standort-Button an, Drag aus) let _maplibreLoaded = false; let _glLayersReady = false; // GL: POI-Sources/Layer angelegt? let _pmtilesProtoReg = false; // pmtiles-Protokoll bei MapLibre registriert? @@ -364,6 +365,10 @@ window.Page_map = (() => { _sdEl?.classList.remove('open'); if (_userPos) { _mapSetView(_userPos.lat, _userPos.lon, 16); + // Follow-Mode (René 2026-06-08): Karte wandert ab jetzt mit dem Standort; + // manuelles Verschieben beendet das Folgen (dragstart-Listener im Map-Init). + _followGps = true; + UI.toast.info('Karte folgt deinem Standort — zum Beenden Karte verschieben.'); } else { UI.toast.error('Standort noch nicht verfügbar.'); } @@ -744,6 +749,7 @@ window.Page_map = (() => { window.addEventListener('resize', () => _map.invalidateSize()); _map.on('moveend zoomend', () => { _autoRetryCount = 0; _updateZoomDisplay(); _scheduleOsmLoad(); }); + _map.on('dragstart', () => { _followGps = false; }); // manuelles Verschieben beendet Follow setTimeout(() => { _updateZoomDisplay(); _scheduleOsmLoad(); }, 800); // Fadenkreuz-Animation beim Kartenverschieben @@ -921,6 +927,7 @@ window.Page_map = (() => { _map.on('movestart', () => { document.getElementById('map-crosshair')?.classList.add('dragging'); }); + _map.on('dragstart', () => { _followGps = false; }); // manuelles Verschieben beendet Follow window.addEventListener('resize', _mapResize); setTimeout(_mapResize, 100); @@ -1070,6 +1077,7 @@ window.Page_map = (() => { _locationMarker = new maplibregl.Marker({ element: elx, anchor: 'center' }) .setLngLat([lon, lat]).addTo(_map); } + if (_followGps && !_recActive) _map.easeTo({ center: [lon, lat], duration: 600 }); }, () => {}, { enableHighAccuracy: true, maximumAge: 5000, timeout: 15000 } @@ -1102,6 +1110,7 @@ window.Page_map = (() => { icon, zIndexOffset: 500, interactive: false, }).addTo(_map); } + if (_followGps && !_recActive) _map.panTo([lat, lon]); }, () => {}, { enableHighAccuracy: true, maximumAge: 5000, timeout: 15000 } @@ -2564,6 +2573,7 @@ window.Page_map = (() => { document.addEventListener('visibilitychange', _onVisibilityChange); _recTimerInt = setInterval(_updateRecStatus, 1000); + _followGps = true; // Aufzeichnung startet im Follow-Mode (Drag pausiert, Standort-Button reaktiviert) _recWatchId = navigator.geolocation.watchPosition( pos => { @@ -2690,7 +2700,7 @@ window.Page_map = (() => { if (_engineGL) { _recTrackGL(); _updateRecMarker(lat, lon); - _map.panTo([lon, lat]); // MapLibre: [lng,lat] + if (_followGps) _map.panTo([lon, lat]); // MapLibre: [lng,lat] — Drag pausiert Follow return; } if (!window.L) return; @@ -2701,7 +2711,7 @@ window.Page_map = (() => { _recPolyline.addLatLng(ll); } _updateRecMarker(lat, lon); - _map.panTo(ll); + if (_followGps) _map.panTo(ll); } function _updateRecStatus() { diff --git a/backend/static/js/pages/routes.js b/backend/static/js/pages/routes.js index 220496d..f7dfeca 100644 --- a/backend/static/js/pages/routes.js +++ b/backend/static/js/pages/routes.js @@ -70,6 +70,7 @@ window.Page_routes = (() => { // Recording-Overlay state let _recOvl = null, _recMap = null; + let _recFollow = true; // Karte folgt dem Standort bei Aufzeichnung (Drag pausiert) let _recActive = false; let _recTrack = [], _recDistKm = 0, _recStartTime = null; let _recTimerInt = null, _recWatchId = null; @@ -762,6 +763,29 @@ window.Page_routes = (() => { _recLocMarker = UI.map.circleMarker([pos.lat, pos.lon], { radius: 8, color: '#fff', weight: 2.5, fillColor: '#3b82f6', fillOpacity: 1 }).addTo(_recMap); + // Follow-Mode (René 2026-06-08): Karte wandert mit dem Standort. Manuelles + // Verschieben pausiert das Folgen; Crosshair-Button schaltet es wieder ein. + _recFollow = true; + const fwrap = ovl.querySelector('#rk-rec-map-wrap'); + if (!fwrap.style.position) fwrap.style.position = 'relative'; + const fb = document.createElement('button'); + fb.id = 'rk-rec-follow'; + fb.type = 'button'; + fb.title = 'Karte folgt dem Standort'; + fb.style.cssText = 'position:absolute;right:10px;bottom:10px;z-index:500;width:42px;height:42px;' + + 'border-radius:50%;border:none;background:var(--c-surface,#fff);' + + 'box-shadow:0 2px 8px rgba(0,0,0,.3);display:flex;align-items:center;justify-content:center;cursor:pointer'; + fb.innerHTML = UI.icon('crosshair'); + const updFb = () => { fb.style.color = _recFollow ? 'var(--c-primary)' : 'var(--c-text-secondary, #9ca3af)'; }; + updFb(); + fb.addEventListener('click', () => { + _recFollow = true; + const last = _recTrack[_recTrack.length - 1] || pos; + _recMap?.setView([last.lat, last.lon]); + updFb(); + }); + fwrap.appendChild(fb); + try { _recMap.on('dragstart', () => { _recFollow = false; updFb(); }); } catch (e) {} } catch { const mapWrap = ovl.querySelector('#rk-rec-map-wrap'); if (mapWrap) mapWrap.innerHTML = @@ -892,7 +916,9 @@ window.Page_routes = (() => { _persistRec(); _recPolyline?.addLatLng([lat, lon]); _recLocMarker?.setLatLng([lat, lon]); + // Follow-Mode: Karte wandert mit (erster Fix setzt den Zoom, danach bleibt er) if (_recTrack.length === 1) _recMap?.setView([lat, lon], 16); + else if (_recFollow) _recMap?.setView([lat, lon]); _updateRecStats(); }, () => {}, { enableHighAccuracy: true, maximumAge: 2000 }); diff --git a/backend/static/landing.html b/backend/static/landing.html index 956917e..58e10c5 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 c3fa9b5..e3f8052 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 = '1237'; +const VER = '1238'; const CACHE_VERSION = `by-v${VER}`; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten