From f2856b8acbc4947b245ef5d128ee951840d6e37b Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 15 May 2026 17:44:59 +0200 Subject: [PATCH] =?UTF-8?q?Fix:=20Lost=20=E2=80=94=20Puls-Animation=20(box?= =?UTF-8?q?-shadow),=20false-offline,=20Pending-Guard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Pulsierender Marker: Wechsel von position:absolute-Ring auf box-shadow-Animation (by-lost-pulse-r/p), kein Overflow-Problem mit Leaflet divIcon, iOS-kompatibel - navigator.onLine iOS-Falsch-Positiv: Formular-Submit versucht API zuerst, fällt nur bei TypeError (fetch failed) auf Pending-Modus zurück - _openDetail(): früher Return für Pending-Einträge (verhindert delete mit string-ID "pending_..." → Backend-Fehler "unable to parse integer") - SW by-v991, APP_VER 991 --- backend/main.py | 2 +- backend/static/js/app.js | 2 +- backend/static/js/pages/lost.js | 62 +++++++++++++++++---------------- backend/static/sw.js | 2 +- 4 files changed, 35 insertions(+), 33 deletions(-) diff --git a/backend/main.py b/backend/main.py index bc3f106..ccf969b 100644 --- a/backend/main.py +++ b/backend/main.py @@ -410,7 +410,7 @@ async def serve_media(path: str, request: _Request): raise _HE(404, "Nicht gefunden.") return _media_response(filepath) -APP_VER = "990" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "991" # muss mit APP_VER in app.js übereinstimmen @app.get("/.well-known/assetlinks.json") async def assetlinks(): diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 4cd3b61..7fdd413 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 = '990'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '991'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt const IS_STAGING = location.hostname === 'staging.banyaro.app'; // Cache-Bust-Parameter nach Update-Reload sofort entfernen diff --git a/backend/static/js/pages/lost.js b/backend/static/js/pages/lost.js index e4be5d8..6f8fe0c 100644 --- a/backend/static/js/pages/lost.js +++ b/backend/static/js/pages/lost.js @@ -60,10 +60,13 @@ window.Page_lost = (() => { _stylesInjected = true; const s = document.createElement('style'); s.textContent = ` - @keyframes by-lost-ping { - 0% { transform: scale(0.9); opacity: 0.7; } - 70% { transform: scale(2.2); opacity: 0; } - 100% { transform: scale(2.2); opacity: 0; } + @keyframes by-lost-pulse-r { + 0%,100% { box-shadow: 0 0 0 0 rgba(231,76,60,.55), 0 2px 6px rgba(0,0,0,.3); } + 50% { box-shadow: 0 0 0 11px rgba(231,76,60,0), 0 2px 6px rgba(0,0,0,.3); } + } + @keyframes by-lost-pulse-p { + 0%,100% { box-shadow: 0 0 0 0 rgba(217,119,6,.55), 0 2px 6px rgba(0,0,0,.3); } + 50% { box-shadow: 0 0 0 11px rgba(217,119,6,0), 0 2px 6px rgba(0,0,0,.3); } } `; document.head.appendChild(s); @@ -165,6 +168,7 @@ window.Page_lost = (() => { // KARTE INITIALISIEREN // ---------------------------------------------------------- function _initMap() { + _injectStyles(); const mapEl = document.getElementById('lost-map'); if (!mapEl || !window.L || _map) return; @@ -293,29 +297,21 @@ window.Page_lost = (() => { // ---------------------------------------------------------- function _renderMarkers() { if (!_map || !window.L) return; - _injectStyles(); _markers.forEach(m => _map.removeLayer(m)); _markers = []; _reports.forEach(r => { - const dotColor = r._isPending ? '#d97706' : '#e74c3c'; - const ringColor = r._isPending ? 'rgba(217,119,6,0.35)' : 'rgba(231,76,60,0.35)'; + const dotColor = r._isPending ? '#d97706' : '#e74c3c'; + const anim = r._isPending ? 'by-lost-pulse-p' : 'by-lost-pulse-r'; const icon = L.divIcon({ className : '', - html : ` -
-
-
🐕
-
`, - iconSize : [44, 44], - iconAnchor : [22, 22], + font-size:17px;border:2px solid #fff; + animation:${anim} 1.8s ease-in-out infinite">🐕`, + iconSize : [34, 34], + iconAnchor : [17, 17], }); const distStr = r.distanz_m !== undefined @@ -472,6 +468,7 @@ window.Page_lost = (() => { // DETAIL-MODAL // ---------------------------------------------------------- function _openDetail(r) { + if (r._isPending) return; // Pending-Einträge haben keine Server-ID const isOwn = _appState.user && _appState.user.id === r.user_id; const isAdmin = _appState.user?.rolle === 'admin'; const distStr = r.distanz_m !== undefined @@ -754,19 +751,24 @@ window.Page_lost = (() => { client_time : API.clientNow(), }; - if (!navigator.onLine) { - const pending = _addPending(payload); - pending.distanz_m = _userPos - ? Math.round(_haversine(_userPos.lat, _userPos.lon, pending.lat, pending.lon)) - : 0; - UI.modal.close(); - UI.toast.success('Offline gespeichert — wird synchronisiert sobald Verbindung besteht.'); - _loadReports(); - return; + let created; + try { + created = await API.lost.report(payload); + } catch (netErr) { + // Netzwerkfehler (TypeError = fetch failed) → offline speichern + if (netErr instanceof TypeError || !navigator.onLine) { + const pending = _addPending(payload); + pending.distanz_m = _userPos + ? Math.round(_haversine(_userPos.lat, _userPos.lon, pending.lat, pending.lon)) + : 0; + UI.modal.close(); + UI.toast.success('Offline gespeichert — wird synchronisiert sobald Verbindung besteht.'); + _loadReports(); + return; + } + throw netErr; // API-Fehler (z.B. 422) → weitergeben } - const created = await API.lost.report(payload); - // Foto hochladen if (photoInput?.files[0]) { try { diff --git a/backend/static/sw.js b/backend/static/sw.js index ab84e7a..d451c5b 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -3,7 +3,7 @@ Offline-Cache + Push Notifications + Tile-Cache ============================================================ */ -const CACHE_VERSION = 'by-v990'; +const CACHE_VERSION = 'by-v991'; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache