Skip to content

Commit

Permalink
Accept any string as a key for m.direct account data
Browse files Browse the repository at this point in the history
  • Loading branch information
MatMaul committed Nov 6, 2024
1 parent 8d07f36 commit b9e4897
Show file tree
Hide file tree
Showing 13 changed files with 101 additions and 27 deletions.
4 changes: 2 additions & 2 deletions crates/matrix-sdk-base/src/response_processors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::{
use ruma::{
events::{AnyGlobalAccountDataEvent, GlobalAccountDataEventType},
serde::Raw,
OwnedUserId, RoomId,
RoomId,
};
use tracing::{debug, instrument, trace, warn};

Expand Down Expand Up @@ -94,7 +94,7 @@ impl AccountDataProcessor {
for event in events {
let AnyGlobalAccountDataEvent::Direct(direct_event) = event else { continue };

let mut new_dms = HashMap::<&RoomId, HashSet<OwnedUserId>>::new();
let mut new_dms = HashMap::<&RoomId, HashSet<String>>::new();
for (user_id, rooms) in direct_event.content.iter() {
for room_id in rooms {
new_dms.entry(room_id).or_default().insert(user_id.clone());
Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk-base/src/rooms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ pub struct BaseRoomInfo {
pub(crate) create: Option<MinimalStateEvent<RoomCreateWithCreatorEventContent>>,
/// A list of user ids this room is considered as direct message, if this
/// room is a DM.
pub(crate) dm_targets: HashSet<OwnedUserId>,
pub(crate) dm_targets: HashSet<String>,
/// The `m.room.encryption` event content that enabled E2EE in this room.
pub(crate) encryption: Option<RoomEncryptionEventContent>,
/// The guest access policy of this room.
Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk-base/src/rooms/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ impl Room {
/// only be considered as guidance. We leave members in this list to allow
/// us to re-find a DM with a user even if they have left, since we may
/// want to re-invite them.
pub fn direct_targets(&self) -> HashSet<OwnedUserId> {
pub fn direct_targets(&self) -> HashSet<String> {
self.inner.read().base_info.dm_targets.clone()
}

Expand Down
20 changes: 10 additions & 10 deletions crates/matrix-sdk-base/src/sliding_sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1331,7 +1331,7 @@ mod tests {
create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Join).await;

// (Sanity: B is a direct target, and is in Join state)
assert!(direct_targets(&client, room_id).contains(user_b_id));
assert!(direct_targets(&client, room_id).contains(user_b_id.as_str()));
assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Join);

// When B leaves
Expand All @@ -1340,7 +1340,7 @@ mod tests {
// Then B is still a direct target, and is in Leave state (B is a direct target
// because we want to return to our old DM in the UI even if the other
// user left, so we can reinvite them. See https://github.com/matrix-org/matrix-rust-sdk/issues/2017)
assert!(direct_targets(&client, room_id).contains(user_b_id));
assert!(direct_targets(&client, room_id).contains(user_b_id.as_str()));
assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Leave);
}

Expand All @@ -1356,7 +1356,7 @@ mod tests {
create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Invite).await;

// (Sanity: B is a direct target, and is in Invite state)
assert!(direct_targets(&client, room_id).contains(user_b_id));
assert!(direct_targets(&client, room_id).contains(user_b_id.as_str()));
assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Invite);

// When B declines the invitation (i.e. leaves)
Expand All @@ -1365,7 +1365,7 @@ mod tests {
// Then B is still a direct target, and is in Leave state (B is a direct target
// because we want to return to our old DM in the UI even if the other
// user left, so we can reinvite them. See https://github.com/matrix-org/matrix-rust-sdk/issues/2017)
assert!(direct_targets(&client, room_id).contains(user_b_id));
assert!(direct_targets(&client, room_id).contains(user_b_id.as_str()));
assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Leave);
}

Expand All @@ -1383,7 +1383,7 @@ mod tests {
assert_eq!(membership(&client, room_id, user_a_id).await, MembershipState::Join);

// (Sanity: B is a direct target, and is in Join state)
assert!(direct_targets(&client, room_id).contains(user_b_id));
assert!(direct_targets(&client, room_id).contains(user_b_id.as_str()));
assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Join);

let room = client.get_room(room_id).unwrap();
Expand All @@ -1407,7 +1407,7 @@ mod tests {
assert_eq!(membership(&client, room_id, user_a_id).await, MembershipState::Join);

// (Sanity: B is a direct target, and is in Join state)
assert!(direct_targets(&client, room_id).contains(user_b_id));
assert!(direct_targets(&client, room_id).contains(user_b_id.as_str()));
assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Invite);

let room = client.get_room(room_id).unwrap();
Expand Down Expand Up @@ -2553,8 +2553,8 @@ mod tests {
set_room_joined(&mut room_response, user_a_id);
let mut response = response_with_room(room_id_1, room_response);
let mut direct_content = BTreeMap::new();
direct_content.insert(user_a_id.to_owned(), vec![room_id_1.to_owned()]);
direct_content.insert(user_b_id.to_owned(), vec![room_id_2.to_owned()]);
direct_content.insert(user_a_id.to_string(), vec![room_id_1.to_owned()]);
direct_content.insert(user_b_id.to_string(), vec![room_id_2.to_owned()]);
response
.extensions
.account_data
Expand Down Expand Up @@ -2665,7 +2665,7 @@ mod tests {
member.membership().clone()
}

fn direct_targets(client: &BaseClient, room_id: &RoomId) -> HashSet<OwnedUserId> {
fn direct_targets(client: &BaseClient, room_id: &RoomId) -> HashSet<String> {
let room = client.get_room(room_id).expect("Room not found!");
room.direct_targets()
}
Expand Down Expand Up @@ -2725,7 +2725,7 @@ mod tests {
room_ids: Vec<OwnedRoomId>,
) {
let mut direct_content = BTreeMap::new();
direct_content.insert(user_id, room_ids);
direct_content.insert(user_id.to_string(), room_ids);
response
.extensions
.account_data
Expand Down
4 changes: 2 additions & 2 deletions crates/matrix-sdk-base/src/store/migration_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use ruma::{
},
EmptyStateKey, EventContent, RedactContent, StateEventContent, StateEventType,
},
OwnedRoomId, OwnedUserId, RoomId,
OwnedRoomId, RoomId,
};
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -155,7 +155,7 @@ fn encryption_state_default() -> bool {
struct BaseRoomInfoV1 {
avatar: Option<MinimalStateEvent<RoomAvatarEventContent>>,
canonical_alias: Option<MinimalStateEvent<RoomCanonicalAliasEventContent>>,
dm_targets: HashSet<OwnedUserId>,
dm_targets: HashSet<String>,
encryption: Option<RoomEncryptionEventContent>,
guest_access: Option<MinimalStateEvent<RoomGuestAccessEventContent>>,
history_visibility: Option<MinimalStateEvent<RoomHistoryVisibilityEventContent>>,
Expand Down
45 changes: 45 additions & 0 deletions crates/matrix-sdk-common/src/direct_event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use std::{
collections::{btree_map, BTreeMap},
ops::{Deref, DerefMut},
};

use ruma::OwnedRoomId;
use ruma_macros::EventContent;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Default, Deserialize, Serialize, EventContent)]
#[allow(clippy::exhaustive_structs)]
#[ruma_event(type = "m.direct", kind = GlobalAccountData)]
pub struct DirectEventContent(pub BTreeMap<String, Vec<OwnedRoomId>>);

impl Deref for DirectEventContent {
type Target = BTreeMap<String, Vec<OwnedRoomId>>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for DirectEventContent {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl IntoIterator for DirectEventContent {
type Item = (String, Vec<OwnedRoomId>);
type IntoIter = btree_map::IntoIter<String, Vec<OwnedRoomId>>;

fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}

impl FromIterator<(String, Vec<OwnedRoomId>)> for DirectEventContent {
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = (String, Vec<OwnedRoomId>)>,
{
Self(BTreeMap::from_iter(iter))
}
}
2 changes: 1 addition & 1 deletion crates/matrix-sdk/src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@ impl Account {
};

for user_id in user_ids {
content.entry(user_id.to_owned()).or_default().push(room_id.to_owned());
content.entry(user_id.to_string()).or_default().push(room_id.to_owned());
}

// TODO: We should probably save the fact that we need to send this out
Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk/src/encryption/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ impl Client {
// 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(user_id)
targets.len() == 1 && targets.contains(user_id.as_str())
});

trace!(?room, "Found room");
Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk/src/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1205,7 +1205,7 @@ impl Room {
room_members.retain(|member| member.user_id() != self.own_user_id());

for member in room_members {
let entry = content.entry(member.user_id().to_owned()).or_default();
let entry = content.entry(member.user_id().to_string()).or_default();
if !entry.iter().any(|room_id| room_id == this_room_id) {
entry.push(this_room_id.to_owned());
}
Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk/tests/integration/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ async fn test_marking_room_as_dm() {
"The body of the PUT /account_data request should be a valid DirectEventContent",
);

let bob_entry = content.get(bob).expect("We should have bob in the direct event content");
let bob_entry = content.get(bob.as_str()).expect("We should have bob in the direct event content");

assert_eq!(content.len(), 2, "We should have entries for bob and foo");
assert_eq!(bob_entry.len(), 3, "Bob should have 3 direct rooms");
Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk/tests/integration/room/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@ async fn test_is_direct() {
// The room is direct now.
let direct_targets = room.direct_targets();
assert_eq!(direct_targets.len(), 1);
assert!(direct_targets.contains(*BOB));
assert!(direct_targets.contains(BOB.as_str()));
assert!(room.is_direct().await.unwrap());

// Unset the room as direct.
Expand Down
37 changes: 33 additions & 4 deletions crates/matrix-sdk/tests/integration/room/joined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@ use ruma::{
api::client::{membership::Invite3pidInit, receipt::create_receipt::v3::ReceiptType},
assign, event_id,
events::{
receipt::ReceiptThread,
room::message::{RoomMessageEventContent, RoomMessageEventContentWithoutRelation},
TimelineEventType,
receipt::ReceiptThread, room::message::{RoomMessageEventContent, RoomMessageEventContentWithoutRelation}, TimelineEventType
},
int, mxc_uri, owned_event_id, room_id, thirdparty, user_id, OwnedUserId, TransactionId,
};
use serde_json::{json, Value};
use serde_json::{from_value, json, Value};
use wiremock::{
matchers::{body_json, body_partial_json, header, method, path_regex},
Mock, ResponseTemplate,
Expand Down Expand Up @@ -630,6 +628,37 @@ async fn test_reset_power_levels() {
room.reset_power_levels().await.unwrap();
}

#[async_test]
async fn test_is_direct_invite_by_3pid() {
let (client, server) = logged_in_client_with_server().await;

let mut sync_builder = SyncResponseBuilder::new();
sync_builder.add_joined_room(JoinedRoomBuilder::default());
let data = json!({
"content": {
"[email protected]": [*DEFAULT_TEST_ROOM_ID],
},
"event_id": "$757957878228ekrDs:localhost",
"origin_server_ts": 17195787,
"sender": "@example:localhost",
"state_key": "",
"type": "m.direct",
"unsigned": {
"age": 139298
}
});
sync_builder.add_global_account_data_bulk(vec![from_value(data).unwrap()]);

mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
mock_encryption_state(&server, false).await;

let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
let _response = client.sync_once(sync_settings).await.unwrap();

let room = client.get_room(&DEFAULT_TEST_ROOM_ID).unwrap();
assert!(room.is_direct().await.unwrap());
}

#[async_test]
async fn test_call_notifications_ring_for_dms() {
let (client, server) = logged_in_client_with_server().await;
Expand Down
4 changes: 2 additions & 2 deletions crates/matrix-sdk/tests/integration/room/left.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ async fn test_forget_direct_room() {
let room = client.get_room(&DEFAULT_TEST_ROOM_ID).unwrap();
assert_eq!(room.state(), RoomState::Left);
assert!(room.is_direct().await.unwrap());
assert!(room.direct_targets().contains(invited_user_id));
assert!(room.direct_targets().contains(invited_user_id.as_str()));

let direct_account_data = client
.account()
Expand All @@ -80,7 +80,7 @@ async fn test_forget_direct_room() {
.expect("no m.direct account data")
.deserialize()
.expect("failed to deserialize m.direct account data");
assert_matches!(direct_account_data.get(invited_user_id), Some(invited_user_dms));
assert_matches!(direct_account_data.get(invited_user_id.as_str()), Some(invited_user_dms));
assert_eq!(invited_user_dms, &[DEFAULT_TEST_ROOM_ID.to_owned()]);

Mock::given(method("POST"))
Expand Down

0 comments on commit b9e4897

Please sign in to comment.