René: 'geclusterte Marker haben kein Symbol'. Jeder Cluster ist genau eine Kategorie →
weißes Phosphor-Icon in der Cluster-Mitte (icon-size skaliert mit point_count). Keine Glyphs nötig.
René: 'wo die Karten-Buttons auftauchen ist eine Zone die Kartenbedienung verhindert'.
.map-statusbar/.map-speed-dial/.map-crosshair/.map-search-wrap(inactive)/.map-rec-panel(inactive)
fingen Touch in ihrer Bounding-Box → tote Zonen. Jetzt pointer-events:none, nur Buttons auto.
Erklärt die 'reagiert nach kurzer Zeit'-Verzögerung (Drag startet tot, greift erst im Karten-Bereich).
Gilt für beide Engines (verbessert auch Leaflet).
- _loadOsmLayers: kein _map.resize() für GL (löste move→moveend→scan-Loop aus, 'pausenlos')
- _initMapGL: touchZoomRotate.disableRotation() + touchPitch.disable() + container touch-action:none
→ Pinch=reines Zoom, bleibt in der Karte (kein iOS-Page-Zoom), keine ungewollte Drehung
Eigenständiges Modul: per-Kategorie GeoJSON-Cluster, rasterisierte Phosphor-Icons,
Danger-Polygone, Sichtbarkeit, Click→Popup. /maplibre-markers-test zum headless-Verifizieren
VOR dem Einbau in map.js (auth-gated).
map.js: _useGL()/loadMapLibre()/_initMapGL() + engine-neutrale Facade
(_mapFlyTo/_mapSetView/_mapGetZoom/_mapResize/_mapGetCenter/_mapPaddedBounds, kapselt
[lat,lon]↔[lng,lat]). init() verzweigt auf GL bei Flag 'by_map_gl'/?mapgl=1. Basemap+
Controls+Dark(setStyle)+Scan-Wiring+Crosshair. POI-Layer/Marker = Runde 2. Flag default AUS.
Main-Thread-Rendering von protomaps-leaflet + App-Map-Logik blockiert UI-Thread.
Greift auch bei localStorage-Flag=1. Performance erst lösen, dann reaktivieren.
Spike-Befund: app-weit kommen nur 200 ohne Accept-Ranges zurück, FileResponse-Range
wird von der BaseHTTPMiddleware gebrochen. MapLibre/pmtiles braucht aber Byte-Ranges.
Route gibt 206 als normales Response (Byte-Slice) zurück. Produktion: nginx/NPM direkt.
Der alte delete_account löschte nur ~6 Tabellen und scheiterte am finalen
DELETE FROM users, sobald der User Zeilen in Non-Cascade-Tabellen hatte
(routes, places, walks, events, forum_threads, invoices …). Jetzt:
PRAGMA defer_foreign_keys + foreign_key_list-Introspektion (CASCADE automatisch,
NO-ACTION-Eigentum löschen, Actor/SET-NULL-Spalten nullen) plus user_id/owner_id-
Scan für Tabellen ohne formale FK. Regressionstest test_account_deletion.py.
Relevant für App-Store-Gl. 4 (In-App-Konto-Löschung muss zuverlässig funktionieren).
Statischer Hinweis präzisiert + Toast beim ersten Ausblenden: Funktion bleibt
über 'Weitere Funktionen' (Ausgeblendete Funktionen) abrufbar, wird nicht gelöscht. SW v1173
Falsche Feldnamen: dog.gewicht/dog.alter existieren nicht. Korrekt: gewicht_kg,
und Alter aus geburtstag berechnen (_alterJahre). Beide werden jetzt im Kalorien-
Rechner vorbefüllt. SW v1171
ernaehrung.js: Das Profil (Marke, Portionen, Notizen, Futter-Typ) wird beim Laden
direkt angezeigt, wenn gespeicherte Daten existieren — vorher nur nach Klick auf
'Kalorienbedarf berechnen'. Gespeicherter Tagesbedarf (kcal_tag) wird 1:1 wieder
gezeigt (kein Neu-Rechnen). _berechne → _showResult refaktoriert. SW v1170
Werben-Zuordnung ging verloren, wenn die geworbene Person den ?ref=-Link öffnete,
die App schloss und sich erst später registrierte (sessionStorage flüchtig, v.a.
iOS-PWA). Jetzt: Code früh in boot.js nach localStorage (vor evtl. SW-Reload, der
die URL ersetzt), 30-Tage-Ablauf, Löschung nach Registrierung. SW v1169
Datenkorrektur separat: nacho_sarah Angie (id=4) als 2. Werbung zugeordnet.
UI.ratingStars lud die Bewertungen via API, speicherte/renderte das ratings-Array
aber nie — nur Durchschnitt+Anzahl. Jetzt wird die Liste aller Bewertungen mit
Kommentar (Name + Sterne + Text) angezeigt; nach dem Speichern neu geladen.
Backend war korrekt. SW v1168
Stufe 1 (Guard): Während aktiver Aufzeichnung wird der SW-/Force-Update-Reload
aufgeschoben (window._byRecording → boot.js/_bySwReload + app.js force-update);
nach Stop/Speichern via window._byReloadIfPending() nachgeholt.
Stufe 2 (Persistenz): Track wird gedrosselt nach localStorage (RecStore) gesichert
und beim nächsten Öffnen der Karten-/Routen-Seite als 'Aufzeichnung fortsetzen?'
angeboten (Resume seedet Track+km+Startzeit). Schützt auch bei Crash/OS-Kill/
manuellem Reload. Greift in map.js UND routes.js. SW v1167
_check_post_limits verglich datetime.utcnow() (UTC) mit created_at, das aber
als Client-Lokalzeit (CEST=UTC+2) gespeichert wird → diff negativ → immer 429.
Jetzt rechnen Cooldown + Stunden-Limit in derselben Zeitbasis (Client-Zeit des
Requests). Backend-only, kein SW-Bump.
- update_streak NICHT mehr in GET /achievements/me (lief bei jedem Öffnen).
Streak wird nur noch von create_route (aufnehmen) + record_walk (ablaufen)
hochgezählt.
- Angezeigter Streak korrekt: nur gültig wenn letzte Tour heute/gestern war,
sonst 0 (gerissen) — ohne den gespeicherten Wert zu verändern.
- record_walk gibt total_km zurück → Toast "🐾 X km gezählt · Lebenswerk Y km".
- Nachtrag-Toast (_flushPendingNavWalk) zeigt ebenfalls km + Lebenswerk.
- list_routes liefert my_walk_count + my_last_walked → Routen-Karte zeigt
"🐾 X× gelaufen · zuletzt heute/gestern/vor N Tagen".
Macht für Angie sichtbar, dass das Ablaufen einer gespeicherten Route mitzählt.
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.
- /osm-auth/status liefert signup_url + sandbox-Flag (Sandbox-URL auf Staging,
echte OSM in Prod).
- Settings-OSM-Karte: ausklappbare Hilfe "Noch kein OSM-Konto? Was ist das?"
mit Erklärung, 3-Schritt-Anleitung, Sandbox-Testphasen-Hinweis und
"Kostenloses OSM-Konto erstellen"-Link zur richtigen Instanz.
- dog=no zusätzlich zu dog=yes (Pächterwechsel → Ort nicht mehr hundefreundlich).
- Map-Popup: ein "Hund willkommen?"-Block mit Daumen hoch/runter statt zwei
Buttons. Beide rufen /dog-friendly mit welcome=true|false.
- Backend generisch: tag_value yes|no; vorhandene Markierung mit anderem Wert
wird umgedreht (Update statt 409); submit_dog_tag(value); Confirm/Revert prüft
gegen den jeweiligen tag_value; Changeset-Kommentar wertabhängig.
Apple-Reviewer braucht eine publik erreichbare Support-URL. Die
SPA-Hilfeseite (/#hilfe) ist hinter dem Welcome-Overlay für nicht
angemeldete User versteckt. Neue /help-Route rendert serverseitig:
- Holt aktive FAQ-Artikel aus help_articles (über bestehendes
TTL-Cache _load_active_help_articles).
- Gruppiert nach Kategorie mit deutschen Labels.
- Native HTML5 <details>/<summary> Akkordeon — kein JS nötig.
- Dark Mode via prefers-color-scheme.
- Direkter mailto support@banyaro.app + Verweis auf die volle
Hilfe nach Login.
Damit haben wir https://banyaro.app/help als Support-URL für App
Store Connect.
Bug: daily_photo_cache zeigte auf gelöschte Tagebuch-Foto-URLs, weil
Löschen eines Eintrags oder einzelnen Medien-Items den Cache nicht
mit-bereinigte. Heim-Tab in der iOS-App lud dann 404 → kein Tagesbild.
Fix in dogs.py /welcome-dashboard:
- Bevor das Cache-Foto zurückgegeben wird, prüfen ob die URL noch in
diary_media existiert. Wenn nicht: Cache-Eintrag löschen und neu
wählen → selbstheilend für alte verwaiste Einträge.
Fix in diary.py:
- delete_diary: vor dem CASCADE-Delete von diary_media die URLs
sammeln und alle daily_photo_cache-Zeilen darauf löschen.
- delete_media_item: gleicher Cleanup für die eine URL.
Cache ist klein (max 1 Eintrag pro Hund pro Tag) — Hygiene-Cleanup
ist günstig und macht das System defensiv.