Skip to content

Commit 140e751

Browse files
authored
feat(timeline): consider unthreaded read receipts for ReceiptThread::Main timelines when computing timeline items states.
This patch updates the Timeline Controller's `handle_explicit_read_receipts` method to also consider unthreaded read receipts on **threaded** **main** timelines when calculating timeline items states and adds a test for it. This picks up from #5442 and fixes #5440
1 parent a66b2c5 commit 140e751

File tree

2 files changed

+74
-2
lines changed

2 files changed

+74
-2
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,8 +515,9 @@ impl<P: RoomDataProvider> TimelineStateTransaction<'_, P> {
515515
}
516516

517517
for (user_id, receipt) in receipts {
518-
if own_receipt_thread == ReceiptThread::Unthreaded {
519-
// If the own receipt thread is unthreaded, we maintain maximal
518+
if matches!(own_receipt_thread, ReceiptThread::Unthreaded | ReceiptThread::Main)
519+
{
520+
// If the own receipt thread is unthreaded or main, we maintain maximal
520521
// compatibility with clients using either unthreaded or main-thread read
521522
// receipts by allowing both here.
522523
if !matches!(

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

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,3 +828,74 @@ async fn test_threaded_latest_user_read_receipt() {
828828
assert_eq!(receipt_event_id, event_id!("$3"));
829829
assert_eq!(receipt.thread, receipt_thread);
830830
}
831+
832+
#[async_test]
833+
async fn test_unthreaded_client_updates_threaded_read_receipts() {
834+
let timeline = TestTimelineBuilder::new()
835+
.settings(TimelineSettings { track_read_receipts: true, ..Default::default() })
836+
.focus(TimelineFocus::Live { hide_threaded_events: true })
837+
.build();
838+
let mut stream = timeline.subscribe().await;
839+
840+
let event_b = event_id!("$event_b");
841+
842+
// Alice sends 2 messages
843+
let f = &timeline.factory;
844+
timeline.handle_live_event(f.text_msg("A").sender(*ALICE)).await;
845+
timeline.handle_live_event(f.text_msg("B").sender(*ALICE).event_id(event_b)).await;
846+
847+
assert_next_matches!(stream, VectorDiff::PushBack { .. });
848+
assert_next_matches!(stream, VectorDiff::PushFront { .. });
849+
assert_next_matches!(stream, VectorDiff::PushBack { .. });
850+
assert_pending!(stream);
851+
852+
// Bob reads the last one from an unthreaded client
853+
timeline
854+
.handle_read_receipts([(
855+
event_b.to_owned(),
856+
ReceiptType::Read,
857+
BOB.to_owned(),
858+
ReceiptThread::Unthreaded,
859+
)])
860+
.await;
861+
862+
// Alice's timeline gets updated
863+
let item_b = assert_next_matches!(stream, VectorDiff::Set { index: 2, value } => value);
864+
let event_b = item_b.as_event().unwrap();
865+
assert_eq!(event_b.read_receipts().len(), 1);
866+
assert!(event_b.read_receipts().get(*BOB).is_some());
867+
assert_pending!(stream);
868+
869+
// Then Alice sends a message in a thread
870+
let event_c = event_id!("$event_c");
871+
let thread_event_id = event_id!("$thread");
872+
873+
timeline
874+
.handle_live_event(
875+
f.text_msg("C")
876+
.sender(*ALICE)
877+
.event_id(event_c)
878+
.in_thread(thread_event_id, event_id!("$latest")),
879+
)
880+
.await;
881+
882+
// Alice is using a threaded client so the main timeline shouldn't change
883+
assert_pending!(stream);
884+
885+
// Bob reads the threaded message
886+
timeline
887+
.handle_read_receipts([(
888+
event_c.to_owned(),
889+
ReceiptType::Read,
890+
BOB.to_owned(),
891+
ReceiptThread::Thread(thread_event_id.to_owned()),
892+
)])
893+
.await;
894+
895+
// The main timeline read receipts are still correct
896+
let event_b = timeline.controller.items().await[2].as_event().unwrap().to_owned();
897+
assert_eq!(event_b.read_receipts().len(), 1);
898+
assert!(event_b.read_receipts().get(*BOB).is_some());
899+
900+
assert_pending!(stream);
901+
}

0 commit comments

Comments
 (0)