Skip to content

Commit 98882b9

Browse files
committed
Store the last timeline event in RoomInfo
and the latest few encrypted events in Room. Use the latest event to provide a room preview, and use the encrypted events to replace the laest event when they are decrypted.
1 parent c600c64 commit 98882b9

File tree

17 files changed

+1393
-98
lines changed

17 files changed

+1393
-98
lines changed

bindings/matrix-sdk-ffi/src/room_list.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,9 +374,7 @@ impl RoomListItem {
374374
}
375375

376376
fn latest_event(&self) -> Option<Arc<EventTimelineItem>> {
377-
RUNTIME.block_on(async {
378-
self.inner.latest_event().await.map(EventTimelineItem).map(Arc::new)
379-
})
377+
self.inner.latest_event().map(EventTimelineItem).map(Arc::new)
380378
}
381379

382380
fn has_unread_notifications(&self) -> bool {

crates/matrix-sdk-base/src/client.rs

Lines changed: 145 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ use tokio::sync::RwLock;
5656
use tokio::sync::RwLockReadGuard;
5757
use tracing::{debug, info, instrument, trace, warn};
5858

59+
#[cfg(all(feature = "e2e-encryption", feature = "experimental-sliding-sync"))]
60+
use crate::latest_event::{is_suitable_for_latest_event, PossibleLatestEvent};
5961
use crate::{
6062
deserialized_responses::{AmbiguityChanges, MembersResponse, SyncTimelineEvent},
6163
error::Result,
@@ -561,24 +563,84 @@ impl BaseClient {
561563
changed_devices: &api::sync::sync_events::DeviceLists,
562564
one_time_keys_counts: &BTreeMap<ruma::DeviceKeyAlgorithm, UInt>,
563565
unused_fallback_keys: Option<&[ruma::DeviceKeyAlgorithm]>,
566+
changes: &mut StateChanges,
564567
) -> Result<Vec<Raw<ruma::events::AnyToDeviceEvent>>> {
565568
if let Some(o) = self.olm_machine().await.as_ref() {
566569
// Let the crypto machine handle the sync response, this
567570
// decrypts to-device events, but leaves room events alone.
568571
// This makes sure that we have the decryption keys for the room
569572
// events at hand.
570-
Ok(o.receive_sync_changes(
571-
to_device_events,
572-
changed_devices,
573-
one_time_keys_counts,
574-
unused_fallback_keys,
575-
)
576-
.await?)
573+
let (events, room_key_updates) = o
574+
.receive_sync_changes(
575+
to_device_events,
576+
changed_devices,
577+
one_time_keys_counts,
578+
unused_fallback_keys,
579+
)
580+
.await?;
581+
582+
#[cfg(feature = "experimental-sliding-sync")]
583+
for room_key_update in room_key_updates {
584+
if let Some(mut room) = self.get_room(&room_key_update.room_id) {
585+
self.decrypt_latest_events(&mut room, changes).await;
586+
}
587+
}
588+
589+
Ok(events)
577590
} else {
591+
// If we have no OlmMachine, just return the events that were passed in.
592+
// This should not happen unless we forget to set things up by calling
593+
// set_session_meta().
578594
Ok(to_device_events)
579595
}
580596
}
581597

598+
/// Decrypt any of this room's latest_encrypted_events
599+
/// that we can and if we can, change latest_event to reflect what we
600+
/// found, and remove any older encrypted events from
601+
/// latest_encrypted_events.
602+
#[cfg(all(feature = "e2e-encryption", feature = "experimental-sliding-sync"))]
603+
async fn decrypt_latest_events(&self, room: &mut Room, changes: &mut StateChanges) {
604+
// Try to find a message we can decrypt and is suitable for using as the latest
605+
// event. If we found one, set it as the latest and delete any older
606+
// encrypted events
607+
if let Some((found, found_index)) = self.decrypt_latest_suitable_event(room).await {
608+
room.on_latest_event_decrypted(found, found_index);
609+
changes.room_infos.insert(room.room_id().to_owned(), room.clone_info());
610+
}
611+
}
612+
613+
/// Attempt to decrypt a latest event, trying the latest stored encrypted
614+
/// one first, and walking backwards, stopping when we find an event
615+
/// that we can decrypt, and that is suitable to be the latest event
616+
/// (i.e. we can usefully display it as a message preview). Returns the
617+
/// decrypted event if we found one, along with its index in the
618+
/// latest_encrypted_events list, or None if we didn't find one.
619+
#[cfg(all(feature = "e2e-encryption", feature = "experimental-sliding-sync"))]
620+
async fn decrypt_latest_suitable_event(
621+
&self,
622+
room: &Room,
623+
) -> Option<(SyncTimelineEvent, usize)> {
624+
let enc_events = room.latest_encrypted_events();
625+
626+
// Walk backwards through the encrypted events, looking for one we can decrypt
627+
for (i, event) in enc_events.iter().enumerate().rev() {
628+
if let Ok(Some(decrypted)) = self.decrypt_sync_room_event(event, room.room_id()).await {
629+
// We found an event we can decrypt
630+
if let Ok(any_sync_event) = decrypted.event.deserialize() {
631+
// We can deserialize it to find its type
632+
if let PossibleLatestEvent::YesMessageLike(_) =
633+
is_suitable_for_latest_event(&any_sync_event)
634+
{
635+
// The event is the right type for us to use as latest_event
636+
return Some((decrypted, i));
637+
}
638+
}
639+
}
640+
}
641+
None
642+
}
643+
582644
/// User has joined a room.
583645
///
584646
/// Update the internal and cached state accordingly. Return the final Room.
@@ -644,6 +706,7 @@ impl BaseClient {
644706
}
645707

646708
let now = Instant::now();
709+
let mut changes = Box::new(StateChanges::new(response.next_batch.clone()));
647710

648711
#[cfg(feature = "e2e-encryption")]
649712
let to_device = self
@@ -652,13 +715,13 @@ impl BaseClient {
652715
&response.device_lists,
653716
&response.device_one_time_keys_count,
654717
response.device_unused_fallback_key_types.as_deref(),
718+
&mut changes,
655719
)
656720
.await?;
657721

658722
#[cfg(not(feature = "e2e-encryption"))]
659723
let to_device = response.to_device.events;
660724

661-
let mut changes = Box::new(StateChanges::new(response.next_batch.clone()));
662725
let mut ambiguity_cache = AmbiguityCache::new(self.store.inner.clone());
663726

664727
self.handle_account_data(&response.account_data.events, &mut changes).await;
@@ -1225,31 +1288,24 @@ impl Default for BaseClient {
12251288
#[cfg(test)]
12261289
mod tests {
12271290
use matrix_sdk_test::{
1228-
async_test, response_from_file, EventBuilder, InvitedRoomBuilder, LeftRoomBuilder,
1229-
StrippedStateTestEvent, TimelineTestEvent,
1291+
async_test, response_from_file, EventBuilder, InvitedRoomBuilder, JoinedRoomBuilder,
1292+
LeftRoomBuilder, StrippedStateTestEvent, TimelineTestEvent,
12301293
};
12311294
use ruma::{
12321295
api::{client as api, IncomingResponse},
1233-
room_id, user_id,
1296+
room_id, user_id, RoomId, UserId,
12341297
};
12351298
use serde_json::json;
12361299

12371300
use super::BaseClient;
1238-
use crate::{DisplayName, RoomState, SessionMeta};
1301+
use crate::{DisplayName, Room, RoomState, SessionMeta, StateChanges};
12391302

12401303
#[async_test]
12411304
async fn invite_after_leaving() {
12421305
let user_id = user_id!("@alice:example.org");
12431306
let room_id = room_id!("!test:example.org");
12441307

1245-
let client = BaseClient::new();
1246-
client
1247-
.set_session_meta(SessionMeta {
1248-
user_id: user_id.to_owned(),
1249-
device_id: "FOOBAR".into(),
1250-
})
1251-
.await
1252-
.unwrap();
1308+
let client = logged_in_client(user_id).await;
12531309

12541310
let mut ev_builder = EventBuilder::new();
12551311

@@ -1295,14 +1351,7 @@ mod tests {
12951351
let user_id = user_id!("@alice:example.org");
12961352
let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org");
12971353

1298-
let client = BaseClient::new();
1299-
client
1300-
.set_session_meta(SessionMeta {
1301-
user_id: user_id.to_owned(),
1302-
device_id: "FOOBAR".into(),
1303-
})
1304-
.await
1305-
.unwrap();
1354+
let client = logged_in_client(user_id).await;
13061355

13071356
let response = api::sync::sync_events::v3::Response::try_from_http_response(response_from_file(&json!({
13081357
"next_batch": "asdkl;fjasdkl;fj;asdkl;f",
@@ -1384,4 +1433,72 @@ mod tests {
13841433
DisplayName::Calculated("Kyra".to_owned())
13851434
);
13861435
}
1436+
1437+
#[cfg(all(feature = "e2e-encryption", feature = "experimental-sliding-sync"))]
1438+
#[async_test]
1439+
async fn when_there_are_no_latest_encrypted_events_decrypting_them_does_nothing() {
1440+
// Given a room
1441+
let user_id = user_id!("@u:u.to");
1442+
let room_id = room_id!("!r:u.to");
1443+
let client = logged_in_client(user_id).await;
1444+
let mut room = process_room_join(&client, room_id, "$1", user_id).await;
1445+
1446+
// Sanity: it has no latest_encrypted_events or latest_event
1447+
assert!(room.latest_encrypted_events().is_empty());
1448+
assert!(room.latest_event().is_none());
1449+
1450+
// When I tell it to do some decryption
1451+
let mut changes = StateChanges::default();
1452+
client.decrypt_latest_events(&mut room, &mut changes).await;
1453+
1454+
// Then nothing changed
1455+
assert!(room.latest_encrypted_events().is_empty());
1456+
assert!(room.latest_event().is_none());
1457+
assert!(changes.room_infos.is_empty());
1458+
}
1459+
1460+
// TODO: I wanted to write more tests here for decrypt_latest_events but I got
1461+
// lost trying to set up my OlmMachine to be able to encrypt and decrypt
1462+
// events. In the meantime, there are tests for the most difficult logic
1463+
// inside Room. --andyb
1464+
1465+
async fn logged_in_client(user_id: &UserId) -> BaseClient {
1466+
let client = BaseClient::new();
1467+
client
1468+
.set_session_meta(SessionMeta {
1469+
user_id: user_id.to_owned(),
1470+
device_id: "FOOBAR".into(),
1471+
})
1472+
.await
1473+
.expect("set_session_meta failed!");
1474+
client
1475+
}
1476+
1477+
#[cfg(feature = "e2e-encryption")]
1478+
async fn process_room_join(
1479+
client: &BaseClient,
1480+
room_id: &RoomId,
1481+
event_id: &str,
1482+
user_id: &UserId,
1483+
) -> Room {
1484+
let mut ev_builder = EventBuilder::new();
1485+
let response = ev_builder
1486+
.add_joined_room(JoinedRoomBuilder::new(room_id).add_timeline_event(
1487+
TimelineTestEvent::Custom(json!({
1488+
"content": {
1489+
"displayname": "Alice",
1490+
"membership": "join",
1491+
},
1492+
"event_id": event_id,
1493+
"origin_server_ts": 1432135524678u64,
1494+
"sender": user_id,
1495+
"state_key": user_id,
1496+
"type": "m.room.member",
1497+
})),
1498+
))
1499+
.build_sync_response();
1500+
client.receive_sync_response(response).await.unwrap();
1501+
1502+
client.get_room(room_id).expect("Just-created room not found!")
1503+
}
13871504
}

0 commit comments

Comments
 (0)