User-Feedback: 600ms Long-Press triggert iOS-Textauswahl.
- Trigger-Dauer von 600ms auf 350ms verkürzt — feuert vor iOS-
Auswahl-Callout (typisch ~500ms)
- Am #worlds-fab user-select:none, -webkit-user-select:none,
-webkit-touch-callout:none — verhindert dass iOS bei Press ein
Lupensymbol/Auswahl-Menü öffnet
- Step 4 wieder strikt: alle 3 (expenses + routes + notes) müssen
im Cache sein, sonst weiß. 5/5 Braun = wirklich alles bereit.
- Long-Press (600ms) auf #worlds-fab öffnet das Status-Modal mit
detaillierter 5-Punkte-Checkliste + 'Fehlende nachladen'-Button.
Normaler Click bleibt unverändert (FAB-Schnellaktion).
Wenn Long-Press feuert, wird nachfolgender Click unterdrückt.
- _bindLongPress wird mehrfach gebunden falls FAB neu gerendert
wird (data-lpBound verhindert Doppel-Binding).
Step 4 verlangte alle 3 von expenses/routes/notes — einer fehlte
(vermutlich notes 401 weil beim Prefetch noch nicht eingeloggt) →
Pfote weiß. Jetzt reichen 2 von 3.
Außerdem läuft _prefetchData() nicht nur beim Init + 2/5/10/20s-
Retries, sondern auch alle 60s mit refresh() — falls Login erst
spät erfolgt, kommt die fehlende API beim nächsten Tick.
Beide nutzen jetzt by_last_position aus localStorage:
- wetter.js: speichert jede erfolgreiche GPS-Position; bei GPS-Fehler
(kein Netz, Permission verweigert) wird der letzte bekannte Ort
als Fallback genutzt — kein Error-Banner mehr wenn man ihn schon
einmal hatte
- offline-indicator.js: _prefetchTiles versucht erst GPS, fällt
dann auf den gespeicherten letzten Ort zurück → Step 5 wird auch
ohne aktive GPS-Permission grün, sobald Wetter (oder andere
Module) einmal eine Position eingeloggt haben
- TILE_MIN von 50 auf 20 gesenkt — 5x4 Tiles reichen für eine
brauchbare Offline-Karte im Nahbereich
Filled-Farbe der Pfoten-Linien von #16a34a (grün) auf #5C3517
(dunkles Ban Yaro Braun) — passt zum Brand statt fremder
Signalfarbe, klar erkennbar auf orangem FAB.
Step 4 der Offline-Pfote (Weitere Listen) blieb weiß weil
/api/notes nicht in _CACHEABLE_GET stand — Notes-Requests wurden
vom SW gar nicht gecacht, egal wie oft sie geladen wurden.
Regex /^\/api\/notes/ ergänzt — jetzt cached der SW Notes-GETs
mit Stale-While-Revalidate (default 5min TTL).
Steps wurden nicht grün weil Probes zu strikt waren (alle 7 Module
bzw. 3 URL-Patterns erforderlich) — Cache-Inhalt zum Refresh-
Zeitpunkt oft unvollständig.
- Step 2 toleriert 1 fehlendes Page-Modul (have >= want-1)
- Step 3 verlangt nur noch Profil ODER welcome-dashboard PLUS Diary
ODER Health (nicht beides)
- Neuer _prefetchPages() lädt alle 10 Page-Module proaktiv beim
App-Start — unabhängig von SW-Install-Status
- _prefetchData() wird jetzt mehrmals retried (2s, 5s, 10s, 20s),
damit hund-spezifische Daten geholt werden sobald
_appState.activeDog gesetzt ist
Steps so umverteilt dass sie genau die Datentypen abdecken die der
User offline braucht — auch wenn er die Seiten nie geöffnet hat:
1 App-Grundgerüst CSS + Core-JS
2 Wichtige Seiten alle 10 Page-Module (precached via SW)
3 Hund-Daten Profil + Tagebuch + Gesundheit
4 Weitere Listen Ausgaben + Routen + Notizen
5 Karten-Kacheln OSM-Tiles im Umkreis
Automatischer Prefetch im _prefetchData() beim App-Start:
- /api/expenses · /api/routes · /api/notes (Step 4)
- /api/dogs/{id}/health · /api/dogs/{id}/diary (Step 3)
- Tiles via _prefetchTiles wenn GPS-Permission da (Step 5)
Wiki, Übungen, Streak, Wetter werden NICHT mehr vorgeladen — kommen
beim normalen Welten-Besuch ins Cache, sind aber nicht Pflicht für
'offline-bereit'.
setTimeout-Retry nach 3s: aktiver Hund ist beim ersten Init oft
noch nicht in _appState, danach kommt der Health/Diary-Prefetch.
- Step 5 misst jetzt 'Welt-Daten' (Streak + Wetter) statt
Wiki/Übungen — die kommen automatisch beim Welten-Aufruf, kein
zusätzliches Prefetch nötig (User-Wunsch: Wiki+Übungen NICHT
preloaden)
- Neuer _prefetchTiles(): beim App-Start werden 49+9 OSM-Tiles
(Zoom 14 +13) im 3km-Umkreis automatisch gecacht — aber NUR wenn
GPS-Permission schon erteilt ist (kein nerviger Popup beim
Start). Damit wird Step 4 nach kurzer Zeit grün.
- _fetchMissing für Step 5 lädt jetzt Streak + Wetter (statt Wiki)
PRIORITY_PAGES erweitert auf 10 Seiten (war 8): zusätzlich
health.js, notes.js, expenses.js. admin.js raus — 233 KB, offline
irrelevant. Damit funktionieren offline ohne vorherigen Besuch:
Tagebuch · Gesundheit · Karte · Gassi · Erste Hilfe · Notizblock
Ausgaben · Routen · Giftköder · Vermisst.
Offline-Indikator Step 2 prüft jetzt alle 7 vom User genannten
Seiten (diary, map, walks, erste-hilfe, notes, expenses, routes) —
Pfote wird grün wenn alle im Static-Cache sind.
CSS-Färbung umgestellt: nur stroke (Linie) wird grün, kein fill
mehr. Pfote behält ihre offene Optik, nur die Outlines wechseln
von weiß zu Grün.
Bug: APP_VER war in app.js nur lokale const, nicht window.APP_VER
→ offline-indicator.js öffnete Cache 'by-v0-static' statt
'by-v1083-static' → fast alle Stufen blieben grau.
Fixes:
- app.js: window.APP_VER + window.APP_VERSION explizit setzen
- offline-indicator.js: _staticCache() Helper findet den aktuellen
Static-Cache per Regex /^by-v\d+-static$/ — versions-unabhängig
- Step 1 (App-Shell) prüft jetzt korrekt auf design-system.css UND
app.js im Static-Cache, nicht mehr caches.match() mit URL
User-Feedback: separater Indikator zu viel — die Pfote IM FAB selbst
soll je nach Score grün eingefärbt werden.
- Separater #offline-indicator Button entfernt (HTML + CSS)
- Welten-FAB-Icon: <use phosphor.svg#paw-print> ersetzt durch
Inline-SVG mit 5 einzelnen paw-elem-Pfaden (1 Ballen + 4 Zehen)
- CSS: Default weiß (wie bisher), .filled wird leuchtendes Grün
(#16a34a) — überzeichnet auf orangem FAB klar erkennbar
- offline-indicator.js: zeigt jetzt nur noch die FAB-Pfade ein/aus,
kein eigenes Element mehr; Klick-Status-Modal als window.OfflineIndicator.openStatus() weiter verfügbar (kann
später bei Bedarf an Long-Press oder Menüpunkt gehängt werden)
Logik umgedreht: Default ist 'sichtbar', JS setzt .is-hidden nur wenn
explizit nicht in Welten. So robust gegen Sibling-Selektor-Probleme
oder CSS-Compositing-Eigenheiten auf iOS PWA.
Außerdem: Hintergrund prominenter (rgba 0.95 statt 0.85), echter
Border statt Glas-Filter, stärkerer Schatten — bei den vorigen
Versuchen war die Pfote vermutlich auch durch Transparenz schwer zu
erkennen auf grauem Hintergrund.
Der reine CSS-Sibling-Selektor klappte nicht zuverlässig (vermutlich
SW-Cache-Mismatch oder DOM-Reihenfolge im aktuellen Zustand des
Users). Lösung: MutationObserver in offline-indicator.js beobachtet
class/style auf #worlds-overlay und togglet .visible auf
#offline-indicator. CSS akzeptiert jetzt beide Wege:
#worlds-overlay.worlds-visible ~ #offline-indicator,
#offline-indicator.visible { display: flex; }
So bleibt das Layout funktional auch wenn CSS-Compositing oder
Cache-Versatz mal nicht greift. console.warn wenn das Element nicht
im DOM ist (z.B. wenn alte index.html aus SW-Cache).
- Position: bottom-right über dem #worlds-fab (right:20px, bottom-
Berechnung folgt FAB + 12px Abstand). Gleiche horizontale Achse
wie FAB → ergibt eine 'Pfoten-Säule' (Indikator oben, FAB unten)
- Sichtbarkeit per CSS-Sibling-Selektor:
#worlds-overlay.worlds-visible ~ #offline-indicator { display:flex }
→ Indikator nur sichtbar wenn Welten aktiv sind. Auf Detail-Seiten
(Tagebuch, Karte, Admin etc.) bleibt er aus.
- z-index 61 (eine Stufe über dem FAB, unter Modals)
Der Header (#app-header) ist in den Welten per 'display:none !important'
ausgeblendet (Welten übernehmen Navigation). Mein Pfötchen saß da
drin und war genau dort unsichtbar wo es sichtbar sein sollte.
- Button aus dem Header rausgeholt, am Ende vom body als schwebendes
Element platziert (position:fixed; top-right; z-index:9000)
- Eigener Stil: 40px runder Glas-Hintergrund, blur-Effekt, leichter
Schatten — passt zur FAB-Optik unten rechts
- Dark-Mode Hintergrund: dunkles Glas
- Sichtbar in allen Welten und auf allen Seiten (auch wo Header da
ist — sitzt daneben)
- 'hidden'-Default raus, Element ist sofort sichtbar (nur Färbung
wartet auf refresh())
- CACHE_API hieß bei mir 'by-api', tatsächlich aber 'ban-yaro-api-v1'
→ korrigiert, sonst hätte step 3+5 nie grün werden können
- Step 5 prüfte auf gecachte Diary-Foto-Previews — die werden vom SW
aber gar nicht gecacht (nur API-Routen sind in _CACHEABLE_GET).
Stattdessen jetzt 'Training & Wissen' (training/exercises +
wiki/rassen) — ist im SW-Cache abgedeckt und passt zur WELT-Welt
- _fetchMissing für Step 5 entsprechend angepasst
Footer-Layout neu strukturiert — kein Umbruch-Chaos mehr:
- Erste Zeile: Abbrechen | Speichern (Grid 1fr 1fr, gleich breit)
oder bei sent/paid nur 'Schließen' volle Breite
- Zweite Zeile (wenn vorhanden): Stornieren als volle Breite,
ghost-Style mit rotem Rand — destruktive Aktion klar getrennt
- Button-Text 'Änderungen speichern' → 'Speichern' (kein Abschneiden
mehr auf iPhone)
- Backend: /admin/upgrade-requests liefert pro Request die offene
Rechnung (id+number+status) per Subquery aus der invoices-Tabelle
(status draft|sent → also nicht bezahlt, nicht storniert)
- Frontend: Wenn schon eine Rechnung existiert, wird statt 'Rechnung
erstellen' (orange) der Button 'Rechnung bearbeiten' (gelb,
#eab308) gezeigt. Klick lädt die Rechnung und öffnet das Modal im
Edit-Modus — kein doppeltes Anlegen, Nummerierung bleibt sauber.
- 'null is not an object (wrap2.remove)': Wrapper-Div hat keine
Klasse .diary-media-thumb-wrap → closest() lieferte null. Fallback
auf btn.parentElement + Null-Check vor remove()
- Bei 404 'Medium nicht gefunden' wird das verwaiste Foto jetzt
trotzdem lokal aufgeräumt (entry.media_items + DOM), statt einen
Error-Toast zu zeigen. Verwaiste Phantome verschwinden so beim
ersten Lösch-Klick.
Statt try/except mit pass: PRAGMA table_info prüft ob Spalte existiert,
loggt explizit ob hinzugefügt oder bereits vorhanden.
SW by-v1032, APP_VER 1032
bday/bdayYear: nur truthy wenn bdayDog.id === dog.id (aktiver Hund)
otherBdayDog: zeigt Hinweis wenn ein ANDERER Hund Geburtstag hat
SW by-v1031, APP_VER 1031
Nach Object.assign(_appState.user, updated) wurde Worlds.refresh() nie
aufgerufen → JETZT-Welt zeigte alten Render ohne Geburtstags-Greeting.
SW by-v1030, APP_VER 1030
Wenn ein anderer Hund (nicht der angezeigte) Geburtstag hat:
- Cake-Icon (Phosphor) animiert bounce in der Info-Karte unten
- "[Name] hat heute/morgen Geburtstag!" + Pfeil-Button
- Klick → wechselt direkt zum Geburtstagshund + zeigt Birthday-Banner
Kein Tab-Indicator (nur HUND-Welt). SW by-v1027, APP_VER 1027
design-system.css: .leaflet-tile-pane bekommt den invert/hue-rotate-Filter
im Dark-Mode — gilt für walks, lost, poison, forum, routes und alle
anderen Seiten mit eingebetteten Leaflet-Karten.
design-system.css ?v=1025, SW by-v1026, APP_VER 1026
Zeigt ein realistisches Beispiel (Luna vom Bergwald) mit Deckdaten,
Trächtigkeits-Meilensteinen und Progesteronkurve — im bestehenden
Züchter-Abschnitt, kein neuer Section-Break.
SW by-v1017, APP_VER 1017
Äußerer Wrapper hatte max-width:860px aber kein margin:0 auto → klebte links.
Toolbar und List-Container bekamen fehlende Seitenabstände.
SW by-v1015, APP_VER 1015