In sliding sync, do all the same processing on an invited room as a joined one

The example in MSC3575 shows a room with invite_state property, but also
timeline, joined_count etc. properties. That may or may not be very
likely in practice, but I think it makes sense to check for all these
properties even when the room is an invite, and use them if they are
present.
This commit is contained in:
Andy Balaam
2023-06-08 16:54:44 +01:00
parent 7e197ec8d4
commit 11adcf425c

View File

@@ -114,9 +114,7 @@ impl BaseClient {
account_data,
)
.await?;
if let Some(room_to_store) = room_to_store {
changes.add_room(room_to_store);
}
changes.add_room(room_to_store);
if let Some(joined_room) = joined_room {
new_rooms.join.insert(room_id.clone(), joined_room);
}
@@ -192,137 +190,131 @@ impl BaseClient {
changes: &mut StateChanges,
ambiguity_cache: &mut AmbiguityCache,
account_data: &AccountData,
) -> Result<(Option<RoomInfo>, Option<JoinedRoom>, Option<InvitedRoom>)> {
// If any invite_state exists, we take it to mean that we are invited to this room
// https://github.com/matrix-org/matrix-spec-proposals/blob/kegan/sync-v3/proposals/3575-sync.md#room-list-parameters
if let Some(invite_state) = &room_data.invite_state {
let room = store.get_or_create_stripped_room(room_id).await;
let mut room_info = room.clone_info();
room_info.mark_state_partially_synced();
) -> Result<(RoomInfo, Option<JoinedRoom>, Option<InvitedRoom>)> {
// If any invite_state exists, we take it to mean that we are invited to this
// room https://github.com/matrix-org/matrix-spec-proposals/blob/kegan/sync-v3/proposals/3575-sync.md#room-list-parameters
let (room, mut room_info, invited_room) =
if let Some(invite_state) = &room_data.invite_state {
let room = store.get_or_create_stripped_room(room_id).await;
let mut room_info = room.clone_info();
// We don't actually know what events are inside invite_state. In theory, they might
// not contain an m.room.member event, or they might set the membership to something
// other than invite. This would be very weird behaviour by the server, because
// invite_state is supposed to contain an m.room.member.
// Later, we will call handle_invited_state, which will reflect any information found
// in the real events inside invite_state, but we default to considering this room
// invited simply because invite_state exists. This is needed in the normal case,
// because the sliding sync server tries to send minimal state, meaning that we
// normally actually just receive {"type": "m.room.member"} with no content at all.
room_info.mark_as_invited();
// We don't actually know what events are inside invite_state. In theory, they
// might not contain an m.room.member event, or they might set the
// membership to something other than invite. This would be very
// weird behaviour by the server, because invite_state is supposed
// to contain an m.room.member. Later, we will call
// handle_invited_state, which will reflect any information found in
// the real events inside invite_state, but we default to considering this room
// invited simply because invite_state exists. This is needed in the normal
// case, because the sliding sync server tries to send minimal
// state, meaning that we normally actually just receive {"type":
// "m.room.member"} with no content at all.
room_info.mark_as_invited();
room_info.mark_state_partially_synced();
self.handle_invited_state(invite_state.as_slice(), &mut room_info, changes);
if !room_data.required_state.is_empty() {
self.handle_state(
&room_data.required_state,
&mut room_info,
changes,
ambiguity_cache,
(
room,
room_info,
Some(v3::InvitedRoom::from(v3::InviteState::from(invite_state.clone()))),
)
.await?;
}
} else {
let room = store.get_or_create_room(room_id, RoomState::Joined).await;
let mut room_info = room.clone_info();
self.handle_invited_state(invite_state.as_slice(), &mut room_info, changes);
// We default to considering this room joined if it's not an invite, but we
// expect to find a m.room.member event in the required_state, so
// this should get fixed to the real correct value when we call
// self.handle_state below.
room_info.mark_as_joined();
let invited_room = v3::InvitedRoom::from(v3::InviteState::from(invite_state.clone()));
(room, room_info, None)
};
Ok((Some(room_info), None, Some(invited_room)))
} else {
let room = store.get_or_create_room(room_id, RoomState::Joined).await;
let mut room_info = room.clone_info();
room_info.mark_state_partially_synced();
// We default to considering this room joined if it's not an invite, but we expect to
// find a m.room.member event in the required_state, so this should get fixed to the
// real correct value when we call self.handle_state below.
room_info.mark_as_joined();
if let Some(name) = &room_data.name {
room_info.update_name(name.to_owned());
}
room_info.mark_state_partially_synced();
// Sliding sync doesn't have a room summary, nevertheless it contains the joined
// and invited member counts. It likely will never have a heroes concept since
// it calculates the room display name for us.
//
// Let's at least fetch the member counts, since they might be useful.
let mut room_summary = RoomSummary::new();
room_summary.invited_member_count = room_data.invited_count;
room_summary.joined_member_count = room_data.joined_count;
room_info.update_summary(&room_summary);
if let Some(name) = &room_data.name {
room_info.update_name(name.to_owned());
}
room_info.set_prev_batch(room_data.prev_batch.as_deref());
// Sliding sync doesn't have a room summary, nevertheless it contains the joined
// and invited member counts. It likely will never have a heroes concept since
// it calculates the room display name for us.
//
// Let's at least fetch the member counts, since they might be useful.
let mut room_summary = RoomSummary::new();
room_summary.invited_member_count = room_data.invited_count;
room_summary.joined_member_count = room_data.joined_count;
room_info.update_summary(&room_summary);
room_info.set_prev_batch(room_data.prev_batch.as_deref());
let mut user_ids = if !room_data.required_state.is_empty() {
self.handle_state(
&room_data.required_state,
&mut room_info,
changes,
ambiguity_cache,
)
let mut user_ids = if !room_data.required_state.is_empty() {
self.handle_state(&room_data.required_state, &mut room_info, changes, ambiguity_cache)
.await?
} else {
Default::default()
};
} else {
Default::default()
};
let room_account_data = if let Some(events) = account_data.rooms.get(room_id) {
self.handle_room_account_data(room_id, events, changes).await;
Some(events.to_vec())
} else {
None
};
let room_account_data = if let Some(events) = account_data.rooms.get(room_id) {
self.handle_room_account_data(room_id, events, changes).await;
Some(events.to_vec())
} else {
None
};
if room_data.limited {
room_info.mark_members_missing();
}
if room_data.limited {
room_info.mark_members_missing();
}
let push_rules = self.get_push_rules(changes).await?;
let push_rules = self.get_push_rules(changes).await?;
let timeline = self
.handle_timeline(
&room,
room_data.limited,
room_data.timeline.clone(),
room_data.prev_batch.clone(),
&push_rules,
&mut user_ids,
&mut room_info,
changes,
ambiguity_cache,
)
.await?;
let timeline = self
.handle_timeline(
&room,
room_data.limited,
room_data.timeline.clone(),
room_data.prev_batch.clone(),
&push_rules,
&mut user_ids,
&mut room_info,
changes,
ambiguity_cache,
)
.await?;
#[cfg(feature = "e2e-encryption")]
if room_info.is_encrypted() {
if let Some(o) = self.olm_machine() {
if !room.is_encrypted() {
// The room turned on encryption in this sync, we need
// to also get all the existing users and mark them for
// tracking.
let user_ids = store.get_user_ids(room_id, RoomMemberships::ACTIVE).await?;
o.update_tracked_users(user_ids.iter().map(Deref::deref)).await?
}
#[cfg(feature = "e2e-encryption")]
if room_info.is_encrypted() {
if let Some(o) = self.olm_machine() {
if !room.is_encrypted() {
// The room turned on encryption in this sync, we need
// to also get all the existing users and mark them for
// tracking.
let user_ids = store.get_user_ids(room_id, RoomMemberships::ACTIVE).await?;
o.update_tracked_users(user_ids.iter().map(Deref::deref)).await?
}
if !user_ids.is_empty() {
o.update_tracked_users(user_ids.iter().map(Deref::deref)).await?;
}
if !user_ids.is_empty() {
o.update_tracked_users(user_ids.iter().map(Deref::deref)).await?;
}
}
let notification_count = room_data.unread_notifications.clone().into();
room_info.update_notification_count(notification_count);
}
let notification_count = room_data.unread_notifications.clone().into();
room_info.update_notification_count(notification_count);
let joined_room = JoinedRoom::new(
let joined_room = if invited_room.is_none() {
Some(JoinedRoom::new(
timeline,
room_data.required_state.clone(),
room_account_data.unwrap_or_default(),
Vec::new(),
notification_count,
);
))
} else {
None
};
Ok((Some(room_info), Some(joined_room), None))
}
Ok((room_info, joined_room, invited_room))
}
}
@@ -394,6 +386,24 @@ mod test {
assert_eq!(client_room.name(), Some("little room".to_owned()));
}
#[tokio::test]
async fn invited_room_name_is_found_when_processing_sliding_sync_response() {
// Given a logged-in client
let client = logged_in_client().await;
let room_id = room_id!("!r:e.uk");
// When I send sliding sync response containing a room with a name
let mut room = v4::SlidingSyncRoom::new();
set_room_invited(&mut room);
room.name = Some("little room".to_owned());
let response = response_with_room(room_id, room).await;
client.process_sliding_sync(&response).await.expect("Failed to process sync");
// Then the room appears in the client with the expected name
let client_room = client.get_room(room_id).expect("No room found");
assert_eq!(client_room.name(), Some("little room".to_owned()));
}
#[tokio::test]
async fn avatar_is_found_when_processing_sliding_sync_response() {
// Given a logged-in client
@@ -526,8 +536,8 @@ mod test {
}
fn set_room_invited(room: &mut v4::SlidingSyncRoom) {
// MSC3575 shows an almost-empty event to indicate that we are invited to a room.
// Just the type is supplied.
// MSC3575 shows an almost-empty event to indicate that we are invited to a
// room. Just the type is supplied.
let evt = Raw::new(&json!({
"type": "m.room.member",