diff --git a/crates/matrix-sdk/CHANGELOG.md b/crates/matrix-sdk/CHANGELOG.md index 8c1815552..313a6c790 100644 --- a/crates/matrix-sdk/CHANGELOG.md +++ b/crates/matrix-sdk/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file. ### Features +- Allow `Client::get_dm_room()` to be called without the `e2e-encryption` crate feature. + ([#5787](https://github.com/matrix-org/matrix-rust-sdk/pull/5787)) - [**breaking**] Add `encryption::secret_storage::SecretStorageError::ImportError` to indicate an error that occurred when importing a secret from secret storage. ([#5647](https://github.com/matrix-org/matrix-rust-sdk/pull/5647)) diff --git a/crates/matrix-sdk/src/client/mod.rs b/crates/matrix-sdk/src/client/mod.rs index 0aa50fd4b..7ff746396 100644 --- a/crates/matrix-sdk/src/client/mod.rs +++ b/crates/matrix-sdk/src/client/mod.rs @@ -73,6 +73,7 @@ use ruma::{ federation::discovery::get_server_version, }, assign, + events::direct::DirectUserIdentifier, push::Ruleset, time::Instant, }; @@ -1800,6 +1801,20 @@ impl Client { self.create_room(request).await } + /// Get the existing DM room with the given user, if any. + pub fn get_dm_room(&self, user_id: &UserId) -> Option { + let rooms = self.joined_rooms(); + + // Find the room we share with the `user_id` and only with `user_id` + let room = rooms.into_iter().find(|r| { + let targets = r.direct_targets(); + targets.len() == 1 && targets.contains(<&DirectUserIdentifier>::from(user_id)) + }); + + trace!(?user_id, ?room, "Found DM room with user"); + room + } + /// Search the homeserver's directory for public rooms with a filter. /// /// # Arguments @@ -3122,7 +3137,7 @@ pub(crate) mod tests { ignored_user_list::IgnoredUserListEventContent, media_preview_config::{InviteAvatars, MediaPreviewConfigEventContent, MediaPreviews}, }, - owned_room_id, owned_user_id, room_alias_id, room_id, + owned_room_id, owned_user_id, room_alias_id, room_id, user_id, }; use serde_json::json; use stream_assert::{assert_next_matches, assert_pending}; @@ -4112,4 +4127,83 @@ pub(crate) mod tests { .await .unwrap(); } + + #[async_test] + async fn test_get_dm_room_returns_the_room_we_have_with_this_user() { + let server = MatrixMockServer::new().await; + let client = server.client_builder().build().await; + // This is the user ID that is inside MemberAdditional. + // Note the confusing username, so we can share + // GlobalAccountDataTestEvent::Direct with the invited test. + let user_id = user_id!("@invited:localhost"); + + // When we receive a sync response saying "invited" is invited to a DM + let f = EventFactory::new(); + let response = SyncResponseBuilder::default() + .add_joined_room( + JoinedRoomBuilder::default().add_state_event(StateTestEvent::MemberAdditional), + ) + .add_global_account_data( + f.direct().add_user(user_id.to_owned().into(), *DEFAULT_TEST_ROOM_ID), + ) + .build_sync_response(); + client.base_client().receive_sync_response(response).await.unwrap(); + + // Then get_dm_room finds this room + let found_room = client.get_dm_room(user_id).expect("DM not found!"); + assert!(found_room.get_member_no_sync(user_id).await.unwrap().is_some()); + } + + #[async_test] + async fn test_get_dm_room_still_finds_room_where_participant_is_only_invited() { + let server = MatrixMockServer::new().await; + let client = server.client_builder().build().await; + // This is the user ID that is inside MemberInvite + let user_id = user_id!("@invited:localhost"); + + // When we receive a sync response saying "invited" is invited to a DM + let f = EventFactory::new(); + let response = SyncResponseBuilder::default() + .add_joined_room( + JoinedRoomBuilder::default().add_state_event(StateTestEvent::MemberInvite), + ) + .add_global_account_data( + f.direct().add_user(user_id.to_owned().into(), *DEFAULT_TEST_ROOM_ID), + ) + .build_sync_response(); + client.base_client().receive_sync_response(response).await.unwrap(); + + // Then get_dm_room finds this room + let found_room = client.get_dm_room(user_id).expect("DM not found!"); + assert!(found_room.get_member_no_sync(user_id).await.unwrap().is_some()); + } + + #[async_test] + async fn test_get_dm_room_still_finds_left_room() { + // See the discussion in https://github.com/matrix-org/matrix-rust-sdk/issues/2017 + // and the high-level issue at https://github.com/vector-im/element-x-ios/issues/1077 + + let server = MatrixMockServer::new().await; + let client = server.client_builder().build().await; + // This is the user ID that is inside MemberAdditional. + // Note the confusing username, so we can share + // GlobalAccountDataTestEvent::Direct with the invited test. + let user_id = user_id!("@invited:localhost"); + + // When we receive a sync response saying "invited" is invited to a DM + let f = EventFactory::new(); + let response = SyncResponseBuilder::default() + .add_joined_room( + JoinedRoomBuilder::default().add_state_event(StateTestEvent::MemberLeave), + ) + .add_global_account_data( + f.direct().add_user(user_id.to_owned().into(), *DEFAULT_TEST_ROOM_ID), + ) + .build_sync_response(); + client.base_client().receive_sync_response(response).await.unwrap(); + + // Then get_dm_room finds this room + let found_room = client.get_dm_room(user_id).expect("DM not found!"); + assert!(found_room.get_member_no_sync(user_id).await.unwrap().is_some()); + } } diff --git a/crates/matrix-sdk/src/encryption/mod.rs b/crates/matrix-sdk/src/encryption/mod.rs index 5c4fc7205..af53b6b1d 100644 --- a/crates/matrix-sdk/src/encryption/mod.rs +++ b/crates/matrix-sdk/src/encryption/mod.rs @@ -64,10 +64,7 @@ use ruma::{ uiaa::{AuthData, UiaaInfo}, }, assign, - events::{ - direct::DirectUserIdentifier, - room::{MediaSource, ThumbnailInfo}, - }, + events::room::{MediaSource, ThumbnailInfo}, }; #[cfg(feature = "experimental-send-custom-to-device")] use ruma::{events::AnyToDeviceEventContent, serde::Raw, to_device::DeviceIdOrAllDevices}; @@ -75,7 +72,7 @@ use serde::{Deserialize, de::Error as _}; use tasks::BundleReceiverTask; use tokio::sync::{Mutex, RwLockReadGuard}; use tokio_stream::wrappers::errors::BroadcastStreamRecvError; -use tracing::{debug, error, instrument, trace, warn}; +use tracing::{debug, error, instrument, warn}; use url::Url; use vodozemac::Curve25519PublicKey; @@ -89,7 +86,7 @@ use self::{ verification::{SasVerification, Verification, VerificationRequest}, }; use crate::{ - Client, Error, HttpError, Result, Room, RumaApiError, TransmissionProgress, + Client, Error, HttpError, Result, RumaApiError, TransmissionProgress, attachment::Thumbnail, client::{ClientInner, WeakClient}, cross_process_lock::CrossProcessLockGuard, @@ -669,20 +666,6 @@ impl Client { Ok(()) } - /// Get the existing DM room with the given user, if any. - pub fn get_dm_room(&self, user_id: &UserId) -> Option { - let rooms = self.joined_rooms(); - - // Find the room we share with the `user_id` and only with `user_id` - let room = rooms.into_iter().find(|r| { - let targets = r.direct_targets(); - targets.len() == 1 && targets.contains(<&DirectUserIdentifier>::from(user_id)) - }); - - trace!(?room, "Found room"); - room - } - async fn send_outgoing_request(&self, r: OutgoingRequest) -> Result<()> { use matrix_sdk_base::crypto::types::requests::AnyOutgoingRequest; @@ -2034,12 +2017,11 @@ mod tests { use matrix_sdk_test::{ DEFAULT_TEST_ROOM_ID, JoinedRoomBuilder, StateTestEvent, SyncResponseBuilder, async_test, - event_factory::EventFactory, test_json, + test_json, }; use ruma::{ event_id, events::{reaction::ReactionEventContent, relation::Annotation}, - user_id, }; use serde_json::json; use wiremock::{ @@ -2106,85 +2088,6 @@ mod tests { room.send_raw("m.reaction", json!({})).await.expect("Sending the reaction should not fail"); } - #[async_test] - async fn test_get_dm_room_returns_the_room_we_have_with_this_user() { - let server = MockServer::start().await; - let client = logged_in_client(Some(server.uri())).await; - // This is the user ID that is inside MemberAdditional. - // Note the confusing username, so we can share - // GlobalAccountDataTestEvent::Direct with the invited test. - let user_id = user_id!("@invited:localhost"); - - // When we receive a sync response saying "invited" is invited to a DM - let f = EventFactory::new(); - let response = SyncResponseBuilder::default() - .add_joined_room( - JoinedRoomBuilder::default().add_state_event(StateTestEvent::MemberAdditional), - ) - .add_global_account_data( - f.direct().add_user(user_id.to_owned().into(), *DEFAULT_TEST_ROOM_ID), - ) - .build_sync_response(); - client.base_client().receive_sync_response(response).await.unwrap(); - - // Then get_dm_room finds this room - let found_room = client.get_dm_room(user_id).expect("DM not found!"); - assert!(found_room.get_member_no_sync(user_id).await.unwrap().is_some()); - } - - #[async_test] - async fn test_get_dm_room_still_finds_room_where_participant_is_only_invited() { - let server = MockServer::start().await; - let client = logged_in_client(Some(server.uri())).await; - // This is the user ID that is inside MemberInvite - let user_id = user_id!("@invited:localhost"); - - // When we receive a sync response saying "invited" is invited to a DM - let f = EventFactory::new(); - let response = SyncResponseBuilder::default() - .add_joined_room( - JoinedRoomBuilder::default().add_state_event(StateTestEvent::MemberInvite), - ) - .add_global_account_data( - f.direct().add_user(user_id.to_owned().into(), *DEFAULT_TEST_ROOM_ID), - ) - .build_sync_response(); - client.base_client().receive_sync_response(response).await.unwrap(); - - // Then get_dm_room finds this room - let found_room = client.get_dm_room(user_id).expect("DM not found!"); - assert!(found_room.get_member_no_sync(user_id).await.unwrap().is_some()); - } - - #[async_test] - async fn test_get_dm_room_still_finds_left_room() { - // See the discussion in https://github.com/matrix-org/matrix-rust-sdk/issues/2017 - // and the high-level issue at https://github.com/vector-im/element-x-ios/issues/1077 - - let server = MockServer::start().await; - let client = logged_in_client(Some(server.uri())).await; - // This is the user ID that is inside MemberAdditional. - // Note the confusing username, so we can share - // GlobalAccountDataTestEvent::Direct with the invited test. - let user_id = user_id!("@invited:localhost"); - - // When we receive a sync response saying "invited" is invited to a DM - let f = EventFactory::new(); - let response = SyncResponseBuilder::default() - .add_joined_room( - JoinedRoomBuilder::default().add_state_event(StateTestEvent::MemberLeave), - ) - .add_global_account_data( - f.direct().add_user(user_id.to_owned().into(), *DEFAULT_TEST_ROOM_ID), - ) - .build_sync_response(); - client.base_client().receive_sync_response(response).await.unwrap(); - - // Then get_dm_room finds this room - let found_room = client.get_dm_room(user_id).expect("DM not found!"); - assert!(found_room.get_member_no_sync(user_id).await.unwrap().is_some()); - } - #[cfg(feature = "sqlite")] #[async_test] async fn test_generation_counter_invalidates_olm_machine() {