Skip to content

Commit

Permalink
sdk-ui: make room encryption optional to create a timeline
Browse files Browse the repository at this point in the history
Instead of forcing the room encryption to be known when the timeline is created and failing if it's not known, take the latest room encryption info as a base value and update it when processing timeline events.

At the time of writing this commit, the encryption info is only used to decide whether shields should be calculated for timeline items or not.
  • Loading branch information
jmartinesp committed Sep 23, 2024
1 parent 794dbb3 commit e44efb4
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 25 deletions.
11 changes: 9 additions & 2 deletions crates/matrix-sdk-ui/src/timeline/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,7 @@ impl TimelineBuilder {
let (_, mut event_subscriber) = room_event_cache.subscribe().await?;

let is_pinned_events = matches!(focus, TimelineFocus::PinnedEvents { .. });
let is_room_encrypted =
room.is_encrypted().await.map_err(|_| Error::UnknownEncryptionState)?;
let is_room_encrypted = room.is_encrypted().await.ok();

let controller = TimelineController::new(
room,
Expand Down Expand Up @@ -196,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 @@ -421,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
30 changes: 29 additions & 1 deletion crates/matrix-sdk-ui/src/timeline/controller/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ impl<P: RoomDataProvider> TimelineController<P> {
focus: TimelineFocus,
internal_id_prefix: Option<String>,
unable_to_decrypt_hook: Option<Arc<UtdHookManager>>,
is_room_encrypted: bool,
is_room_encrypted: Option<bool>,
) -> Self {
let (focus_data, focus_kind) = match focus {
TimelineFocus::Live => (TimelineFocusData::Live, TimelineFocusKind::Live),
Expand Down Expand Up @@ -345,6 +345,34 @@ 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. This will then cause a refresh of those
/// 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 changed = {
let state = self.state.read().await;
let mut old_is_room_encrypted = state.meta.is_room_encrypted.write().unwrap();
let is_encrypted_now = info.is_encrypted();

if *old_is_room_encrypted != Some(is_encrypted_now) {
*old_is_room_encrypted = Some(is_encrypted_now);
true
} else {
false
}
};

if changed {
let mut state = self.state.write().await;
state.update_all_events_is_room_encrypted();
}
}
}

pub(crate) async fn reload_pinned_events(
&self,
) -> Result<Vec<SyncTimelineEvent>, PinnedEventsLoaderError> {
Expand Down
46 changes: 38 additions & 8 deletions crates/matrix-sdk-ui/src/timeline/controller/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{collections::VecDeque, future::Future, num::NonZeroUsize, sync::Arc};
use std::{
collections::VecDeque,
future::Future,
num::NonZeroUsize,
sync::{Arc, RwLock},
};

use eyeball_im::{ObservableVector, ObservableVectorTransaction, ObservableVectorTransactionEntry};
use itertools::Itertools as _;
Expand Down Expand Up @@ -83,7 +88,7 @@ impl TimelineState {
room_version: RoomVersionId,
internal_id_prefix: Option<String>,
unable_to_decrypt_hook: Option<Arc<UtdHookManager>>,
is_room_encrypted: bool,
is_room_encrypted: Option<bool>,
) -> Self {
Self {
// Upstream default capacity is currently 16, which is making
Expand Down Expand Up @@ -295,6 +300,16 @@ impl TimelineState {
result
}

pub(super) fn update_all_events_is_room_encrypted(&mut self) {
let is_room_encrypted = *self.meta.is_room_encrypted.read().unwrap();

// When this transaction finishes, all items in the timeline will be emitted
// again with the updated encryption value
let mut txn = self.transaction();
txn.update_all_events_is_room_encrypted(is_room_encrypted);
txn.commit();
}

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

/// This method replaces the `is_room_encrypted` value for all timeline
/// items to its updated version and creates a `VectorDiff::Set` operation
/// for each item which will be added to this transaction.
fn update_all_events_is_room_encrypted(&mut self, is_encrypted: Option<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 = is_encrypted;

// Replace the existing item with a new version with the right encryption flag
let item = item.with_kind(cloned_event);
self.items.set(idx, item);
}
}
}
}

#[derive(Clone)]
Expand Down Expand Up @@ -754,10 +787,7 @@ 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: bool,
pub(crate) is_room_encrypted: Arc<RwLock<Option<bool>>>,

/// Matrix room version of the timeline's room, or a sensible default.
///
Expand Down Expand Up @@ -814,7 +844,7 @@ impl TimelineMetadata {
room_version: RoomVersionId,
internal_id_prefix: Option<String>,
unable_to_decrypt_hook: Option<Arc<UtdHookManager>>,
is_room_encrypted: bool,
is_room_encrypted: Option<bool>,
) -> Self {
Self {
own_user_id,
Expand All @@ -831,7 +861,7 @@ impl TimelineMetadata {
room_version,
unable_to_decrypt_hook,
internal_id_prefix,
is_room_encrypted,
is_room_encrypted: Arc::new(RwLock::new(is_room_encrypted)),
}
}

Expand Down
8 changes: 7 additions & 1 deletion crates/matrix-sdk-ui/src/timeline/day_dividers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,13 @@ mod tests {
}

fn test_metadata() -> TimelineMetadata {
TimelineMetadata::new(owned_user_id!("@a:b.c"), ruma::RoomVersionId::V11, None, None, false)
TimelineMetadata::new(
owned_user_id!("@a:b.c"),
ruma::RoomVersionId::V11,
None,
None,
Some(false),
)
}

#[test]
Expand Down
8 changes: 7 additions & 1 deletion crates/matrix-sdk-ui/src/timeline/event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,8 @@ 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 should_add {
self.add_item(TimelineItemContent::OtherState(OtherState {
state_key,
Expand Down Expand Up @@ -955,7 +957,11 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
}
};

let is_room_encrypted = self.meta.is_room_encrypted;
let is_room_encrypted = if let Ok(is_room_encrypted) = self.meta.is_room_encrypted.read() {
is_room_encrypted.unwrap_or_default()
} else {
false
};

let mut item = EventTimelineItem::new(
sender,
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
18 changes: 12 additions & 6 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 @@ -103,7 +104,7 @@ impl TestTimeline {
TimelineFocus::Live,
Some(prefix),
None,
false,
Some(false),
),
event_builder: EventBuilder::new(),
factory: EventFactory::new(),
Expand All @@ -117,7 +118,7 @@ impl TestTimeline {
TimelineFocus::Live,
None,
None,
false,
Some(false),
),
event_builder: EventBuilder::new(),
factory: EventFactory::new(),
Expand All @@ -131,7 +132,7 @@ impl TestTimeline {
TimelineFocus::Live,
None,
Some(hook),
true,
Some(true),
),
event_builder: EventBuilder::new(),
factory: EventFactory::new(),
Expand All @@ -146,7 +147,7 @@ impl TestTimeline {
TimelineFocus::Live,
None,
None,
encrypted,
Some(encrypted),
),
event_builder: EventBuilder::new(),
factory: EventFactory::new(),
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
Loading

0 comments on commit e44efb4

Please sign in to comment.