leafittome/lib/features/auth/data/household_bootstrap_provider.dart
cschlaefke 0cc39f69b1 Fix: Bootstrap-Maßstab ist householdId, nicht users-Doc-Existenz
Die Push-Registrierung legt users/{uid} per merge selbst an (fcmTokens,
timezone) — beim kaputten Apple-Account existierte das Dokument daher
ohne householdId, und Gate + Bootstrap hielten den Account für fertig.
Jetzt prüfen beide auf householdId; der Bootstrap schreibt das
users-Dokument mit merge, damit die Push-Felder erhalten bleiben.
Neuer Test bildet exakt diesen Zustand ab.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 21:43:23 +02:00

73 lines
2.7 KiB
Dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/firebase/firebase_providers.dart';
import 'auth_repository.dart';
/// users/{uid}-Dokument des angemeldeten Nutzers, live (null = ausgeloggt).
///
/// Existiert das Dokument nicht (erster Login), stößt
/// [householdBootstrapProvider] den Haushalts-Bootstrap an.
final userProfileProvider =
StreamProvider<DocumentSnapshot<Map<String, dynamic>>?>((ref) {
final user = ref.watch(authStateProvider).value;
if (user == null) return Stream.value(null);
return ref
.watch(firestoreProvider)
.collection('users')
.doc(user.uid)
.snapshots();
});
/// Reaktiver Haushalts-Bootstrap: legt Profil + eigenen Haushalt an, sobald
/// ein Nutzer angemeldet ist, für den noch kein users-Dokument existiert.
///
/// Deckt alle Login-Wege ab (E-Mail-Registrierung, Apple, künftig Google) —
/// unabhängig davon, wann der Router weiterleitet. Das BootstrapGate hält
/// die App zurück, bis das Profil da ist, und zeigt Fehler samt
/// „Erneut versuchen“ an.
class HouseholdBootstrap extends Notifier<AsyncValue<void>> {
@override
AsyncValue<void> build() {
final user = ref.watch(authStateProvider).value;
final profile = ref.watch(userProfileProvider).value;
// Nur aktiv, wenn der Snapshot sicher zum aktuellen Nutzer gehört und
// der Haushalt fehlt — bei Kontowechsel hält .value kurz den alten Stand.
// Maßstab ist householdId, nicht die Dokument-Existenz: die Push-
// Registrierung legt das users-Dokument auch ohne Haushalt an.
if (user == null ||
profile == null ||
profile.id != user.uid ||
profile.data()?['householdId'] != null) {
return const AsyncValue.data(null);
}
_run(user);
return const AsyncValue.loading();
}
Future<void> _run(User user) async {
try {
await ref
.read(authRepositoryProvider)
.ensureHouseholdBootstrap(user.uid, user.email);
// Kein Erfolgs-State nötig: das neue users-Dokument kommt über den
// Snapshot-Stream herein und baut diesen Notifier als „fertig“ neu.
} catch (error, stackTrace) {
if (!ref.mounted) return;
state = AsyncValue.error(error, stackTrace);
}
}
/// Nach einem Fehler erneut versuchen (Knopf im BootstrapGate).
void retry() {
final user = ref.read(authStateProvider).value;
if (user == null) return;
state = const AsyncValue.loading();
_run(user);
}
}
final householdBootstrapProvider =
NotifierProvider<HouseholdBootstrap, AsyncValue<void>>(
HouseholdBootstrap.new);