Skip to content

Commit e90dbb3

Browse files
committed
fix: Filter out threaded events from event focused timeline
1 parent 1136eb4 commit e90dbb3

3 files changed

Lines changed: 187 additions & 6 deletions

File tree

crates/matrix-sdk-ui/tests/integration/timeline/focus_event.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,3 +753,144 @@ async fn test_focused_timeline_handles_other_thread_event_when_forcing_threaded_
753753
assert_let!(VectorDiff::PushBack { value: item } = &timeline_updates[0]);
754754
assert_eq!(item.as_event().unwrap().content().as_message().unwrap().body(), "Next");
755755
}
756+
757+
#[async_test]
758+
async fn test_focused_timeline_filters_out_threaded_events() {
759+
let room_id = room_id!("!a98sd12bjh:example.org");
760+
let server = MatrixMockServer::new().await;
761+
let client = server.client_builder().build().await;
762+
let user_id = client.user_id().unwrap();
763+
764+
let prev_thread_event_id = event_id!("$prev:example.org");
765+
let root_thread_id = event_id!("$root-id:example.org");
766+
767+
let f = EventFactory::new().room(room_id).sender(user_id);
768+
let focus_event = f.text_msg("Hey").into_event();
769+
let focus_event_id = focus_event.event_id().unwrap().clone();
770+
771+
// Mock the initial /context request to check if the event is in a thread.
772+
let events_before = vec![
773+
f.text_msg("Unrelated before 1").into_event(),
774+
f.text_msg("Unrelated before 2").into_event(),
775+
f.text_msg("Thread before").in_thread(root_thread_id, prev_thread_event_id).into_event(),
776+
];
777+
let events_after = vec![
778+
f.text_msg("Unrelated after 1").into_event(),
779+
f.text_msg("Unrelated after 2").into_event(),
780+
f.text_msg("Thread after").in_thread(root_thread_id, prev_thread_event_id).into_event(),
781+
];
782+
server
783+
.mock_room_event_context()
784+
.room(room_id)
785+
.ok(RoomContextResponseTemplate::new(focus_event)
786+
.events_before(events_before)
787+
.events_after(events_after)
788+
.start("prev_token_1")
789+
.end("next_token_1"))
790+
.mock_once()
791+
.mount()
792+
.await;
793+
794+
let focus = TimelineFocus::Event {
795+
target: focus_event_id,
796+
num_context_events: 10,
797+
thread_mode: TimelineEventFocusThreadMode::Automatic { hide_threaded_events: true },
798+
};
799+
800+
let room = server.sync_joined_room(&client, room_id).await;
801+
let timeline = TimelineBuilder::new(&room)
802+
.with_focus(focus)
803+
.build()
804+
.await
805+
.expect("Could not build focused timeline");
806+
807+
assert!(
808+
timeline.live_back_pagination_status().await.is_none(),
809+
"there should be no live back-pagination status for a focused timeline"
810+
);
811+
812+
let (items, mut timeline_stream) = timeline.subscribe().await;
813+
// The 2 unrelated events before + 2 after + the item + the date divider
814+
assert_eq!(items.len(), 6);
815+
assert!(items[0].is_date_divider());
816+
assert_eq!(
817+
items[1].as_event().unwrap().content().as_message().unwrap().body(),
818+
"Unrelated before 2"
819+
);
820+
assert_eq!(
821+
items[2].as_event().unwrap().content().as_message().unwrap().body(),
822+
"Unrelated before 1"
823+
);
824+
assert_eq!(items[3].as_event().unwrap().content().as_message().unwrap().body(), "Hey");
825+
assert_eq!(
826+
items[4].as_event().unwrap().content().as_message().unwrap().body(),
827+
"Unrelated after 1"
828+
);
829+
assert_eq!(
830+
items[5].as_event().unwrap().content().as_message().unwrap().body(),
831+
"Unrelated after 2"
832+
);
833+
834+
// We paginate back once
835+
server
836+
.mock_room_messages()
837+
.match_from("prev_token_1")
838+
.ok(RoomMessagesResponseTemplate {
839+
chunk: vec![
840+
f.text_msg("Prev")
841+
.sender(user_id)
842+
.in_thread(root_thread_id, prev_thread_event_id)
843+
.into_raw_timeline(),
844+
f.text_msg("Prev no thread").sender(user_id).into_raw_timeline(),
845+
],
846+
start: "prev_token_1".to_owned(),
847+
end: Some("prev_token_2".to_owned()),
848+
state: Vec::new(),
849+
delay: None,
850+
})
851+
.mock_once()
852+
.mount()
853+
.await;
854+
855+
let start_of_timeline =
856+
timeline.paginate_backwards(10).await.expect("Could not paginate backwards");
857+
assert!(!start_of_timeline);
858+
859+
// Only the non-threaded event is inserted at the start.
860+
assert_let_timeout!(Some(timeline_updates) = timeline_stream.next());
861+
assert_eq!(timeline_updates.len(), 1);
862+
// The new item loaded is inserted at the start, just after the date divider.
863+
assert_let!(VectorDiff::Insert { index: 1, value: item } = &timeline_updates[0]);
864+
assert_eq!(item.as_event().unwrap().content().as_message().unwrap().body(), "Prev no thread");
865+
866+
// Then we paginate forwards
867+
server
868+
.mock_room_messages()
869+
.match_from("next_token_1")
870+
.ok(RoomMessagesResponseTemplate {
871+
chunk: vec![
872+
f.text_msg("Next no thread").sender(user_id).into_raw_timeline(),
873+
f.text_msg("Next1")
874+
.sender(user_id)
875+
.in_thread(root_thread_id, prev_thread_event_id)
876+
.into_raw_timeline(),
877+
],
878+
start: "next_token_1".to_owned(),
879+
end: Some("next_token_2".to_owned()),
880+
state: Vec::new(),
881+
delay: None,
882+
})
883+
.mock_once()
884+
.mount()
885+
.await;
886+
887+
let end_of_timeline =
888+
timeline.paginate_forwards(10).await.expect("Could not paginate forwards");
889+
assert!(!end_of_timeline);
890+
891+
// Only the non-threaded event is pushed to the end.
892+
assert_let_timeout!(Some(timeline_updates) = timeline_stream.next());
893+
assert_eq!(timeline_updates.len(), 1);
894+
assert_let!(VectorDiff::PushBack { value: item } = &timeline_updates[0]);
895+
assert_eq!(item.as_event().unwrap().content().as_message().unwrap().body(), "Next no thread");
896+
}

crates/matrix-sdk/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ All notable changes to this project will be documented in this file.
140140

141141
### Bugfix
142142

143+
- When threads are enabled, a focused event timeline is used and the focused event is not part of a thread,
144+
hide other threaded events by default like it happens on the live focus timeline.
145+
([#6519](https://github.com/matrix-org/matrix-rust-sdk/pull/6519))
143146
- Add a recursion limit attribute that raises it from the default value of 128 to 256.
144147
([#6489](https://github.com/matrix-org/matrix-rust-sdk/pull/6489))
145148
- Reject invalid edits as candidates for the latest event.

crates/matrix-sdk/src/event_cache/caches/event_focused/mod.rs

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ pub enum EventFocusThreadMode {
8181
pub(crate) enum EventFocusedPaginationMode {
8282
/// Standard room pagination (for all events as for an unthreaded/main room
8383
/// linked chunk).
84-
Room,
84+
Room { hide_thread_events: bool },
8585

8686
/// Threaded pagination (the focused event is part of a thread).
8787
Thread {
@@ -210,12 +210,27 @@ impl EventFocusedCacheInner {
210210
self.add_initial_events_with_gaps(thread_events, backward_token, forward_token);
211211
} else {
212212
trace!("focused event is not part of a thread, setting up room pagination");
213-
self.pagination_mode = EventFocusedPaginationMode::Room;
214213

215214
let backward_token = tokens.previous.into_token();
216215
let forward_token = tokens.next.into_token();
217216

218-
self.add_initial_events_with_gaps(result.events.clone(), backward_token, forward_token);
217+
let hide_thread_events =
218+
matches!(thread_mode, EventFocusThreadMode::Automatic) && thread_root.is_none();
219+
220+
self.pagination_mode = EventFocusedPaginationMode::Room { hide_thread_events };
221+
222+
let events = if hide_thread_events {
223+
result
224+
.events
225+
.iter()
226+
.filter(|event| extract_thread_root(event.raw()).is_none())
227+
.cloned()
228+
.collect()
229+
} else {
230+
result.events.clone()
231+
};
232+
233+
self.add_initial_events_with_gaps(events, backward_token, forward_token);
219234
}
220235

221236
self.propagate_changes();
@@ -304,7 +319,7 @@ impl EventFocusedCacheInner {
304319

305320
// Fetch events based on pagination mode.
306321
let (mut events, new_token) = match &self.pagination_mode {
307-
EventFocusedPaginationMode::Room => {
322+
EventFocusedPaginationMode::Room { .. } => {
308323
Self::fetch_room_backwards(&room, num_events, &token).await?
309324
}
310325
EventFocusedPaginationMode::Thread { thread_root } => {
@@ -319,6 +334,17 @@ impl EventFocusedCacheInner {
319334
let hit_end = new_token.is_none();
320335
let new_gap = new_token.map(|t| Gap { token: t });
321336

337+
let hide_thread_events = match &self.pagination_mode {
338+
EventFocusedPaginationMode::Room { hide_thread_events } => *hide_thread_events,
339+
EventFocusedPaginationMode::Thread { .. } => false,
340+
};
341+
342+
let events = if hide_thread_events {
343+
events.into_iter().filter(|event| extract_thread_root(event.raw()).is_none()).collect()
344+
} else {
345+
events
346+
};
347+
322348
// Replace the gap and insert the new events.
323349
self.chunk.push_backwards_pagination_events(Some(gap_id), new_gap, &events);
324350

@@ -405,7 +431,7 @@ impl EventFocusedCacheInner {
405431

406432
// Fetch events based on pagination mode.
407433
let (events, new_token) = match &self.pagination_mode {
408-
EventFocusedPaginationMode::Room => {
434+
EventFocusedPaginationMode::Room { .. } => {
409435
Self::fetch_room_forwards(&room, num_events, &token).await?
410436
}
411437
EventFocusedPaginationMode::Thread { thread_root } => {
@@ -416,6 +442,17 @@ impl EventFocusedCacheInner {
416442
let hit_end = new_token.is_none();
417443
let new_gap = new_token.map(|t| Gap { token: t });
418444

445+
let hide_thread_events = match &self.pagination_mode {
446+
EventFocusedPaginationMode::Room { hide_thread_events } => *hide_thread_events,
447+
EventFocusedPaginationMode::Thread { .. } => false,
448+
};
449+
450+
let events = if hide_thread_events {
451+
events.into_iter().filter(|event| extract_thread_root(event.raw()).is_none()).collect()
452+
} else {
453+
events
454+
};
455+
419456
// Replace the gap and insert new events.
420457
self.chunk.push_forwards_pagination_events(Some(gap_id), new_gap, &events);
421458

@@ -497,7 +534,7 @@ impl EventFocusedCache {
497534
inner: Arc::new(RwLock::new(EventFocusedCacheInner {
498535
room,
499536
focused_event_id,
500-
pagination_mode: EventFocusedPaginationMode::Room,
537+
pagination_mode: EventFocusedPaginationMode::Room { hide_thread_events: false },
501538
chunk: EventLinkedChunk::new(),
502539
sender: Sender::new(32),
503540
linked_chunk_update_sender,

0 commit comments

Comments
 (0)