feat(flutter): add poke notification service
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
|
||||
import '../src/rust/api.dart' as rust;
|
||||
|
||||
class PokeNotificationService {
|
||||
PokeNotificationService({FlutterLocalNotificationsPlugin? notifications})
|
||||
: _notifications = notifications ?? FlutterLocalNotificationsPlugin();
|
||||
|
||||
static const _strongAndroidChannelId = 'chanora_pokes_strong_v1';
|
||||
static const _defaultAndroidChannelId = 'chanora_pokes_default_v1';
|
||||
static const _groupKey = 'chanora.pokes';
|
||||
static const _darwinThreadId = 'chanora.pokes';
|
||||
static const _windowsHeader = WindowsHeader(
|
||||
id: 'chanora.pokes',
|
||||
title: 'Pokes',
|
||||
arguments: 'pokes',
|
||||
);
|
||||
static const _windowsAppUserModelId = 'Chanora.Client';
|
||||
static const _windowsGuid = '6B7F3DCB-4418-4E0A-8CC7-02B7C95B675E';
|
||||
|
||||
final FlutterLocalNotificationsPlugin _notifications;
|
||||
bool _initialized = false;
|
||||
|
||||
Future<void> init() async {
|
||||
if (_initialized) return;
|
||||
await _notifications.initialize(
|
||||
settings: const InitializationSettings(
|
||||
android: AndroidInitializationSettings('ic_chanora_notification'),
|
||||
iOS: DarwinInitializationSettings(
|
||||
requestAlertPermission: false,
|
||||
requestBadgePermission: false,
|
||||
// TODO(event-sounds): handled by future EventSoundService, not the OS channel.
|
||||
requestSoundPermission: false,
|
||||
// TODO(event-sounds): handled by future EventSoundService, not the OS channel.
|
||||
defaultPresentSound: false,
|
||||
),
|
||||
macOS: DarwinInitializationSettings(
|
||||
requestAlertPermission: false,
|
||||
requestBadgePermission: false,
|
||||
// TODO(event-sounds): handled by future EventSoundService, not the OS channel.
|
||||
requestSoundPermission: false,
|
||||
// TODO(event-sounds): handled by future EventSoundService, not the OS channel.
|
||||
defaultPresentSound: false,
|
||||
),
|
||||
linux: LinuxInitializationSettings(
|
||||
defaultActionName: 'Open',
|
||||
// TODO(event-sounds): handled by future EventSoundService, not the OS channel.
|
||||
defaultSuppressSound: true,
|
||||
),
|
||||
windows: WindowsInitializationSettings(
|
||||
appName: 'Chanora',
|
||||
appUserModelId: _windowsAppUserModelId,
|
||||
guid: _windowsGuid,
|
||||
),
|
||||
),
|
||||
);
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
Future<bool> requestPermission() async {
|
||||
await init();
|
||||
if (kIsWeb) return true;
|
||||
|
||||
final android = _notifications
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin
|
||||
>();
|
||||
if (android != null) {
|
||||
return await android.requestNotificationsPermission() ?? true;
|
||||
}
|
||||
|
||||
final ios = _notifications
|
||||
.resolvePlatformSpecificImplementation<
|
||||
IOSFlutterLocalNotificationsPlugin
|
||||
>();
|
||||
if (ios != null) {
|
||||
return await ios.requestPermissions(alert: true, badge: true) ?? false;
|
||||
}
|
||||
|
||||
final macOS = _notifications
|
||||
.resolvePlatformSpecificImplementation<
|
||||
MacOSFlutterLocalNotificationsPlugin
|
||||
>();
|
||||
if (macOS != null) {
|
||||
return await macOS.requestPermissions(alert: true, badge: true) ?? false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> show({
|
||||
required String senderName,
|
||||
required String message,
|
||||
required BigInt senderId,
|
||||
required rust.BridgePokeStrength strength,
|
||||
}) async {
|
||||
await init();
|
||||
final permitted = await requestPermission();
|
||||
if (!permitted) return;
|
||||
|
||||
final trimmedMessage = message.trim();
|
||||
final body = trimmedMessage.isEmpty
|
||||
? '$senderName pokes you'
|
||||
: trimmedMessage;
|
||||
|
||||
await _notifications.show(
|
||||
id: senderId.toUnsigned(31).toInt(),
|
||||
title: 'Poke from $senderName',
|
||||
body: body,
|
||||
notificationDetails: NotificationDetails(
|
||||
android: _androidDetails(strength),
|
||||
iOS: _darwinDetails(strength),
|
||||
macOS: _darwinDetails(strength),
|
||||
linux: _linuxDetails(strength),
|
||||
windows: _windowsDetails(strength),
|
||||
),
|
||||
payload: 'poke:$senderId',
|
||||
);
|
||||
}
|
||||
|
||||
AndroidNotificationDetails _androidDetails(rust.BridgePokeStrength strength) {
|
||||
final isStrong = strength == rust.BridgePokeStrength.strong;
|
||||
return AndroidNotificationDetails(
|
||||
isStrong ? _strongAndroidChannelId : _defaultAndroidChannelId,
|
||||
isStrong ? 'Pokes' : 'Pokes (quiet)',
|
||||
channelDescription: 'TeamSpeak poke notifications',
|
||||
importance: isStrong ? Importance.max : Importance.defaultImportance,
|
||||
priority: isStrong ? Priority.high : Priority.defaultPriority,
|
||||
// TODO(event-sounds): handled by future EventSoundService, not the OS channel.
|
||||
playSound: false,
|
||||
// TODO(event-sounds): handled by future EventSoundService, not the OS channel.
|
||||
silent: true,
|
||||
groupKey: _groupKey,
|
||||
category: AndroidNotificationCategory.message,
|
||||
visibility: NotificationVisibility.private,
|
||||
);
|
||||
}
|
||||
|
||||
DarwinNotificationDetails _darwinDetails(rust.BridgePokeStrength strength) {
|
||||
return DarwinNotificationDetails(
|
||||
// TODO(event-sounds): handled by future EventSoundService, not the OS channel.
|
||||
presentSound: false,
|
||||
threadIdentifier: _darwinThreadId,
|
||||
interruptionLevel: switch (strength) {
|
||||
rust.BridgePokeStrength.strong => InterruptionLevel.timeSensitive,
|
||||
rust.BridgePokeStrength.suppressed => InterruptionLevel.active,
|
||||
rust.BridgePokeStrength.suppressedOverflow => InterruptionLevel.passive,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
LinuxNotificationDetails _linuxDetails(rust.BridgePokeStrength strength) {
|
||||
return LinuxNotificationDetails(
|
||||
// TODO(event-sounds): handled by future EventSoundService, not the OS channel.
|
||||
suppressSound: true,
|
||||
urgency: switch (strength) {
|
||||
rust.BridgePokeStrength.strong => LinuxNotificationUrgency.critical,
|
||||
rust.BridgePokeStrength.suppressed => LinuxNotificationUrgency.normal,
|
||||
rust.BridgePokeStrength.suppressedOverflow =>
|
||||
LinuxNotificationUrgency.low,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
WindowsNotificationDetails _windowsDetails(rust.BridgePokeStrength strength) {
|
||||
return WindowsNotificationDetails(
|
||||
// TODO(event-sounds): handled by future EventSoundService, not the OS channel.
|
||||
audio: WindowsNotificationAudio.silent(),
|
||||
header: _windowsHeader,
|
||||
scenario: strength == rust.BridgePokeStrength.strong
|
||||
? WindowsNotificationScenario.urgent
|
||||
: null,
|
||||
duration: strength == rust.BridgePokeStrength.strong
|
||||
? WindowsNotificationDuration.long
|
||||
: WindowsNotificationDuration.short,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user