Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions crates/matrix-sdk-ui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ All notable changes to this project will be documented in this file.

### Features

- Utilize the cache and include common relations when focusing a timeline on an event without
requestion context.
([#5858](https://github.com/matrix-org/matrix-rust-sdk/pull/5858))
- Add push actions to `NotificationItem`.
([#5835](https://github.com/matrix-org/matrix-rust-sdk/pull/5835))
- Add support for top level space ordering through [MSC3230](https://github.com/matrix-org/matrix-spec-proposals/pull/3230)
Expand Down
48 changes: 36 additions & 12 deletions crates/matrix-sdk-ui/src/timeline/controller/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use imbl::Vector;
#[cfg(test)]
use matrix_sdk::Result;
use matrix_sdk::{
config::RequestConfig,
deserialized_responses::TimelineEvent,
event_cache::{RoomEventCache, RoomPaginationStatus},
paginators::{PaginationResult, PaginationToken, Paginator},
Expand All @@ -41,7 +42,7 @@ use ruma::{
poll::unstable_start::UnstablePollStartEventContent,
reaction::ReactionEventContent,
receipt::{Receipt, ReceiptThread, ReceiptType},
relation::Annotation,
relation::{Annotation, RelationType},
room::message::{MessageType, Relation},
},
room_version_rules::RoomVersionRules,
Expand Down Expand Up @@ -474,16 +475,40 @@ impl<P: RoomDataProvider> TimelineController<P> {

let event_paginator = Paginator::new(self.room_data_provider.clone());

// Start a /context request so we can know if the event is in a thread or not,
// and know which kind of pagination we'll be using then.
let start_from_result = event_paginator
.start_from(event_id, (*num_context_events).into())
.await
.map_err(PaginationError::Paginator)?;
let events = if *num_context_events == 0 {
// If no context is requested, try to load the event from the cache first and
// include common relations such as reactions and edits.
let request_config = Some(RequestConfig::default().retry_limit(3));
let relations_filter =
Some(vec![RelationType::Annotation, RelationType::Replacement]);

// Load the event from the cache or, failing that, the server.
match self
.room_data_provider
.load_event_with_relations(event_id, request_config, relations_filter)
.await
{
Ok((event, related_events)) => {
let mut events = vec![event];
events.extend(related_events);
events
}
Err(err) => {
warn!("error when loading focussed event: {err}");
vec![]
}
}
} else {
// Start a /context request to load the focussed event and surrounding events.
let start_from_result = event_paginator
.start_from(event_id, (*num_context_events).into())
.await
.map_err(PaginationError::Paginator)?;
start_from_result.events
};
Comment on lines +478 to +508
Copy link
Contributor Author

@Johennes Johennes Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A potential issue I'm now seeing is that the if branch will leave the paginator in initial state meaning you cannot use it to extend the timeline later. This isn't really an issue for the intended event details use case but might still be problematic. Maybe this is further reason to extend TimelineFocus::PinnedEvents rather than TimelineFocus::Event? We could just add a different loader type that loads a single event only based on a specified ID.


// Find the target event, and see if it's part of a thread.
let thread_root_event_id = start_from_result
.events
let thread_root_event_id = events
.iter()
.find(
|event| {
Expand All @@ -499,7 +524,7 @@ impl<P: RoomDataProvider> TimelineController<P> {
// Look if the thread root event is part of the /context response. This
// allows us to spare some backwards pagination with
// /relations.
let includes_root_event = start_from_result.events.iter().any(|event| {
let includes_root_event = events.iter().any(|event| {
if let Some(id) = event.event_id() { id == root_id } else { false }
});

Expand All @@ -522,8 +547,7 @@ impl<P: RoomDataProvider> TimelineController<P> {
},
});

let has_events = !start_from_result.events.is_empty();
let events = start_from_result.events;
let has_events = !events.is_empty();

match paginator.get().expect("Paginator was not instantiated") {
AnyPaginator::Unthreaded { .. } => {
Expand Down
4 changes: 2 additions & 2 deletions crates/matrix-sdk-ui/tests/integration/timeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ async fn test_timeline_is_threaded() {
let timeline = TimelineBuilder::new(&room)
.with_focus(TimelineFocus::Event {
target: owned_event_id!("$target"),
num_context_events: 0,
num_context_events: 2,
hide_threaded_events: true,
})
.build()
Expand Down Expand Up @@ -147,7 +147,7 @@ async fn test_timeline_is_threaded() {
let timeline = TimelineBuilder::new(&room)
.with_focus(TimelineFocus::Event {
target: owned_event_id!("$target"),
num_context_events: 0,
num_context_events: 2,
hide_threaded_events: true,
})
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1848,7 +1848,7 @@ async fn test_permalink_doesnt_listen_to_thread_sync() {
let timeline = TimelineBuilder::new(&room)
.with_focus(TimelineFocus::Event {
target: owned_event_id!("$target"),
num_context_events: 0,
num_context_events: 2,
hide_threaded_events: true,
})
.build()
Expand Down
Loading