diff --git a/crates/matrix-sdk-base/src/client.rs b/crates/matrix-sdk-base/src/client.rs index 5e0c3af13e7..2210904d4c1 100644 --- a/crates/matrix-sdk-base/src/client.rs +++ b/crates/matrix-sdk-base/src/client.rs @@ -535,10 +535,16 @@ impl BaseClient { ) -> Result<()> { let mut state_events = BTreeMap::new(); + let session_user_id = self + .session_meta() + .expect("Sliding sync shouldn't run without an authenticated user.") + .user_id + .to_owned(); + for raw_event in events { match raw_event.deserialize() { Ok(e) => { - room_info.handle_stripped_state_event(&e); + room_info.handle_stripped_state_event(&session_user_id, &e); state_events .entry(e.event_type()) .or_insert_with(BTreeMap::new) @@ -1148,7 +1154,7 @@ impl BaseClient { ); let mut room_info = room.clone_info(); - room_info.mark_as_invited(); + room_info.mark_as_knocked(); room_info.mark_state_fully_synced(); self.handle_invited_state( diff --git a/crates/matrix-sdk-base/src/rooms/normal.rs b/crates/matrix-sdk-base/src/rooms/normal.rs index 9f790ba33f4..b27a5f50a86 100644 --- a/crates/matrix-sdk-base/src/rooms/normal.rs +++ b/crates/matrix-sdk-base/src/rooms/normal.rs @@ -1262,8 +1262,27 @@ impl RoomInfo { /// Handle the given stripped state event. /// /// Returns true if the event modified the info, false otherwise. - pub fn handle_stripped_state_event(&mut self, event: &AnyStrippedStateEvent) -> bool { - self.base_info.handle_stripped_state_event(event) + pub fn handle_stripped_state_event( + &mut self, + current_user_id: &UserId, + event: &AnyStrippedStateEvent, + ) -> bool { + if let AnyStrippedStateEvent::RoomMember(member) = event { + if member.state_key == current_user_id { + if member.content.membership == MembershipState::Knock { + // If it has a knocked membership, mark it as knocked. + self.mark_as_knocked(); + } else { + // Otherwise, assume it's an error and it should be invited instead. + self.mark_as_invited(); + } + return true; + } + // If the membership event wasn't for the current user, make no changes. + false + } else { + self.base_info.handle_stripped_state_event(event) + } } /// Handle the given redaction. diff --git a/crates/matrix-sdk-base/src/sliding_sync/mod.rs b/crates/matrix-sdk-base/src/sliding_sync/mod.rs index ffe15f9f7cd..8be490b9af7 100644 --- a/crates/matrix-sdk-base/src/sliding_sync/mod.rs +++ b/crates/matrix-sdk-base/src/sliding_sync/mod.rs @@ -28,10 +28,7 @@ use ruma::api::client::sync::sync_events::v5; use ruma::events::AnyToDeviceEvent; use ruma::{ api::client::sync::sync_events::v3::{self, InvitedRoom, KnockedRoom}, - events::{ - room::member::MembershipState, AnyRoomAccountDataEvent, AnyStrippedStateEvent, - AnySyncStateEvent, - }, + events::{AnyRoomAccountDataEvent, AnyStrippedStateEvent, AnySyncStateEvent}, serde::Raw, JsOption, OwnedRoomId, RoomId, UInt, }; @@ -416,7 +413,7 @@ impl BaseClient { } #[allow(unused_mut)] // Required for some feature flag combinations - let (mut room, mut room_info, invited_room, knocked_room) = self + let (mut room, mut room_info, invite_state_events) = self .process_sliding_sync_room_membership( room_data.as_ref(), &state_events, @@ -544,9 +541,18 @@ impl BaseClient { None, )), - RoomState::Invited => Ok((room_info, None, None, invited_room, None)), + RoomState::Invited => { + let invited_room = InvitedRoom::from(v3::InviteState::from( + invite_state_events.unwrap_or_default(), + )); + Ok((room_info, None, None, Some(invited_room), None)) + } - RoomState::Knocked => Ok((room_info, None, None, None, knocked_room)), + RoomState::Knocked => { + let knock_state = assign!(v3::KnockState::default(), { events: invite_state_events.unwrap_or_default() }); + let knocked_room = assign!(KnockedRoom::default(), { knock_state: knock_state }); + Ok((room_info, None, None, None, Some(knocked_room))) + } } } @@ -562,49 +568,28 @@ impl BaseClient { store: &Store, room_id: &RoomId, room_info_notable_updates: &mut BTreeMap, - ) -> (Room, RoomInfo, Option, Option) { - let user_id = self - .session_meta() - .expect("Sliding sync shouldn't run without an authenticated user.") - .user_id - .to_owned(); + ) -> (Room, RoomInfo, Option>>) { if let Some(invite_state) = &room_data.invite_state { let room = store.get_or_create_room( room_id, RoomState::Invited, self.room_info_notable_update_sender.clone(), ); - let mut room_info = room.clone_info(); - - // We need to find the membership event since it could be for either an invited - // or knocked room - let membership_event_content = invite_state.iter().find_map(|raw| { - raw.deserialize().ok().iter().find_map(|e| { - if let AnyStrippedStateEvent::RoomMember(membership_event) = e { - if membership_event.sender == user_id { - return Some(membership_event.content.clone()); - } - } - None - }) - }); - - if let Some(membership_event_content) = membership_event_content { - if membership_event_content.membership == MembershipState::Knock { - // If we have a `Knock` membership state, set the room as such - room_info.mark_as_knocked(); - let knock_state = - assign!(v3::KnockState::default(), { events: invite_state.clone() }); - let knocked_room = - assign!(KnockedRoom::default(), { knock_state: knock_state }); - return (room, room_info, None, Some(knocked_room)); - } - } - - // Otherwise assume it's an invited room - room_info.mark_as_invited(); - let invited_room = InvitedRoom::from(v3::InviteState::from(invite_state.clone())); - (room, room_info, Some(invited_room), None) + let 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 or knock. This + // would be very weird behaviour by the server, because invite_state + // is supposed to contain an m.room.member. 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, room_info, Some(invite_state.clone())) } else { let room = store.get_or_create_room( room_id, @@ -630,7 +615,7 @@ impl BaseClient { room_info_notable_updates, ); - (room, room_info, None, None) + (room, room_info, None) } } diff --git a/crates/matrix-sdk-base/src/store/integration_tests.rs b/crates/matrix-sdk-base/src/store/integration_tests.rs index 7fd99b32a28..40d6ff3ab5c 100644 --- a/crates/matrix-sdk-base/src/store/integration_tests.rs +++ b/crates/matrix-sdk-base/src/store/integration_tests.rs @@ -184,7 +184,7 @@ impl StateStoreIntegrationTests for DynStateStore { serde_json::from_value::>(stripped_name_json.clone()) .unwrap(); let stripped_name_event = stripped_name_raw.deserialize().unwrap(); - stripped_room.handle_stripped_state_event(&stripped_name_event); + stripped_room.handle_stripped_state_event(user_id, &stripped_name_event); changes.stripped_state.insert( stripped_room_id.to_owned(), BTreeMap::from([(