import 'dart:async'; import 'dart:io'; import 'package:firebase_messaging/firebase_messaging.dart'; typedef FcmTokenCallback = Future Function(String token, String platform); class PushService { PushService({required this.firebaseReady}); final bool firebaseReady; final _subscriptions = >[]; bool _listening = false; Future listen({ required void Function(String ticketId) onOpenTicket, required void Function(RemoteMessage message) onForegroundMessage, required FcmTokenCallback onToken, required bool Function() isSignedIn, }) async { if (!firebaseReady || _listening) { return; } _listening = true; _subscriptions ..add( FirebaseMessaging.instance.onTokenRefresh.listen((token) async { try { if (!isSignedIn()) { return; } final settings = await FirebaseMessaging.instance .getNotificationSettings(); if (!_allowsNotifications(settings.authorizationStatus)) { return; } await onToken(token, _platform); } on Object { // Keep the previous registration when a token refresh cannot be saved. } }), ) ..add(FirebaseMessaging.onMessage.listen(onForegroundMessage)) ..add( FirebaseMessaging.onMessageOpenedApp.listen((message) { final ticketId = _ticketId(message); if (ticketId != null) { onOpenTicket(ticketId); } }), ); try { final initial = await FirebaseMessaging.instance.getInitialMessage(); if (initial != null) { final ticketId = _ticketId(initial); if (ticketId != null) { onOpenTicket(ticketId); } } } on Object { // Opening from a notification can wait until the next tap. } } Future register(FcmTokenCallback onToken) async { if (!firebaseReady) { return; } try { final settings = await FirebaseMessaging.instance.requestPermission( alert: true, badge: true, sound: true, ); if (!_allowsNotifications(settings.authorizationStatus)) { return; } final token = await FirebaseMessaging.instance.getToken(); if (token == null || token.isEmpty) { return; } await onToken(token, _platform); } on Object { // Denied permission or a missing Firebase file should not block the inbox. } } String? _ticketId(RemoteMessage message) { final value = message.data['ticketId']; if (value is String && value.isNotEmpty) { return value; } return null; } bool _allowsNotifications(AuthorizationStatus status) { return status == AuthorizationStatus.authorized || status == AuthorizationStatus.provisional; } String get _platform => Platform.isIOS ? 'ios' : 'android'; }