Skip to content

Commit

Permalink
sdk-ui: use RoomInfo to update the room encryption for the `Timelin…
Browse files Browse the repository at this point in the history
…e` and its items
  • Loading branch information
jmartinesp committed Sep 19, 2024
1 parent 2ccfad1 commit 0bddcfd
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 27 deletions.
8 changes: 8 additions & 0 deletions crates/matrix-sdk-ui/src/timeline/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,13 @@ impl TimelineBuilder {
None
};

let encryption_changes_handle = spawn({
let inner = controller.clone();
async move {
inner.handle_encryption_state_changes().await;
}
});

let room_update_join_handle = spawn({
let room_event_cache = room_event_cache.clone();
let inner = controller.clone();
Expand Down Expand Up @@ -420,6 +427,7 @@ impl TimelineBuilder {
room_key_backup_enabled_join_handle,
local_echo_listener_handle,
_event_cache_drop_handle: event_cache_drop,
encryption_changes_handle,
}),
};

Expand Down
22 changes: 22 additions & 0 deletions crates/matrix-sdk-ui/src/timeline/controller/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,28 @@ impl<P: RoomDataProvider> TimelineController<P> {
}
}

/// Listens to encryption state changes for the room in [`matrix_sdk_base::RoomInfo`]
/// and applies the new value to the existing timeline items.
pub async fn handle_encryption_state_changes(&self) {
let mut room_info = self.room_data_provider.room_info();
while let Some(info) = room_info.next().await {
let mut changed = false;
if let Ok(mut is_room_encrypted) =
self.state.read().await.meta.is_room_encrypted.write()
{
let is_encrypted_now = info.is_encrypted();
if is_room_encrypted.is_none() || is_room_encrypted.unwrap() != is_encrypted_now {
changed = true;
*is_room_encrypted = Some(is_encrypted_now);
}
}
if changed {
let mut state = self.state.write().await;
state.reload_shields();
}
}
}

pub(crate) async fn reload_pinned_events(
&self,
) -> Result<Vec<SyncTimelineEvent>, PinnedEventsLoaderError> {
Expand Down
28 changes: 25 additions & 3 deletions crates/matrix-sdk-ui/src/timeline/controller/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,19 @@ impl TimelineState {
result
}

pub(super) fn reload_shields(&mut self) {
let Ok(is_room_encrypted) = self.meta.is_room_encrypted.read() else {
return;
};

if let Some(is_encrypted) = *is_room_encrypted {
drop(is_room_encrypted);
let mut txn = self.transaction();
txn.replace_all_events_encryption(is_encrypted);
txn.commit();
}
}

pub(super) fn transaction(&mut self) -> TimelineStateTransaction<'_> {
let items = self.items.transaction();
let meta = self.meta.clone();
Expand Down Expand Up @@ -725,6 +738,18 @@ impl TimelineStateTransaction<'_> {
fn adjust_day_dividers(&mut self, mut adjuster: DayDividerAdjuster) {
adjuster.run(&mut self.items, &mut self.meta);
}

fn replace_all_events_encryption(&mut self, is_encrypted: bool) {
for idx in 0..self.items.len() {
let item = &self.items[idx];
if let Some(event) = item.as_event() {
let mut cloned_event = event.clone();
cloned_event.is_room_encrypted = Some(is_encrypted);
let item = item.with_kind(cloned_event);
self.items.set(idx, item);
}
}
}
}

#[derive(Clone)]
Expand Down Expand Up @@ -759,9 +784,6 @@ pub(in crate::timeline) struct TimelineMetadata {

/// A boolean indicating whether the room the timeline is attached to is
/// actually encrypted or not.
/// TODO: this is misplaced, it should be part of the room provider as this
/// value can change over time when a room switches from non-encrypted
/// to encrypted, see also #3850.
pub(crate) is_room_encrypted: Arc<RwLock<Option<bool>>>,

/// Matrix room version of the timeline's room, or a sensible default.
Expand Down
9 changes: 0 additions & 9 deletions crates/matrix-sdk-ui/src/timeline/event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,9 +404,6 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
TimelineEventKind::OtherState { state_key, content } => {
// Update room encryption if a `m.room.encryption` event is found in the
// timeline
if let AnyOtherFullStateEventContent::RoomEncryption(_) = &content {
self.handle_enabled_encryption();
}
if should_add {
self.add_item(TimelineItemContent::OtherState(OtherState {
state_key,
Expand Down Expand Up @@ -913,12 +910,6 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
false
}

fn handle_enabled_encryption(&self) {
if let Ok(mut is_room_encrypted) = self.meta.is_room_encrypted.write() {
is_room_encrypted.replace(true);
}
}

/// Add a new event item in the timeline.
fn add_item(&mut self, content: TimelineItemContent) {
self.result.item_added = true;
Expand Down
2 changes: 2 additions & 0 deletions crates/matrix-sdk-ui/src/timeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,7 @@ struct TimelineDropHandle {
room_key_backup_enabled_join_handle: JoinHandle<()>,
local_echo_listener_handle: JoinHandle<()>,
_event_cache_drop_handle: Arc<EventCacheDropHandles>,
encryption_changes_handle: JoinHandle<()>,
}

impl Drop for TimelineDropHandle {
Expand All @@ -855,6 +856,7 @@ impl Drop for TimelineDropHandle {
self.room_update_join_handle.abort();
self.room_key_from_backups_join_handle.abort();
self.room_key_backup_enabled_join_handle.abort();
self.encryption_changes_handle.abort();
}
}

Expand Down
10 changes: 8 additions & 2 deletions crates/matrix-sdk-ui/src/timeline/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use std::{
sync::Arc,
};

use eyeball::{SharedObservable, Subscriber};
use eyeball_im::VectorDiff;
use futures_core::Stream;
use futures_util::FutureExt as _;
Expand All @@ -33,8 +34,8 @@ use matrix_sdk::{
test_utils::events::EventFactory,
BoxFuture,
};
use matrix_sdk_base::latest_event::LatestEvent;
use matrix_sdk_test::{EventBuilder, ALICE, BOB};
use matrix_sdk_base::{latest_event::LatestEvent, RoomInfo, RoomState};
use matrix_sdk_test::{EventBuilder, ALICE, BOB, DEFAULT_TEST_ROOM_ID};
use ruma::{
event_id,
events::{
Expand Down Expand Up @@ -444,4 +445,9 @@ impl RoomDataProvider for TestRoomDataProvider {
}
.boxed()
}

fn room_info(&self) -> Subscriber<RoomInfo> {
let info = RoomInfo::new(*DEFAULT_TEST_ROOM_ID, RoomState::Joined);
SharedObservable::new(info).subscribe()
}
}
9 changes: 8 additions & 1 deletion crates/matrix-sdk-ui/src/timeline/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

use std::future::Future;

use eyeball::Subscriber;
use futures_util::FutureExt as _;
use indexmap::IndexMap;
#[cfg(test)]
Expand All @@ -22,7 +23,7 @@ use matrix_sdk::{
deserialized_responses::TimelineEvent, event_cache::paginator::PaginableRoom, BoxFuture,
Result, Room,
};
use matrix_sdk_base::latest_event::LatestEvent;
use matrix_sdk_base::{latest_event::LatestEvent, RoomInfo};
use ruma::{
events::{
fully_read::FullyReadEventContent,
Expand Down Expand Up @@ -107,6 +108,8 @@ pub(super) trait RoomDataProvider:
reason: Option<&'a str>,
transaction_id: Option<OwnedTransactionId>,
) -> BoxFuture<'a, Result<(), super::Error>>;

fn room_info(&self) -> Subscriber<RoomInfo>;
}

impl RoomDataProvider for Room {
Expand Down Expand Up @@ -271,6 +274,10 @@ impl RoomDataProvider for Room {
}
.boxed()
}

fn room_info(&self) -> Subscriber<RoomInfo> {
self.subscribe_info()
}
}

// Internal helper to make most of retry_event_decryption independent of a room
Expand Down
27 changes: 15 additions & 12 deletions crates/matrix-sdk-ui/tests/integration/timeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ use matrix_sdk_test::{
SyncResponseBuilder, BOB,
};
use matrix_sdk_ui::{
timeline::{EventSendState, RoomExt, TimelineItemContent, VirtualTimelineItem},
RoomListService,
timeline::{
AnyOtherFullStateEventContent, EventSendState, RoomExt, TimelineItemContent,
VirtualTimelineItem,
},
RoomListService, Timeline,
};
use ruma::{
event_id,
Expand Down Expand Up @@ -677,7 +680,9 @@ async fn test_timeline_without_encryption_can_update() {

let room = client.get_room(room_id).unwrap();
// Previously this would have panicked.
let timeline = room.timeline().await.unwrap();
// We're creating a timeline without read receipts tracking to check only the
// encryption changes
let timeline = Timeline::builder(&room).build().await.unwrap();

let (items, mut stream) = timeline.subscribe().await;
assert_eq!(items.len(), 2);
Expand All @@ -695,20 +700,18 @@ async fn test_timeline_without_encryption_can_update() {
let _response = client.sync_once(sync_settings.clone()).await.unwrap();
server.reset().await;

// Previous timeline event keeps its value
// Previous timeline event now has a shield
assert_next_matches!(stream, VectorDiff::Set { index, value } => {
assert_eq!(index, 1);
assert!(value.as_event().unwrap().get_shield(false).is_none());
});
// New encryption event has shield info
assert_next_matches!(stream, VectorDiff::PushBack { value } => {
assert!(value.as_event().unwrap().get_shield(false).is_some());
});
assert_next_matches!(stream, VectorDiff::Set { index, value } => {
assert_eq!(index, 2);
// Room encryption event is received
assert_next_matches!(stream, VectorDiff::PushBack { value } => {
assert_let!(TimelineItemContent::OtherState(other_state) = value.as_event().unwrap().content());
assert_let!(AnyOtherFullStateEventContent::RoomEncryption(_) = other_state.content());
assert!(value.as_event().unwrap().get_shield(false).is_some());
});
// New message has shield info
// New message event is received and has a shield
assert_next_matches!(stream, VectorDiff::PushBack { value } => {
assert!(value.as_event().unwrap().get_shield(false).is_some());
});
Expand Down Expand Up @@ -741,7 +744,7 @@ impl PinningTestSetup<'_> {
Self { event_id, room_id, client, server, sync_settings, sync_builder }
}

async fn timeline(&self) -> matrix_sdk_ui::Timeline {
async fn timeline(&self) -> Timeline {
mock_encryption_state(&self.server, false).await;
let room = self.client.get_room(self.room_id).unwrap();
room.timeline().await.unwrap()
Expand Down

0 comments on commit 0bddcfd

Please sign in to comment.