// lib/app/services/notification_service.dart import 'package:flutter/material.dart'; import 'package:awesome_notifications/awesome_notifications.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import '../models/notification_model.dart' as local; class NotificationService { static final NotificationService _instance = NotificationService._internal(); factory NotificationService() => _instance; NotificationService._internal(); final FirebaseFirestore _firestore = FirebaseFirestore.instance; final Map _lastNotificationTime = {}; final Duration _notificationCooldown = const Duration(minutes: 5); Future initialize() async { await AwesomeNotifications().initialize(null, [ NotificationChannel( channelKey: 'infus_alert_channel', channelName: 'Infus Alerts', channelDescription: 'Alerts for infusion monitoring', defaultColor: const Color(0xFF9D50DD), ledColor: Colors.white, importance: NotificationImportance.High, channelShowBadge: true, playSound: true, enableVibration: true, ), ]); await _requestPermissions(); _setupNotificationListeners(); } Future _requestPermissions() async { await AwesomeNotifications().isNotificationAllowed().then(( isAllowed, ) async { if (!isAllowed) { await AwesomeNotifications().requestPermissionToSendNotifications(); } }); } void _setupNotificationListeners() { AwesomeNotifications().setListeners( onActionReceivedMethod: _onNotificationTapped, onNotificationCreatedMethod: _onNotificationCreated, onNotificationDisplayedMethod: _onNotificationDisplayed, onDismissActionReceivedMethod: _onNotificationDismissed, ); } @pragma("vm:entry-point") static Future _onNotificationCreated( ReceivedNotification receivedNotification, ) async { print('Notification created: ${receivedNotification.id}'); } @pragma("vm:entry-point") static Future _onNotificationDisplayed( ReceivedNotification receivedNotification, ) async { print('Notification displayed: ${receivedNotification.id}'); } @pragma("vm:entry-point") static Future _onNotificationTapped( ReceivedAction receivedAction, ) async { print('Notification tapped: ${receivedAction.payload}'); } @pragma("vm:entry-point") static Future _onNotificationDismissed( ReceivedAction receivedAction, ) async { print('Notification dismissed: ${receivedAction.id}'); } Future showDropRateAlert({ required String patientId, required String patientName, required String deviceId, required String roomName, }) async { if (_isInCooldown(deviceId)) { return; } final title = 'Peringatan Infus ⚠️'; final message = 'Infus $patientName di ruang $roomName telah berhenti menetes! Segera lakukan Tindakan!'; await AwesomeNotifications().createNotification( content: NotificationContent( id: deviceId.hashCode, channelKey: 'infus_alert_channel', title: title, body: message, notificationLayout: NotificationLayout.Default, payload: { 'device_id': deviceId, 'patient_id': patientId, 'patient_name': patientName, 'room_name': roomName, }, category: NotificationCategory.Alarm, wakeUpScreen: true, fullScreenIntent: true, autoDismissible: false, backgroundColor: Colors.red, color: Colors.red, ), actionButtons: [ NotificationActionButton( key: 'VIEW', label: 'Lihat Detail', actionType: ActionType.Default, ), NotificationActionButton( key: 'DISMISS', label: 'Tutup', actionType: ActionType.DismissAction, isDangerousOption: true, ), ], ); await _saveNotificationToFirestore( patientId: patientId, deviceId: deviceId, title: title, message: message, ); _lastNotificationTime[deviceId] = DateTime.now(); print('Notification sent for device: $deviceId'); } Future showDropRateAlertForPatient({ required String patientId, required String patientName, required String deviceId, required String roomName, }) async { if (_isInCooldown(deviceId)) { return; } final title = 'Peringatan Infus ⚠️'; final message = 'Infus $patientName di ruang $roomName telah berhenti menetes! Segera hubungi Perawat!'; await AwesomeNotifications().createNotification( content: NotificationContent( id: deviceId.hashCode, channelKey: 'infus_alert_channel', title: title, body: message, notificationLayout: NotificationLayout.Default, payload: { 'device_id': deviceId, 'patient_id': patientId, 'patient_name': patientName, 'room_name': roomName, }, category: NotificationCategory.Alarm, wakeUpScreen: true, fullScreenIntent: true, autoDismissible: false, backgroundColor: Colors.red, color: Colors.red, ), actionButtons: [ NotificationActionButton( key: 'CALL', label: 'Hubungi Perawat', actionType: ActionType.Default, ), NotificationActionButton( key: 'DISMISS', label: 'Tutup', actionType: ActionType.DismissAction, isDangerousOption: true, ), ], ); await _saveNotificationToFirestore( patientId: patientId, deviceId: deviceId, title: title, message: message, ); _lastNotificationTime[deviceId] = DateTime.now(); print('Notification sent for patient device: $deviceId'); } Future _saveNotificationToFirestore({ required String patientId, required String deviceId, required String title, required String message, }) async { try { final notification = local.NotificationModel( id: '', patientId: patientId, deviceId: deviceId, title: title, message: message, isRead: false, createdAt: DateTime.now(), ); await _firestore .collection('notifications') .add(notification.toFirestore()); print('Notification saved to Firestore'); } catch (e) { print('Error saving notification to Firestore: $e'); } } bool _isInCooldown(String deviceId) { if (!_lastNotificationTime.containsKey(deviceId)) { return false; } final lastTime = _lastNotificationTime[deviceId]!; final difference = DateTime.now().difference(lastTime); return difference < _notificationCooldown; } void clearCooldown(String deviceId) { _lastNotificationTime.remove(deviceId); } void clearAllCooldowns() { _lastNotificationTime.clear(); } Future cancelNotification(int id) async { await AwesomeNotifications().cancel(id); } Future cancelNotificationByDeviceId(String deviceId) async { await AwesomeNotifications().cancel(deviceId.hashCode); } Future cancelAllNotifications() async { await AwesomeNotifications().cancelAll(); } void dispose() { _lastNotificationTime.clear(); } }