diff --git a/README.md b/README.md
index a71904654..5a2287fe4 100644
--- a/README.md
+++ b/README.md
@@ -53,4 +53,6 @@ https://gitlab.com/famedly/fluffychat/-/wikis/How-To-Build
* Noto Emoji Font for the awesome emojis.
+* WoodenBeaver sound theme for the notification sound.
+
* The Matrix Foundation for making and maintaining the [emoji translations](https://github.com/matrix-org/matrix-doc/blob/main/data-definitions/sas-emoji.json) used for emoji verification, licensed Apache 2.0
diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb
index ef4877897..40f0d56ac 100644
--- a/assets/l10n/intl_en.arb
+++ b/assets/l10n/intl_en.arb
@@ -2706,5 +2706,7 @@
"storyPrivacyWarning": "Please note that people can see and contact each other in your story. Your stories will be visible for 24 hours but there is no guarantee that they will be deleted from all devices and servers.",
"@storyPrivacyWarning": {},
"iUnderstand": "I understand",
- "@iUnderstand": {}
+ "@iUnderstand": {},
+ "dismiss": "Dismiss",
+ "markAsRead": "Mark as read"
}
diff --git a/assets/sounds/WoodenBeaver_stereo_message-new-instant.ogg b/assets/sounds/WoodenBeaver_stereo_message-new-instant.ogg
new file mode 100644
index 000000000..3950f4903
Binary files /dev/null and b/assets/sounds/WoodenBeaver_stereo_message-new-instant.ogg differ
diff --git a/assets/sounds/notification.wav b/assets/sounds/notification.wav
deleted file mode 100644
index ec51a786b..000000000
Binary files a/assets/sounds/notification.wav and /dev/null differ
diff --git a/lib/widgets/local_notifications_extension.dart b/lib/widgets/local_notifications_extension.dart
new file mode 100644
index 000000000..36a7920e0
--- /dev/null
+++ b/lib/widgets/local_notifications_extension.dart
@@ -0,0 +1,92 @@
+import 'dart:io';
+
+import 'package:flutter/foundation.dart';
+
+import 'package:desktop_notifications/desktop_notifications.dart';
+import 'package:flutter_cache_manager/flutter_cache_manager.dart';
+import 'package:flutter_gen/gen_l10n/l10n.dart';
+import 'package:matrix/matrix.dart';
+import 'package:universal_html/html.dart' as html;
+
+import 'package:fluffychat/config/app_config.dart';
+import 'package:fluffychat/utils/matrix_sdk_extensions.dart/matrix_locals.dart';
+import 'package:fluffychat/widgets/matrix.dart';
+
+extension LocalNotificationsExtension on MatrixState {
+ void showLocalNotification(EventUpdate eventUpdate) async {
+ final roomId = eventUpdate.roomID;
+ if (webHasFocus && activeRoomId == roomId) return;
+ final room = client.getRoomById(roomId);
+ if (room.notificationCount == 0) return;
+ final event = Event.fromJson(eventUpdate.content, room);
+ final title =
+ room.getLocalizedDisplayname(MatrixLocals(L10n.of(widget.context)));
+ final body = event.getLocalizedBody(
+ MatrixLocals(L10n.of(widget.context)),
+ withSenderNamePrefix:
+ !room.isDirectChat || room.lastEvent.senderId == client.userID,
+ plaintextBody: true,
+ hideReply: true,
+ hideEdit: true,
+ );
+ final icon = event.sender.avatarUrl?.getThumbnail(client,
+ width: 64, height: 64, method: ThumbnailMethod.crop) ??
+ room.avatar?.getThumbnail(client,
+ width: 64, height: 64, method: ThumbnailMethod.crop);
+ if (kIsWeb) {
+ html.AudioElement()
+ ..src =
+ 'assets/assets/sounds/WoodenBeaver_stereo_message-new-instant.ogg'
+ ..autoplay = true
+ ..load();
+ html.Notification(
+ title,
+ body: body,
+ icon: icon.toString(),
+ );
+ } else if (Platform.isLinux) {
+ final appIconUrl = room.avatar
+ ?.getThumbnail(
+ room.client,
+ width: 56,
+ height: 56,
+ )
+ .toString();
+ final appIconFile = await DefaultCacheManager().getSingleFile(appIconUrl);
+ final notification = await linuxNotifications.notify(
+ title,
+ body: body,
+ replacesId: linuxNotificationIds[roomId] ?? 0,
+ appName: AppConfig.applicationName,
+ appIcon: appIconFile.path,
+ actions: [
+ NotificationAction(
+ DesktopNotificationActions.dismiss.name,
+ L10n.of(widget.context).dismiss,
+ ),
+ NotificationAction(
+ DesktopNotificationActions.seen.name,
+ L10n.of(widget.context).markAsRead,
+ ),
+ ],
+ hints: [
+ NotificationHint.soundName('message-new-instant'),
+ ],
+ );
+ notification.action.then((actionStr) {
+ final action = DesktopNotificationActions.values
+ .singleWhere((a) => a.name == actionStr);
+ switch (action) {
+ case DesktopNotificationActions.seen:
+ room.setReadMarker(event.eventId, mRead: event.eventId);
+ break;
+ case DesktopNotificationActions.dismiss:
+ break;
+ }
+ });
+ linuxNotificationIds[roomId] = notification.id;
+ }
+ }
+}
+
+enum DesktopNotificationActions { seen, dismiss }
diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart
index 82350ab24..ac992e3ab 100644
--- a/lib/widgets/matrix.dart
+++ b/lib/widgets/matrix.dart
@@ -21,7 +21,6 @@ import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/utils/client_manager.dart';
-import 'package:fluffychat/utils/matrix_sdk_extensions.dart/matrix_locals.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/sentry_controller.dart';
import 'package:fluffychat/utils/uia_request_manager.dart';
@@ -32,6 +31,7 @@ import '../utils/account_bundles.dart';
import '../utils/background_push.dart';
import '../utils/famedlysdk_store.dart';
import '../utils/platform_infos.dart';
+import 'local_notifications_extension.dart';
// import 'package:flutter_secure_storage/flutter_secure_storage.dart';
@@ -229,51 +229,9 @@ class MatrixState extends State with WidgetsBindingObserver {
String get activeRoomId =>
VRouter.of(navigatorContext).pathParameters['roomid'];
- void _showLocalNotification(EventUpdate eventUpdate) async {
- final roomId = eventUpdate.roomID;
- if (webHasFocus && activeRoomId == roomId) return;
- final room = client.getRoomById(roomId);
- if (room.notificationCount == 0) return;
- final event = Event.fromJson(eventUpdate.content, room);
- final title =
- room.getLocalizedDisplayname(MatrixLocals(L10n.of(widget.context)));
- final body = event.getLocalizedBody(
- MatrixLocals(L10n.of(widget.context)),
- withSenderNamePrefix:
- !room.isDirectChat || room.lastEvent.senderId == client.userID,
- plaintextBody: true,
- hideReply: true,
- hideEdit: true,
- );
- final icon = event.sender.avatarUrl?.getThumbnail(client,
- width: 64, height: 64, method: ThumbnailMethod.crop) ??
- room.avatar?.getThumbnail(client,
- width: 64, height: 64, method: ThumbnailMethod.crop);
- if (kIsWeb) {
- html.AudioElement()
- ..src = 'assets/assets/sounds/notification.wav'
- ..autoplay = true
- ..load();
- html.Notification(
- title,
- body: body,
- icon: icon.toString(),
- );
- } else if (Platform.isLinux) {
- final notification = await linuxNotifications.notify(
- title,
- body: body,
- replacesId: _linuxNotificationIds[roomId] ?? 0,
- appName: AppConfig.applicationName,
- appIcon: "im.fluffychat.Fluffychat",
- );
- _linuxNotificationIds[roomId] = notification.id;
- }
- }
-
final linuxNotifications =
PlatformInfos.isLinux ? NotificationsClient() : null;
- final Map _linuxNotificationIds = {};
+ final Map linuxNotificationIds = {};
@override
void initState() {
@@ -388,7 +346,7 @@ class MatrixState extends State with WidgetsBindingObserver {
[EventTypes.Message, EventTypes.Sticker, EventTypes.Encrypted]
.contains(e.content['type']) &&
e.content['sender'] != c.userID)
- .listen(_showLocalNotification);
+ .listen(showLocalNotification);
});
}
}