Skip to content

Commit f978960

Browse files
committed
timeline: don't insert a read marker when all subsequent events have been inserted by ourselves
1 parent 3f93324 commit f978960

File tree

5 files changed

+110
-29
lines changed

5 files changed

+110
-29
lines changed

crates/matrix-sdk-ui/src/timeline/controller/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ impl<P: RoomDataProvider> TimelineController<P> {
272272

273273
let state = TimelineState::new(
274274
focus_kind,
275+
room_data_provider.own_user_id().to_owned(),
275276
room_data_provider.room_version(),
276277
internal_id_prefix,
277278
unable_to_decrypt_hook,

crates/matrix-sdk-ui/src/timeline/controller/state.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub(in crate::timeline) struct TimelineState {
7070
impl TimelineState {
7171
pub(super) fn new(
7272
timeline_focus: TimelineFocusKind,
73+
own_user_id: OwnedUserId,
7374
room_version: RoomVersionId,
7475
internal_id_prefix: Option<String>,
7576
unable_to_decrypt_hook: Option<Arc<UtdHookManager>>,
@@ -81,6 +82,7 @@ impl TimelineState {
8182
// small enough.
8283
items: ObservableVector::with_capacity(32),
8384
meta: TimelineMetadata::new(
85+
own_user_id,
8486
room_version,
8587
internal_id_prefix,
8688
unable_to_decrypt_hook,
@@ -691,6 +693,9 @@ pub(in crate::timeline) struct TimelineMetadata {
691693
/// This value is constant over the lifetime of the metadata.
692694
pub room_version: RoomVersionId,
693695

696+
/// The own [`OwnedUserId`] of the client who opened the timeline.
697+
own_user_id: OwnedUserId,
698+
694699
// **** DYNAMIC FIELDS ****
695700
/// The next internal identifier for timeline items, used for both local and
696701
/// remote echoes.
@@ -717,12 +722,14 @@ pub(in crate::timeline) struct TimelineMetadata {
717722

718723
impl TimelineMetadata {
719724
pub(crate) fn new(
725+
own_user_id: OwnedUserId,
720726
room_version: RoomVersionId,
721727
internal_id_prefix: Option<String>,
722728
unable_to_decrypt_hook: Option<Arc<UtdHookManager>>,
723729
is_room_encrypted: bool,
724730
) -> Self {
725731
Self {
732+
own_user_id,
726733
all_events: Default::default(),
727734
next_internal_id: Default::default(),
728735
reactions: Default::default(),
@@ -805,7 +812,38 @@ impl TimelineMetadata {
805812
trace!(?fully_read_event, "Updating read marker");
806813

807814
let read_marker_idx = items.iter().rposition(|item| item.is_read_marker());
808-
let fully_read_event_idx = rfind_event_by_id(items, fully_read_event).map(|(idx, _)| idx);
815+
816+
let mut fully_read_event_idx =
817+
rfind_event_by_id(items, fully_read_event).map(|(idx, _)| idx);
818+
819+
if let Some(i) = &mut fully_read_event_idx {
820+
// The item at position `i` is the first item that's fully read, we're about to
821+
// insert a read marker just after it.
822+
//
823+
// Do another forward pass to skip all the events we've sent too.
824+
825+
// Find the position of the first element…
826+
let next = items
827+
.iter()
828+
.enumerate()
829+
// …strictly *after* the fully read event…
830+
.skip(*i + 1)
831+
// …that's not virtual and not sent by us…
832+
.find(|(_, item)| {
833+
item.as_event().map_or(false, |event| event.sender() != self.own_user_id)
834+
})
835+
.map(|(i, _)| i);
836+
837+
if let Some(next) = next {
838+
// `next` point to the first item that's not sent by us, so the *previous* of
839+
// next is the right place where to insert the fully read marker.
840+
*i = next.wrapping_sub(1);
841+
} else {
842+
// There's no event after the read marker that's not sent by us, i.e. the full
843+
// timeline has been read: the fully read marker goes to the end.
844+
*i = items.len().wrapping_sub(1);
845+
}
846+
}
809847

810848
match (read_marker_idx, fully_read_event_idx) {
811849
(None, None) => {

crates/matrix-sdk-ui/src/timeline/day_dividers.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -642,12 +642,16 @@ mod tests {
642642
)
643643
}
644644

645+
fn test_metadata() -> TimelineMetadata {
646+
TimelineMetadata::new(owned_user_id!("@a:b.c"), ruma::RoomVersionId::V11, None, None, false)
647+
}
648+
645649
#[test]
646650
fn test_no_trailing_day_divider() {
647651
let mut items = ObservableVector::new();
648652
let mut txn = items.transaction();
649653

650-
let mut meta = TimelineMetadata::new(ruma::RoomVersionId::V11, None, None, false);
654+
let mut meta = test_metadata();
651655

652656
let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
653657
let timestamp_next_day =
@@ -681,7 +685,7 @@ mod tests {
681685
let mut items = ObservableVector::new();
682686
let mut txn = items.transaction();
683687

684-
let mut meta = TimelineMetadata::new(ruma::RoomVersionId::V11, None, None, false);
688+
let mut meta = test_metadata();
685689

686690
let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
687691
let timestamp_next_day =
@@ -713,7 +717,7 @@ mod tests {
713717
let mut items = ObservableVector::new();
714718
let mut txn = items.transaction();
715719

716-
let mut meta = TimelineMetadata::new(ruma::RoomVersionId::V11, None, None, false);
720+
let mut meta = test_metadata();
717721

718722
let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
719723
let timestamp_next_day =
@@ -747,7 +751,7 @@ mod tests {
747751
let mut items = ObservableVector::new();
748752
let mut txn = items.transaction();
749753

750-
let mut meta = TimelineMetadata::new(ruma::RoomVersionId::V11, None, None, false);
754+
let mut meta = test_metadata();
751755

752756
let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
753757
let timestamp_next_day =
@@ -777,7 +781,7 @@ mod tests {
777781
let mut items = ObservableVector::new();
778782
let mut txn = items.transaction();
779783

780-
let mut meta = TimelineMetadata::new(ruma::RoomVersionId::V11, None, None, false);
784+
let mut meta = test_metadata();
781785

782786
let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
783787

@@ -803,7 +807,7 @@ mod tests {
803807
let mut items = ObservableVector::new();
804808
let mut txn = items.transaction();
805809

806-
let mut meta = TimelineMetadata::new(ruma::RoomVersionId::V11, None, None, false);
810+
let mut meta = test_metadata();
807811

808812
let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
809813

@@ -827,8 +831,7 @@ mod tests {
827831
let mut items = ObservableVector::new();
828832
let mut txn = items.transaction();
829833

830-
let mut meta = TimelineMetadata::new(ruma::RoomVersionId::V11, None, None, false);
831-
834+
let mut meta = test_metadata();
832835
let timestamp = MilliSecondsSinceUnixEpoch(uint!(42));
833836

834837
txn.push_back(meta.new_timeline_item(VirtualTimelineItem::ReadMarker));

crates/matrix-sdk-ui/src/timeline/tests/echo.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ async fn test_day_divider_removed_after_local_echo_disappeared() {
267267
}
268268

269269
#[async_test]
270-
async fn test_read_marker_removed_after_local_echo_disappeared() {
270+
async fn test_no_read_marker_with_local_echo() {
271271
let event_id = event_id!("$1");
272272

273273
let timeline = TestTimeline::with_room_data_provider(
@@ -306,11 +306,10 @@ async fn test_read_marker_removed_after_local_echo_disappeared() {
306306

307307
let items = timeline.controller.items().await;
308308

309-
assert_eq!(items.len(), 4);
309+
assert_eq!(items.len(), 3);
310310
assert!(items[0].is_day_divider());
311311
assert!(items[1].is_remote_event());
312-
assert!(items[2].is_read_marker());
313-
assert!(items[3].is_local_echo());
312+
assert!(items[2].is_local_echo());
314313

315314
// Cancel the local echo.
316315
timeline

crates/matrix-sdk-ui/src/timeline/tests/virt.rs

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use assert_matches::assert_matches;
1616
use assert_matches2::assert_let;
1717
use chrono::{Datelike, Local, TimeZone};
1818
use eyeball_im::VectorDiff;
19+
use futures_util::{FutureExt, StreamExt as _};
1920
use matrix_sdk_test::{async_test, ALICE, BOB};
2021
use ruma::{
2122
event_id,
@@ -24,7 +25,7 @@ use ruma::{
2425
use stream_assert::assert_next_matches;
2526

2627
use super::TestTimeline;
27-
use crate::timeline::{TimelineItemKind, VirtualTimelineItem};
28+
use crate::timeline::{traits::RoomDataProvider as _, TimelineItemKind, VirtualTimelineItem};
2829

2930
#[async_test]
3031
async fn test_day_divider() {
@@ -91,60 +92,99 @@ async fn test_update_read_marker() {
9192
let timeline = TestTimeline::new();
9293
let mut stream = timeline.subscribe().await;
9394

94-
let f = &timeline.factory;
95+
let own_user = timeline.controller.room_data_provider.own_user_id().to_owned();
9596

96-
timeline.handle_live_event(f.text_msg("A").sender(&ALICE)).await;
97+
let f = &timeline.factory;
98+
timeline.handle_live_event(f.text_msg("A").sender(&own_user)).await;
9799

98100
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
99-
let first_event_id = item.as_event().unwrap().event_id().unwrap().to_owned();
101+
let event_id1 = item.as_event().unwrap().event_id().unwrap().to_owned();
100102

101103
let day_divider = assert_next_matches!(stream, VectorDiff::PushFront { value } => value);
102104
assert!(day_divider.is_day_divider());
103105

104-
timeline.controller.handle_fully_read_marker(first_event_id.clone()).await;
106+
timeline.controller.handle_fully_read_marker(event_id1.to_owned()).await;
105107

106108
// Nothing should happen, the marker cannot be added at the end.
107109

108110
timeline.handle_live_event(f.text_msg("B").sender(&BOB)).await;
109111
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
110-
let second_event_id = item.as_event().unwrap().event_id().unwrap().to_owned();
112+
let event_id2 = item.as_event().unwrap().event_id().unwrap().to_owned();
111113

112114
// Now the read marker appears after the first event.
113115
let item = assert_next_matches!(stream, VectorDiff::Insert { index: 2, value } => value);
114116
assert_matches!(item.as_virtual(), Some(VirtualTimelineItem::ReadMarker));
115117

116-
timeline.controller.handle_fully_read_marker(second_event_id.clone()).await;
118+
timeline.controller.handle_fully_read_marker(event_id2.clone()).await;
117119

118120
// The read marker is removed but not reinserted, because it cannot be added at
119121
// the end.
120122
assert_next_matches!(stream, VectorDiff::Remove { index: 2 });
121123

122-
timeline.handle_live_event(f.text_msg("C").sender(&ALICE)).await;
124+
timeline.handle_live_event(f.text_msg("C").sender(&BOB)).await;
123125
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
124-
let third_event_id = item.as_event().unwrap().event_id().unwrap().to_owned();
126+
let event_id3 = item.as_event().unwrap().event_id().unwrap().to_owned();
125127

126128
// Now the read marker is reinserted after the second event.
127129
let marker = assert_next_matches!(stream, VectorDiff::Insert { index: 3, value } => value);
128130
assert_matches!(marker.kind, TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker));
129131

130-
// Nothing should happen if the fully read event is set back to an older event.
131-
timeline.controller.handle_fully_read_marker(first_event_id).await;
132+
// Nothing should happen if the fully read event is set back to an older event
133+
// sent by another user.
134+
timeline.controller.handle_fully_read_marker(event_id1.to_owned()).await;
135+
assert!(stream.next().now_or_never().is_none());
132136

133137
// Nothing should happen if the fully read event isn't found.
134138
timeline.controller.handle_fully_read_marker(event_id!("$fake_event_id").to_owned()).await;
139+
assert!(stream.next().now_or_never().is_none());
135140

136141
// Nothing should happen if the fully read event is referring to an event
137142
// that has already been marked as fully read.
138-
timeline.controller.handle_fully_read_marker(second_event_id).await;
143+
timeline.controller.handle_fully_read_marker(event_id2).await;
144+
assert!(stream.next().now_or_never().is_none());
139145

140-
timeline.handle_live_event(f.text_msg("D").sender(&ALICE)).await;
146+
timeline.handle_live_event(f.text_msg("D").sender(&BOB)).await;
141147
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
142-
item.as_event().unwrap();
148+
let event_id4 = item.as_event().unwrap().event_id().unwrap().to_owned();
143149

144-
timeline.controller.handle_fully_read_marker(third_event_id).await;
150+
timeline.controller.handle_fully_read_marker(event_id3).await;
145151

146-
// The read marker is moved after the third event.
152+
// The read marker is moved after the third event (sent by another user).
147153
assert_next_matches!(stream, VectorDiff::Remove { index: 3 });
148154
let marker = assert_next_matches!(stream, VectorDiff::Insert { index: 4, value } => value);
149155
assert_matches!(marker.kind, TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker));
156+
157+
// If the current user sends an event afterwards, the read marker doesn't move.
158+
timeline.handle_live_event(f.text_msg("E").sender(&own_user)).await;
159+
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
160+
item.as_event().unwrap();
161+
162+
assert!(stream.next().now_or_never().is_none());
163+
164+
// If the marker moved forward to another user's event, and there's no other
165+
// event sent from another user, then it will be removed.
166+
timeline.controller.handle_fully_read_marker(event_id4).await;
167+
assert_next_matches!(stream, VectorDiff::Remove { index: 4 });
168+
169+
assert!(stream.next().now_or_never().is_none());
170+
171+
// When a last event is inserted by ourselves, still no read marker.
172+
timeline.handle_live_event(f.text_msg("F").sender(&own_user)).await;
173+
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
174+
item.as_event().unwrap();
175+
176+
timeline.handle_live_event(f.text_msg("G").sender(&own_user)).await;
177+
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
178+
item.as_event().unwrap();
179+
180+
assert!(stream.next().now_or_never().is_none());
181+
182+
// But when it's another user who sent the event, then we get a read marker for
183+
// their message.
184+
timeline.handle_live_event(f.text_msg("H").sender(&BOB)).await;
185+
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
186+
item.as_event().unwrap();
187+
188+
let marker = assert_next_matches!(stream, VectorDiff::Insert { index: 8, value } => value);
189+
assert_matches!(marker.kind, TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker));
150190
}

0 commit comments

Comments
 (0)