From 0a52fc895ee768caa8b52af0bee7ed4419bb9cbd Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Wed, 9 Aug 2023 16:29:32 +0200 Subject: [PATCH 1/2] ffi: Allow to get the user-defined notification mode for a room --- .../src/notification_settings.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/bindings/matrix-sdk-ffi/src/notification_settings.rs b/bindings/matrix-sdk-ffi/src/notification_settings.rs index c34dddd9d..e5fbeb078 100644 --- a/bindings/matrix-sdk-ffi/src/notification_settings.rs +++ b/bindings/matrix-sdk-ffi/src/notification_settings.rs @@ -176,6 +176,24 @@ impl NotificationSettings { Ok(()) } + /// Get the user defined room notification mode + pub async fn get_user_defined_room_notification_mode( + &self, + room_id: String, + ) -> Result, NotificationSettingsError> { + let notification_settings = self.sdk_notification_settings.read().await; + let parsed_room_id = RoomId::parse(&room_id) + .map_err(|_e| NotificationSettingsError::InvalidRoomId(room_id))?; + // Get the current user defined mode for this room + if let Some(mode) = + notification_settings.get_user_defined_room_notification_mode(&parsed_room_id).await + { + Ok(Some(mode.into())) + } else { + Ok(None) + } + } + /// Get the default room notification mode /// /// The mode will depend on the associated `PushRule` based on whether the From 8c6e3949e0d16d1ca847e6675c85bae137503288 Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Wed, 9 Aug 2023 16:30:08 +0200 Subject: [PATCH 2/2] sdk+ffi: allow to get all room IDs for which a user-defined rule exists. --- .../src/notification_settings.rs | 6 + .../src/notification_settings/mod.rs | 5 + .../src/notification_settings/rules.rs | 114 +++++++++++++++++- 3 files changed, 123 insertions(+), 2 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/notification_settings.rs b/bindings/matrix-sdk-ffi/src/notification_settings.rs index e5fbeb078..30b5641cd 100644 --- a/bindings/matrix-sdk-ffi/src/notification_settings.rs +++ b/bindings/matrix-sdk-ffi/src/notification_settings.rs @@ -253,6 +253,12 @@ impl NotificationSettings { Ok(()) } + /// Get all room IDs for which a user-defined rule exists. + pub async fn get_rooms_with_user_defined_rules(&self, enabled: Option) -> Vec { + let notification_settings = self.sdk_notification_settings.read().await; + notification_settings.get_rooms_with_user_defined_rules(enabled).await + } + /// Get whether some enabled keyword rules exist. pub async fn contains_keywords_rules(&self) -> bool { let notification_settings = self.sdk_notification_settings.read().await; diff --git a/crates/matrix-sdk/src/notification_settings/mod.rs b/crates/matrix-sdk/src/notification_settings/mod.rs index 991ce09c8..f72933ab3 100644 --- a/crates/matrix-sdk/src/notification_settings/mod.rs +++ b/crates/matrix-sdk/src/notification_settings/mod.rs @@ -130,6 +130,11 @@ impl NotificationSettings { self.rules.read().await.get_default_room_notification_mode(is_encrypted, members_count) } + /// Get all room IDs for which a user-defined rule exists. + pub async fn get_rooms_with_user_defined_rules(&self, enabled: Option) -> Vec { + self.rules.read().await.get_rooms_with_user_defined_rules(enabled) + } + /// Get whether the given ruleset contains some enabled keywords rules. pub async fn contains_keyword_rules(&self) -> bool { self.rules.read().await.contains_keyword_rules() diff --git a/crates/matrix-sdk/src/notification_settings/rules.rs b/crates/matrix-sdk/src/notification_settings/rules.rs index a8adf831e..a4811d623 100644 --- a/crates/matrix-sdk/src/notification_settings/rules.rs +++ b/crates/matrix-sdk/src/notification_settings/rules.rs @@ -1,9 +1,10 @@ //! Ruleset utility struct +use imbl::HashSet; use ruma::{ push::{ - PredefinedContentRuleId, PredefinedOverrideRuleId, PredefinedUnderrideRuleId, - PushCondition, RuleKind, Ruleset, + AnyPushRuleRef, PredefinedContentRuleId, PredefinedOverrideRuleId, + PredefinedUnderrideRuleId, PushCondition, RuleKind, Ruleset, }, RoomId, }; @@ -123,6 +124,39 @@ impl Rules { } } + /// Get all room IDs for which a user-defined rule exists. + pub(crate) fn get_rooms_with_user_defined_rules(&self, enabled: Option) -> Vec { + let test_if_enabled = enabled.is_some(); + let must_be_enabled = enabled.unwrap_or(false); + + let mut room_ids = HashSet::new(); + for rule in &self.ruleset { + if rule.is_server_default() { + continue; + } + if test_if_enabled && rule.enabled() != must_be_enabled { + continue; + } + match rule { + AnyPushRuleRef::Override(r) | AnyPushRuleRef::Underride(r) => { + for condition in &r.conditions { + if let PushCondition::EventMatch { key, pattern } = condition { + if key == "room_id" { + room_ids.insert(pattern.clone()); + break; + } + } + } + } + AnyPushRuleRef::Room(r) => { + room_ids.insert(r.rule_id.to_string()); + } + _ => {} + } + } + Vec::from_iter(room_ids) + } + /// Get whether the `IsUserMention` rule is enabled. fn is_user_mention_enabled(&self) -> bool { // Search for an `Override` rule `IsUserMention` (MSC3952). @@ -245,6 +279,7 @@ pub(crate) fn get_predefined_underride_room_rule_id( #[cfg(test)] pub(crate) mod tests { + use imbl::HashSet; use matrix_sdk_test::{ async_test, notification_settings::{build_ruleset, get_server_default_ruleset}, @@ -567,4 +602,79 @@ pub(crate) mod tests { .is_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction.as_str()) .unwrap()); } + + #[async_test] + async fn test_get_rooms_with_user_defined_rules() { + // Without user-defined rules + let rules = Rules::new(get_server_default_ruleset()); + let room_ids = rules.get_rooms_with_user_defined_rules(None); + assert!(room_ids.is_empty()); + + // With one rule. + let room_id = RoomId::parse("!room_a:matrix.org").unwrap(); + let ruleset = build_ruleset(vec![(RuleKind::Override, &room_id, false)]); + let rules = Rules::new(ruleset); + + let room_ids = rules.get_rooms_with_user_defined_rules(None); + assert_eq!(room_ids.len(), 1); + + // With duplicates + let ruleset = build_ruleset(vec![ + (RuleKind::Override, &room_id, false), + (RuleKind::Underride, &room_id, false), + (RuleKind::Room, &room_id, false), + ]); + let rules = Rules::new(ruleset); + + let room_ids = rules.get_rooms_with_user_defined_rules(None); + assert_eq!(room_ids.len(), 1); + assert_eq!(room_ids[0], room_id.to_string()); + + // With multiple rules + let ruleset = build_ruleset(vec![ + (RuleKind::Room, &RoomId::parse("!room_a:matrix.org").unwrap(), false), + (RuleKind::Room, &RoomId::parse("!room_b:matrix.org").unwrap(), false), + (RuleKind::Room, &RoomId::parse("!room_c:matrix.org").unwrap(), false), + (RuleKind::Override, &RoomId::parse("!room_d:matrix.org").unwrap(), false), + (RuleKind::Underride, &RoomId::parse("!room_e:matrix.org").unwrap(), false), + ]); + let rules = Rules::new(ruleset); + + let room_ids = rules.get_rooms_with_user_defined_rules(None); + assert_eq!(room_ids.len(), 5); + let expected_set: HashSet = vec![ + "!room_a:matrix.org", + "!room_b:matrix.org", + "!room_c:matrix.org", + "!room_d:matrix.org", + "!room_e:matrix.org", + ] + .into_iter() + .collect(); + assert!(expected_set.difference(HashSet::from(room_ids)).is_empty()); + + // Only disabled rules + let room_ids = rules.get_rooms_with_user_defined_rules(Some(false)); + assert_eq!(room_ids.len(), 0); + + // Only enabled rules + let room_ids = rules.get_rooms_with_user_defined_rules(Some(true)); + assert_eq!(room_ids.len(), 5); + + let mut ruleset = build_ruleset(vec![ + (RuleKind::Room, &RoomId::parse("!room_a:matrix.org").unwrap(), false), + (RuleKind::Room, &RoomId::parse("!room_b:matrix.org").unwrap(), false), + (RuleKind::Override, &RoomId::parse("!room_c:matrix.org").unwrap(), false), + (RuleKind::Underride, &RoomId::parse("!room_d:matrix.org").unwrap(), false), + ]); + ruleset.set_enabled(RuleKind::Room, "!room_b:matrix.org", false).unwrap(); + ruleset.set_enabled(RuleKind::Override, "!room_c:matrix.org", false).unwrap(); + let rules = Rules::new(ruleset); + // Only room_a and room_d rules are enabled + let room_ids = rules.get_rooms_with_user_defined_rules(Some(true)); + assert_eq!(room_ids.len(), 2); + let expected_set: HashSet = + vec!["!room_a:matrix.org", "!room_d:matrix.org"].into_iter().collect(); + assert!(expected_set.difference(HashSet::from(room_ids)).is_empty()); + } }