Der Bootstrap hing an signInWithApple und lief erst, nachdem der Router den Nutzer schon in die App geleitet hatte — Fehler blieben unsichtbar, der Account blieb ohne users-Doc/Haushalt und alle Writes scheiterten an den Rules. Jetzt beobachtet householdBootstrapProvider das users-Dokument und legt Profil + Haushalt reaktiv an (alle Login-Wege); das BootstrapGate hält die App so lange zurück und macht Fehler mit „Erneut versuchen" sichtbar. Bestehende kaputte Accounts heilen beim nächsten Start selbst. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
78 lines
9.3 KiB
Markdown
78 lines
9.3 KiB
Markdown
# Handoff: LeafItToMe (Pflanzenpflege-App)
|
||
|
||
Stand: 2026-07-23. Sprache mit dem Nutzer (Chris): **Deutsch**.
|
||
|
||
## Projekt in einem Satz
|
||
|
||
Private Flutter-App (iOS+Android) zur Pflanzenpflege für Haushalt + Pflanzen-Sitter: Foto-Erkennung (PlantNet + Claude), Gieß-/Düngeplan mit Tages-Checkliste, tägliche Sammel-Push, Haushalt-Teilen per Einladungscode mit Rollen.
|
||
|
||
## Wo alles liegt
|
||
|
||
- **Code:** `~/Documents/Projekte/Planty` (Ordner heißt noch Planty, Projekt heißt seit Umbenennung `leafittome`, App-Name „LeafItToMe", Bundle-ID-Platzhalter `dev.leafittome.app`)
|
||
- **Git-Remote:** selbst gehostetes Forgejo `https://git.cshomelabs.work/cschlaefke/leafittome.git` (HTTPS + Token im macOS-Schlüsselbund, `git push` läuft ohne Abfrage)
|
||
- **Doku (im Repo, deutsch, aktuell halten!):** `README.md`, `docs/architektur.md`, `docs/firebase-einrichtung.md` (Schritt-für-Schritt-Historie aller Backend-Einrichtungen), `docs/git-forgejo.md`
|
||
- **Claude-Memory:** Projektplan, Nutzerprofil und Backlog liegen im persistenten Memory-Verzeichnis dieser Claude-Code-Installation (werden automatisch geladen)
|
||
- **Firebase-Projekt:** `leaf-it-to-me-app` (Blaze aktiv, Budget-Alarm eingerichtet; Firebase CLI ist eingeloggt)
|
||
- **Functions:** `functions/` (TypeScript, Node 22, Region europe-west3): `identifyPlant` (PlantNet + Claude, Secrets `PLANTNET_API_KEY`/`ANTHROPIC_API_KEY` im Secret Manager), `sendDailyReminders` (alle 15 Min, sammelt über alle Haushalte eines Nutzers), `joinHousehold` (Einladungs-Einlösung), `leaveHousehold` (Haushalt verlassen), `removeMember` (Mitglied entfernen, nur Besitzer)
|
||
- **Auth:** E-Mail/Passwort + **Anmelden mit Apple** (iOS nativ, Android Web-Flow über Firebase-Handler) — Code in `lib/features/auth/data/auth_repository.dart`, Login-UI in `.../presentation/login_screen.dart`. Haushalts-Bootstrap (Profil + eigener Haushalt beim ersten Login) im gemeinsamen Helper `_bootstrapHouseholdIfNeeded`.
|
||
|
||
## Verifizierter Stand
|
||
|
||
- **V1 komplett** und auf Android + iOS Ende-zu-Ende getestet: Foto → Erkennung → vorbefülltes Profil; Tages-Checkliste; Sammel-Push kam im regulären Scheduler-Lauf auf dem Sperrbildschirm an. **iOS-Push seit 2026-07-20 eingerichtet und laufend** (Apple Developer Account freigeschaltet, APNs-Schlüssel in Firebase, Push-/Background-Capability in Xcode — Details + Stolperfalle „Personal Team" in `docs/firebase-einrichtung.md` Schritt 6).
|
||
- **V2 implementiert, deployt und vom Nutzer erfolgreich getestet** (Einladungscodes member/sitter, `joinHousehold`-Function, Rules-Enforcement für Sitter, „bestätigt von"-Anzeige).
|
||
- **V2.1 Multi-Haushalt implementiert und getestet**: Nutzer bleiben Mitglied aller Haushalte (`memberUids` ist Quelle der Wahrheit, `users.householdId` nur noch aktiver Zeiger), `sendDailyReminders` sammelt Aufgaben aus allen Haushalten.
|
||
- **V2.2 implementiert, deployt und vom Nutzer vollständig getestet und bestätigt**: Besitzer-Konzept (`ownerUid`, Alt-Haushalte: memberUids[0]), Besitzer-Anzeige, Haushalt umbenennen (Rules: Clients dürfen nur noch `name` ändern), Haushalts-Wechsler im Drawer, Tages-Checkliste gruppiert Aufgaben aus allen Haushalten, Functions `leaveHousehold`/`removeMember` (biegen den aktiven Zeiger des Betroffenen um, legen notfalls frischen Haushalt an). Zwei Nachbesserungen aus dem Live-Test, beide behoben und bestätigt:
|
||
- `leaveHousehold`/`removeMember` warfen zunächst HTTP 401 — fehlende Cloud-Run-Invoker-Berechtigung bei frisch angelegten Gen-2-Functions (kein Auth-Bug im Code); Fix war ein gezielter Neu-Deploy dieser beiden Functions.
|
||
- Besitzer-Anzeige blieb bei Alt-Haushalten aus der V1-Zeit leer (kein `members`-Eintrag für den Ersteller, die Map kam erst mit V2). Fix: `memberEntrySyncProvider` trägt beim App-Start die eigene E-Mail in allen eigenen Haushalten nach, plus eine eng gefasste Rules-Ausnahme (jeder darf nur den eigenen `members`-Eintrag pflegen, Rollenwechsel bleibt gesperrt).
|
||
|
||
## Offen / Nächstes
|
||
|
||
### 🟡 Apple-Login-Bug: Fix implementiert, wartet auf Gerätetest durch den Nutzer
|
||
|
||
**Der Bug:** Mit einem neuen Apple-Account schlug das Anlegen einer Pflanze fehl („Speichern fehlgeschlagen"). Ursache (per Code-Analyse bestätigt, Firestore-Sichtung stand aus): Der Haushalts-Bootstrap hing an `signInWithApple` **nach** `signInWithProvider` — der Router leitet aber schon bei „angemeldet" weiter (redirect prüft nur `currentUser != null`). Wirft der Bootstrap oder wird die App vorher beendet, bleibt der Nutzer dauerhaft ohne `users`-Doc + Haushalt, und die (korrekten) Rules lehnen jeden Schreibzugriff ab.
|
||
|
||
**Der Fix (2026-07-23 implementiert, Option „reaktiver Bootstrap"):**
|
||
- `householdBootstrapProvider` (`lib/features/auth/data/household_bootstrap_provider.dart`): beobachtet das `users/{uid}`-Dokument; bei „angemeldet, aber kein Profil" legt er Profil + Haushalt an. Deckt **alle** Login-Wege ab (auch künftig Google).
|
||
- `BootstrapGate` (`lib/features/auth/presentation/bootstrap_gate.dart`, eingehängt in `app.dart` builder): hält die App zurück, bis das Profil existiert; zeigt Warte-Bildschirm bzw. Fehler mit „Erneut versuchen".
|
||
- `AuthRepository`: Bootstrap jetzt öffentlich (`ensureHouseholdBootstrap`, idempotent + In-Flight-Schutz); `signInWithApple` ruft ihn nicht mehr selbst auf.
|
||
- **Selbstheilend:** Der bereits kaputte Apple-Account wird beim nächsten App-Start automatisch repariert (Gate sieht „kein Profil" → legt Haushalt an).
|
||
- Analyzer grün, alle 9 Tests grün (neuer Test: „Erster Login ohne Profil → Haushalt wird reaktiv angelegt").
|
||
|
||
**Was der Nutzer jetzt testen sollte:**
|
||
1. App auf dem iPhone neu bauen/starten, mit dem **bestehenden Apple-Account** anmelden → kurzer „Dein Haushalt wird eingerichtet …"-Bildschirm, danach sollte Pflanze-Anlegen (mit Foto!) funktionieren.
|
||
2. Optional Härtetest „echter Erst-Login": unter <https://appleid.apple.com> die Freigabe für „LeafItToMe" entfernen → nächster Login ist wieder „erster Login" → Ende-zu-Ende inkl. Pflanze mit Foto.
|
||
3. Falls es **weiter** fehlschlägt, obwohl der Haushalt angezeigt wird (Menü → Haushalt): dann liegt es doch an Storage-Rules/IAM — dann isolieren, ob Foto-Upload (Storage) oder Pflanze-Schreiben (Firestore) scheitert.
|
||
|
||
### Zuletzt erledigt
|
||
|
||
- **2026-07-23 (2):** Reaktiver Haushalts-Bootstrap + BootstrapGate implementiert (Fix für den Apple-Login-Bug, siehe oben); Architektur-Doku um die Entscheidung ergänzt.
|
||
- **2026-07-23 (1):** Apple-Login **implementiert und committet** (`signInWithApple` via eingebautem `AppleAuthProvider`, „oder"-Trenner + Button im Login, iOS-Entitlement `com.apple.developer.applesignin`, l10n-Strings, Doku `firebase-einrichtung.md` Schritt 7). Konfiguration vom Nutzer erledigt, Login funktioniert.
|
||
- **2026-07-20:** iOS-Push eingerichtet (Apple Developer Account freigeschaltet, APNs/Firebase/Xcode). V1 damit auch auf iOS vollständig.
|
||
|
||
### Weitere Kandidaten (nach dem Bug)
|
||
|
||
1. **V3:** Krankheits-Diagnose per Foto, Stellplatz-Bewertung (Standort-Analyse pro Stellplatz + Eignungs-Sterne pro Pflanze — Zwei-Ebenen-Konzept siehe Memory), Umtopf-Erinnerungen.
|
||
2. **Backlog:** Google-Login (nach Apple, `firebase-einrichtung.md` Schritt 8); Button „KI-Zweitmeinung" bei unbefriedigendem PlantNet-Ergebnis; Push-Anzeige auch bei App im Vordergrund; App Check vor App-Store-Release; Englisch (`app_en.arb`).
|
||
|
||
## Stolperfallen (teuer erkauft, nicht erneut zahlen)
|
||
|
||
- `firebase functions:log` hinkt **15–20 Minuten** hinterher — „keine Läufe" heißt meist nur Log-Verzögerung. Scheduler-Status im Cloud-Scheduler-Console prüfen lassen.
|
||
- Android zeigt FCM-Pushes **nur bei App im Hintergrund**.
|
||
- `flutter_timezone` ≥5 liefert `TimezoneInfo`-Objekt → `.identifier` verwenden (Objekt crasht Firestore-Writes still).
|
||
- Storage-Rules mit `firestore.get()` brauchen die IAM-Rolle „Firebase Rules Firestore Service Agent" fürs Storage-Dienstkonto (wurde manuell erteilt).
|
||
- Erster Functions-Deploy in frischem Projekt kann mit 403 `iam.serviceaccounts.actAs` scheitern → wenige Minuten warten, erneut deployen.
|
||
- Analyzer: `build/` und `ios/build/` sind in `analysis_options.yaml` ausgeschlossen (SwiftPM legt dort Fremdcode ab).
|
||
- iOS-Fehler „requires minimum platform version 15.0 … but this target supports 13.0": Das von Flutter generierte SwiftPM-Paket (`ios/Flutter/ephemeral/…/Package.swift`) wird bei `pub get` mit iOS 13 erzeugt; erst `flutter build ios`/`flutter run` hebt es auf das Projekt-Target (15.0) an. Nach `flutter clean` oder beim Bauen direkt aus Xcode daher einmal `flutter build ios --config-only` ausführen.
|
||
- Nutzer wünscht zu jedem Backend-Baustein **begleitende deutsche Doku** in `docs/` (keine Firebase-Vorerfahrung).
|
||
|
||
## Arbeitsabläufe
|
||
|
||
- Prüfen: `flutter analyze && flutter test` (Tests mocken Firebase via `firebase_auth_mocks`/`fake_cloud_firestore`); Android-Verifikation: `flutter build apk --debug`
|
||
- Texte: nur in `lib/l10n/app_de.arb` (typografische Anführungszeichen „…“ verwenden, gerade `"` brechen das JSON), dann `flutter gen-l10n`
|
||
- Deploy: `firebase deploy --only functions|firestore|storage --project leaf-it-to-me-app`; nach jedem Arbeitsstand committen + `git push`
|
||
|
||
## Suggested skills
|
||
|
||
- `/verify` — nach nichttrivialen Änderungen den echten Flow treiben (Gerät/Simulator), nicht nur Tests
|
||
- `/code-review` — vor größeren Merges über den Diff laufen lassen
|
||
- `/run` — App starten/screenshotten, wenn Änderungen sichtbar geprüft werden sollen
|