From 26219e3fe7b165db82c3bcd01f4119bbda94c982 Mon Sep 17 00:00:00 2001 From: sherlock Date: Thu, 7 Sep 2023 00:34:50 +0700 Subject: [PATCH 1/4] TW-504: config notifications for default push rules --- .../settings_notifications.dart | 5 ++ .../client_push_rules_extension.dart | 23 ++++++ lib/widgets/matrix.dart | 76 ++++++++++++++++++- 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 lib/utils/matrix_sdk_extensions/client_push_rules_extension.dart diff --git a/lib/pages/settings_notifications/settings_notifications.dart b/lib/pages/settings_notifications/settings_notifications.dart index 5f2d5ff5e..f56c32e36 100644 --- a/lib/pages/settings_notifications/settings_notifications.dart +++ b/lib/pages/settings_notifications/settings_notifications.dart @@ -45,6 +45,11 @@ class NotificationSettingsItem { '.m.rule.suppress_notices', (c) => L10n.of(c)!.botMessages, ), + NotificationSettingsItem( + PushRuleKind.content, + '.m.rule.group_chat_name_changed', + (c) => "Group chat name changed", + ), ]; } diff --git a/lib/utils/matrix_sdk_extensions/client_push_rules_extension.dart b/lib/utils/matrix_sdk_extensions/client_push_rules_extension.dart new file mode 100644 index 000000000..0b1a7b300 --- /dev/null +++ b/lib/utils/matrix_sdk_extensions/client_push_rules_extension.dart @@ -0,0 +1,23 @@ +import 'package:matrix/matrix.dart'; + +extension ClientPushRulesExtension on Client { + Future setupUserDefinedPushRule({ + required String ruleId, + List? conditions, + String? after, + String? before, + }) async { + await setPushRule( + 'global', + PushRuleKind.underride, + ruleId, + [ + PushRuleAction.notify, + {"set_tweak": "sound", "value": "default"} + ], + conditions: conditions, + after: after, + before: before, + ); + } +} diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index 054e60069..50e30f74d 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -15,6 +15,7 @@ import 'package:fluffychat/domain/model/tom_server_information.dart'; import 'package:fluffychat/domain/repository/tom_configurations_repository.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/client_push_rules_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/uia_request_manager.dart'; import 'package:fluffychat/utils/url_launcher.dart'; @@ -448,6 +449,77 @@ class MatrixState extends State with WidgetsBindingObserver { createVoipPlugin(); } + Future _setUpUserDefinedPushRules(Client client) async { + await client.setPushRuleEnabled( + 'global', + PushRuleKind.override, + '.m.rule.invite_for_me', + false, + ); + await client.setPushRuleEnabled( + 'global', + PushRuleKind.override, + '.m.rule.member_event', + false, + ); + + await client.setupUserDefinedPushRule( + ruleId: 'm.rule.invite_for_me', + conditions: [ + PushCondition( + kind: 'event_match', + key: 'type', + pattern: EventTypes.RoomMember, + ), + PushCondition( + kind: 'event_match', + key: 'content.membership', + pattern: 'invite', + ), + PushCondition( + kind: 'event_match', + key: 'state_key', + pattern: '${client.userID}', + ), + ], + ); + + await client.setupUserDefinedPushRule( + ruleId: 'm.rule.change_group_name', + conditions: [ + PushCondition( + kind: 'event_match', + key: 'type', + pattern: EventTypes.RoomName, + ), + ], + after: 'm.rule.invite_for_me', + ); + + await client.setupUserDefinedPushRule( + ruleId: 'm.rule.change_avatar_group', + conditions: [ + PushCondition( + kind: 'event_match', + key: 'type', + pattern: EventTypes.RoomAvatar, + ), + ], + after: 'm.rule.invite_for_me', + ); + + await client.setupUserDefinedPushRule( + ruleId: 'm.rule.set_me_as_admin', + conditions: [ + PushCondition( + kind: 'event_match', + key: 'type', + pattern: EventTypes.RoomPowerLevels, + ), + ], + ); + } + void createVoipPlugin() async { if (await store.getItemBool(SettingKeys.experimentalVoip) == false) { voipPlugin = null; @@ -501,6 +573,7 @@ class MatrixState extends State with WidgetsBindingObserver { if (homeServer != null) { _setUpHomeServer(homeServer.baseUrl); } + _storeToMConfiguration(client, tomServer, identityServer); setUpAuthorization(client); } @@ -522,7 +595,7 @@ class MatrixState extends State with WidgetsBindingObserver { } } - void _setUpHomeServer(Uri homeServerUri) { + void _setUpHomeServer(Uri homeServerUri) async { final homeServerUrlInterceptor = getIt.get( instanceName: NetworkDI.homeServerUrlInterceptorName, ); @@ -530,6 +603,7 @@ class MatrixState extends State with WidgetsBindingObserver { 'MatrixState::_setUpHomeServer: ${homeServerUrlInterceptor.baseUrl}', ); homeServerUrlInterceptor.changeBaseUrl(homeServerUri.toString()); + _setUpUserDefinedPushRules(client); } void _setUpIdentityServer(IdentityServerInformation identityServer) { From 7ad282d6d579bcb7a238557526e74b1b54e419a2 Mon Sep 17 00:00:00 2001 From: sherlock Date: Thu, 7 Sep 2023 10:33:56 +0700 Subject: [PATCH 2/4] TW-504: add enable/disable button for push rules --- .../settings_notifications.dart | 26 ++++++------------- .../client_push_rules_extension.dart | 2 +- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/lib/pages/settings_notifications/settings_notifications.dart b/lib/pages/settings_notifications/settings_notifications.dart index f56c32e36..85a5f2779 100644 --- a/lib/pages/settings_notifications/settings_notifications.dart +++ b/lib/pages/settings_notifications/settings_notifications.dart @@ -22,33 +22,23 @@ class NotificationSettingsItem { ), NotificationSettingsItem( PushRuleKind.override, - '.m.rule.contains_display_name', - (c) => L10n.of(c)!.containsDisplayName, - ), - NotificationSettingsItem( - PushRuleKind.content, - '.m.rule.contains_user_name', - (c) => L10n.of(c)!.containsUserName, - ), - NotificationSettingsItem( - PushRuleKind.override, - '.m.rule.invite_for_me', + 'm.rule.invite_for_me', (c) => L10n.of(c)!.inviteForMe, ), NotificationSettingsItem( PushRuleKind.override, - '.m.rule.member_event', - (c) => L10n.of(c)!.memberChanges, + '.m.rule.suppress_notices', + (c) => L10n.of(c)!.botMessages, ), NotificationSettingsItem( PushRuleKind.override, - '.m.rule.suppress_notices', - (c) => L10n.of(c)!.botMessages, + 'm.rule.change_group_name', + (c) => "Group chat name change", ), NotificationSettingsItem( - PushRuleKind.content, - '.m.rule.group_chat_name_changed', - (c) => "Group chat name changed", + PushRuleKind.override, + 'm.rule.change_avatar_group', + (c) => "Group chat avatar change", ), ]; } diff --git a/lib/utils/matrix_sdk_extensions/client_push_rules_extension.dart b/lib/utils/matrix_sdk_extensions/client_push_rules_extension.dart index 0b1a7b300..e7504f343 100644 --- a/lib/utils/matrix_sdk_extensions/client_push_rules_extension.dart +++ b/lib/utils/matrix_sdk_extensions/client_push_rules_extension.dart @@ -9,7 +9,7 @@ extension ClientPushRulesExtension on Client { }) async { await setPushRule( 'global', - PushRuleKind.underride, + PushRuleKind.override, ruleId, [ PushRuleAction.notify, From 176cd8f7f1a66ed962e618ef53fe224a26a50b25 Mon Sep 17 00:00:00 2001 From: sherlock Date: Mon, 11 Sep 2023 02:09:01 +0700 Subject: [PATCH 3/4] fixup! TW-504: add enable/disable button for push rules --- assets/l10n/intl_en.arb | 5 + .../settings_notifications.dart | 21 ++- .../client_push_rules_extension.dart | 35 ++-- lib/widgets/matrix.dart | 155 ++++++++++-------- 4 files changed, 132 insertions(+), 84 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 712db462d..7e36f9159 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2621,6 +2621,11 @@ "searchSuggestion": "For now, search by typing a person’s name or public server address", "loadingContacts": "Loading contacts...", "recentlyChats": "Recent chats", + "groupNameChange": "Group chat name change", + "groupAvatarChange": "Group chat avatar change", + "setMeAsAdmin": "Set me as admin", + "mentionsMe": "Mention me", + "groupChat": "Group chat", "search": "Search", "forwardTo": "Forward to...", "noConnection": "No connection", diff --git a/lib/pages/settings_notifications/settings_notifications.dart b/lib/pages/settings_notifications/settings_notifications.dart index 85a5f2779..988cdfbd1 100644 --- a/lib/pages/settings_notifications/settings_notifications.dart +++ b/lib/pages/settings_notifications/settings_notifications.dart @@ -17,9 +17,14 @@ class NotificationSettingsItem { static List items = [ NotificationSettingsItem( PushRuleKind.underride, - '.m.rule.room_one_to_one', + '.m.rule.encrypted_room_one_to_one', (c) => L10n.of(c)!.directChats, ), + NotificationSettingsItem( + PushRuleKind.underride, + 'm.rule.encrypted_group_chat', + (c) => L10n.of(c)!.groupChat, + ), NotificationSettingsItem( PushRuleKind.override, 'm.rule.invite_for_me', @@ -33,12 +38,22 @@ class NotificationSettingsItem { NotificationSettingsItem( PushRuleKind.override, 'm.rule.change_group_name', - (c) => "Group chat name change", + (c) => L10n.of(c)!.groupNameChange, ), NotificationSettingsItem( PushRuleKind.override, 'm.rule.change_avatar_group', - (c) => "Group chat avatar change", + (c) => L10n.of(c)!.groupAvatarChange, + ), + NotificationSettingsItem( + PushRuleKind.override, + 'm.rule.set_me_as_admin', + (c) => L10n.of(c)!.setMeAsAdmin, + ), + NotificationSettingsItem( + PushRuleKind.override, + '.m.rule.is_user_mention', + (c) => L10n.of(c)!.mentionsMe, ), ]; } diff --git a/lib/utils/matrix_sdk_extensions/client_push_rules_extension.dart b/lib/utils/matrix_sdk_extensions/client_push_rules_extension.dart index e7504f343..8a8a9803e 100644 --- a/lib/utils/matrix_sdk_extensions/client_push_rules_extension.dart +++ b/lib/utils/matrix_sdk_extensions/client_push_rules_extension.dart @@ -2,22 +2,33 @@ import 'package:matrix/matrix.dart'; extension ClientPushRulesExtension on Client { Future setupUserDefinedPushRule({ + String scope = 'global', required String ruleId, List? conditions, + PushRuleKind kind = PushRuleKind.override, String? after, String? before, }) async { - await setPushRule( - 'global', - PushRuleKind.override, - ruleId, - [ - PushRuleAction.notify, - {"set_tweak": "sound", "value": "default"} - ], - conditions: conditions, - after: after, - before: before, - ); + if (!containsRule(ruleId)) { + await setPushRule( + scope, + kind, + ruleId, + [ + PushRuleAction.notify, + {"set_tweak": "sound", "value": "default"} + ], + conditions: conditions, + after: after, + before: before, + ); + } + } + + bool containsRule(String ruleId) { + final rule = devicePushRules?.override?.firstWhere((PushRule rule) { + return rule.ruleId == ruleId; + }); + return rule?.ruleId == ruleId; } } diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index 50e30f74d..942b19f03 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -450,74 +450,91 @@ class MatrixState extends State with WidgetsBindingObserver { } Future _setUpUserDefinedPushRules(Client client) async { - await client.setPushRuleEnabled( - 'global', - PushRuleKind.override, - '.m.rule.invite_for_me', - false, - ); - await client.setPushRuleEnabled( - 'global', - PushRuleKind.override, - '.m.rule.member_event', - false, - ); - - await client.setupUserDefinedPushRule( - ruleId: 'm.rule.invite_for_me', - conditions: [ - PushCondition( - kind: 'event_match', - key: 'type', - pattern: EventTypes.RoomMember, - ), - PushCondition( - kind: 'event_match', - key: 'content.membership', - pattern: 'invite', - ), - PushCondition( - kind: 'event_match', - key: 'state_key', - pattern: '${client.userID}', - ), - ], - ); - - await client.setupUserDefinedPushRule( - ruleId: 'm.rule.change_group_name', - conditions: [ - PushCondition( - kind: 'event_match', - key: 'type', - pattern: EventTypes.RoomName, - ), - ], - after: 'm.rule.invite_for_me', - ); - - await client.setupUserDefinedPushRule( - ruleId: 'm.rule.change_avatar_group', - conditions: [ - PushCondition( - kind: 'event_match', - key: 'type', - pattern: EventTypes.RoomAvatar, - ), - ], - after: 'm.rule.invite_for_me', - ); - - await client.setupUserDefinedPushRule( - ruleId: 'm.rule.set_me_as_admin', - conditions: [ - PushCondition( - kind: 'event_match', - key: 'type', - pattern: EventTypes.RoomPowerLevels, - ), - ], - ); + await Future.wait([ + client.setPushRuleEnabled( + 'global', + PushRuleKind.override, + '.m.rule.invite_for_me', + false, + ), + client.setPushRuleEnabled( + 'global', + PushRuleKind.override, + '.m.rule.member_event', + false, + ), + client.setupUserDefinedPushRule( + ruleId: 'm.rule.invite_for_me', + conditions: [ + PushCondition( + kind: 'event_match', + key: 'type', + pattern: EventTypes.RoomMember, + ), + PushCondition( + kind: 'event_match', + key: 'content.membership', + pattern: 'invite', + ), + PushCondition( + kind: 'event_match', + key: 'state_key', + pattern: '${client.userID}', + ), + ], + ), + client.setupUserDefinedPushRule( + ruleId: 'm.rule.set_me_as_admin', + conditions: [ + PushCondition( + kind: 'event_match', + key: 'type', + pattern: EventTypes.RoomPowerLevels, + ), + ], + ), + client.setupUserDefinedPushRule( + kind: PushRuleKind.underride, + ruleId: 'm.rule.encrypted_group_chat', + conditions: [ + PushCondition( + kind: 'event_match', + key: 'type', + pattern: EventTypes.Encrypted, + ), + PushCondition( + kind: 'event_match', + key: 'content.is_direct', + pattern: 'false', + ), + ], + ), + ]); + + await Future.wait([ + client.setupUserDefinedPushRule( + ruleId: 'm.rule.change_group_name', + conditions: [ + PushCondition( + kind: 'event_match', + key: 'type', + pattern: EventTypes.RoomName, + ), + ], + after: 'm.rule.invite_for_me', + ), + client.setupUserDefinedPushRule( + ruleId: 'm.rule.change_avatar_group', + conditions: [ + PushCondition( + kind: 'event_match', + key: 'type', + pattern: EventTypes.RoomAvatar, + ), + ], + after: 'm.rule.invite_for_me', + ), + ]); } void createVoipPlugin() async { @@ -572,6 +589,7 @@ class MatrixState extends State with WidgetsBindingObserver { } if (homeServer != null) { _setUpHomeServer(homeServer.baseUrl); + _setUpUserDefinedPushRules(client); } _storeToMConfiguration(client, tomServer, identityServer); @@ -603,7 +621,6 @@ class MatrixState extends State with WidgetsBindingObserver { 'MatrixState::_setUpHomeServer: ${homeServerUrlInterceptor.baseUrl}', ); homeServerUrlInterceptor.changeBaseUrl(homeServerUri.toString()); - _setUpUserDefinedPushRules(client); } void _setUpIdentityServer(IdentityServerInformation identityServer) { From efa54020259bbb3f1495540f48bbd72297a84bac Mon Sep 17 00:00:00 2001 From: sherlock Date: Mon, 11 Sep 2023 10:55:30 +0700 Subject: [PATCH 4/4] fixup! fixup! TW-504: add enable/disable button for push rules --- docs/adr/0005-push-rules-decision.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 docs/adr/0005-push-rules-decision.md diff --git a/docs/adr/0005-push-rules-decision.md b/docs/adr/0005-push-rules-decision.md new file mode 100644 index 000000000..61ab308a9 --- /dev/null +++ b/docs/adr/0005-push-rules-decision.md @@ -0,0 +1,25 @@ +# 5. Push rules decision +Date: 2023-09-11 + +## Status + +Accepted + +## Context + +In the Matrix protocol, push rules define how messages are filtered and displayed on devices. These rules are customizable through APIs, allowing us to enable, disable, and specify actions for each rule. Furthermore, there are various types of push rules (override, content, sender, underride, etc.), each serving different purposes and enabling the configuration of push notifications. For reference, see the [Matrix Push Rules Specification](https://spec.matrix.org/v1.8/client-server-api/#push-rules). However, push rules have exhibited several limitations. For instance, notifications for group chats may still be received by users even after they have disabled them ([example](https://photos.app.goo.gl/JoaysSunovsdPtAs8)). + +## Decision + +To address these limitations, we propose the introduction of optional custom fields within the content of events. These custom fields can be utilized in the future to create custom push rules. For example, the `setName()` API for rooms lacks the capability for clients to determine whether the room already exists. To mitigate this, we suggest adding a custom field, such as `'roomState': 'created'`, to the content of `setName()` APIs. Subsequently, this `roomState` custom field can be utilized as a condition within push rules. To maintain consistency and facilitate tracking, we recommend defining metadata custom fields exclusively and documenting them in a constants file for easy reference by other developers. + +## Consequences + +- **Advantages:** + - Simplifies the definition of push rules, enhancing clarity and organization. + - Provides flexibility to define customized push rules. + - Promotes extendability and maintainability. + - Compatible with various home servers. + +- **Disadvantages:** + - Push notifications may behave inconsistently across different client applications. In essence, push rules are optimized for Twake applications and may not offer consistent behavior in other environments. For instance, in the Element application, notifications may be displayed for actions such as message sending or group name changes, regardless of user preferences regarding name change notifications.