Commit graph

37 commits

Author SHA1 Message Date
546386dcbd Hunde-Cache leeren bei Logout/401 — kein Durchschimmern auf neuen User
Bug: ActiveDogStore cached dogs + activeDogId (UserDefaults). Beim
Login mit einem neuen Account waren die Hunde des vorigen Users
weiter zu sehen, weil HeimView nur loadDogs() ruft wenn dogs.isEmpty.

Fix:
- ActiveDogStore hat jetzt reset() (dogs=[], activeDogId=0,
  UserDefaults gelöscht).
- ActiveDogStore hört auf .userDidLogout und auf .apiUnauthorized,
  beides löst reset() aus.
- AuthSession.logout() postet jetzt .userDidLogout.
- Nach Login holt HeimView's .task automatisch die neuen Hunde
  (dogs.isEmpty → loadDogs).
2026-05-30 18:25:23 +02:00
89d1d47ca4 Tagebuch: Detail-Ansicht + tappbare Listen-Zeilen
DiaryDetailView mit Header (Datum, Meilenstein-Badge, Ort), Foto-
Galerie als TabView mit Page-Indicator, Volltext, Tags als Chips,
Mini-Karte für GPS-Eintrag mit Tap nach Apple Maps, und 'Eintrag
löschen' mit Bestätigungs-Alert → DELETE /api/dogs/{id}/diary/{eid}.

Liste in TagebuchView jetzt mit NavigationLink statt nur Anzeige.

App-Store-Material:
- AppStore/marketing.md mit Name, Untertitel, Beschreibung 2700
  Zeichen, Keywords, Privacy-Antworten, Reviewer-Notiz
- AppStore/screenshots/ — 8 Stück 1320x2868 (iPhone 17 Pro Max 6.9'')
2026-05-30 14:53:44 +02:00
7848817cbe GPX-Import via Teilen-Menü und 'Öffnen mit'
Andere Apps können jetzt GPX-Tracks zu Ban Yaro Go schicken (Komoot,
Outdooractive, GPSies, AllTrails, Files-App, Mail-Anhänge, AirDrop).

- Info.plist:
  - UTImportedTypeDeclarations: com.topografix.gpx (conforms to
    public.xml/data/content, ext gpx, MIME application/gpx+xml)
  - CFBundleDocumentTypes registriert die UTI als Viewer (LSHandlerRank
    Alternate, damit wir nicht die Default-App werden)
  - LSSupportsOpeningDocumentsInPlace=true
- Support/GPXParser.swift: schlanker XMLParser/SAX-Reader für
  <trkpt>/<wpt>/<rtept>, Track-Name aus <trk><name>, ele + ISO8601 time
- Views/GPXImportSheet.swift: Sheet mit Map(MapPolyline)+Start/Ziel-Pins,
  Distanz/Punkte/Dauer-Karte, zwei Aktionen:
    1. 'Als Tour übernehmen' — Name editierbar, Hunde-Picker (FlowDogs),
       öffentlich-Toggle → POST /api/routes
    2. 'Nur ansehen' — Startpunkt in Apple Maps
- BanYaroGoApp.swift: .onOpenURL prüft .gpx, security-scoped resource,
  parst und triggert das Sheet via TrackBox-Wrapper
2026-05-30 14:34:40 +02:00
3d229d42ce App-Store Vorbereitung: Privacy-Manifest + Konto-Löschen + Region de
- PrivacyInfo.xcprivacy: NSPrivacyTracking=false, deklariert gesammelte
  Daten (Email, Name, User-ID, präziser/grober Standort, Fotos, Fitness,
  Sonstiger Nutzerinhalt) — alle linked to user, kein Tracking, nur
  AppFunctionality. Required-Reason APIs: UserDefaults (CA92.1),
  FileTimestamp (C617.1), DiskSpace (E174.1), SystemBootTime (35F9.1).
- SettingsView: Section 'Konto löschen' mit zwei Bestätigungs-Alerts →
  DELETE /api/profile/account → automatischer logout. Erfüllt Apple-
  Pflicht seit iOS 16.4 (in-App-Löschung statt Web-Redirect).
- Info.plist: CFBundleDevelopmentRegion explizit 'de' (statt der
  $(DEVELOPMENT_LANGUAGE)-Variable, die sonst 'en' auflöst) → Store
  zeigt App als deutschsprachig an.
- NSHealthShareUsageDescription präziser formuliert (Reviewer-Hint).
2026-05-30 14:30:21 +02:00
09a90f7325 Gassi-Treffen-Liste: Karte oben mit Treffpunkt-Pins (analog Verlorene/Giftköder)
- Map(position:) mit UserAnnotation + Pin pro Treffen (pawprint.circle.fill)
- Tap auf Pin öffnet das Detail-Sheet (mit 'Schließen'-Button)
- Re-Center-Button oben rechts, .onAppear zentriert auf User
- Drunter wie gehabt: Liste mit NavigationLink, Empty-State, Spinner
2026-05-30 14:21:38 +02:00
00dba257b5 Gassi-Treffen-Detail: Fokus auf 'hinfinden'
Detail-Karte zeigt jetzt Treffpunkt + User-Position via UserAnnotation,
darunter eine Navigation-Karte mit:
- Ortsname
- Luftlinien-Distanz vom aktuellen Standort
- 'Route'-Button öffnet Apple Maps (MKMapItem.openInMaps) — User wählt
  dort Walking/Driving

Plus Teilnehmer-Sektion mit GET /api/walks/{id}/participants:
- Liste der Zugesagten ('yes'-RSVP) mit Namen + Hunden
- 'Noch niemand zugesagt'-Hinweis bei leerer Liste
- myRsvp-Status für korrekte Join-Button-Anzeige
- isOwn → 'Dein Treffen'-Badge statt Beitreten-Button

Footer-Hinweis: 'Fotos vom Treffen kannst du später in der banyaro.app
teilen' — Foto-Funktion bewusst PWA-only (User-Wunsch).
2026-05-30 14:16:43 +02:00
b49883ca79 Gassi: Tabs Treffen + Stamm-Gassis wie in der PWA
In der PWA ist die Seite 'Gassi-Treffen' mit drei Tabs:
- Treffen (walks.py — sich verabreden)
- Challenge (Monatsfoto)
- Stamm-Gassis (gassi_zeiten.py — regelmäßige Runden)

Mein bisheriger Mehr-Eintrag hieß 'Stamm-Gassi-Zeiten' und zeigte nur die
Stamm-Gassi-Funktion isoliert — das stimmte nicht mit der PWA überein.

Neu:
- GassiView mit Segmented Picker (Treffen / Stamm-Gassis)
- GassiTreffenList: GET /api/walks?lat&lon&radius=20000, Liste mit Datum,
  Uhrzeit, Ort, Teilnehmer-Zahl
- GassiTreffenDetail: Karte mit Pin, Stats, Beitreten/Verlassen
  (POST/DELETE /api/walks/{id}/join), Owner-Check
- AddWalkSheet: Titel, Datum, Uhrzeit, Treffpunkt-Name, Max-Teilnehmer,
  Beschreibung — POST /api/walks
- StammGassisList = bisherige GassiZeitenView umbenannt + Nav-Title raus
  (wird vom GassiView vergeben)

Im Mehr-Tab heißt der Link jetzt 'Gassi-Treffen' (pawprint-Icon) statt
'Stamm-Gassi-Zeiten' (alarm-Icon).

DTOs: WalkMeeting, WalkCreateBody, WalkJoinBody.
2026-05-30 14:02:42 +02:00
5c4754caff Wetter: stündliche Niederschlags-Timeline (WeatherKit hourlyForecast)
Säulen-Diagramm zwischen Stats-Grid und Hunde-Hinweisen:
- Heute: nächste 12h ab jetzt
- Andere Tage: 06–22h des Tages
- Säulenhöhe aus precipitationAmount in mm, Farbgradient aus Intensität
  (chance + mm). Header zeigt durchschnittliche Regenwahrscheinlichkeit
  oder 'trocken' wenn < 5%.
2026-05-30 13:51:14 +02:00
70e0a238e9 Karten: Auto-Zentrieren beim Erscheinen via .onAppear (.onChange bleibt) 2026-05-30 13:42:17 +02:00
a6ea7b5b8f Karten: zentriert auf Standort + Re-Center-Button
- GiftkoederView: Map.position als State (war initialPosition, einmalig),
  Re-Center-Button rechts oben (location.fill in Capsule auf material),
  bei Standort-Update wird Kamera mit easeInOut animiert auf Position
- VerloreneHundeView: dieselbe Map-Struktur ergänzt, fehlte komplett.
  Pins als magnifyingglass.circle.fill in Akzentfarbe, tappable → öffnet
  LostDogDetailView als Sheet (mit eigenem 'Schließen'-Button)
2026-05-30 13:37:42 +02:00
fb00468c8c Verlorene Hunde: Melden-Button + Sheet analog zu Giftköder
Toolbar-Button rechts oben (exclamationmark.bubble), Sheet mit:
- Name (Pflicht), Rasse (optional), Eigener-Hund-Picker (optional)
- Beschreibung (Pflicht, min 3 Zeichen)
- PhotosPicker für ein Foto
- Standort vom OneShotLocation (read-only Anzeige)
- POST /api/lost, danach optional POST /api/lost/{id}/foto (multipart,
  resized via ImageResize)
2026-05-30 13:34:10 +02:00
08069d6ea4 Wetter: deutsche Conditions + Asphalt-Label entrümpelt
- WeatherCondition deutsch: 'Mostly Clear' → 'Überwiegend klar' etc.
- AsphaltLevel.label nur noch ein Wort (Heiß/Warm/Gefährlich),
  die safety-Info wandert in den advice-Text → Title bleibt einzeilig
2026-05-30 13:30:01 +02:00
357c57e880 Gassi-Wetter mit WeatherKit + banyaro-Logik
WeatherKit als Datenquelle (statt OpenMeteo-Proxy via banyaro-Backend):
- BanYaroGo.entitlements: com.apple.developer.weatherkit
- WetterView komplett neu mit WeatherService.shared.weather(for:)
- DayWeather.symbolName als SF-Symbol direkt, kein WMO-Mapping nötig

GassiWetter-Logik (1:1-Port aus banyaro PWA wetter.js):
- gassiScore(...) 1-10 mit Temp/Regen/Wind/Asphalt/Gewitter
- asphaltTemp(airMax, uvMax) — gleiche Formel mit t_factor und UV-Bonus
- asphaltLevel safe/warm/hot/danger mit Advice-Texten
- schnueffelIndex aus Feuchte (precipProb-derived) und Temperatur
- tickRisk March-Oktober, Schwellen 7/12/20°C
- pawColdProtection bei tempMin <= 0

UI:
- Horizontaler Tag-Picker (Heute/Morgen + EEE) mit Mini-Stats
- Großer Gassi-Score-Badge in Empfehlungs-Farbe (grün/amber/rot)
- Stats-Grid 2x2: Niederschlag, Wind, UV, Asphalt
- Hunde-Hinweise als farbige Boxen (Asphalt, Pfoten, Gewitter, Zecken)
- Schnüffel-Index als kompakte Karte mit Emoji

Color(hex:)-Extension für die HEX-Werte aus dem PWA übernommen.
2026-05-30 13:16:48 +02:00
0867a2171f Statistik weg, Mehr entrümpelt, Gassi-Zeiten korrekt gerahmt
- Statistik-Tab raus (für Go-Companion nicht relevant)
- Mehr-Duplikate raus: Meine Hunde, Tagebuch, Wetter, Erste Hilfe sitzen
  bereits auf Heim als Quick-Action bzw. im Dog-Picker
- Im PWA ist 'Gassi' der social walks-Bereich (walks.py) und 'Stamm-Gassi-
  Zeiten' nur ein Tab darin (Community-Pool, gassi_zeiten.py). Meine
  Implementierung als 'tägliche Erinnerungen' war fachlich falsch:
  + Mehr-Eintrag heißt jetzt 'Stamm-Gassi-Zeiten'
  + ContentUnavailableView + Footer erklären die Community-Komponente
  + Pitch-Karte unterscheidet jetzt klar: 'Gassi-Treffen' (sich verabreden)
    und 'Stamm-Gassi-Zeiten' (regelmäßige Runden + Pool)
  + 'Hunde-Orte' getrennt als eigener Pitch-Punkt
2026-05-30 13:04:35 +02:00
5dc76db8cb Heim: Welcome näher ans Foto (Spacer 300→250) 2026-05-30 12:58:14 +02:00
82639acf7f Heim: Spacer 200→300, damit Welcome und Themen-Karten unter dem Foto sitzen 2026-05-30 12:55:47 +02:00
f2e9d5deaf Heim: weißer Gradient raus, Tagesbild pro Tag cachen wie die PWA
- LinearGradient zum systemBackground entfernt — der Übergang war zu hart
  weiß und hat das Foto verschluckt
- UserDefaults-Cache 'heimPhoto_{userId}_{dogId}_YYYY-MM-DD' analog zur PWA
  (bg3_{userId}_YYYY-MM-DD). Sobald ein Foto pro Tag gewählt ist, sticht es
  bis Mitternacht — damit kippt's nicht mehr, wenn zwischendrin neue Bilder
  hochgeladen werden und die tick%len-Rotation auf einen anderen Index zeigt
2026-05-30 12:51:16 +02:00
3059a5eb2c Tagebuch: Geolocation + Reverse-Geocoding beim Anlegen
- OneShotLocation beim Sheet-Öffnen, Toggle 'Standort verwenden' (Default an)
- CLGeocoder reverseGeocodeLocation (de_DE-Locale) füllt locationName aus
  Placemark.name, Fallback thoroughfare + locality. User kann überschreiben.
- gps_lat/gps_lon werden mit dem Eintrag gesendet, wenn Toggle an — die PWA
  rendert daraus die POI-Karte im Tagebuch.
2026-05-30 12:45:18 +02:00
12f8ba0be8 APIClient: URL.appending(path:) frisst Query-Strings — String-Concat statt
URL.appending(path:) behandelt den Input als reinen Path-Component und
percent-encoded Sonderzeichen, also auch ?. Damit wurde aus
/api/dogs/123/diary?limit=50 ein /api/dogs/123/diary%3Flimit=50, und der
Server lieferte was Anderes als JSON zurück → 'data was not valid JSON'.

Betraf auch Wetter, Giftköder, Verlorene Hunde, Gassi-Zeiten und alle
anderen Endpoints mit Query. Jetzt: baseURL.absoluteString + path.
2026-05-30 12:41:50 +02:00
3373305b23 Tagebuch-Decode-Fix: media_items statt media, is_milestone als Int 0/1
Backend liefert das Foto-Array unter 'media_items' (in _entry_dict),
nicht 'media'. Außerdem ist is_milestone wieder eine SQLite-Int-Spalte
0/1 — der bekannte Bool-Quirk. Mit Bool? in der DTO bricht die Decoder
ab und die ganze Liste verschwindet stillschweigend in den Fehler-Zustand.
2026-05-30 12:35:50 +02:00
ef7907d74b Heim: nur noch 'Nächster Termin'; letzter Eintrag/Gewicht/Anzahl raus 2026-05-30 12:33:25 +02:00
cf625f3391 Ausgaben-Kategorien dynamisch vom Backend
ExpenseCategory DTO + GET /api/expenses/categories beim Öffnen der Liste
sowie bei Refresh. Falls der Endpunkt noch nicht ausgerollt ist (oder
fehlschlägt), Fallback auf eine lokale Default-Liste mit den aktuellen
sechs Kategorien.

AddExpenseSheet bekommt die Kategorien als Parameter, statt eigene
Liste zu führen — Source of Truth ist jetzt das Backend.
2026-05-30 12:31:59 +02:00
c03f018c0c Ausgaben: Kategorien backend-konform (kleingeschrieben, sechs Werte)
Backend-Whitelist: tierarzt, futter, zubehoer, versicherung, sitter, sonstiges.
Bisher schickte ich großgeschriebene Display-Strings, daher HTTP 400
'Ungültige Kategorie: Futter'. Jetzt: interner Key kleingeschrieben für die
API, label() für die Anzeige in der Liste und im Picker.
2026-05-30 12:28:04 +02:00
f054b2a07f Tagebuch + Heim-Tab mit täglichem Background
Tagebuch (Diary):
- DiaryEntry + DiaryMedia + DiaryCreateBody DTOs
- TagebuchView: Liste der Einträge für aktiven Hund mit Titel, Text,
  Ortsname, Meilenstein-Stern, Foto-Strip
- AddDiaryEntrySheet: Titel/Text/Datum/Meilenstein/Ort/Tags +
  PhotosPicker, nach POST /api/dogs/{id}/diary werden Fotos einzeln
  via POST /api/dogs/{id}/diary/{entry_id}/media hochgeladen (mit
  ImageResize.resizedJPEG)

Heim-Tab als neuer 1. Tab:
- DashboardSnapshot DTO für /api/dogs/{id}/welcome-dashboard
- ActiveDogStore (@Observable + UserDefaults("activeDogId")): hält
  den aktiven Hund app-weit
- HeimView: tägliches Hintergrundfoto aus random_photo.url (rotiert
  pro Tag, vom Backend gewählt), Gradient zur Lesbarkeit, Tagezeit-
  Begrüßung mit User-Namen, Hund-Picker (Menu), Info-Karten für
  letzten Eintrag/nächsten Termin/Gewicht/Eintragszahl,
  Quick-Action-Buttons (Tagebuch, Wetter, Erste Hilfe)

Reorganisation:
- 5 Tabs: Heim, Touren, Aufnehmen, Statistik, Mehr
- Hunde-Liste wandert in Mehr → "Hund & Alltag"
- Tagebuch in Mehr → "Hund & Alltag" + erreichbar von Heim
2026-05-30 12:22:51 +02:00
68b084be97 Sechs Offline-Features: Erste Hilfe, Ausgaben, Wetter, Gassi-Zeiten, Giftköder, Verlorene
Pitch-Karte erweitert um die neuen Features (sowie Hundesitting, Züchter).

Neue DTOs in DTOs.swift:
- Expense + ExpenseCreateBody
- GassiZeit + GassiZeitCreateBody (mit wochentage [String], radius_m)
- PoisonAlert + PoisonCreateBody
- LostDog + LostDogCreateBody
- WeatherForecast + WeatherDay (mit asphalt_temp, zecken, pollen-Felder)

Neue Views:
- ErsteHilfeView + Detail: sechs Notfall-Topics (Vergiftung, Hitzschlag,
  Wunden, Atemnot, Krampfanfall, Magendrehung) — komplett offline, kein API
- AusgabenView: Liste mit Total, AddExpenseSheet mit Kategorie/Betrag/
  Datum/Hund-Picker
- WetterView: One-Shot Location + /api/weather/forecast, 7-Tage-Vorhersage
  mit Hunde-Tipps (Hitze ab 25°/30°, Frost, Asphalt ≥50°, Zecken, Regen)
- GassiZeitenView: eigene Zeiten + Add-Sheet (Wochentag-Picker, Hund-
  Auswahl), automatische lokale UNCalendarNotifications via Scheduler
- GiftkoederView: Map mit Pins + Liste in 5km Umkreis, Report-Sheet mit
  Typ-Auswahl
- VerloreneHundeView: Liste mit Foto/Distanz, Detail mit Karte

Support:
- OneShotLocation: kleiner CLLocationManager-Wrapper für einmalige
  Positionsabfrage (Wetter, Giftköder)
- GassiZeitenScheduler: UNCalendarNotificationTrigger pro Wochentag,
  Identifier-Schema "gz-{id}-{weekday}"

Navigation: Section "Hund & Alltag" im Mehr-Tab mit NavigationLinks zu
allen sechs neuen Ansichten.
2026-05-30 12:03:24 +02:00
f1b3ff4035 Login: Pitch-Karte ausklappbar + Züchter/Sitting, Icon mit sichtbarem Pfad
- icon_transparent.py: pur-weiße Pixel werden zu #C4843A umgefärbt — der
  GPS-Pfad zwischen Pin und Hund ist jetzt auf jedem Hintergrund sichtbar
- LoginView Pitch jetzt als ausklappbare Karte ('Tippen für Details')
  mit spring-Animation
- Sechs Punkte statt vier: Gassi-Touren, Hunde-Community, Tagebuch & Impfpass,
  Verifizierte Züchter (rosette), Giftköder-Alarm, Hundesitting (house.fill)
2026-05-30 11:46:41 +02:00
4ee84d5a1a Login-Page für Newcomer + freigestelltes App-Icon im Hero
- icon_transparent.py: blauer Sky-Hintergrund per RGB-Schwelle entfernt
  (b > 0.55, b > r+0.05 …), trimmt auf Content-Bbox
- AppIconHero.imageset mit dem freigestellten Icon
- LoginView komplett neu strukturiert als ScrollView:
  - Hero: freigestelltes App-Icon (kein SF Symbol mehr) + Titel + Tagline
  - Pitch-Karte: vier Feature-Highlights (Gassi-Tracking, Community,
    Tagebuch, Giftköder-Alarm) für Leute, die banyaro nicht kennen
  - "Schon angemeldet?"-Karte mit dem klassischen Login-Form
  - "Neu hier?"-Karte mit kostenlos/DSGVO/DE-Hosting-Pitch und großem
    Register-Button mit person.crop.circle.badge.plus-Icon
2026-05-30 11:42:32 +02:00
500a645bfd Phase 4.A.1: Live Activity + Dynamic Island für laufende Gassi-Tour
Neues Widget-Extension-Target BanYaroGoWidgetExtension:
- Bundle-ID app.banyaro.ios.BanYaroGoWidget
- NSExtensionPointIdentifier = com.apple.widgetkit-extension
- Synced root group + explizite Info.plist + Embed-Phase in App-Target
- Cross-Membership der Shared/WalkActivityAttributes.swift in beiden Targets

Shared/WalkActivityAttributes.swift:
- ActivityAttributes mit startedAt (fix)
- ContentState mit distanceMeters, elapsedSeconds, pointCount, isPaused, isAutoPaused

BanYaroGoWidget/WalkLiveActivity.swift:
- Lock-Screen-View: Pfote-Icon + Status-Pille (Live/Pause/Auto-Pause) +
  Stats-Spalten (Distanz/Dauer/Punkte)
- Dynamic Island compact: Pfote leading, Distanz trailing
- Dynamic Island minimal: nur Pfote
- Dynamic Island expanded: Distanz/Dauer/Status mit Pfote zentriert

WalkActivityController:
- @MainActor Facade um ActivityKit
- start() prüft areActivitiesEnabled, killt orphaned current, request mit Initial-State
- update() async via Task
- end() mit dismissalPolicy.immediate

TrackingView:
- .onChange(of: tracker.isTracking) → Start/End
- persistTicker (5s) → update
- .onChange(of: tracker.isPaused/isAutoPaused) → sofort update für saubere UX

BanYaroGo-Info.plist: NSSupportsLiveActivities = true
2026-05-30 11:35:43 +02:00
fec7c79b05 Login-Hinweis 'Kostenlos registrieren' + PWA-Install-Anleitung in Mehr 2026-05-30 11:28:05 +02:00
c01e3d6be7 Phase 3.6: B+C+D komplett + HealthKit Sync
D.10 401-Handling: APIError.unauthorized, NotificationCenter-Bridge,
  AuthSession.logout() bei 401 → User landet wieder im Login

D.12 PWA-Deep-Links: Settings-Section mit Forum/Hunde/Walks/Settings
  öffnet Safari per https://banyaro.app/#fragment

B.4 Auto-Pause: 2-min-Inaktivität → isAutoPaused, automatischer Resume bei
  nächstem GPS-Update. Settings-Toggle, im UI eigenes Badge "Auto-Pause"
  (grau vs. Pause orange).

C.7 Edit/Delete: RouteUpdateBody + APIClient.patch + APIClient.delete,
  EditRouteSheet (Name/Beschreibung/Public), Menu in Toolbar (nur eigene
  Touren), Alert für Delete.

C.9 Statistik-Tab: neuer Tab "Statistik" zwischen Hunde und Mehr. Filtert
  /api/routes auf meine Touren, rechnet Woche/Monat/Allzeit (Distanz, Dauer,
  Touren), Längste Tour, aktuelle Streak (Tage in Folge).

B.5 Walk-Review: Map-Header an die Spitze des FinishWalkSheet-Forms.

B.6 Geo-Fotos: CapturedPhoto (Data + GPSPoint?), PhotoLocation @Model in
  SwiftData. Kamera während Walk taggt mit tracker.points.last. Nach Upload:
  foto_url aus Response → PhotoLocation persistiert. MiniRouteMap rendert
  Annotations mit Tap-Callback, PhotoViewerSheet zeigt Foto fullscreen.

C.8 Share PNG+GPX: RouteShareImage (MKMapSnapshotter + Polyline overlay +
  SwiftUI ShareCard via ImageRenderer), GPXExporter (Tempfile mit XML),
  ShareSheet (UIActivityViewController-Wrapper), Menu in Route-Toolbar.

D.11 Icon-Varianten: AppIcon-Dark (0.45 Brightness), AppIcon-Tinted
  (Grayscale + Kontrastverstärkung), Contents.json mit appearance entries.

A.2 HealthKit: BanYaroGo.entitlements (com.apple.developer.healthkit),
  NSHealthShare/UpdateUsageDescription. WalkHealthSync.shared mit
  HKWorkoutBuilder (.walking) + HKWorkoutRouteBuilder, Timestamps gleichmäßig
  über Walk-Dauer verteilt. Settings-Toggle mit Permission-Request.
2026-05-30 11:19:53 +02:00
30e0fbe7ec Icon: neue Variante aus icon-full.png, Himmelblau-Hintergrund auf 1024 ohne Alpha 2026-05-30 10:57:43 +02:00
5473bbf41f Phase 3.5: Pause/Resume, SwiftData-Persistenz, Kamera-Capture, Fotos zu bestehender Tour
LocationTracker:
- isPaused, pausedAt, accumulatedPausedSeconds
- pause()/resume()/restore() Methoden
- effectiveElapsedSeconds rechnet Pausen raus
- restore() für nach App-Crash: Offline-Lücke wird als Pause gezählt

ActiveWalk @Model (SwiftData):
- startedAt, lastUpdate, pausedAt, accumulatedPausedSeconds, pointsData
- Container in BanYaroGoApp registriert

TrackingView:
- Persistenz alle 5s via Timer
- confirmationDialog beim Erscheinen wenn ActiveWalk vorhanden:
  Fortsetzen / Jetzt speichern / Verwerfen
- Pause/Resume-Button + Stop-Button
- Floating Kamera-Button rechts unten
- Foto-Counter in der Stats-Karte
- Pause-Badge oben links bei Pause

CameraPicker: UIImagePickerController-Wrapper (Fallback auf Library im Simulator).

FinishWalkSheet: initialPhotos: [Data] für Kamera-Fotos während Tour.

RouteDetailView: PhotosPicker zum Hinzufügen von Fotos zu bestehender Tour,
sequentieller Upload mit Progress, Detail wird nach Upload refreshed.

NSCameraUsageDescription in BanYaroGo-Info.plist.
2026-05-30 10:52:15 +02:00
e27fa39620 Phase 3: Foto-Upload + Mindeststrecken-Warnung
- APIClient.uploadFile: multipart POST mit Bearer-Token, generischer
  field/filename/mime
- ImageResize: längste Kante max 2048px, JPEG q=0.8 — iPhone-Fotos sonst
  5-10MB pro Stück
- FinishWalkSheet:
  - PhotosPicker (iOS 16+, kein NSPhotoLibraryUsageDescription nötig)
  - Thumbnail-Strip der gewählten Fotos
  - Sequentieller Upload nach POST /api/routes, Toolbar zeigt "N/M"
  - Bei < 50m: orangene Warnung "Sehr kurze Tour — du kannst trotzdem speichern"
  - Save-Button blockt korrekt während Upload, Verwerfen auch
2026-05-30 10:18:08 +02:00
0b95e3e6d1 Phase 2: Live-GPS-Tracking + neues Icon
- BanYaroGo-Info.plist (explizit, statt INFOPLIST_KEY_*): UIBackgroundModes
  location, NSLocationWhenInUseUsageDescription,
  NSLocationAlwaysAndWhenInUseUsageDescription
- LocationTracker: CLLocationManager-Wrapper (@Observable @MainActor), Distanz
  via CLLocation.distance, Permission-Handling, Background-Updates
- RouteCreateBody + Encoder mit convertToSnakeCase für POST /api/routes
- TrackingView: Start-Hero-Screen + Live-Karte mit MapPolyline + Stats-Karte
- FinishWalkSheet: Name + Hunde-Multiselect + POST /api/routes
- MainTabView: neuer Aufnehmen-Tab zwischen Touren und Hunde
- AppIcon: neues Hund-mit-GPS-Pin (vom User bereitgestellt, weiße Ränder
  weggeschnitten + Ecken mit Hintergrundfarbe gefüllt)
2026-05-30 10:08:02 +02:00
5bac31109d Fix /me-Decoding: is_founder/is_partner kommen als SQLite-Int 0/1, nicht Bool
Das Backend bool-konvertiert nur is_premium explizit; alle anderen 0/1-Spalten
gehen unverändert durch FastAPI. Decode-Fehler vorher still verschluckt → jetzt
auch geloggt, damit das nicht nochmal passiert.
2026-05-30 09:40:25 +02:00
bfd327bd40 Settings: echtes Profil via /api/auth/me (Rolle, Founder, Abo, Avatar)
Login liefert nur {token, name, is_premium}. Für Admin-/Founder-/Tier-Info
holen wir nach Login (und beim Erscheinen von MainTabView) /api/auth/me und
zeigen ein echtes Profil mit Avatar, Email, Rolle und nur dann Premium-Status,
wenn das relevant ist.
2026-05-30 09:37:12 +02:00
81681130e6 Ban Yaro Go — Phase 1 Foundation
SwiftUI/SwiftData iOS-Client, redet mit https://banyaro.app FastAPI-Backend.
Bundle-ID app.banyaro.ios, Xcode-26-Projekt mit synchronisierten Ordnern.

Drin:
- APIClient (URLSession + Bearer + convertFromSnakeCase Decoder)
- KeychainStore + AuthSession (@Observable) für persistenten Login
- LoginView, MainTabView, SettingsView (mit Logout)
- RoutesListView + RouteDetailView mit MapKit-Polyline aus preview_track
- DogsListView mit Foto-Avatar
- App-Icon (Pfote auf Banyaro-Amber)
2026-05-30 09:25:48 +02:00