From 0e538a7c6739f84ee1fac99ff9eaab2c0d0eb6d4 Mon Sep 17 00:00:00 2001 From: Devin R Date: Tue, 5 May 2020 16:13:14 -0400 Subject: [PATCH] event_emitter/async_client: receive and emit events for invited and left rooms --- matrix_sdk/src/async_client.rs | 103 +++++++++++++++++++++++++++- matrix_sdk/src/base_client.rs | 85 +++++++++++++++++++++++ matrix_sdk/src/event_emitter/mod.rs | 36 ++++++++++ matrix_sdk/src/models/room.rs | 47 +++++++++---- 4 files changed, 253 insertions(+), 18 deletions(-) diff --git a/matrix_sdk/src/async_client.rs b/matrix_sdk/src/async_client.rs index c304b273..bc6740a3 100644 --- a/matrix_sdk/src/async_client.rs +++ b/matrix_sdk/src/async_client.rs @@ -645,6 +645,22 @@ impl AsyncClient { let mut response = self.send(request).await?; + // when events change state updated signals to state store to update database + let mut updated = self.iter_joined_rooms(&mut response).await?; + if self.iter_invited_rooms(&mut response).await? { + updated = true; + } + if self.iter_left_rooms(&mut response).await? { + updated = true; + } + + let mut client = self.base_client.write().await; + client.receive_sync_response(&mut response, updated).await?; + + Ok(response) + } + + async fn iter_joined_rooms(&self, response: &mut sync_events::Response) -> Result { let mut updated = false; for (room_id, room) in &mut response.rooms.join { let matrix_room = { @@ -745,11 +761,92 @@ impl AsyncClient { } } } + Ok(updated) + } - let mut client = self.base_client.write().await; - client.receive_sync_response(&mut response, updated).await?; + async fn iter_left_rooms(&self, response: &mut sync_events::Response) -> Result { + let mut updated = false; + for (room_id, left_room) in &mut response.rooms.leave { + let matrix_room = { + let mut client = self.base_client.write().await; + for mut event in &mut left_room.timeline.events { + let decrypted_event = { + let mut client = self.base_client.write().await; + let (decrypt_ev, timeline_update) = client + .receive_joined_timeline_event(room_id, &mut event) + .await; + if timeline_update { + updated = true; + }; + decrypt_ev + }; - Ok(response) + if let Some(e) = decrypted_event { + *event = e; + } + + if let Ok(e) = event.deserialize() { + let client = self.base_client.read().await; + client.emit_timeline_event(&room_id, &e).await; + } + } + + client.get_or_create_room(&room_id).clone() + }; + + // re looping is not ideal here + for event in &mut left_room.state.events { + if let Ok(e) = event.deserialize() { + let client = self.base_client.read().await; + client.emit_state_event(&room_id, &e).await; + } + } + + if updated { + if let Some(store) = self.base_client.read().await.state_store.as_ref() { + store + .store_room_state(matrix_room.read().await.deref()) + .await?; + } + } + } + Ok(updated) + } + + async fn iter_invited_rooms(&self, response: &mut sync_events::Response) -> Result { + let mut updated = false; + // INVITED ROOMS + for (room_id, invited_room) in &mut response.rooms.invite { + let matrix_room = { + let mut client = self.base_client.write().await; + for event in &invited_room.invite_state.events { + if let Ok(e) = event.deserialize() { + if client.receive_invite_state_event(&room_id, &e).await { + updated = true; + } + } + } + + client.get_or_create_room(&room_id).clone() + }; + + // re looping is not ideal here + for event in &mut invited_room.invite_state.events { + if let Ok(e) = event.deserialize() { + let client = self.base_client.read().await; + client.emit_stripped_state_event(&room_id, &e).await; + } + } + + if updated { + if let Some(store) = self.base_client.read().await.state_store.as_ref() { + store + .store_room_state(matrix_room.read().await.deref()) + .await?; + } + } + } + Ok(updated) } /// Repeatedly call sync to synchronize the client state with the server. diff --git a/matrix_sdk/src/base_client.rs b/matrix_sdk/src/base_client.rs index 121f7627..db35c6b4 100644 --- a/matrix_sdk/src/base_client.rs +++ b/matrix_sdk/src/base_client.rs @@ -30,6 +30,7 @@ use crate::events::presence::PresenceEvent; use crate::events::collections::only::Event as NonRoomEvent; use crate::events::ignored_user_list::IgnoredUserListEvent; use crate::events::push_rules::{PushRulesEvent, Ruleset}; +use crate::events::stripped::AnyStrippedStateEvent; use crate::events::EventJson; use crate::identifiers::{RoomId, UserId}; use crate::models::Room; @@ -333,6 +334,25 @@ impl Client { room.receive_state_event(event) } + /// Receive a state event for a room the user has been invited to. + /// + /// Returns true if the state of the room changed, false + /// otherwise. + /// + /// # Arguments + /// + /// * `room_id` - The unique id of the room the event belongs to. + /// + /// * `event` - A `AnyStrippedStateEvent` that should be handled by the client. + pub async fn receive_invite_state_event( + &mut self, + room_id: &RoomId, + event: &AnyStrippedStateEvent, + ) -> bool { + let mut room = self.get_or_create_room(room_id).write().await; + room.receive_stripped_state_event(event) + } + /// Receive a presence event from a sync response and updates the client state. /// /// Returns true if the membership list of the room changed, false @@ -707,6 +727,71 @@ impl Client { } } + pub(crate) async fn emit_stripped_state_event( + &self, + room_id: &RoomId, + event: &AnyStrippedStateEvent, + ) { + match event { + AnyStrippedStateEvent::RoomMember(member) => { + if let Some(ee) = &self.event_emitter { + if let Some(room) = self.get_room(&room_id) { + ee.on_stripped_state_member(Arc::clone(&room), &member) + .await; + } + } + } + AnyStrippedStateEvent::RoomName(name) => { + if let Some(ee) = &self.event_emitter { + if let Some(room) = self.get_room(&room_id) { + ee.on_stripped_state_name(Arc::clone(&room), &name).await; + } + } + } + AnyStrippedStateEvent::RoomCanonicalAlias(canonical) => { + if let Some(ee) = &self.event_emitter { + if let Some(room) = self.get_room(&room_id) { + ee.on_stripped_state_canonical_alias(Arc::clone(&room), &canonical) + .await; + } + } + } + AnyStrippedStateEvent::RoomAliases(aliases) => { + if let Some(ee) = &self.event_emitter { + if let Some(room) = self.get_room(&room_id) { + ee.on_stripped_state_aliases(Arc::clone(&room), &aliases) + .await; + } + } + } + AnyStrippedStateEvent::RoomAvatar(avatar) => { + if let Some(ee) = &self.event_emitter { + if let Some(room) = self.get_room(&room_id) { + ee.on_stripped_state_avatar(Arc::clone(&room), &avatar) + .await; + } + } + } + AnyStrippedStateEvent::RoomPowerLevels(power) => { + if let Some(ee) = &self.event_emitter { + if let Some(room) = self.get_room(&room_id) { + ee.on_stripped_state_power_levels(Arc::clone(&room), &power) + .await; + } + } + } + AnyStrippedStateEvent::RoomJoinRules(rules) => { + if let Some(ee) = &self.event_emitter { + if let Some(room) = self.get_room(&room_id) { + ee.on_stripped_state_join_rules(Arc::clone(&room), &rules) + .await; + } + } + } + _ => {} + } + } + pub(crate) async fn emit_state_event(&self, room_id: &RoomId, event: &StateEvent) { match event { StateEvent::RoomMember(member) => { diff --git a/matrix_sdk/src/event_emitter/mod.rs b/matrix_sdk/src/event_emitter/mod.rs index c49b1b65..90fd25c1 100644 --- a/matrix_sdk/src/event_emitter/mod.rs +++ b/matrix_sdk/src/event_emitter/mod.rs @@ -19,6 +19,7 @@ use crate::events::{ ignored_user_list::IgnoredUserListEvent, presence::PresenceEvent, push_rules::PushRulesEvent, + receipt::ReceiptEvent, room::{ aliases::AliasesEvent, avatar::AvatarEvent, @@ -31,6 +32,11 @@ use crate::events::{ redaction::RedactionEvent, tombstone::TombstoneEvent, }, + stripped::{ + StrippedRoomAliases, StrippedRoomAvatar, StrippedRoomCanonicalAlias, StrippedRoomJoinRules, + StrippedRoomMember, StrippedRoomName, StrippedRoomPowerLevels, + }, + typing::TypingEvent, }; use crate::models::Room; use tokio::sync::RwLock; @@ -101,6 +107,10 @@ pub trait EventEmitter: Send + Sync { async fn on_room_power_levels(&self, _: Arc>, _: &PowerLevelsEvent) {} /// Fires when `AsyncClient` receives a `RoomEvent::Tombstone` event. async fn on_room_tombstone(&self, _: Arc>, _: &TombstoneEvent) {} + /// Fires when `AsyncClient` receives a `NonRoomEvent::Typing` event. + async fn on_account_data_typing(&self, _: Arc>, _: &TypingEvent) {} + /// Fires when `AsyncClient` receives a `NonRoomEvent::Typing` event. + async fn on_account_data_receipt(&self, _: Arc>, _: &ReceiptEvent) {} // `RoomEvent`s from `IncomingState` /// Fires when `AsyncClient` receives a `StateEvent::RoomMember` event. @@ -118,6 +128,32 @@ pub trait EventEmitter: Send + Sync { /// Fires when `AsyncClient` receives a `StateEvent::RoomJoinRules` event. async fn on_state_join_rules(&self, _: Arc>, _: &JoinRulesEvent) {} + // `AnyStrippedStateEvent`s + /// Fires when `AsyncClient` receives a `StateEvent::RoomMember` event. + async fn on_stripped_state_member(&self, _: Arc>, _: &StrippedRoomMember) {} + /// Fires when `AsyncClient` receives a `StateEvent::RoomName` event. + async fn on_stripped_state_name(&self, _: Arc>, _: &StrippedRoomName) {} + /// Fires when `AsyncClient` receives a `StateEvent::RoomCanonicalAlias` event. + async fn on_stripped_state_canonical_alias( + &self, + _: Arc>, + _: &StrippedRoomCanonicalAlias, + ) { + } + /// Fires when `AsyncClient` receives a `StateEvent::RoomAliases` event. + async fn on_stripped_state_aliases(&self, _: Arc>, _: &StrippedRoomAliases) {} + /// Fires when `AsyncClient` receives a `StateEvent::RoomAvatar` event. + async fn on_stripped_state_avatar(&self, _: Arc>, _: &StrippedRoomAvatar) {} + /// Fires when `AsyncClient` receives a `StateEvent::RoomPowerLevels` event. + async fn on_stripped_state_power_levels( + &self, + _: Arc>, + _: &StrippedRoomPowerLevels, + ) { + } + /// Fires when `AsyncClient` receives a `StateEvent::RoomJoinRules` event. + async fn on_stripped_state_join_rules(&self, _: Arc>, _: &StrippedRoomJoinRules) {} + // `NonRoomEvent` (this is a type alias from ruma_events) from `IncomingAccountData` /// Fires when `AsyncClient` receives a `NonRoomEvent::RoomMember` event. async fn on_account_presence(&self, _: Arc>, _: &PresenceEvent) {} diff --git a/matrix_sdk/src/models/room.rs b/matrix_sdk/src/models/room.rs index 92c808a7..7fe6dcfe 100644 --- a/matrix_sdk/src/models/room.rs +++ b/matrix_sdk/src/models/room.rs @@ -30,6 +30,7 @@ use crate::events::room::{ power_levels::{NotificationPowerLevels, PowerLevelsEvent, PowerLevelsEventContent}, tombstone::TombstoneEvent, }; +use crate::events::stripped::AnyStrippedStateEvent; use crate::events::EventType; use crate::identifiers::{RoomAliasId, RoomId, UserId}; @@ -183,7 +184,7 @@ impl RoomName { }) .collect::>(); names.sort(); - // TODO what is the length the spec wants us to use here and in the `else` + // TODO what length does the spec want us to use here and in the `else` format!("{}, and {} others", names.join(", "), (joined + invited)) } else { format!("Empty Room (was {} others)", members.len()) @@ -396,15 +397,15 @@ impl Room { pub fn receive_timeline_event(&mut self, event: &RoomEvent) -> bool { match event { // update to the current members of the room - RoomEvent::RoomMember(m) => self.handle_membership(m), + RoomEvent::RoomMember(member) => self.handle_membership(member), // finds all events related to the name of the room for later use - RoomEvent::RoomName(n) => self.handle_room_name(n), - RoomEvent::RoomCanonicalAlias(ca) => self.handle_canonical(ca), - RoomEvent::RoomAliases(a) => self.handle_room_aliases(a), + RoomEvent::RoomName(name) => self.handle_room_name(name), + RoomEvent::RoomCanonicalAlias(c_alias) => self.handle_canonical(c_alias), + RoomEvent::RoomAliases(alias) => self.handle_room_aliases(alias), // power levels of the room members - RoomEvent::RoomPowerLevels(p) => self.handle_power_level(p), - RoomEvent::RoomTombstone(t) => self.handle_tombstone(t), - RoomEvent::RoomEncryption(e) => self.handle_encryption_event(e), + RoomEvent::RoomPowerLevels(power) => self.handle_power_level(power), + RoomEvent::RoomTombstone(tomb) => self.handle_tombstone(tomb), + RoomEvent::RoomEncryption(encrypt) => self.handle_encryption_event(encrypt), _ => false, } } @@ -418,17 +419,33 @@ impl Room { /// * `event` - The event of the room. pub fn receive_state_event(&mut self, event: &StateEvent) -> bool { match event { - StateEvent::RoomMember(m) => self.handle_membership(m), - StateEvent::RoomName(n) => self.handle_room_name(n), - StateEvent::RoomCanonicalAlias(ca) => self.handle_canonical(ca), - StateEvent::RoomAliases(a) => self.handle_room_aliases(a), - StateEvent::RoomPowerLevels(p) => self.handle_power_level(p), - StateEvent::RoomTombstone(t) => self.handle_tombstone(t), - StateEvent::RoomEncryption(e) => self.handle_encryption_event(e), + // update to the current members of the room + StateEvent::RoomMember(member) => self.handle_membership(member), + // finds all events related to the name of the room for later use + StateEvent::RoomName(name) => self.handle_room_name(name), + StateEvent::RoomCanonicalAlias(c_alias) => self.handle_canonical(c_alias), + StateEvent::RoomAliases(alias) => self.handle_room_aliases(alias), + // power levels of the room members + StateEvent::RoomPowerLevels(power) => self.handle_power_level(power), + StateEvent::RoomTombstone(tomb) => self.handle_tombstone(tomb), + StateEvent::RoomEncryption(encrypt) => self.handle_encryption_event(encrypt), _ => false, } } + /// Receive a stripped state event for this room and update the room state. + /// + /// Returns true if the state of the `Room` has changed, false otherwise. + /// + /// # Arguments + /// + /// * `event` - The `AnyStrippedStateEvent` sent by the server for invited but not + /// joined rooms. + pub fn receive_stripped_state_event(&mut self, _event: &AnyStrippedStateEvent) -> bool { + // TODO do we want to do anything with the events from an invited room? + true + } + /// Receive a presence event from an `IncomingResponse` and updates the client state. /// /// This will only update the user if found in the current room looped through by `AsyncClient::sync`.