use std::fmt;
use futures_util::future::join_all;
use matrix_sdk_common::{debug::DebugStructExt as _, deserialized_responses::TimelineEvent};
use ruma::{
OwnedEventId, RoomId, UInt,
api::{
Direction,
client::{
filter::RoomEventFilter,
message::get_message_events,
relations,
threads::get_threads::{self, v1::IncludeThreads},
},
},
assign,
events::{AnyStateEvent, TimelineEventType, relation::RelationType},
serde::Raw,
uint,
};
use super::Room;
use crate::Result;
#[non_exhaustive]
pub struct MessagesOptions {
pub from: Option<String>,
pub to: Option<String>,
pub dir: Direction,
pub limit: UInt,
pub filter: RoomEventFilter,
}
impl MessagesOptions {
pub fn new(dir: Direction) -> Self {
Self { from: None, to: None, dir, limit: uint!(10), filter: RoomEventFilter::default() }
}
pub fn backward() -> Self {
Self::new(Direction::Backward)
}
pub fn forward() -> Self {
Self::new(Direction::Forward)
}
pub fn from<'a>(self, from: impl Into<Option<&'a str>>) -> Self {
Self { from: from.into().map(ToOwned::to_owned), ..self }
}
pub(super) fn into_request(self, room_id: &RoomId) -> get_message_events::v3::Request {
assign!(get_message_events::v3::Request::new(room_id.to_owned(), self.dir), {
from: self.from,
to: self.to,
limit: self.limit,
filter: self.filter,
})
}
}
#[cfg(not(tarpaulin_include))]
impl fmt::Debug for MessagesOptions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { from, to, dir, limit, filter } = self;
let mut s = f.debug_struct("MessagesOptions");
s.maybe_field("from", from).maybe_field("to", to).field("dir", dir).field("limit", limit);
if !filter.is_empty() {
s.field("filter", filter);
}
s.finish()
}
}
#[derive(Debug, Default)]
pub struct Messages {
pub start: String,
pub end: Option<String>,
pub chunk: Vec<TimelineEvent>,
pub state: Vec<Raw<AnyStateEvent>>,
}
#[derive(Debug, Default)]
pub struct EventWithContextResponse {
pub event: Option<TimelineEvent>,
pub events_before: Vec<TimelineEvent>,
pub events_after: Vec<TimelineEvent>,
pub prev_batch_token: Option<String>,
pub next_batch_token: Option<String>,
pub state: Vec<Raw<AnyStateEvent>>,
}
#[derive(Debug, Default)]
pub struct ListThreadsOptions {
pub include_threads: IncludeThreads,
pub from: Option<String>,
pub limit: Option<UInt>,
}
impl ListThreadsOptions {
pub(super) fn into_request(self, room_id: &RoomId) -> get_threads::v1::Request {
assign!(get_threads::v1::Request::new(room_id.to_owned()), {
from: self.from,
include: self.include_threads,
limit: self.limit,
})
}
}
#[derive(Debug)]
pub struct ThreadRoots {
pub chunk: Vec<TimelineEvent>,
pub prev_batch_token: Option<String>,
}
#[derive(Clone, Debug, Default)]
pub enum IncludeRelations {
#[default]
AllRelations,
RelationsOfType(RelationType),
RelationsOfTypeAndEventType(RelationType, TimelineEventType),
}
#[derive(Clone, Debug, Default)]
pub struct RelationsOptions {
pub from: Option<String>,
pub dir: Direction,
pub limit: Option<UInt>,
pub include_relations: IncludeRelations,
pub recurse: bool,
}
impl RelationsOptions {
pub(super) async fn send(self, room: &Room, event: OwnedEventId) -> Result<Relations> {
macro_rules! fill_params {
($request:expr) => {
assign! { $request, {
from: self.from,
dir: self.dir,
limit: self.limit,
recurse: self.recurse,
}}
};
}
let (chunk, prev_batch, next_batch, recursion_depth) = match self.include_relations {
IncludeRelations::AllRelations => {
let request = fill_params!(relations::get_relating_events::v1::Request::new(
room.room_id().to_owned(),
event,
));
let response = room.client.send(request).await?;
(response.chunk, response.prev_batch, response.next_batch, response.recursion_depth)
}
IncludeRelations::RelationsOfType(relation_type) => {
let request =
fill_params!(relations::get_relating_events_with_rel_type::v1::Request::new(
room.room_id().to_owned(),
event,
relation_type,
));
let response = room.client.send(request).await?;
(response.chunk, response.prev_batch, response.next_batch, response.recursion_depth)
}
IncludeRelations::RelationsOfTypeAndEventType(relation_type, timeline_event_type) => {
let request = fill_params!(
relations::get_relating_events_with_rel_type_and_event_type::v1::Request::new(
room.room_id().to_owned(),
event,
relation_type,
timeline_event_type,
)
);
let response = room.client.send(request).await?;
(response.chunk, response.prev_batch, response.next_batch, response.recursion_depth)
}
};
let push_ctx = room.push_context().await?;
let chunk = join_all(chunk.into_iter().map(|ev| {
room.try_decrypt_event(ev.cast(), push_ctx.as_ref())
}))
.await;
Ok(Relations {
chunk,
prev_batch_token: prev_batch,
next_batch_token: next_batch,
recursion_depth,
})
}
}
#[derive(Debug)]
pub struct Relations {
pub chunk: Vec<TimelineEvent>,
pub prev_batch_token: Option<String>,
pub next_batch_token: Option<String>,
pub recursion_depth: Option<UInt>,
}