From a88849c2dbaf30228d6541515f48489030aec1c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Fri, 3 Oct 2025 09:14:53 +0200 Subject: [PATCH] chore: Follow up notification actions --- lib/utils/background_push.dart | 3 + .../notification_background_handler.dart | 87 +++++++++++++++++-- lib/utils/push_helper.dart | 21 ++++- 3 files changed, 103 insertions(+), 8 deletions(-) diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart index ca45e6076..eaea22d99 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -89,6 +89,7 @@ class BackgroundPush { NotificationResponseJson.fromJsonString(message), client: client, router: FluffyChatApp.router, + l10n: l10n, ); } catch (e, s) { Logs().wtf('Main Notification Tap crashed', e, s); @@ -105,6 +106,7 @@ class BackgroundPush { response, client: client, router: FluffyChatApp.router, + l10n: l10n, ), onDidReceiveBackgroundNotificationResponse: notificationTapBackground, ); @@ -313,6 +315,7 @@ class BackgroundPush { response, client: client, router: FluffyChatApp.router, + l10n: l10n, ); } }); diff --git a/lib/utils/notification_background_handler.dart b/lib/utils/notification_background_handler.dart index 8c9a279ee..972121443 100644 --- a/lib/utils/notification_background_handler.dart +++ b/lib/utils/notification_background_handler.dart @@ -8,7 +8,13 @@ import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/utils/client_manager.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; +import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/utils/push_helper.dart'; +import '../config/app_config.dart'; +import '../config/setting_keys.dart'; bool _vodInitialized = false; @@ -52,9 +58,10 @@ void notificationTapBackground( await vod.init(); _vodInitialized = true; } + final store = await SharedPreferences.getInstance(); final client = (await ClientManager.getClients( initialize: false, - store: await SharedPreferences.getInstance(), + store: store, )) .first; await client.abortSync(); @@ -62,6 +69,11 @@ void notificationTapBackground( waitForFirstSync: false, waitUntilLoadCompletedLoaded: false, ); + + AppConfig.sendPublicReadReceipts = + store.getBool(SettingKeys.sendPublicReadReceipts) ?? + AppConfig.sendPublicReadReceipts; + if (!client.isLogged()) { throw Exception('Notification tab in background but not logged in!'); } @@ -77,14 +89,17 @@ Future notificationTap( NotificationResponse notificationResponse, { GoRouter? router, required Client client, + L10n? l10n, }) async { Logs().d( 'Notification action handler started', notificationResponse.notificationResponseType.name, ); + final payload = + FluffyChatPushPayload.fromString(notificationResponse.payload ?? ''); switch (notificationResponse.notificationResponseType) { case NotificationResponseType.selectedNotification: - final roomId = notificationResponse.payload; + final roomId = payload.roomId; if (roomId == null) return; if (router == null) { @@ -111,7 +126,7 @@ Future notificationTap( if (actionType == null) { throw Exception('Selected notification with action but no action ID'); } - final roomId = notificationResponse.payload; + final roomId = payload.roomId; if (roomId == null) { throw Exception('Selected notification with action but no payload'); } @@ -127,9 +142,9 @@ Future notificationTap( switch (actionType) { case FluffyChatNotificationActions.markAsRead: await room.setReadMarker( - room.lastEvent!.eventId, - mRead: room.lastEvent!.eventId, - public: false, // TODO: Load preference here + payload.eventId ?? room.lastEvent!.eventId, + mRead: payload.eventId ?? room.lastEvent!.eventId, + public: AppConfig.sendPublicReadReceipts, ); case FluffyChatNotificationActions.reply: final input = notificationResponse.input; @@ -138,7 +153,65 @@ Future notificationTap( 'Selected notification with reply action but without input', ); } - await room.sendTextEvent(input); + + final eventId = await room.sendTextEvent(input); + + if (PlatformInfos.isAndroid) { + final messagingStyleInformation = + await AndroidFlutterLocalNotificationsPlugin() + .getActiveNotificationMessagingStyle(room.id.hashCode); + if (messagingStyleInformation == null) return; + l10n ??= await lookupL10n(const Locale('en')); + messagingStyleInformation.messages?.add( + Message( + input, + DateTime.now(), + Person(key: room.client.userID, name: l10n.you), + ), + ); + + await FlutterLocalNotificationsPlugin().show( + room.id.hashCode, + room.getLocalizedDisplayname(MatrixLocals(l10n)), + input, + NotificationDetails( + android: AndroidNotificationDetails( + AppConfig.pushNotificationsChannelId, + l10n.incomingMessages, + category: AndroidNotificationCategory.message, + shortcutId: room.id, + styleInformation: messagingStyleInformation, + groupKey: room.id, + playSound: false, + enableVibration: false, + actions: [ + AndroidNotificationAction( + FluffyChatNotificationActions.reply.name, + l10n.reply, + inputs: [ + AndroidNotificationActionInput( + label: l10n.writeAMessage, + ), + ], + cancelNotification: false, + allowGeneratedReplies: true, + semanticAction: SemanticAction.reply, + ), + AndroidNotificationAction( + FluffyChatNotificationActions.markAsRead.name, + l10n.markAsRead, + semanticAction: SemanticAction.markAsRead, + ), + ], + ), + ), + payload: FluffyChatPushPayload( + client.clientName, + room.id, + eventId, + ).toString(), + ); + } } } } diff --git a/lib/utils/push_helper.dart b/lib/utils/push_helper.dart index 2a4dc4700..7c2d1c15c 100644 --- a/lib/utils/push_helper.dart +++ b/lib/utils/push_helper.dart @@ -315,11 +315,30 @@ Future _tryPushHelper( title, body, platformChannelSpecifics, - payload: event.roomId, + payload: + FluffyChatPushPayload(client.clientName, event.room.id, event.eventId) + .toString(), ); Logs().v('Push helper has been completed!'); } +class FluffyChatPushPayload { + final String? clientName, roomId, eventId; + + FluffyChatPushPayload(this.clientName, this.roomId, this.eventId); + + factory FluffyChatPushPayload.fromString(String payload) { + final parts = payload.split('|'); + if (parts.length != 3) { + return FluffyChatPushPayload(null, null, null); + } + return FluffyChatPushPayload(parts[0], parts[1], parts[2]); + } + + @override + String toString() => '$clientName|$roomId|$eventId'; +} + /// Creates a shortcut for Android platform but does not block displaying the /// notification. This is optional but provides a nicer view of the /// notification popup.