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>
73 lines
2.7 KiB
Dart
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);
|