mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-02-07 22:22:57 -05:00
feat(timeline): utilize the cache and include common relations when focusing on an event without context
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
This commit is contained in:
committed by
Ivan Enderlin
parent
9b15400936
commit
dfd607f195
@@ -18,6 +18,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))
|
||||
- [**breaking**] `EventTimelineItem::get_shield` now returns a new type,
|
||||
`TimelineEventShieldState`, which extends the old `ShieldState` with a code
|
||||
for `SentInClear`, now that the latter has been removed from `ShieldState`.
|
||||
|
||||
@@ -22,6 +22,7 @@ use imbl::Vector;
|
||||
#[cfg(test)]
|
||||
use matrix_sdk::Result;
|
||||
use matrix_sdk::{
|
||||
config::RequestConfig,
|
||||
deserialized_responses::TimelineEvent,
|
||||
event_cache::{DecryptionRetryRequest, RoomEventCache, RoomPaginationStatus},
|
||||
paginators::{PaginationResult, PaginationToken, Paginator},
|
||||
@@ -40,7 +41,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,
|
||||
@@ -469,16 +470,46 @@ 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 load_events_with_context = || async {
|
||||
// Start a /context request to load the focused event and surrounding events.
|
||||
event_paginator
|
||||
.start_from(event_id, (*num_context_events).into())
|
||||
.await
|
||||
.map(|r| r.events)
|
||||
.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) => {
|
||||
error!("error when loading focussed event: {err}");
|
||||
// Fall back to load the focused event using /context.
|
||||
load_events_with_context().await?
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Start a /context request to load the focussed event and surrounding events.
|
||||
load_events_with_context().await?
|
||||
};
|
||||
|
||||
// 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| {
|
||||
@@ -494,7 +525,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 }
|
||||
});
|
||||
|
||||
@@ -517,8 +548,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 { .. } => {
|
||||
|
||||
@@ -20,7 +20,9 @@ use eyeball_im::VectorDiff;
|
||||
use futures_util::StreamExt;
|
||||
use matrix_sdk::{
|
||||
linked_chunk::{ChunkIdentifier, LinkedChunkId, Position, Update},
|
||||
test_utils::mocks::{MatrixMockServer, RoomContextResponseTemplate},
|
||||
test_utils::mocks::{
|
||||
MatrixMockServer, RoomContextResponseTemplate, RoomRelationsResponseTemplate,
|
||||
},
|
||||
};
|
||||
use matrix_sdk_test::{
|
||||
ALICE, BOB, JoinedRoomBuilder, RoomAccountDataTestEvent, StateTestEvent, async_test,
|
||||
@@ -37,13 +39,15 @@ use matrix_sdk_ui::{
|
||||
use ruma::{
|
||||
EventId, MilliSecondsSinceUnixEpoch, event_id,
|
||||
events::{
|
||||
MessageLikeEventType, TimelineEventType,
|
||||
AnyTimelineEvent, MessageLikeEventType, TimelineEventType,
|
||||
room::{
|
||||
encryption::RoomEncryptionEventContent,
|
||||
message::{RedactedRoomMessageEventContent, RoomMessageEventContent},
|
||||
},
|
||||
},
|
||||
owned_event_id, room_id, user_id,
|
||||
owned_event_id, room_id,
|
||||
serde::Raw,
|
||||
user_id,
|
||||
};
|
||||
use serde_json::json;
|
||||
use sliding_sync::assert_timeline_stream;
|
||||
@@ -97,6 +101,71 @@ async fn test_timeline_is_threaded() {
|
||||
assert!(timeline.is_threaded());
|
||||
}
|
||||
|
||||
{
|
||||
// An event-focused timeline, focused on a non-thread event, isn't threaded when
|
||||
// no context is requested.
|
||||
let f = EventFactory::new();
|
||||
let event_id = event_id!("$target");
|
||||
let event =
|
||||
f.text_msg("hello world").event_id(event_id).room(room_id).sender(&ALICE).into_event();
|
||||
server.mock_room_event().match_event_id().ok(event).mock_once().mount().await;
|
||||
server
|
||||
.mock_room_relations()
|
||||
.match_target_event(event_id.to_owned())
|
||||
.ok(RoomRelationsResponseTemplate::default()
|
||||
.events(Vec::<Raw<AnyTimelineEvent>>::new()))
|
||||
.mock_once()
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
let timeline = TimelineBuilder::new(&room)
|
||||
.with_focus(TimelineFocus::Event {
|
||||
target: owned_event_id!("$target"),
|
||||
num_context_events: 0,
|
||||
hide_threaded_events: true,
|
||||
})
|
||||
.build()
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(timeline.is_threaded().not());
|
||||
}
|
||||
|
||||
{
|
||||
// But an event-focused timeline, focused on an in-thread event, is threaded
|
||||
// when no context is requested \o/
|
||||
let f = EventFactory::new();
|
||||
let thread_root = event_id!("$thread_root");
|
||||
let event_id = event_id!("$thetarget");
|
||||
let event = f
|
||||
.text_msg("hey to you too")
|
||||
.event_id(event_id)
|
||||
.in_thread(thread_root, thread_root)
|
||||
.room(room_id)
|
||||
.sender(&ALICE)
|
||||
.into_event();
|
||||
|
||||
server.mock_room_event().match_event_id().ok(event).mock_once().mount().await;
|
||||
server
|
||||
.mock_room_relations()
|
||||
.match_target_event(event_id.to_owned())
|
||||
.ok(RoomRelationsResponseTemplate::default()
|
||||
.events(Vec::<Raw<AnyTimelineEvent>>::new()))
|
||||
.mock_once()
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
let timeline = TimelineBuilder::new(&room)
|
||||
.with_focus(TimelineFocus::Event {
|
||||
target: owned_event_id!("$thetarget"),
|
||||
num_context_events: 0,
|
||||
hide_threaded_events: true,
|
||||
})
|
||||
.build()
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(timeline.is_threaded());
|
||||
}
|
||||
|
||||
{
|
||||
// An event-focused timeline, focused on a non-thread event, isn't threaded.
|
||||
let f = EventFactory::new();
|
||||
@@ -116,7 +185,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()
|
||||
@@ -147,7 +216,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()
|
||||
|
||||
@@ -1859,7 +1859,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()
|
||||
|
||||
Reference in New Issue
Block a user