Commit 4a96f652bdcd
tarball3 files changed+154-34
@@ -777,9 +777,27 @@ impl<P: RoomDataProvider> TimelineState<P> {
777777
room_data_provider: &P,778778
receipt_type: ReceiptType,779779
) {780
+ self.meta781
+ .populate_initial_user_receipt(782
+ self.focus.receipt_thread(),783
+ room_data_provider,784
+ receipt_type,785
+ )786
+ .await;787
+ }788
+}789
+790
+impl TimelineMetadata {791
+ /// Populates our own latest read receipt in the in-memory by-user read792
+ /// receipt cache.793
+ pub(super) async fn populate_initial_user_receipt<P: RoomDataProvider>(794
+ &mut self,795
+ receipt_thread: ReceiptThread,796
+ room_data_provider: &P,797
+ receipt_type: ReceiptType,798
+ ) {780799
let own_user_id = room_data_provider.own_user_id().to_owned();781800
782
- let receipt_thread = self.focus.receipt_thread();783801
let wants_unthreaded_receipts = receipt_thread == ReceiptThread::Unthreaded;784802
785803
let mut read_receipt = room_data_provider@@ -794,10 +812,47 @@ impl<P: RoomDataProvider> TimelineState<P> {
794812
}795813
796814
if let Some(read_receipt) = read_receipt {797
- self.meta.read_receipts.upsert_latest(own_user_id, receipt_type, read_receipt);815
+ self.read_receipts.upsert_latest(own_user_id, receipt_type, read_receipt);798816
}799817
}800818
819
+ /// Get the ID of the visible timeline event with the latest read receipt820
+ /// for the given user.821
+ pub(super) fn latest_user_read_receipt_timeline_event_id(822
+ &self,823
+ user_id: &UserId,824
+ all_remote_events: &AllRemoteEvents,825
+ ) -> Option<OwnedEventId> {826
+ // We only need to use the local map, since receipts for known events are827
+ // already loaded from the store.828
+ let public_read_receipt = self.read_receipts.get_latest(user_id, &ReceiptType::Read);829
+ let private_read_receipt =830
+ self.read_receipts.get_latest(user_id, &ReceiptType::ReadPrivate);831
+832
+ // Let's assume that a private read receipt should be more recent than a public833
+ // read receipt, otherwise there's no point in the private read receipt,834
+ // and use it as default.835
+ let (latest_receipt_id, _) = match TimelineMetadata::compare_optional_receipts(836
+ public_read_receipt,837
+ private_read_receipt,838
+ all_remote_events,839
+ ) {840
+ Ordering::Greater => public_read_receipt?,841
+ Ordering::Less => private_read_receipt?,842
+ _ => unreachable!(),843
+ };844
+845
+ // Find the corresponding visible event.846
+ all_remote_events847
+ .iter()848
+ .rev()849
+ .skip_while(|ev| ev.event_id != *latest_receipt_id)850
+ .find(|ev| ev.visible && ev.can_show_read_receipts)851
+ .map(|ev| ev.event_id.clone())852
+ }853
+}854
+855
+impl<P: RoomDataProvider> TimelineState<P> {801856
/// Get the latest read receipt for the given user.802857
///803858
/// Useful to get the latest read receipt, whether it's private or public.@@ -851,33 +906,8 @@ impl<P: RoomDataProvider> TimelineState<P> {
851906
&self,852907
user_id: &UserId,853908
) -> Option<OwnedEventId> {854
- // We only need to use the local map, since receipts for known events are855
- // already loaded from the store.856
- let public_read_receipt = self.meta.read_receipts.get_latest(user_id, &ReceiptType::Read);857
- let private_read_receipt =858
- self.meta.read_receipts.get_latest(user_id, &ReceiptType::ReadPrivate);859
-860
- // Let's assume that a private read receipt should be more recent than a public861
- // read receipt, otherwise there's no point in the private read receipt,862
- // and use it as default.863
- let (latest_receipt_id, _) = match TimelineMetadata::compare_optional_receipts(864
- public_read_receipt,865
- private_read_receipt,866
- self.items.all_remote_events(),867
- ) {868
- Ordering::Greater => public_read_receipt?,869
- Ordering::Less => private_read_receipt?,870
- _ => unreachable!(),871
- };872
-873
- // Find the corresponding visible event.874
- self.items875
- .all_remote_events()876
- .iter()877
- .rev()878
- .skip_while(|ev| ev.event_id != *latest_receipt_id)879
- .find(|ev| ev.visible && ev.can_show_read_receipts)880
- .map(|ev| ev.event_id.clone())909
+ self.meta910
+ .latest_user_read_receipt_timeline_event_id(user_id, self.items.all_remote_events())881911
}882912
}883913
@@ -214,9 +214,31 @@ impl<'a, P: RoomDataProvider> TimelineStateTransaction<'a, P> {
214214
}215215
216216
VectorDiff::Reset { values: events } => {217
- recycled_timeline_ids.clear();217
+ for event_meta in self.items.all_remote_events().iter() {218
+ if let Some(index) = event_meta.timeline_item_index {219
+ recycled_timeline_ids.insert(220
+ event_meta.event_id.clone(),221
+ self.items[index].unique_id().clone(),222
+ );223
+ }224
+ }218225
self.clear();226
+ let track_read_receipts = settings.track_read_receipts.is_enabled();227
+ if track_read_receipts {228
+ for receipt_type in [ReceiptType::Read, ReceiptType::ReadPrivate] {229
+ self.meta230
+ .populate_initial_user_receipt(231
+ self.focus.receipt_thread(),232
+ room_data_provider,233
+ receipt_type,234
+ )235
+ .await;236
+ }237
+ }219238
for event in events {239
+ let recycled_timeline_id = event240
+ .event_id()241
+ .and_then(|event_id| recycled_timeline_ids.remove(&event_id));220242
self.handle_remote_event(221243
event,222244
TimelineItemPosition::End { origin },@@ -224,10 +246,22 @@ impl<'a, P: RoomDataProvider> TimelineStateTransaction<'a, P> {
224246
settings,225247
&mut date_divider_adjuster,226248
&mut cached_profiles,227
- None,249
+ recycled_timeline_id,228250
)229251
.await;230252
}253
+ if track_read_receipts {254
+ let fully_read = match room_data_provider.load_fully_read_marker().await {255
+ Some(fully_read) => Some(fully_read),256
+ None => self.meta.latest_user_read_receipt_timeline_event_id(257
+ room_data_provider.own_user_id(),258
+ self.items.all_remote_events(),259
+ ),260
+ };261
+ if let Some(fully_read) = fully_read {262
+ self.set_fully_read_event(fully_read);263
+ }264
+ }231265
}232266
233267
v => unimplemented!("{v:?}"),@@ -2,14 +2,17 @@ use eyeball_im::VectorDiff;
22
use futures_util::{FutureExt, StreamExt};33
use imbl::vector;44
use matrix_sdk::event_cache::{HistoryGap, HistoryGapPosition, HistoryGapStatus};5
-use matrix_sdk_test::{ALICE, async_test};5
+use matrix_sdk_test::{ALICE, BOB, async_test};66
use ruma::{77
TransactionId, event_id,88
events::{AnyMessageLikeEventContent, room::message::RoomMessageEventContent},99
};1010
11
-use super::TestTimeline;12
-use crate::timeline::{VirtualTimelineItem, event_item::RemoteEventOrigin};11
+use super::{TestRoomDataProvider, TestTimeline, TestTimelineBuilder};12
+use crate::timeline::{13
+ TimelineReadReceiptTracking, VirtualTimelineItem, controller::TimelineSettings,14
+ event_item::RemoteEventOrigin,15
+};1316
1417
#[async_test]1518
async fn test_segmented_history_initial_snapshot_keeps_retained_side() {@@ -226,3 +229,56 @@ async fn test_segmented_history_reset_preserves_pending_sends() {
226229
1227230
);228231
}232
+233
+#[async_test]234
+async fn test_history_reset_keeps_read_marker_and_item_identities() {235
+ let old = event_id!("$old");236
+ let new = event_id!("$new");237
+ let timeline = TestTimelineBuilder::new()238
+ .provider(TestRoomDataProvider::default().with_fully_read_marker(old.to_owned()))239
+ .settings(TimelineSettings {240
+ track_read_receipts: TimelineReadReceiptTracking::AllEvents,241
+ ..Default::default()242
+ })243
+ .build();244
+ let f = &timeline.factory;245
+ timeline246
+ .controller247
+ .replace_with_initial_remote_events(248
+ [249
+ f.text_msg("old").sender(*ALICE).event_id(old).into_event(),250
+ f.text_msg("new").sender(*BOB).event_id(new).into_event(),251
+ ],252
+ RemoteEventOrigin::Cache,253
+ )254
+ .await;255
+ let unique_id = |items: &imbl::Vector<std::sync::Arc<crate::timeline::TimelineItem>>| {256
+ items257
+ .iter()258
+ .find(|item| item.as_event().and_then(|event| event.event_id()) == Some(new))259
+ .unwrap()260
+ .unique_id()261
+ .clone()262
+ };263
+ let before = unique_id(&timeline.controller.items().await);264
+265
+ timeline266
+ .controller267
+ .handle_history_update(268
+ vec![VectorDiff::Reset {269
+ values: vector![270
+ f.text_msg("old").sender(*ALICE).event_id(old).into(),271
+ f.text_msg("new").sender(*BOB).event_id(new).into(),272
+ ],273
+ }],274
+ None,275
+ RemoteEventOrigin::Cache,276
+ )277
+ .await;278
+279
+ let items = timeline.controller.items().await;280
+ assert_eq!(unique_id(&items), before);281
+ assert!(282
+ items.iter().any(|item| matches!(item.as_virtual(), Some(VirtualTimelineItem::ReadMarker)))283
+ );284
+}