Skip to content
Merged
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
63 changes: 8 additions & 55 deletions crates/matrix-sdk-ui/src/timeline/pinned_events_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,12 @@
use std::{fmt::Formatter, sync::Arc};

use futures_util::{StreamExt, stream};
use matrix_sdk::{
BoxFuture, Room, SendOutsideWasm, SyncOutsideWasm,
config::RequestConfig,
room::{IncludeRelations, RelationsOptions},
};
use matrix_sdk::{BoxFuture, Room, SendOutsideWasm, SyncOutsideWasm, config::RequestConfig};
use matrix_sdk_base::deserialized_responses::TimelineEvent;
use ruma::{
EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, events::relation::RelationType, uint,
};
use ruma::{EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, events::relation::RelationType};
use thiserror::Error;
use tokio::sync::Mutex;
use tracing::{debug, warn};
use tracing::warn;

/// Utility to load the pinned events in a room.
pub struct PinnedEventsLoader {
Expand Down Expand Up @@ -176,52 +170,11 @@ impl PinnedEventsRoom for Room {
request_config: Option<RequestConfig>,
related_event_filters: Option<Vec<RelationType>>,
) -> BoxFuture<'a, Result<(TimelineEvent, Vec<TimelineEvent>), matrix_sdk::Error>> {
Box::pin(async move {
// First try to load both the event and its relations from the cache.
if let Ok((cache, _handles)) = self.event_cache().await
&& let Some(ret) =
cache.find_event_with_relations(event_id, related_event_filters.clone()).await?
{
debug!("Loaded pinned event {event_id} and related events from cache");
return Ok(ret);
}

// The event was not in the cache. Try loading it from the homeserver.
debug!("Loading pinned event {event_id} from HS");
let event = self.event(event_id, request_config).await?;

// Maybe at least some relations are in the cache?
if let Ok((cache, _handles)) = self.event_cache().await {
let related_events =
cache.find_event_relations(event_id, related_event_filters).await?;
if !related_events.is_empty() {
debug!("Loaded relations for pinned event {event_id} from cache");
return Ok((event, related_events));
}
}

// No relations were found in the cache. Try loading them from the homeserver.
debug!("Loading relations of pinned event {event_id} from HS");
let mut related_events = Vec::new();
let mut opts = RelationsOptions {
include_relations: IncludeRelations::AllRelations,
recurse: true,
limit: Some(uint!(256)),
..Default::default()
};

loop {
let relations = self.relations(event_id.to_owned(), opts.clone()).await?;
related_events.extend(relations.chunk);
if let Some(next_from) = relations.next_batch_token {
opts.from = Some(next_from);
} else {
break;
}
}

Ok((event, related_events))
})
Box::pin(self.load_or_fetch_event_with_relations(
event_id,
related_event_filters,
request_config,
))
}

fn pinned_event_ids(&self) -> Option<Vec<OwnedEventId>> {
Expand Down
99 changes: 98 additions & 1 deletion crates/matrix-sdk/src/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ use ruma::{
direct::DirectEventContent,
marked_unread::MarkedUnreadEventContent,
receipt::{Receipt, ReceiptThread, ReceiptType},
relation::RelationType,
room::{
ImageInfo, MediaSource, ThumbnailInfo,
avatar::{self, RoomAvatarEventContent},
Expand Down Expand Up @@ -137,6 +138,7 @@ use ruma::{
push::{Action, AnyPushRuleRef, PushConditionRoomCtx, Ruleset},
serde::Raw,
time::Instant,
uint,
};
#[cfg(feature = "experimental-encrypted-state-events")]
use ruma::{
Expand Down Expand Up @@ -828,6 +830,101 @@ impl Room {
self.event(event_id, request_config).await
}

/// Try to load the event and its relations from the
/// [`EventCache`][crate::event_cache], if it's enabled, or fetch it
/// from the homeserver.
///
/// You can control which types of related events are retrieved using
/// `filter`. A `None` value will retrieve any type of related event.
///
/// If the event is found in the event cache, but we can't find any
/// relations for it there, then we will still attempt to fetch the
/// relations from the homeserver.
///
/// When running any request against the homeserver, it uses the given
/// [`RequestConfig`] if provided, or the client's default one
/// otherwise.
///
/// Returns a tuple formed of the event and a vector of its relations (that
/// can be empty).
pub async fn load_or_fetch_event_with_relations(
&self,
event_id: &EventId,
filter: Option<Vec<RelationType>>,
request_config: Option<RequestConfig>,
) -> Result<(TimelineEvent, Vec<TimelineEvent>)> {
let fetch_relations = async || {
let mut opts = RelationsOptions {
include_relations: IncludeRelations::AllRelations,
recurse: true,
limit: Some(uint!(256)),
..Default::default()
};

let mut events = Vec::new();
loop {
match self.relations(event_id.to_owned(), opts.clone()).await {
Ok(relations) => {
events.extend(relations.chunk);
if let Some(next_from) = relations.next_batch_token {
opts.from = Some(next_from);
} else {
break events;
}
}

Err(err) => {
warn!(%event_id, "error when loading relations of pinned event from server: {err}");
break events;
}
}
}
};

// First, try to load the event *and* its relations from the event cache, all at
// once.
let event_cache = match self.event_cache().await {
Ok((event_cache, drop_handles)) => {
if let Some((event, mut relations)) =
event_cache.find_event_with_relations(event_id, filter.clone()).await?
{
if relations.is_empty() {
// The event cache doesn't have any relations for this event, try to fetch
// them from the server instead.
relations = fetch_relations().await;
}

return Ok((event, relations));
}

// Otherwise, get the event from the server.
Some((event_cache, drop_handles))
}

Err(err) => {
debug!("error when getting the event cache: {err}");
// Fallthrough: try with a request.
None
}
};

// Fetch the event from the server. A failure here is fatal, as we must return
// the target event.
let event = self.event(event_id, request_config).await?;

// Try to get the relations from the event cache (if we have one).
if let Some((event_cache, _drop_handles)) = event_cache
&& let Some(relations) = event_cache.find_event_relations(event_id, filter).await.ok()
&& !relations.is_empty()
{
return Ok((event, relations));
}

// We couldn't find the relations in the event cache; fetch them from the
// server.
Ok((event, fetch_relations().await))
}

/// Fetch the event with the given `EventId` in this room, using the
/// `/context` endpoint to get more information.
pub async fn event_with_context(
Expand Down Expand Up @@ -4115,7 +4212,7 @@ impl Room {
}

/// Retrieve a list of relations for the given event, according to the given
/// options.
/// options, using the network.
///
/// Since this client-server API is paginated, the return type may include a
/// token used to resuming back-pagination into the list of results, in
Expand Down
Loading