registerDevice lief nur bei Auth-Änderungen (ref.listen) – bestand der Login schon beim App-Start, wurde nie ein FCM-Token gespeichert. Jetzt reaktiv über pushSetupProvider (ref.watch). Zusätzlich: temporäre debugReminders-Function zur Diagnose der Push-Strecke. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
83 lines
2.9 KiB
Dart
83 lines
2.9 KiB
Dart
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:flutter_timezone/flutter_timezone.dart';
|
|
|
|
import '../../../core/firebase/firebase_providers.dart';
|
|
|
|
/// Registriert das Gerät für Push-Erinnerungen und hält die
|
|
/// Erinnerungs-Einstellungen im Nutzer-Dokument aktuell.
|
|
///
|
|
/// Abstrakte Basis, damit Tests eine No-Op-Variante einsetzen können.
|
|
abstract class PushRegistrationService {
|
|
Future<void> registerDevice(User user);
|
|
Future<void> syncReminderTime(TimeOfDay time);
|
|
}
|
|
|
|
class FirebasePushRegistrationService implements PushRegistrationService {
|
|
FirebasePushRegistrationService(this._firestore, this._auth);
|
|
|
|
final FirebaseFirestore _firestore;
|
|
final FirebaseAuth _auth;
|
|
bool _tokenListenerAttached = false;
|
|
|
|
@override
|
|
Future<void> registerDevice(User user) async {
|
|
final messaging = FirebaseMessaging.instance;
|
|
|
|
// Fragt beim ersten Mal die Push-Berechtigung ab (iOS-Dialog,
|
|
// Android 13+ Systemdialog); danach ist der Aufruf ein No-Op.
|
|
final settings = await messaging.requestPermission();
|
|
if (settings.authorizationStatus == AuthorizationStatus.denied) return;
|
|
|
|
final token = await messaging.getToken();
|
|
if (token != null) {
|
|
await _saveToken(user.uid, token);
|
|
}
|
|
if (!_tokenListenerAttached) {
|
|
_tokenListenerAttached = true;
|
|
messaging.onTokenRefresh.listen((newToken) {
|
|
final current = _auth.currentUser;
|
|
if (current != null) _saveToken(current.uid, newToken);
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> _saveToken(String uid, String token) async {
|
|
final timezone = await FlutterTimezone.getLocalTimezone();
|
|
await _firestore.collection('users').doc(uid).set({
|
|
'fcmTokens': FieldValue.arrayUnion([token]),
|
|
'timezone': timezone,
|
|
}, SetOptions(merge: true));
|
|
}
|
|
|
|
@override
|
|
Future<void> syncReminderTime(TimeOfDay time) async {
|
|
final user = _auth.currentUser;
|
|
if (user == null) return;
|
|
final formatted =
|
|
'${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}';
|
|
await _firestore.collection('users').doc(user.uid).set({
|
|
'reminderTime': formatted,
|
|
}, SetOptions(merge: true));
|
|
}
|
|
}
|
|
|
|
final pushRegistrationServiceProvider = Provider<PushRegistrationService>((ref) {
|
|
return FirebasePushRegistrationService(
|
|
ref.watch(firestoreProvider),
|
|
ref.watch(firebaseAuthProvider),
|
|
);
|
|
});
|
|
|
|
/// Sorgt dafür, dass das Gerät registriert ist, sobald (und solange) ein
|
|
/// Nutzer angemeldet ist — auch wenn der Login schon vor dem App-Start
|
|
/// bestand. Wird in app.dart per ref.watch aktiv gehalten.
|
|
final pushSetupProvider = Provider<void>((ref) {
|
|
final user = ref.watch(authStateProvider).value;
|
|
if (user != null) {
|
|
ref.read(pushRegistrationServiceProvider).registerDevice(user);
|
|
}
|
|
});
|