meshcore-open/lib/services/notification_service.dart
446564 05fb5a13fa remove direct msg notification prefix
The prefix "New message from " takes up a lot of space and was not localized anyway.
2026-02-05 08:33:07 -08:00

241 lines
6.1 KiB
Dart

import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter/foundation.dart';
class NotificationService {
static final NotificationService _instance = NotificationService._internal();
factory NotificationService() => _instance;
NotificationService._internal();
final FlutterLocalNotificationsPlugin _notifications =
FlutterLocalNotificationsPlugin();
bool _isInitialized = false;
Future<void> initialize() async {
if (_isInitialized) return;
const androidSettings = AndroidInitializationSettings(
'@mipmap/ic_launcher',
);
const iosSettings = DarwinInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
);
const macSettings = DarwinInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
);
const initSettings = InitializationSettings(
android: androidSettings,
iOS: iosSettings,
macOS: macSettings,
);
try {
await _notifications.initialize(
initSettings,
onDidReceiveNotificationResponse: _onNotificationTapped,
);
_isInitialized = true;
} catch (e) {
debugPrint('Error initializing notifications: $e');
}
}
Future<bool> requestPermissions() async {
if (!_isInitialized) {
await initialize();
}
// Request Android 13+ notification permission
final androidPlugin = _notifications
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin
>();
if (androidPlugin != null) {
final granted = await androidPlugin.requestNotificationsPermission();
return granted ?? false;
}
// iOS permissions are requested during initialization
final iosPlugin = _notifications
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin
>();
if (iosPlugin != null) {
final granted = await iosPlugin.requestPermissions(
alert: true,
badge: true,
sound: true,
);
return granted ?? false;
}
return true;
}
Future<void> showMessageNotification({
required String contactName,
required String message,
String? contactId,
int? badgeCount,
}) async {
if (!_isInitialized) {
await initialize();
}
final androidDetails = AndroidNotificationDetails(
'messages',
'Messages',
channelDescription: 'New message notifications',
importance: Importance.high,
priority: Priority.high,
icon: '@mipmap/ic_launcher',
number: badgeCount,
);
final iosDetails = DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
badgeNumber: badgeCount,
);
final macDetails = DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
badgeNumber: badgeCount,
);
final notificationDetails = NotificationDetails(
android: androidDetails,
iOS: iosDetails,
macOS: macDetails,
);
await _notifications.show(
contactId?.hashCode ?? 0,
contactName,
message,
notificationDetails,
payload: 'message:$contactId',
);
}
Future<void> showAdvertNotification({
required String contactName,
required String contactType,
String? contactId,
}) async {
if (!_isInitialized) {
await initialize();
}
const androidDetails = AndroidNotificationDetails(
'adverts',
'Advertisements',
channelDescription: 'New node advertisement notifications',
importance: Importance.defaultImportance,
priority: Priority.defaultPriority,
icon: '@mipmap/ic_launcher',
);
const iosDetails = DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
);
const macDetails = DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
);
const notificationDetails = NotificationDetails(
android: androidDetails,
iOS: iosDetails,
macOS: macDetails,
);
await _notifications.show(
contactId?.hashCode ?? DateTime.now().millisecondsSinceEpoch,
'New $contactType discovered',
contactName,
notificationDetails,
payload: 'advert:$contactId',
);
}
Future<void> showChannelMessageNotification({
required String channelName,
required String message,
int? channelIndex,
int? badgeCount,
}) async {
if (!_isInitialized) {
await initialize();
}
final androidDetails = AndroidNotificationDetails(
'channel_messages',
'Channel Messages',
channelDescription: 'New channel message notifications',
importance: Importance.high,
priority: Priority.high,
icon: '@mipmap/ic_launcher',
number: badgeCount,
);
final iosDetails = DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
badgeNumber: badgeCount,
);
final macDetails = DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
badgeNumber: badgeCount,
);
final notificationDetails = NotificationDetails(
android: androidDetails,
iOS: iosDetails,
macOS: macDetails,
);
final preview = message.trim();
final body = preview.isEmpty ? 'Received new message' : preview;
await _notifications.show(
channelIndex?.hashCode ?? DateTime.now().millisecondsSinceEpoch,
channelName,
body,
notificationDetails,
payload: 'channel:$channelIndex',
);
}
void _onNotificationTapped(NotificationResponse response) {
final payload = response.payload;
if (payload != null) {
debugPrint('Notification tapped: $payload');
// Handle navigation based on payload
// This can be extended to navigate to specific screens
}
}
Future<void> cancelAll() async {
await _notifications.cancelAll();
}
Future<void> cancel(int id) async {
await _notifications.cancel(id);
}
}