From f0d722099f22e41298c051effc4e3935853ef0ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Sat, 27 Jan 2024 16:19:16 +0100 Subject: [PATCH] sdk: Allow to receive ambiguity changes per-room. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Commaille --- crates/matrix-sdk/CHANGELOG.md | 2 + crates/matrix-sdk/src/sync.rs | 57 ++++++++++++++--- crates/matrix-sdk/tests/integration/client.rs | 62 +++++++++++++++++++ 3 files changed, 111 insertions(+), 10 deletions(-) diff --git a/crates/matrix-sdk/CHANGELOG.md b/crates/matrix-sdk/CHANGELOG.md index 9c7d99809..563cfaa07 100644 --- a/crates/matrix-sdk/CHANGELOG.md +++ b/crates/matrix-sdk/CHANGELOG.md @@ -5,6 +5,8 @@ Breaking changes: - Replace the `Notification` type from Ruma in `SyncResponse` and `Client::register_notification_handler` by a custom one - `Room::can_user_redact` and `Member::can_redact` are split between `*_redact_own` and `*_redact_other` +- `RoomUpdate` also contains the ambiguity changes of a `Room` with `AmbiguityCache` containing the + room member's user ID. # 0.7.0 diff --git a/crates/matrix-sdk/src/sync.rs b/crates/matrix-sdk/src/sync.rs index 5c845a593..be40128e1 100644 --- a/crates/matrix-sdk/src/sync.rs +++ b/crates/matrix-sdk/src/sync.rs @@ -23,7 +23,7 @@ use std::{ pub use matrix_sdk_base::sync::*; use matrix_sdk_base::{ debug::{DebugInvitedRoom, DebugListOfRawEventsNoId}, - deserialized_responses::AmbiguityChanges, + deserialized_responses::{AmbiguityChange, AmbiguityChanges}, instant::Instant, sync::SyncResponse as BaseSyncResponse, }; @@ -31,7 +31,7 @@ use ruma::{ api::client::sync::sync_events::{self, v3::InvitedRoom}, events::{presence::PresenceEvent, AnyGlobalAccountDataEvent, AnyToDeviceEvent}, serde::Raw, - OwnedRoomId, RoomId, + OwnedEventId, OwnedRoomId, RoomId, }; use tracing::{debug, error, warn}; @@ -103,6 +103,11 @@ pub enum RoomUpdate { room: Room, /// Updates to the room. updates: LeftRoom, + /// Collection of ambiguity changes that room member events trigger. + /// + /// This is a map of event ID of the `m.room.member` event to the + /// details of the ambiguity change. + ambiguity_changes: BTreeMap, }, /// Updates to a room the user is currently in. Joined { @@ -110,6 +115,11 @@ pub enum RoomUpdate { room: Room, /// Updates to the room. updates: JoinedRoom, + /// Collection of ambiguity changes that room member events trigger. + /// + /// This is a map of event ID of the `m.room.member` event to the + /// details of the ambiguity change. + ambiguity_changes: BTreeMap, }, /// Updates to a room the user is invited to. Invited { @@ -117,6 +127,11 @@ pub enum RoomUpdate { room: Room, /// Updates to the room. updates: InvitedRoom, + /// Collection of ambiguity changes that room member events trigger. + /// + /// This is a map of event ID of the `m.room.member` event to the + /// details of the ambiguity change. + ambiguity_changes: BTreeMap, }, } @@ -124,16 +139,23 @@ pub enum RoomUpdate { impl fmt::Debug for RoomUpdate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Left { room, updates } => { - f.debug_struct("Left").field("room", room).field("updates", updates).finish() - } - Self::Joined { room, updates } => { - f.debug_struct("Joined").field("room", room).field("updates", updates).finish() - } - Self::Invited { room, updates } => f + Self::Left { room, updates, ambiguity_changes } => f + .debug_struct("Left") + .field("room", room) + .field("updates", updates) + .field("ambiguity_changes", ambiguity_changes) + .finish(), + Self::Joined { room, updates, ambiguity_changes } => f + .debug_struct("Joined") + .field("room", room) + .field("updates", updates) + .field("ambiguity_changes", ambiguity_changes) + .finish(), + Self::Invited { room, updates, ambiguity_changes } => f .debug_struct("Invited") .field("room", room) .field("updates", &DebugInvitedRoom(updates)) + .field("ambiguity_changes", ambiguity_changes) .finish(), } } @@ -169,7 +191,7 @@ impl Client { presence, account_data, to_device, - ambiguity_changes: _, + ambiguity_changes, notifications, } = response; @@ -187,6 +209,11 @@ impl Client { self.send_room_update(room_id, || RoomUpdate::Joined { room: room.clone(), updates: room_info.clone(), + ambiguity_changes: ambiguity_changes + .changes + .get(room_id) + .cloned() + .unwrap_or_default(), }); let JoinedRoom { unread_notifications: _, timeline, state, account_data, ephemeral } = @@ -210,6 +237,11 @@ impl Client { self.send_room_update(room_id, || RoomUpdate::Left { room: room.clone(), updates: room_info.clone(), + ambiguity_changes: ambiguity_changes + .changes + .get(room_id) + .cloned() + .unwrap_or_default(), }); let LeftRoom { timeline, state, account_data } = room_info; @@ -229,6 +261,11 @@ impl Client { self.send_room_update(room_id, || RoomUpdate::Invited { room: room.clone(), updates: room_info.clone(), + ambiguity_changes: ambiguity_changes + .changes + .get(room_id) + .cloned() + .unwrap_or_default(), }); let invite_state = &room_info.invite_state.events; diff --git a/crates/matrix-sdk/tests/integration/client.rs b/crates/matrix-sdk/tests/integration/client.rs index 702e0612f..e8be3437b 100644 --- a/crates/matrix-sdk/tests/integration/client.rs +++ b/crates/matrix-sdk/tests/integration/client.rs @@ -34,6 +34,8 @@ use ruma::{ uint, user_id, OwnedUserId, }; use serde_json::{json, Value as JsonValue}; +use stream_assert::{assert_next_matches, assert_pending}; +use tokio_stream::wrappers::BroadcastStream; use wiremock::{ matchers::{header, method, path, path_regex}, Mock, Request, ResponseTemplate, @@ -911,6 +913,9 @@ async fn ambiguity_changes() { let example_2_id = user_id!("@example2:localhost"); let example_3_id = user_id!("@example3:localhost"); + let mut updates = BroadcastStream::new(client.subscribe_to_room_updates(&DEFAULT_TEST_ROOM_ID)); + assert_pending!(updates); + // Initial sync, adds 2 members. mock_sync(&server, &*test_json::SYNC, None).await; let response = client.sync_once(SyncSettings::default()).await.unwrap(); @@ -938,6 +943,20 @@ async fn ambiguity_changes() { let example_2 = room.get_member_no_sync(example_2_id).await.unwrap().unwrap(); assert!(!example_2.name_ambiguous()); + let changes = assert_next_matches!(updates, Ok(RoomUpdate::Joined { ambiguity_changes, .. }) => ambiguity_changes); + + let example_change = changes.get(event_id!("$151800140517rfvjc:localhost")).unwrap(); + assert_eq!(example_change.member_id, example_id); + assert!(!example_change.member_ambiguous); + assert_eq!(example_change.ambiguated_member, None); + assert_eq!(example_change.disambiguated_member, None); + + let example_2_change = changes.get(event_id!("$152034824468gOeNB:localhost")).unwrap(); + assert_eq!(example_2_change.member_id, example_2_id); + assert!(!example_2_change.member_ambiguous); + assert_eq!(example_2_change.ambiguated_member, None); + assert_eq!(example_2_change.disambiguated_member, None); + // Add 1 member and set all 3 to the same display name. let example_2_rename_1_event_id = event_id!("$example_2_rename_1"); let example_3_join_event_id = event_id!("$example_3_join"); @@ -998,6 +1017,20 @@ async fn ambiguity_changes() { let example_3 = room.get_member_no_sync(example_3_id).await.unwrap().unwrap(); assert!(example_3.name_ambiguous()); + let changes = assert_next_matches!(updates, Ok(RoomUpdate::Joined { ambiguity_changes, .. }) => ambiguity_changes); + + let example_2_change = changes.get(example_2_rename_1_event_id).unwrap(); + assert_eq!(example_2_change.member_id, example_2_id); + assert!(example_2_change.member_ambiguous); + assert_eq!(example_2_change.ambiguated_member.as_deref(), Some(example_id)); + assert_eq!(example_2_change.disambiguated_member, None); + + let example_3_change = changes.get(example_3_join_event_id).unwrap(); + assert_eq!(example_3_change.member_id, example_3_id); + assert!(example_3_change.member_ambiguous); + assert_eq!(example_3_change.ambiguated_member, None); + assert_eq!(example_3_change.disambiguated_member, None); + // Rename example 2 to a unique name. let example_2_rename_2_event_id = event_id!("$example_2_rename_2"); @@ -1036,6 +1069,14 @@ async fn ambiguity_changes() { let example_3 = room.get_member_no_sync(example_3_id).await.unwrap().unwrap(); assert!(example_3.name_ambiguous()); + let changes = assert_next_matches!(updates, Ok(RoomUpdate::Joined { ambiguity_changes, .. }) => ambiguity_changes); + + let example_2_change = changes.get(example_2_rename_2_event_id).unwrap(); + assert_eq!(example_2_change.member_id, example_2_id); + assert!(!example_2_change.member_ambiguous); + assert_eq!(example_2_change.ambiguated_member, None); + assert_eq!(example_2_change.disambiguated_member, None); + // Rename example 3, using the same name as example 2. let example_3_rename_event_id = event_id!("$example_3_rename"); @@ -1074,6 +1115,14 @@ async fn ambiguity_changes() { let example_3 = room.get_member_no_sync(example_3_id).await.unwrap().unwrap(); assert!(example_3.name_ambiguous()); + let changes = assert_next_matches!(updates, Ok(RoomUpdate::Joined { ambiguity_changes, .. }) => ambiguity_changes); + + let example_3_change = changes.get(example_3_rename_event_id).unwrap(); + assert_eq!(example_3_change.member_id, example_3_id); + assert!(example_3_change.member_ambiguous); + assert_eq!(example_3_change.ambiguated_member.as_deref(), Some(example_2_id)); + assert_eq!(example_3_change.disambiguated_member.as_deref(), Some(example_id)); + // Rename example, still using a unique name. let example_rename_event_id = event_id!("$example_rename"); @@ -1112,6 +1161,14 @@ async fn ambiguity_changes() { let example_3 = room.get_member_no_sync(example_3_id).await.unwrap().unwrap(); assert!(example_3.name_ambiguous()); + let changes = assert_next_matches!(updates, Ok(RoomUpdate::Joined { ambiguity_changes, .. }) => ambiguity_changes); + + let example_change = changes.get(example_rename_event_id).unwrap(); + assert_eq!(example_change.member_id, example_id); + assert!(!example_change.member_ambiguous); + assert_eq!(example_change.ambiguated_member, None); + assert_eq!(example_change.disambiguated_member, None); + // Change avatar. let example_avatar_event_id = event_id!("$example_avatar"); @@ -1136,4 +1193,9 @@ async fn ambiguity_changes() { // Avatar change does not trigger ambiguity change. assert!(response.ambiguity_changes.changes.get(*DEFAULT_TEST_ROOM_ID).is_none()); + + let changes = assert_next_matches!(updates, Ok(RoomUpdate::Joined { ambiguity_changes, .. }) => ambiguity_changes); + assert!(changes.is_empty()); + + assert_pending!(updates); }