diff --git a/matrix_sdk/src/async_client.rs b/matrix_sdk/src/async_client.rs index bc6740a3..fcad6177 100644 --- a/matrix_sdk/src/async_client.rs +++ b/matrix_sdk/src/async_client.rs @@ -621,6 +621,7 @@ impl AsyncClient { /// /// * `sync_settings` - Settings for the sync call. #[instrument] + #[allow(clippy::useless_let_if_seq)] pub async fn sync(&self, mut sync_settings: SyncSettings) -> Result { { // if the client has been synced from the state store don't sync again @@ -647,9 +648,11 @@ impl AsyncClient { // 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? { + + if self.iter_invited_rooms(&response).await? { updated = true; } + if self.iter_left_rooms(&mut response).await? { updated = true; } @@ -660,6 +663,7 @@ impl AsyncClient { Ok(response) } + #[inline] 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 { @@ -764,35 +768,32 @@ impl AsyncClient { Ok(updated) } + #[inline] 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 + 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 + }; - 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; - } + if let Some(e) = decrypted_event { + *event = e; } - client.get_or_create_room(&room_id).clone() - }; + if let Ok(e) = event.deserialize() { + let client = self.base_client.read().await; + client.emit_timeline_event(&room_id, &e).await; + } + } // re looping is not ideal here for event in &mut left_room.state.events { @@ -804,19 +805,19 @@ impl AsyncClient { 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?; + let mut client = self.base_client.write().await; + let room = client.get_or_create_room(&room_id).clone(); + store.store_room_state(room.read().await.deref()).await?; } } } Ok(updated) } - async fn iter_invited_rooms(&self, response: &mut sync_events::Response) -> Result { + #[inline] + async fn iter_invited_rooms(&self, response: &sync_events::Response) -> Result { let mut updated = false; - // INVITED ROOMS - for (room_id, invited_room) in &mut response.rooms.invite { + for (room_id, invited_room) in &response.rooms.invite { let matrix_room = { let mut client = self.base_client.write().await; for event in &invited_room.invite_state.events { @@ -831,7 +832,7 @@ impl AsyncClient { }; // re looping is not ideal here - for event in &mut invited_room.invite_state.events { + for event in &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; diff --git a/matrix_sdk/src/event_emitter/mod.rs b/matrix_sdk/src/event_emitter/mod.rs index 90fd25c1..f87115ae 100644 --- a/matrix_sdk/src/event_emitter/mod.rs +++ b/matrix_sdk/src/event_emitter/mod.rs @@ -129,29 +129,29 @@ pub trait EventEmitter: Send + Sync { async fn on_state_join_rules(&self, _: Arc>, _: &JoinRulesEvent) {} // `AnyStrippedStateEvent`s - /// Fires when `AsyncClient` receives a `StateEvent::RoomMember` event. + /// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomMember` event. async fn on_stripped_state_member(&self, _: Arc>, _: &StrippedRoomMember) {} - /// Fires when `AsyncClient` receives a `StateEvent::RoomName` event. + /// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomName` event. async fn on_stripped_state_name(&self, _: Arc>, _: &StrippedRoomName) {} - /// Fires when `AsyncClient` receives a `StateEvent::RoomCanonicalAlias` event. + /// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomCanonicalAlias` event. async fn on_stripped_state_canonical_alias( &self, _: Arc>, _: &StrippedRoomCanonicalAlias, ) { } - /// Fires when `AsyncClient` receives a `StateEvent::RoomAliases` event. + /// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomAliases` event. async fn on_stripped_state_aliases(&self, _: Arc>, _: &StrippedRoomAliases) {} - /// Fires when `AsyncClient` receives a `StateEvent::RoomAvatar` event. + /// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomAvatar` event. async fn on_stripped_state_avatar(&self, _: Arc>, _: &StrippedRoomAvatar) {} - /// Fires when `AsyncClient` receives a `StateEvent::RoomPowerLevels` event. + /// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomPowerLevels` event. async fn on_stripped_state_power_levels( &self, _: Arc>, _: &StrippedRoomPowerLevels, ) { } - /// Fires when `AsyncClient` receives a `StateEvent::RoomJoinRules` event. + /// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomJoinRules` event. async fn on_stripped_state_join_rules(&self, _: Arc>, _: &StrippedRoomJoinRules) {} // `NonRoomEvent` (this is a type alias from ruma_events) from `IncomingAccountData` @@ -232,6 +232,52 @@ mod test { self.0.lock().await.push("state rules".to_string()) } + async fn on_stripped_state_member(&self, _: Arc>, _: &StrippedRoomMember) { + self.0 + .lock() + .await + .push("stripped state member".to_string()) + } + async fn on_stripped_state_name(&self, _: Arc>, _: &StrippedRoomName) { + self.0.lock().await.push("stripped state name".to_string()) + } + async fn on_stripped_state_canonical_alias( + &self, + _: Arc>, + _: &StrippedRoomCanonicalAlias, + ) { + self.0 + .lock() + .await + .push("stripped state canonical".to_string()) + } + async fn on_stripped_state_aliases(&self, _: Arc>, _: &StrippedRoomAliases) { + self.0 + .lock() + .await + .push("stripped state aliases".to_string()) + } + async fn on_stripped_state_avatar(&self, _: Arc>, _: &StrippedRoomAvatar) { + self.0 + .lock() + .await + .push("stripped state avatar".to_string()) + } + async fn on_stripped_state_power_levels( + &self, + _: Arc>, + _: &StrippedRoomPowerLevels, + ) { + self.0.lock().await.push("stripped state power".to_string()) + } + async fn on_stripped_state_join_rules( + &self, + _: Arc>, + _: &StrippedRoomJoinRules, + ) { + self.0.lock().await.push("stripped state rules".to_string()) + } + async fn on_account_presence(&self, _: Arc>, _: &PresenceEvent) { self.0.lock().await.push("account presence".to_string()) } @@ -260,7 +306,7 @@ mod test { use std::time::Duration; #[tokio::test] - async fn event_emitter() { + async fn event_emitter_sync() { let homeserver = Url::from_str(&mockito::server_url()).unwrap(); let session = Session { @@ -279,7 +325,7 @@ mod test { let vec = Arc::new(Mutex::new(Vec::new())); let test_vec = Arc::clone(&vec); - let emitter = Box::new(EvEmitterTest(vec)) as Box<(dyn EventEmitter)>; + let emitter = Box::new(EvEmitterTest(vec)); let mut client = AsyncClient::new(homeserver, Some(session)).unwrap(); client.add_event_emitter(emitter).await; @@ -304,4 +350,81 @@ mod test { ], ) } + + #[tokio::test] + async fn event_emitter_invite() { + let homeserver = Url::from_str(&mockito::server_url()).unwrap(); + + let session = Session { + access_token: "1234".to_owned(), + user_id: UserId::try_from("@example:example.com").unwrap(), + device_id: "DEVICEID".to_owned(), + }; + + let _m = mock( + "GET", + Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()), + ) + .with_status(200) + .with_body_from_file("../test_data/invite_sync.json") + .create(); + + let vec = Arc::new(Mutex::new(Vec::new())); + let test_vec = Arc::clone(&vec); + let emitter = Box::new(EvEmitterTest(vec)); + let mut client = AsyncClient::new(homeserver, Some(session)).unwrap(); + client.add_event_emitter(emitter).await; + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + let _response = client.sync(sync_settings).await.unwrap(); + + let v = test_vec.lock().await; + assert_eq!( + v.as_slice(), + ["stripped state name", "stripped state member"], + ) + } + + #[tokio::test] + async fn event_emitter_leave() { + let homeserver = Url::from_str(&mockito::server_url()).unwrap(); + + let session = Session { + access_token: "1234".to_owned(), + user_id: UserId::try_from("@example:example.com").unwrap(), + device_id: "DEVICEID".to_owned(), + }; + + let _m = mock( + "GET", + Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()), + ) + .with_status(200) + .with_body_from_file("../test_data/leave_sync.json") + .create(); + + let vec = Arc::new(Mutex::new(Vec::new())); + let test_vec = Arc::clone(&vec); + let emitter = Box::new(EvEmitterTest(vec)); + let mut client = AsyncClient::new(homeserver, Some(session)).unwrap(); + client.add_event_emitter(emitter).await; + + let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); + let _response = client.sync(sync_settings).await.unwrap(); + + let v = test_vec.lock().await; + assert_eq!( + v.as_slice(), + [ + "message", + "state rules", + "state member", + "state aliases", + "state power", + "state canonical", + "state member", + "state member" + ], + ) + } } diff --git a/test_data/invite_sync.json b/test_data/invite_sync.json new file mode 100644 index 00000000..42852a2e --- /dev/null +++ b/test_data/invite_sync.json @@ -0,0 +1,56 @@ +{ + "device_one_time_keys_count": {}, + "next_batch": "s526_47314_0_7_1_1_1_11444_1", + "device_lists": { + "changed": [ + "@example:example.org" + ], + "left": [] + }, + "rooms": { + "invite": { + "!696r7674:example.com": { + "invite_state": { + "events": [ + { + "sender": "@alice:example.com", + "type": "m.room.name", + "state_key": "", + "content": { + "name": "My Room Name" + } + }, + { + "sender": "@alice:example.com", + "type": "m.room.member", + "state_key": "@bob:example.com", + "content": { + "membership": "invite" + } + } + ] + } + } + }, + "join": {}, + "leave": {} + }, + "to_device": { + "events": [] + }, + "presence": { + "events": [ + { + "content": { + "avatar_url": "mxc://localhost:wefuiwegh8742w", + "currently_active": false, + "last_active_ago": 1, + "presence": "online", + "status_msg": "Making cupcakes" + }, + "sender": "@example:localhost", + "type": "m.presence" + } + ] + } +} diff --git a/test_data/leave_sync.json b/test_data/leave_sync.json new file mode 100644 index 00000000..bcac1680 --- /dev/null +++ b/test_data/leave_sync.json @@ -0,0 +1,277 @@ +{ + "device_one_time_keys_count": {}, + "next_batch": "s526_47314_0_7_1_1_1_11444_1", + "device_lists": { + "changed": [ + "@example:example.org" + ], + "left": [] + }, + "rooms": { + "invite": {}, + "join": {}, + "leave": { + "!SVkFJHzfwvuaIEawgC:localhost": { + "summary": {}, + "account_data": { + "events": [ + { + "content": { + "event_id": "$someplace:example.org" + }, + "room_id": "!roomid:room.com", + "type": "m.fully_read" + }, + { + "content": { + "ignored_users": { + "@someone:example.org": {} + } + }, + "type": "m.ignored_user_list" + } + ] + }, + "ephemeral": { + "events": [ + { + "content": { + "$151680659217152dPKjd:localhost": { + "m.read": { + "@example:localhost": { + "ts": 1516809890615 + } + } + } + }, + "type": "m.receipt" + } + ] + }, + "state": { + "events": [ + { + "content": { + "join_rule": "public" + }, + "event_id": "$15139375514WsgmR:localhost", + "origin_server_ts": 1513937551539, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.join_rules", + "unsigned": { + "age": 7034220355 + } + }, + { + "content": { + "avatar_url": null, + "displayname": "example", + "membership": "join" + }, + "event_id": "$151800140517rfvjc:localhost", + "membership": "join", + "origin_server_ts": 1518001405556, + "sender": "@example:localhost", + "state_key": "@example:localhost", + "type": "m.room.member", + "unsigned": { + "age": 2970366338, + "replaces_state": "$151800111315tsynI:localhost" + } + }, + { + "content": { + "history_visibility": "shared" + }, + "event_id": "$15139375515VaJEY:localhost", + "origin_server_ts": 1513937551613, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.history_visibility", + "unsigned": { + "age": 7034220281 + } + }, + { + "content": { + "creator": "@example:localhost" + }, + "event_id": "$15139375510KUZHi:localhost", + "origin_server_ts": 1513937551203, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.create", + "unsigned": { + "age": 7034220691 + } + }, + { + "content": { + "aliases": [ + "#tutorial:localhost" + ] + }, + "event_id": "$15139375516NUgtD:localhost", + "origin_server_ts": 1513937551720, + "sender": "@example:localhost", + "state_key": "localhost", + "type": "m.room.aliases", + "unsigned": { + "age": 7034220174 + } + }, + { + "content": { + "topic": "\ud83d\ude00" + }, + "event_id": "$151957878228ssqrJ:localhost", + "origin_server_ts": 1519578782185, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.topic", + "unsigned": { + "age": 1392989709, + "prev_content": { + "topic": "test" + }, + "prev_sender": "@example:localhost", + "replaces_state": "$151957069225EVYKm:localhost" + } + }, + { + "content": { + "ban": 50, + "events": { + "m.room.avatar": 50, + "m.room.canonical_alias": 50, + "m.room.history_visibility": 100, + "m.room.name": 50, + "m.room.power_levels": 100 + }, + "events_default": 0, + "invite": 0, + "kick": 50, + "redact": 50, + "state_default": 50, + "users": { + "@example:localhost": 100 + }, + "users_default": 0 + }, + "event_id": "$15139375512JaHAW:localhost", + "origin_server_ts": 1513937551359, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.power_levels", + "unsigned": { + "age": 7034220535 + } + }, + { + "content": { + "alias": "#tutorial:localhost" + }, + "event_id": "$15139375513VdeRF:localhost", + "origin_server_ts": 1513937551461, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.canonical_alias", + "unsigned": { + "age": 7034220433 + } + }, + { + "content": { + "avatar_url": null, + "displayname": "example2", + "membership": "join" + }, + "event_id": "$152034824468gOeNB:localhost", + "membership": "join", + "origin_server_ts": 1520348244605, + "sender": "@example2:localhost", + "state_key": "@example2:localhost", + "type": "m.room.member", + "unsigned": { + "age": 623527289, + "prev_content": { + "membership": "leave" + }, + "prev_sender": "@example:localhost", + "replaces_state": "$152034819067QWJxM:localhost" + } + }, + { + "content": { + "membership": "leave", + "reason": "offline", + "avatar_url": "avatar.com", + "displayname": "example" + }, + "event_id": "$1585345508297748AIUBh:matrix.org", + "origin_server_ts": 1585345508223, + "sender": "@example:localhost", + "state_key": "@example:localhost", + "type": "m.room.member", + "unsigned": { + "replaces_state": "$1585345354296486IGZfp:localhost", + "prev_content": { + "avatar_url": "avatar.com", + "displayname": "example", + "membership": "join" + }, + "prev_sender": "@example2:localhost", + "age": 6992 + }, + "room_id": "!roomid:room.com" + } + ] + }, + "timeline": { + "events": [ + { + "content": { + "body": "baba", + "format": "org.matrix.custom.html", + "formatted_body": "baba", + "msgtype": "m.text" + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1520372800469, + "sender": "@example:localhost", + "type": "m.room.message", + "unsigned": { + "age": 598971425 + } + } + ], + "limited": true, + "prev_batch": "t392-516_47314_0_7_1_1_1_11444_1" + }, + "unread_notifications": { + "highlight_count": 0, + "notification_count": 11 + } + } + } + }, + "to_device": { + "events": [] + }, + "presence": { + "events": [ + { + "content": { + "avatar_url": "mxc://localhost:wefuiwegh8742w", + "currently_active": false, + "last_active_ago": 1, + "presence": "online", + "status_msg": "Making cupcakes" + }, + "sender": "@example:localhost", + "type": "m.presence" + } + ] + } +}