diff --git a/VERSION b/VERSION index b0ba9ce..0d30c07 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1188 \ No newline at end of file +1189 \ No newline at end of file diff --git a/backend/static/index.html b/backend/static/index.html index 686e0c9..e5fc107 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 ea58fb5..47ca74f 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 = '1188'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '1189'; // ← 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-markers.js b/backend/static/js/map-gl-markers.js index 789d433..64972a2 100644 --- a/backend/static/js/map-gl-markers.js +++ b/backend/static/js/map-gl-markers.js @@ -14,6 +14,7 @@ var _onClick = null; // (props, key) -> true = Klick behandelt, Popup unterdrücken var _activePopup = null; var _dangerKeys = []; + var _clickBound = {}; // Click/Hover-Handler pro Kategorie nur EINMAL binden function _empty() { return { type: 'FeatureCollection', features: [] }; } @@ -112,27 +113,37 @@ } }); } if (!_map.getLayer('clsym-' + key)) { - // Weißes Kategorie-Icon mittig auf dem Cluster-Kreis (Symbol für den Cluster). + // Anzahl als weiße Zahl mittig auf dem Cluster (braucht Glyphs aus dem Style). _map.addLayer({ id: 'clsym-' + key, type: 'symbol', source: src, filter: ['has', 'point_count'], layout: { - 'icon-image': 'cli-' + key, 'icon-allow-overlap': true, 'icon-ignore-placement': true, - 'icon-size': ['step', ['get', 'point_count'], 0.5, 10, 0.62, 50, 0.78], - } }); + 'text-field': ['get', 'point_count_abbreviated'], + 'text-font': ['Open Sans Regular'], + 'text-size': ['step', ['get', 'point_count'], 12, 100, 14, 1000, 16], + 'text-allow-overlap': true, 'text-ignore-placement': true, + }, + paint: { 'text-color': '#ffffff', 'text-halo-color': 'rgba(52,68,36,0.55)', 'text-halo-width': 1 } }); } if (!_map.getLayer('pt-' + key)) { _map.addLayer({ id: 'pt-' + key, type: 'symbol', source: src, filter: ['!', ['has', 'point_count']], layout: { 'icon-image': 'poi-' + key, 'icon-allow-overlap': true, 'icon-ignore-placement': true, 'icon-size': 0.9 } }); } - // Click: Einzel-POI → Popup; Cluster → reinzoomen - _map.on('click', 'pt-' + key, function (e) { _onPoiClick(e, key); }); - _map.on('click', 'cl-' + key, function (e) { - var f = e.features[0]; - _map.getSource(src).getClusterExpansionZoom(f.properties.cluster_id, function (err, z) { - if (!err) _map.easeTo({ center: f.geometry.coordinates, zoom: z }); + // Click/Hover NUR EINMAL binden (Handler überleben setStyle/Theme-Wechsel, + // sind an die Layer-ID gebunden → sonst doppelte Popups nach Theme-Switch). + if (!_clickBound[key]) { + _clickBound[key] = true; + _map.on('click', 'pt-' + key, function (e) { _onPoiClick(e, key); }); + _map.on('click', 'cl-' + key, function (e) { + var f = e.features[0]; + var s = _map.getSource('poi-' + key); + if (s && s.getClusterExpansionZoom) { + s.getClusterExpansionZoom(f.properties.cluster_id, function (err, z) { + if (!err) _map.easeTo({ center: f.geometry.coordinates, zoom: z }); + }); + } }); - }); - _map.on('mouseenter', 'pt-' + key, function () { _map.getCanvas().style.cursor = 'pointer'; }); - _map.on('mouseleave', 'pt-' + key, function () { _map.getCanvas().style.cursor = ''; }); + _map.on('mouseenter', 'pt-' + key, function () { _map.getCanvas().style.cursor = 'pointer'; }); + _map.on('mouseleave', 'pt-' + key, function () { _map.getCanvas().style.cursor = ''; }); + } }); // Danger-Radius-Layer (poison/giftkoeder), unter den Markern. diff --git a/backend/static/js/pages/map.js b/backend/static/js/pages/map.js index 584b4f7..b9d7120 100644 --- a/backend/static/js/pages/map.js +++ b/backend/static/js/pages/map.js @@ -422,6 +422,8 @@ window.Page_map = (() => { let _radarTimer = null; let _tempLayer = null; let _tempActive = false; + let _tempUrl = null; // GL: zum Re-Add nach Theme-Wechsel (setStyle löscht Raster-Layer) + let _tempMaxZoom = 18; let _tempMarkers = []; let _tempDebounce = null; @@ -466,7 +468,8 @@ window.Page_map = (() => { btn?.classList.add('active'); try { const cfg = await API.get('/weather/layer-tiles?layer=temp_new'); - _tempLayer = _wxAddRaster('temp', cfg.url, 1.0, cfg.maxNativeZoom ?? 18); + _tempUrl = cfg.url; _tempMaxZoom = cfg.maxNativeZoom ?? 18; + _tempLayer = _wxAddRaster('temp', _tempUrl, 1.0, _tempMaxZoom); _showTempLegend(); _mapOnMove(_debounceTempLabels); await _loadTempLabels(); @@ -650,12 +653,16 @@ window.Page_map = (() => { // MapLibre-GL-Engine (zentrale Karte) — GPU/Worker, performant. // Flag-gated; Raster-Leaflet bleibt Default. [lng,lat]-Reihenfolge! // ========================================================== - // Flag: ?mapgl=1/0 → localStorage 'by_map_gl'. Default AUS (bis vollständig portiert). + // Flag: ?mapgl=1/0 → localStorage 'by_map_gl'. Default: auf Staging AN (Breitentest), + // auf Produktion AUS (bis Freigabe). Explizit per Flag überschreibbar. function _useGL() { try { const u = new URLSearchParams(location.search); if (u.has('mapgl')) localStorage.setItem('by_map_gl', u.get('mapgl') === '0' ? '0' : '1'); - return localStorage.getItem('by_map_gl') === '1'; + const flag = localStorage.getItem('by_map_gl'); + if (flag === '1') return true; + if (flag === '0') return false; + return /(^|\.)staging\.banyaro\.app$/.test(location.hostname); } catch (e) { return false; } } @@ -808,9 +815,14 @@ window.Page_map = (() => { _glLayersReady = false; _map.setStyle(MapGLStyle.build({ dark: _isDarkMode() })); // setStyle entfernt eigene Sources/Layer → nach Style-Load neu anlegen + Daten neu setzen. + // (DOM-basierte maplibregl.Marker — Standort/Temp-Pillen/Rec-Dot — überleben setStyle.) _map.once('styledata', () => { _initPoiLayersGL(); Object.keys(TYPEN).forEach(_glPushLayer); + // Wetter-Raster + Rec-Track waren Style-Layer → neu anlegen, falls aktiv. + if (_radarActive) _loadRadar(); + if (_tempActive && _tempUrl) _tempLayer = _wxAddRaster('temp', _tempUrl, 1.0, _tempMaxZoom); + if (_recActive && _recTrack.length) _recTrackGL(); _scheduleOsmLoad(); }); } diff --git a/backend/static/landing.html b/backend/static/landing.html index 382560f..2d6215c 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 558ccf0..795b49c 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 = '1188'; +const VER = '1189'; const CACHE_VERSION = `by-v${VER}`; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten