room: extend event_with_context so it returns a fully decrypted /context response

This commit is contained in:
Benjamin Bouvier
2024-04-05 16:36:36 +02:00
parent b903ee4b42
commit 406fd011ff
3 changed files with 87 additions and 22 deletions

View File

@@ -480,8 +480,10 @@ impl NotificationClient {
return Err(Error::UnknownRoom);
};
let (mut timeline_event, state_events) =
room.event_with_context(event_id, true).await?.ok_or(Error::ContextMissingEvent)?;
let response = room.event_with_context(event_id, true, uint!(0)).await?;
let mut timeline_event = response.event.ok_or(Error::ContextMissingEvent)?;
let state_events = response.state;
if let Some(decrypted_event) =
self.retry_decryption(&room, timeline_event.event.cast_ref()).await?

View File

@@ -121,7 +121,7 @@ impl fmt::Debug for MessagesOptions {
}
}
/// The result of a `Room::messages` call.
/// The result of a [`super::Room::messages`] call.
///
/// In short, this is a possibly decrypted version of the response of a
/// `room/messages` api call.
@@ -139,3 +139,38 @@ pub struct Messages {
/// A list of state events relevant to showing the `chunk`.
pub state: Vec<Raw<AnyStateEvent>>,
}
/// The result of a [`super::Room::event_with_context`] query.
///
/// This is a wrapper around
/// [`ruma::api::client::context::get_context::v3::Response`], with events
/// decrypted if needs be.
#[derive(Debug)]
pub struct EventWithContextResponse {
/// The event targeted by the /context query.
pub event: Option<TimelineEvent>,
/// Events before the target event, if a non-zero context size was
/// requested.
///
/// Like the corresponding Ruma response, these are in reverse chronological
/// order.
pub events_before: Vec<TimelineEvent>,
/// Events after the target event, if a non-zero context size was requested.
///
/// Like the corresponding Ruma response, these are in chronological order.
pub events_after: Vec<TimelineEvent>,
/// Token to paginate backwards, aka "start" token.
pub prev_batch_token: Option<String>,
/// Token to paginate forwards, aka "end" token.
pub next_batch_token: Option<String>,
/// State events related to the request.
///
/// If lazy-loading of members was requested, this may contain room
/// membership events.
pub state: Vec<Raw<AnyStateEvent>>,
}

View File

@@ -10,7 +10,10 @@ use std::{
use eyeball::SharedObservable;
use futures_core::Stream;
use futures_util::stream::FuturesUnordered;
use futures_util::{
future::{try_join, try_join_all},
stream::FuturesUnordered,
};
use matrix_sdk_base::{
deserialized_responses::{
RawAnySyncOrStrippedState, RawSyncOrStrippedState, SyncOrStrippedState, TimelineEvent,
@@ -65,14 +68,14 @@ use ruma::{
space::{child::SpaceChildEventContent, parent::SpaceParentEventContent},
tag::{TagInfo, TagName},
typing::SyncTypingEvent,
AnyRoomAccountDataEvent, AnyStateEvent, EmptyStateKey, MessageLikeEventContent,
AnyRoomAccountDataEvent, AnyTimelineEvent, EmptyStateKey, MessageLikeEventContent,
MessageLikeEventType, RedactContent, RedactedStateEventContent, RoomAccountDataEvent,
RoomAccountDataEventContent, RoomAccountDataEventType, StateEventContent, StateEventType,
StaticEventContent, StaticStateEventContent, SyncStateEvent,
},
push::{Action, PushConditionRoomCtx},
serde::Raw,
uint, EventId, Int, MatrixToUri, MatrixUri, MxcUri, OwnedEventId, OwnedRoomId, OwnedServerName,
EventId, Int, MatrixToUri, MatrixUri, MxcUri, OwnedEventId, OwnedRoomId, OwnedServerName,
OwnedTransactionId, OwnedUserId, TransactionId, UInt, UserId,
};
use serde::de::DeserializeOwned;
@@ -80,7 +83,10 @@ use thiserror::Error;
use tokio::sync::broadcast;
use tracing::{debug, info, instrument, warn};
use self::futures::{SendAttachment, SendMessageLikeEvent, SendRawMessageLikeEvent};
use self::{
futures::{SendAttachment, SendMessageLikeEvent, SendRawMessageLikeEvent},
messages::EventWithContextResponse,
};
pub use self::{
member::{RoomMember, RoomMemberRole},
messages::{Messages, MessagesOptions},
@@ -382,11 +388,12 @@ impl Room {
&self,
event_id: &EventId,
lazy_load_members: bool,
) -> Result<Option<(TimelineEvent, Vec<Raw<AnyStateEvent>>)>> {
context_size: UInt,
) -> Result<EventWithContextResponse> {
let mut request =
context::get_context::v3::Request::new(self.room_id().to_owned(), event_id.to_owned());
request.limit = uint!(0);
request.limit = context_size;
if lazy_load_members {
request.filter.lazy_load_options =
@@ -395,23 +402,44 @@ impl Room {
let response = self.client.send(request, None).await?;
let Some(event) = response.event else {
return Ok(None);
// Decrypts one event, if needs be.
let try_decrypt = |event: Raw<AnyTimelineEvent>| async move {
#[cfg(feature = "e2e-encryption")]
if let Ok(AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomEncrypted(
SyncMessageLikeEvent::Original(_),
))) = event.deserialize_as::<AnySyncTimelineEvent>()
{
if let Ok(event) = self.decrypt_event(event.cast_ref()).await {
return Result::<_, Error>::Ok(event);
}
}
let push_actions = self.event_push_actions(&event).await?;
Result::<_, Error>::Ok(TimelineEvent {
event: event.clone(),
encryption_info: None,
push_actions,
})
};
#[cfg(feature = "e2e-encryption")]
if let Ok(AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomEncrypted(
SyncMessageLikeEvent::Original(_),
))) = event.deserialize_as::<AnySyncTimelineEvent>()
{
if let Ok(event) = self.decrypt_event(event.cast_ref()).await {
return Ok(Some((event, response.state)));
}
}
let target_event =
if let Some(event) = response.event { Some(try_decrypt(event).await?) } else { None };
let push_actions = self.event_push_actions(&event).await?;
let (events_before, events_after) = try_join(
try_join_all(response.events_before.into_iter().map(try_decrypt)),
try_join_all(response.events_after.into_iter().map(try_decrypt)),
)
.await?;
Ok(Some((TimelineEvent { event, encryption_info: None, push_actions }, response.state)))
Ok(EventWithContextResponse {
event: target_event,
events_before,
events_after,
state: response.state,
prev_batch_token: response.start,
next_batch_token: response.end,
})
}
pub(crate) async fn request_members(&self) -> Result<()> {