import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; class NotificationService { static final NotificationService _instance = NotificationService._internal(); factory NotificationService() => _instance; NotificationService._internal(); FirebaseMessaging? _messaging; static const String vapidKey = 'BKrFSFm2cb2DNtEpTNmEy3acpi2ziRA5DhzKSyjshqAWANaoydztUTa0Cn3jwh1v7KN6pHUQfsODFXUWrKG6aSU'; static const List topics = [ 'all', 'newposts', 'newinvites', 'invitesfollowup', 'appnews', ]; Future initialize() async { try { if (!kIsWeb) { print('Notifications are only supported on web platform'); return; } if (!_isNotificationSupported()) { print('Notifications are not supported in this browser'); return; } _messaging = FirebaseMessaging.instance; await _requestPermission(); await _subscribeToTopics(); await _setupMessageHandlers(); } catch (e) { print('Error initializing notifications: $e'); } } bool _isNotificationSupported() { if (!kIsWeb) return false; // Check if the browser supports notifications try { // This will throw an error if notifications are not supported return true; // Firebase already handles browser support checks } catch (e) { return false; } } Future _requestPermission() async { if (_messaging == null) return; try { NotificationSettings settings = await _messaging!.requestPermission( alert: true, announcement: false, badge: true, carPlay: false, criticalAlert: false, provisional: false, sound: true, ); print('User granted permission: ${settings.authorizationStatus}'); if (settings.authorizationStatus == AuthorizationStatus.authorized) { print('User granted permission for notifications'); } else if (settings.authorizationStatus == AuthorizationStatus.provisional) { print('User granted provisional permission for notifications'); } else { print('User declined or has not accepted permission for notifications'); } } catch (e) { print('Error requesting notification permission: $e'); } } Future _subscribeToTopics() async { if (_messaging == null) return; for (String topic in topics) { try { await _messaging!.subscribeToTopic(topic); print('Subscribed to topic: $topic'); } catch (e) { print('Error subscribing to topic $topic: $e'); } } } Future _setupMessageHandlers() async { if (_messaging == null) return; // Handle foreground messages FirebaseMessaging.onMessage.listen((RemoteMessage message) { print('Got a message whilst in the foreground!'); print('Message data: ${message.data}'); if (message.notification != null) { print('Message also contained a notification: ${message.notification}'); _showNotification(message.notification!); } }); // Handle background messages FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); // Handle notification taps FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { print('A new onMessageOpenedApp event was published!'); print('Message data: ${message.data}'); _handleNotificationTap(message); }); // Get the initial message if the app was opened from a notification RemoteMessage? initialMessage = await _messaging!.getInitialMessage(); if (initialMessage != null) { print('App opened from notification: ${initialMessage.data}'); _handleNotificationTap(initialMessage); } // Get the FCM token for this device String? token = await _messaging!.getToken(vapidKey: vapidKey); print('FCM Token: $token'); } void _showNotification(RemoteNotification notification) { // For web, we rely on the service worker to show notifications // This is mainly for logging and debugging print('Notification Title: ${notification.title}'); print('Notification Body: ${notification.body}'); } void _handleNotificationTap(RemoteMessage message) { // Handle notification tap actions here print('Notification tapped: ${message.data}'); // You can navigate to specific screens based on the notification data // For example: // if (message.data['type'] == 'newpost') { // // Navigate to posts screen // } else if (message.data['type'] == 'newinvite') { // // Navigate to invitations screen // } } Future unsubscribeFromTopic(String topic) async { if (_messaging == null) return; try { await _messaging!.unsubscribeFromTopic(topic); print('Unsubscribed from topic: $topic'); } catch (e) { print('Error unsubscribing from topic $topic: $e'); } } Future subscribeToTopic(String topic) async { if (_messaging == null) return; try { await _messaging!.subscribeToTopic(topic); print('Subscribed to topic: $topic'); } catch (e) { print('Error subscribing to topic $topic: $e'); } } Future getToken() async { if (_messaging == null) return null; return await _messaging!.getToken(vapidKey: vapidKey); } Future getNotificationStatus() async { if (!kIsWeb) { return NotificationStatus.notSupported; } if (!_isNotificationSupported()) { return NotificationStatus.notSupported; } if (_messaging == null) { return NotificationStatus.notInitialized; } try { NotificationSettings settings = await _messaging!.getNotificationSettings(); switch (settings.authorizationStatus) { case AuthorizationStatus.authorized: return NotificationStatus.enabled; case AuthorizationStatus.provisional: return NotificationStatus.provisional; case AuthorizationStatus.denied: return NotificationStatus.denied; case AuthorizationStatus.notDetermined: return NotificationStatus.notDetermined; default: return NotificationStatus.denied; } } catch (e) { print('Error getting notification status: $e'); return NotificationStatus.error; } } } enum NotificationStatus { enabled, provisional, denied, notDetermined, notSupported, notInitialized, error } // Background message handler must be a top-level function Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { print('Handling a background message: ${message.messageId}'); print('Message data: ${message.data}'); if (message.notification != null) { print('Background message contained a notification: ${message.notification}'); } }