From 409fccb7090feaaad78e2c39bda2b4e75f2edacb Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 10 Dec 2024 09:38:03 +0100 Subject: [PATCH] task(ui): Support `VectorDiff::Insert` in `TimelineStateTransaction::handle_remote_events_with_diffs`. This patch updates `TimelineStateTransaction::handle_remote_events_with_diffs` to support `VectorDiff::Insert`. --- .../src/timeline/controller/state.rs | 22 ++++++- .../src/timeline/event_handler.rs | 60 ++++++++++++++++++- 2 files changed, 78 insertions(+), 4 deletions(-) diff --git a/crates/matrix-sdk-ui/src/timeline/controller/state.rs b/crates/matrix-sdk-ui/src/timeline/controller/state.rs index cbc931724..9a9050ace 100644 --- a/crates/matrix-sdk-ui/src/timeline/controller/state.rs +++ b/crates/matrix-sdk-ui/src/timeline/controller/state.rs @@ -485,6 +485,17 @@ impl TimelineStateTransaction<'_> { .await; } + VectorDiff::Insert { index: event_index, value: event } => { + self.handle_remote_event( + event, + TimelineItemPosition::At { event_index, origin }, + room_data_provider, + settings, + &mut day_divider_adjuster, + ) + .await; + } + VectorDiff::Clear => { self.clear(); } @@ -550,7 +561,8 @@ impl TimelineStateTransaction<'_> { // Retrieve the origin of the event. let origin = match position { TimelineItemPosition::End { origin } - | TimelineItemPosition::Start { origin } => origin, + | TimelineItemPosition::Start { origin } + | TimelineItemPosition::At { origin, .. } => origin, TimelineItemPosition::UpdateDecrypted { timeline_item_index: idx } => self .items @@ -826,6 +838,10 @@ impl TimelineStateTransaction<'_> { self.items.push_back_remote_event(event_meta.base_meta()); } + TimelineItemPosition::At { event_index, .. } => { + self.items.insert_remote_event(event_index, event_meta.base_meta()); + } + TimelineItemPosition::UpdateDecrypted { .. } => { if let Some(event) = self.items.get_remote_event_by_event_id_mut(event_meta.event_id) @@ -846,7 +862,9 @@ impl TimelineStateTransaction<'_> { if settings.track_read_receipts && matches!( position, - TimelineItemPosition::Start { .. } | TimelineItemPosition::End { .. } + TimelineItemPosition::Start { .. } + | TimelineItemPosition::End { .. } + | TimelineItemPosition::At { .. } ) { self.load_read_receipts_for_event(event_meta.event_id, room_data_provider).await; diff --git a/crates/matrix-sdk-ui/src/timeline/event_handler.rs b/crates/matrix-sdk-ui/src/timeline/event_handler.rs index 475c1f30a..3cb790740 100644 --- a/crates/matrix-sdk-ui/src/timeline/event_handler.rs +++ b/crates/matrix-sdk-ui/src/timeline/event_handler.rs @@ -290,6 +290,15 @@ pub(super) enum TimelineItemPosition { origin: RemoteEventOrigin, }, + /// One item is inserted to the timeline. + At { + /// Where to insert the remote event. + event_index: usize, + + /// The origin of the new item. + origin: RemoteEventOrigin, + }, + /// A single item is updated, after it's been successfully decrypted. /// /// This happens when an item that was a UTD must be replaced with the @@ -595,7 +604,9 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> { replacement: PendingEdit, ) { match position { - TimelineItemPosition::Start { .. } | TimelineItemPosition::UpdateDecrypted { .. } => { + TimelineItemPosition::Start { .. } + | TimelineItemPosition::At { .. } + | TimelineItemPosition::UpdateDecrypted { .. } => { // Only insert the edit if there wasn't any other edit // before. // @@ -1039,7 +1050,8 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> { Flow::Remote { event_id, raw_event, position, txn_id, encryption_info, .. } => { let origin = match *position { TimelineItemPosition::Start { origin } - | TimelineItemPosition::End { origin } => origin, + | TimelineItemPosition::End { origin } + | TimelineItemPosition::At { origin, .. } => origin, // For updates, reuse the origin of the encrypted event. TimelineItemPosition::UpdateDecrypted { timeline_item_index: idx } => self @@ -1108,6 +1120,50 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> { self.items.push_front(item, Some(0)); } + Flow::Remote { position: TimelineItemPosition::At { event_index, .. }, .. } => { + let all_remote_events = self.items.all_remote_events(); + let event_index = *event_index; + + // Look for the closest `timeline_item_index` at the left of `event_index`. + let timeline_item_index = all_remote_events + .range(0..=event_index) + .rev() + .find_map(|event_meta| event_meta.timeline_item_index) + // The new `timeline_item_index` is the previous + 1. + .map(|timeline_item_index| timeline_item_index + 1); + + // No index? Look for the closest `timeline_item_index` at the right of + // `event_index`. + let timeline_item_index = timeline_item_index.or_else(|| { + all_remote_events + .range(event_index + 1..) + .find_map(|event_meta| event_meta.timeline_item_index) + }); + + // Still no index? Well, it means there is no existing `timeline_item_index` + // so we are inserting at the last non-local item position as a fallback. + let timeline_item_index = timeline_item_index.unwrap_or_else(|| { + self.items + .iter() + .enumerate() + .rev() + .find_map(|(timeline_item_index, timeline_item)| { + (!timeline_item.as_event()?.is_local_echo()) + .then_some(timeline_item_index + 1) + }) + .unwrap_or(0) + }); + + trace!( + ?event_index, + ?timeline_item_index, + "Adding new remote timeline at specific event index" + ); + + let item = self.meta.new_timeline_item(item); + self.items.insert(timeline_item_index, item, Some(event_index)); + } + Flow::Remote { position: TimelineItemPosition::End { .. }, txn_id, event_id, .. } => {