diff --git a/matrix_sdk/src/lib.rs b/matrix_sdk/src/lib.rs index 078de2e1..0b97e007 100644 --- a/matrix_sdk/src/lib.rs +++ b/matrix_sdk/src/lib.rs @@ -52,7 +52,8 @@ pub use matrix_sdk_base::crypto::LocalTrust; #[cfg(not(target_arch = "wasm32"))] pub use matrix_sdk_base::JsonStore; pub use matrix_sdk_base::{ - CustomEvent, Error as BaseError, EventEmitter, Room, RoomState, Session, StateStore, SyncRoom, + CustomEvent, Error as BaseError, EventEmitter, Room, RoomMember, RoomState, Session, + StateStore, SyncRoom, }; #[cfg(feature = "messages")] diff --git a/matrix_sdk_base/src/client.rs b/matrix_sdk_base/src/client.rs index eb0ad8e1..9ac9a6e8 100644 --- a/matrix_sdk_base/src/client.rs +++ b/matrix_sdk_base/src/client.rs @@ -27,7 +27,7 @@ use matrix_sdk_common::locks::Mutex; use matrix_sdk_common::{ api::r0 as api, events::{ - ignored_user_list::IgnoredUserListEvent, push_rules::PushRulesEvent, + direct::DirectEvent, ignored_user_list::IgnoredUserListEvent, push_rules::PushRulesEvent, room::member::MemberEventContent, AnyBasicEvent, AnyStrippedStateEvent, AnySyncEphemeralRoomEvent, AnySyncMessageEvent, AnySyncRoomEvent, AnySyncStateEvent, }, @@ -695,6 +695,25 @@ impl BaseClient { // } } + /// Handle a m.direct event, updating rooms states if necessary. + /// + /// Returns true if any room changed, false otherwise. + pub(crate) async fn handle_direct(&self, event: &DirectEvent) -> Vec>> { + let mut updated_rooms = vec![]; + + for (user_id, rooms) in event.content.iter() { + for room_id in rooms.iter() { + if let Some(room) = &self.get_joined_room(room_id).await { + let mut room_locked = room.write().await; + if room_locked.handle_direct(user_id) { + updated_rooms.push(room.to_owned()); + } + } + } + } + updated_rooms + } + /// Receive a timeline event for a joined room and update the client state. /// /// Returns a bool, true when the `Room` state has been updated. @@ -875,7 +894,8 @@ impl BaseClient { } } - /// Receive an account data event from a sync response and updates the client state. + /// Receive an account data event associated to a room from a sync + /// response and updates the client state. /// /// Returns true if the state of the `Room` has changed, false otherwise. /// @@ -884,7 +904,7 @@ impl BaseClient { /// * `room_id` - The unique id of the room the event belongs to. /// /// * `event` - The presence event for a specified room member. - pub async fn receive_account_data_event(&self, _: &RoomId, event: &AnyBasicEvent) -> bool { + pub async fn receive_room_account_data_event(&self, _: &RoomId, event: &AnyBasicEvent) -> bool { match event { AnyBasicEvent::IgnoredUserList(event) => self.handle_ignored_users(event).await, AnyBasicEvent::PushRules(event) => self.handle_push_rules(event).await, @@ -892,6 +912,24 @@ impl BaseClient { } } + /// Receive an account data event from a sync response and updates + /// the client state. + /// + /// Returns true if the state of any room has changed, false otherwise. + /// + /// # Arguments + /// + /// * `event` - The presence event for a specified room member. + pub async fn receive_account_data_event( + &self, + event: &AnyBasicEvent, + ) -> Vec>> { + match event { + AnyBasicEvent::Direct(event) => self.handle_direct(event).await, + _ => vec![], + } + } + /// Receive an ephemeral event from a sync response and updates the client state. /// /// Returns true if the state of the `Room` has changed, false otherwise. @@ -952,6 +990,7 @@ impl BaseClient { self.iter_joined_rooms(response).await?; self.iter_invited_rooms(response).await?; self.iter_left_rooms(response).await?; + self.iter_account_data(response).await?; let store = self.state_store.read().await; @@ -1052,7 +1091,7 @@ impl BaseClient { // FIXME: receive_* and emit_* methods shouldn't be called in parallel. We // should only pass events to receive_* methods and then let *them* emit. if let Ok(e) = account_data.deserialize() { - if self.receive_account_data_event(&room_id, &e).await { + if self.receive_room_account_data_event(&room_id, &e).await { updated = true; } self.emit_account_data_event(room_id, &e, RoomStateType::Joined) @@ -1168,6 +1207,30 @@ impl BaseClient { Ok(updated) } + async fn iter_account_data( + &self, + response: &mut api::sync::sync_events::Response, + ) -> Result { + let mut updated = false; + for account_data in &response.account_data.events { + { + // FIXME: emit_account_data_event assumes a room is given + if let Ok(e) = account_data.deserialize() { + for room in self.receive_account_data_event(&e).await { + if let Some(store) = self.state_store.read().await.as_ref() { + // FIXME: currently only operate on Joined rooms + store + .store_room_state(RoomState::Joined(room.read().await.deref())) + .await?; + } + updated = true; + } + } + } + } + Ok(updated) + } + async fn iter_invited_rooms( &self, response: &api::sync::sync_events::Response, diff --git a/matrix_sdk_base/src/lib.rs b/matrix_sdk_base/src/lib.rs index 1934faf5..6f4377b4 100644 --- a/matrix_sdk_base/src/lib.rs +++ b/matrix_sdk_base/src/lib.rs @@ -53,7 +53,7 @@ mod state; pub use client::{BaseClient, BaseClientConfig, RoomState, RoomStateType}; pub use event_emitter::{CustomEvent, EventEmitter, SyncRoom}; -pub use models::Room; +pub use models::{Room, RoomMember}; pub use state::{AllRooms, ClientState}; #[cfg(feature = "encryption")] diff --git a/matrix_sdk_base/src/models/message.rs b/matrix_sdk_base/src/models/message.rs index 9cac09c9..9403c6c0 100644 --- a/matrix_sdk_base/src/models/message.rs +++ b/matrix_sdk_base/src/models/message.rs @@ -206,6 +206,7 @@ mod test { }, "own_user_id": "@example:example.com", "creator": null, + "direct_target": null, "joined_members": {}, "invited_members": {}, "messages": [ msg ], @@ -253,6 +254,7 @@ mod test { }, "own_user_id": "@example:example.com", "creator": null, + "direct_target": null, "joined_members": {}, "invited_members": {}, "messages": [ msg ], diff --git a/matrix_sdk_base/src/models/room.rs b/matrix_sdk_base/src/models/room.rs index 33150622..aa333c57 100644 --- a/matrix_sdk_base/src/models/room.rs +++ b/matrix_sdk_base/src/models/room.rs @@ -190,6 +190,8 @@ pub struct Room { pub own_user_id: UserId, /// The mxid of the room creator. pub creator: Option, + /// The mxid of the "direct" target if any + pub direct_target: Option, // TODO: Track banned members, e.g. for /unban support? /// The map of invited room members. pub invited_members: HashMap, @@ -312,6 +314,7 @@ impl Room { room_name: RoomName::default(), own_user_id: own_user_id.clone(), creator: None, + direct_target: None, invited_members: HashMap::new(), joined_members: HashMap::new(), #[cfg(feature = "messages")] @@ -799,6 +802,20 @@ impl Room { true } + /// Handle setting direct attribute as part of a m.direct event, + /// updating the room if necessary + /// + /// Returns true if the direct_target changed, false otherwise. + pub fn handle_direct(&mut self, user_id: &UserId) -> bool { + if let Some(old_target) = &self.direct_target { + if old_target == user_id { + return false; + } + } + self.direct_target = Some(user_id.clone()); + true + } + fn handle_encryption_event(&mut self, event: &SyncStateEvent) -> bool { self.encrypted = Some(event.into()); true diff --git a/matrix_sdk_base/src/models/room_member.rs b/matrix_sdk_base/src/models/room_member.rs index efe31aba..393d54ac 100644 --- a/matrix_sdk_base/src/models/room_member.rs +++ b/matrix_sdk_base/src/models/room_member.rs @@ -83,6 +83,14 @@ impl PartialEq for RoomMember { } impl RoomMember { + /// Create a new room member. + /// + /// # Arguments + /// + /// * `event` - event associated with a member joining, leaving or getting + /// invited to a room. + /// + /// * `room_id` - The unique id of the room this member is part of. pub fn new(event: &SyncStateEvent, room_id: &RoomId) -> Self { Self { name: event.state_key.clone(), diff --git a/matrix_sdk_base/src/state/mod.rs b/matrix_sdk_base/src/state/mod.rs index 4d18a47c..ffd53d58 100644 --- a/matrix_sdk_base/src/state/mod.rs +++ b/matrix_sdk_base/src/state/mod.rs @@ -160,6 +160,7 @@ mod test { }, "own_user_id": "@example:example.com", "creator": null, + "direct_target": null, "joined_members": {}, "invited_members": {}, "typing_users": [], @@ -188,6 +189,7 @@ mod test { }, "own_user_id": "@example:example.com", "creator": null, + "direct_target": null, "joined_members": {}, "invited_members": {}, "messages": [],