262 lines
7.3 KiB
Dart
262 lines
7.3 KiB
Dart
// 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<String, DateTime> _lastNotificationTime = {};
|
|
final Duration _notificationCooldown = const Duration(minutes: 5);
|
|
|
|
Future<void> 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<void> _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<void> _onNotificationCreated(
|
|
ReceivedNotification receivedNotification,
|
|
) async {
|
|
print('Notification created: ${receivedNotification.id}');
|
|
}
|
|
|
|
@pragma("vm:entry-point")
|
|
static Future<void> _onNotificationDisplayed(
|
|
ReceivedNotification receivedNotification,
|
|
) async {
|
|
print('Notification displayed: ${receivedNotification.id}');
|
|
}
|
|
|
|
@pragma("vm:entry-point")
|
|
static Future<void> _onNotificationTapped(
|
|
ReceivedAction receivedAction,
|
|
) async {
|
|
print('Notification tapped: ${receivedAction.payload}');
|
|
}
|
|
|
|
@pragma("vm:entry-point")
|
|
static Future<void> _onNotificationDismissed(
|
|
ReceivedAction receivedAction,
|
|
) async {
|
|
print('Notification dismissed: ${receivedAction.id}');
|
|
}
|
|
|
|
Future<void> 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<void> 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<void> _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<void> cancelNotification(int id) async {
|
|
await AwesomeNotifications().cancel(id);
|
|
}
|
|
|
|
Future<void> cancelNotificationByDeviceId(String deviceId) async {
|
|
await AwesomeNotifications().cancel(deviceId.hashCode);
|
|
}
|
|
|
|
Future<void> cancelAllNotifications() async {
|
|
await AwesomeNotifications().cancelAll();
|
|
}
|
|
|
|
void dispose() {
|
|
_lastNotificationTime.clear();
|
|
}
|
|
}
|