Services

Firebase Service

The Firebase service file provides the implementation of internal notifications using Firebase Firestore. It contains the necessary methods and structures to interact with Firebase Firestore and manage internal notifications.

Internal Notifications With Firebase Class

The InternalNotificationsWithFirebase class implements the InternalNotificationsService abstract class specifically for handling internal notifications using Firebase Firestore. It includes methods for initializing the service, subscribing to and unsubscribing from notifications, pushing notifications to users, and managing internal notification streams.

Integration

To integrate Firebase-based internal notifications using this service file, follow these steps:

  1. Ensure that you have set up Firebase Firestore in your Flutter project separately and have the required collections and permissions in place to store internal notifications.
  2. Ensure that you have the necessary Firebase-related configurations set up in your Flutter project by running the flutterfire configure command. This step ensures that your project is properly connected to Firebase.
  3. Implement the necessary logic to initialize Firebase Firestore and retrieve the user ID based on your application's authentication or identification system. Also when subscribing for perticular user user id is needed so for that logic should be implemented by developer in the init() method of this class to assign user id in the service, the logic may vary depending on the project.

::alert{type="info" }class="flex items-center p-4 mb-4 text-sm text-blue-800 border border-blue-300 rounded-lg bg-blue-50 dark:bg-gray-800 dark:text-blue-400 dark:border-blue-800" role="alert" Note:
Remember, the Firebase service file provided here serves as a part of the overall internal notifications system and should be used within the context of the main internal notifications file. It provides the implementation for Firebase Firestore-based notifications, while the main file acts as a central point for integrating different notification services. ::

Souce Code

import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';

import './base_service.dart';
import '../../models/notification.dart';

class InternalNotificationsWithFirebase implements InternalNotificationsService {
  final FirebaseFirestore _firebaseFirestore = FirebaseFirestore.instance;

  final StreamController<int> _pendingNotificationsCountStreamController =
      StreamController<int>.broadcast();

  @override
  late final Stream<int> pendingNotificationsCountStream;

  final StreamController<List<InternalNotification>> _notificationsStreamController =
      StreamController<List<InternalNotification>>.broadcast();

  @override
  late final Stream<List<InternalNotification>> notificationsStream;

  List<InternalNotification> _notifications = [];

  @override
  List<InternalNotification> get notifications => _notifications;

  // collection ids
  final String _notificationsCollection = 'notifications';
  final String _notificationsDataCollection = 'notification_data';

  late final String userId;

  StreamSubscription? _firebaseNotificationStream;

  @override
  Future<void> init() async {
    pendingNotificationsCountStream = _pendingNotificationsCountStreamController.stream;
    notificationsStream = _notificationsStreamController.stream;

    // Write your logic here to get user id
    userId = 'test';

    final bool hasSubscribed =
        (await _firebaseFirestore.collection(_notificationsCollection).doc(userId).get())
            .data()?['has_subscribed'];

    if (hasSubscribed) {
      _firebaseNotificationStream = _firebaseFirestore
          .collection(_notificationsCollection)
          .doc(userId)
          .collection(_notificationsDataCollection)
          .snapshots()
          .listen(_notificationsUpdated);
    }
  }

  @override
  Future<void> dispose() async {
    _firebaseNotificationStream?.cancel();
    _pendingNotificationsCountStreamController.close();
    _notificationsStreamController.close();
  }

  @override
  Future<void> subscribe() async {
    await _firebaseFirestore
        .collection(_notificationsCollection)
        .doc(userId)
        .set({'has_subscribed': true});

    _firebaseNotificationStream?.cancel(); // To be safe we cancel old subscription if there is any
    _firebaseNotificationStream = _firebaseFirestore
        .collection(_notificationsCollection)
        .doc(userId)
        .collection(_notificationsDataCollection)
        .snapshots()
        .listen(_notificationsUpdated);
  }

  @override
  Future<void> unsubscribe() async {
    await _firebaseFirestore
        .collection(_notificationsCollection)
        .doc(userId)
        .set({'has_subscribed': false});
    _firebaseNotificationStream?.cancel();
  }

  @override
  Future<void> push(List<String> userIds, InternalNotification notification) async {
    for (final id in userIds) {
      await _firebaseFirestore
          .collection(_notificationsCollection)
          .doc(id)
          .collection(_notificationsDataCollection)
          .add(notification.toJson());
    }
  }

  void _notificationsUpdated(QuerySnapshot<Map<String, dynamic>> event) {
    int count = 0;
    final List<InternalNotification> updatedNotifications = [];
    for (final doc in event.docs) {
      final InternalNotification notification = InternalNotification.fromJson(doc.data());
      if (!notification.opened) count++;
      updatedNotifications.add(notification);
    }
    _pendingNotificationsCountStreamController.add(count);
    _notificationsStreamController.add(updatedNotifications);
    _notifications = updatedNotifications;
  }
}

Copyright © 2024