Skip to content

Commit

Permalink
feat(knocking): add code to process knocked rooms separately during sync
Browse files Browse the repository at this point in the history
  • Loading branch information
jmartinesp committed Oct 15, 2024
1 parent ee4ef2e commit d74902e
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 38 deletions.
26 changes: 26 additions & 0 deletions crates/matrix-sdk-base/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,32 @@ impl BaseClient {
new_rooms.invite.insert(room_id, new_info);
}

for (room_id, new_info) in response.rooms.knock {
let room = self.store.get_or_create_room(
&room_id,
RoomState::Knocked,
self.room_info_notable_update_sender.clone(),
);

let mut room_info = room.clone_info();
room_info.mark_as_invited();
room_info.mark_state_fully_synced();

self.handle_invited_state(
&room,
&new_info.knock_state.events,
&push_rules,
&mut room_info,
&mut changes,
&mut notifications,
)
.await?;

changes.add_room(room_info);

new_rooms.knocked.insert(room_id, new_info);
}

account_data_processor.apply(&mut changes, &self.store).await;

changes.presence = response
Expand Down
19 changes: 18 additions & 1 deletion crates/matrix-sdk-base/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
use std::fmt;

pub use matrix_sdk_common::debug::*;
use ruma::{api::client::sync::sync_events::v3::InvitedRoom, serde::Raw};
use ruma::{
api::client::sync::sync_events::v3::{InvitedRoom, KnockedRoom},
serde::Raw,
};

/// A wrapper around a slice of `Raw` events that implements `Debug` in a way
/// that only prints the event type of each item.
Expand Down Expand Up @@ -46,6 +49,20 @@ impl<'a> fmt::Debug for DebugInvitedRoom<'a> {
}
}

/// A wrapper around a knocked on room as found in `/sync` responses that
/// implements `Debug` in a way that only prints the event ID and event type for
/// the raw events contained in `knock_state`.
pub struct DebugKnockedRoom<'a>(pub &'a KnockedRoom);

#[cfg(not(tarpaulin_include))]
impl<'a> fmt::Debug for DebugKnockedRoom<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("KnockedRoom")
.field("knock_state", &DebugListOfRawEvents(&self.0.knock_state.events))
.finish()
}
}

pub(crate) struct DebugListOfRawEvents<'a, T>(pub &'a [Raw<T>]);

#[cfg(not(tarpaulin_include))]
Expand Down
97 changes: 67 additions & 30 deletions crates/matrix-sdk-base/src/sliding_sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ use ruma::api::client::sync::sync_events::v5;
#[cfg(feature = "e2e-encryption")]
use ruma::events::AnyToDeviceEvent;
use ruma::{
api::client::sync::sync_events::v3::{self, InvitedRoom},
events::{AnyRoomAccountDataEvent, AnySyncStateEvent},
api::client::sync::sync_events::v3::{self, InvitedRoom, KnockedRoom},
events::{
room::member::MembershipState, AnyRoomAccountDataEvent, AnyStrippedStateEvent,
AnySyncStateEvent,
},
serde::Raw,
JsOption, OwnedRoomId, RoomId, UInt,
};
Expand All @@ -47,6 +50,7 @@ use crate::{
normal::{RoomHero, RoomInfoNotableUpdateReasons},
RoomState,
},
ruma::assign,
store::{ambiguity_map::AmbiguityCache, StateChanges, Store},
sync::{JoinedRoomUpdate, LeftRoomUpdate, Notification, RoomUpdates, SyncResponse},
Room, RoomInfo,
Expand Down Expand Up @@ -168,7 +172,7 @@ impl BaseClient {
let mut rooms_account_data = extensions.account_data.rooms.clone();

for (room_id, response_room_data) in rooms {
let (room_info, joined_room, left_room, invited_room) = self
let (room_info, joined_room, left_room, invited_room, knocked_room) = self
.process_sliding_sync_room(
room_id,
response_room_data,
Expand Down Expand Up @@ -196,6 +200,10 @@ impl BaseClient {
if let Some(invited_room) = invited_room {
new_rooms.invite.insert(room_id.clone(), invited_room);
}

if let Some(knocked_room) = knocked_room {
new_rooms.knocked.insert(room_id.clone(), knocked_room);
}
}

// Handle read receipts and typing notifications independently of the rooms:
Expand Down Expand Up @@ -347,8 +355,13 @@ impl BaseClient {
notifications: &mut BTreeMap<OwnedRoomId, Vec<Notification>>,
ambiguity_cache: &mut AmbiguityCache,
with_msc4186: bool,
) -> Result<(RoomInfo, Option<JoinedRoomUpdate>, Option<LeftRoomUpdate>, Option<InvitedRoom>)>
{
) -> Result<(
RoomInfo,
Option<JoinedRoomUpdate>,
Option<LeftRoomUpdate>,
Option<InvitedRoom>,
Option<KnockedRoom>,
)> {
// This method may change `room_data` (see the terrible hack describes below)
// with `timestamp` and `invite_state. We don't want to change the `room_data`
// from outside this method, hence `Cow` is perfectly suited here.
Expand Down Expand Up @@ -403,13 +416,14 @@ impl BaseClient {
}

#[allow(unused_mut)] // Required for some feature flag combinations
let (mut room, mut room_info, invited_room) = self.process_sliding_sync_room_membership(
room_data.as_ref(),
&state_events,
store,
room_id,
room_info_notable_updates,
);
let (mut room, mut room_info, invited_room, knocked_room) = self
.process_sliding_sync_room_membership(
room_data.as_ref(),
&state_events,
store,
room_id,
room_info_notable_updates,
);

room_info.mark_state_partially_synced();

Expand All @@ -428,6 +442,7 @@ impl BaseClient {

let push_rules = self.get_push_rules(account_data_processor).await?;

// This will be used for both invited and knocked rooms
if let Some(invite_state) = &room_data.invite_state {
self.handle_invited_state(
&room,
Expand Down Expand Up @@ -512,6 +527,7 @@ impl BaseClient {
)),
None,
None,
None,
))
}

Expand All @@ -525,12 +541,12 @@ impl BaseClient {
ambiguity_changes,
)),
None,
None,
)),

RoomState::Invited => Ok((room_info, None, None, invited_room)),
RoomState::Invited => Ok((room_info, None, None, invited_room, None)),

// TODO: implement special logic for retrieving the knocked room info
RoomState::Knocked => Ok((room_info, None, None, None)),
RoomState::Knocked => Ok((room_info, None, None, None, knocked_room)),
}
}

Expand All @@ -546,7 +562,12 @@ impl BaseClient {
store: &Store,
room_id: &RoomId,
room_info_notable_updates: &mut BTreeMap<OwnedRoomId, RoomInfoNotableUpdateReasons>,
) -> (Room, RoomInfo, Option<InvitedRoom>) {
) -> (Room, RoomInfo, Option<InvitedRoom>, Option<KnockedRoom>) {
let user_id = self
.session_meta()
.expect("Sliding sync shouldn't run without an authenticated user.")
.user_id
.to_owned();
if let Some(invite_state) = &room_data.invite_state {
let room = store.get_or_create_room(
room_id,
Expand All @@ -555,20 +576,35 @@ impl BaseClient {
);
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. 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 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
})
});

(room, room_info, Some(InvitedRoom::from(v3::InviteState::from(invite_state.clone()))))
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)
} else {
let room = store.get_or_create_room(
room_id,
Expand All @@ -594,7 +630,7 @@ impl BaseClient {
room_info_notable_updates,
);

(room, room_info, None)
(room, room_info, None, None)
}
}

Expand Down Expand Up @@ -1014,6 +1050,7 @@ mod tests {
assert!(!sync_resp.rooms.join.contains_key(room_id));
assert!(!sync_resp.rooms.leave.contains_key(room_id));
assert!(sync_resp.rooms.invite.contains_key(room_id));
assert!(!sync_resp.rooms.knocked.contains_key(room_id));
}

#[async_test]
Expand Down
17 changes: 15 additions & 2 deletions crates/matrix-sdk-base/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use std::{collections::BTreeMap, fmt};
use matrix_sdk_common::{debug::DebugRawEvent, deserialized_responses::SyncTimelineEvent};
use ruma::{
api::client::sync::sync_events::{
v3::InvitedRoom as InvitedRoomUpdate,
v3::{InvitedRoom as InvitedRoomUpdate, KnockedRoom},
UnreadNotificationsCount as RumaUnreadNotificationsCount,
},
events::{
Expand All @@ -33,7 +33,7 @@ use ruma::{
use serde::{Deserialize, Serialize};

use crate::{
debug::{DebugInvitedRoom, DebugListOfRawEvents, DebugListOfRawEventsNoId},
debug::{DebugInvitedRoom, DebugKnockedRoom, DebugListOfRawEvents, DebugListOfRawEventsNoId},
deserialized_responses::{AmbiguityChange, RawAnySyncOrStrippedTimelineEvent},
store::Store,
};
Expand Down Expand Up @@ -77,6 +77,8 @@ pub struct RoomUpdates {
pub join: BTreeMap<OwnedRoomId, JoinedRoomUpdate>,
/// The rooms that the user has been invited to.
pub invite: BTreeMap<OwnedRoomId, InvitedRoomUpdate>,
/// The rooms that the user has knocked on.
pub knocked: BTreeMap<OwnedRoomId, KnockedRoom>,
}

impl RoomUpdates {
Expand All @@ -89,6 +91,7 @@ impl RoomUpdates {
.keys()
.chain(self.join.keys())
.chain(self.invite.keys())
.chain(self.knocked.keys())
.filter_map(|room_id| store.room(room_id))
{
let _ = room.compute_display_name().await;
Expand All @@ -103,6 +106,7 @@ impl fmt::Debug for RoomUpdates {
.field("leave", &self.leave)
.field("join", &self.join)
.field("invite", &DebugInvitedRoomUpdates(&self.invite))
.field("knocked", &DebugKnockedRoomUpdates(&self.knocked))
.finish()
}
}
Expand Down Expand Up @@ -250,6 +254,15 @@ impl<'a> fmt::Debug for DebugInvitedRoomUpdates<'a> {
}
}

struct DebugKnockedRoomUpdates<'a>(&'a BTreeMap<OwnedRoomId, KnockedRoom>);

#[cfg(not(tarpaulin_include))]
impl<'a> fmt::Debug for DebugKnockedRoomUpdates<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map().entries(self.0.iter().map(|(k, v)| (k, DebugKnockedRoom(v)))).finish()
}
}

/// A notification triggered by a sync response.
#[derive(Clone)]
pub struct Notification {
Expand Down
34 changes: 32 additions & 2 deletions crates/matrix-sdk/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ use std::{

pub use matrix_sdk_base::sync::*;
use matrix_sdk_base::{
debug::{DebugInvitedRoom, DebugListOfRawEventsNoId},
debug::{DebugInvitedRoom, DebugKnockedRoom, DebugListOfRawEventsNoId},
sync::SyncResponse as BaseSyncResponse,
};
use ruma::{
api::client::sync::sync_events::{self, v3::InvitedRoom},
api::client::sync::sync_events::{
self,
v3::{InvitedRoom, KnockedRoom},
},
events::{presence::PresenceEvent, AnyGlobalAccountDataEvent, AnyToDeviceEvent},
serde::Raw,
time::Instant,
Expand Down Expand Up @@ -100,6 +103,13 @@ pub enum RoomUpdate {
/// Updates to the room.
updates: InvitedRoom,
},
/// Updates to a room the user knocked on.
Knocked {
/// Room object with general information on the room.
room: Room,
/// Updates to the room.
updates: KnockedRoom,
},
}

#[cfg(not(tarpaulin_include))]
Expand All @@ -117,6 +127,11 @@ impl fmt::Debug for RoomUpdate {
.field("room", room)
.field("updates", &DebugInvitedRoom(updates))
.finish(),
Self::Knocked { room, updates } => f
.debug_struct("Knocked")
.field("room", room)
.field("updates", &DebugKnockedRoom(updates))
.finish(),
}
}
}
Expand Down Expand Up @@ -225,6 +240,21 @@ impl Client {
self.handle_sync_events(HandlerKind::StrippedState, Some(&room), invite_state).await?;
}

for (room_id, room_info) in &rooms.knocked {
let Some(room) = self.get_room(room_id) else {
error!(?room_id, "Can't call event handler, room not found");
continue;
};

self.send_room_update(room_id, || RoomUpdate::Knocked {
room: room.clone(),
updates: room_info.clone(),
});

let knock_state = &room_info.knock_state.events;
self.handle_sync_events(HandlerKind::StrippedState, Some(&room), knock_state).await?;
}

debug!("Ran event handlers in {:?}", now.elapsed());

let now = Instant::now();
Expand Down
Loading

0 comments on commit d74902e

Please sign in to comment.