From 848de833cc97fb76f5311bf9153b8e2bbcf610db Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Thu, 4 May 2023 16:00:54 +0200 Subject: [PATCH] sdk: Fill in replied-to event from timeline items when available --- .../src/room/timeline/event_handler.rs | 8 ++- .../src/room/timeline/event_item/content.rs | 59 ++++++++++++++----- .../src/room/timeline/tests/basic.rs | 3 +- .../tests/integration/room/timeline/mod.rs | 2 +- 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/crates/matrix-sdk/src/room/timeline/event_handler.rs b/crates/matrix-sdk/src/room/timeline/event_handler.rs index 71a6d572b..f6730963b 100644 --- a/crates/matrix-sdk/src/room/timeline/event_handler.rs +++ b/crates/matrix-sdk/src/room/timeline/event_handler.rs @@ -15,7 +15,7 @@ use std::{collections::HashMap, sync::Arc}; use chrono::{Datelike, Local, TimeZone}; -use eyeball_im::ObservableVector; +use eyeball_im::{ObservableVector, Vector}; use indexmap::{map::Entry, IndexMap, IndexSet}; use matrix_sdk_base::deserialized_responses::EncryptionInfo; use ruma::{ @@ -289,7 +289,7 @@ impl<'a> TimelineEventHandler<'a> { self.handle_room_message_edit(re); } AnyMessageLikeEventContent::RoomMessage(c) => { - self.add(NewEventTimelineItem::message(c, relations)); + self.add(NewEventTimelineItem::message(c, relations, self.items)); } AnyMessageLikeEventContent::RoomEncrypted(c) => self.handle_room_encrypted(c), AnyMessageLikeEventContent::Sticker(c) => { @@ -965,8 +965,10 @@ impl NewEventTimelineItem { fn message( c: RoomMessageEventContent, relations: BundledMessageLikeRelations, + timeline_items: &Vector>, ) -> Self { - let content = TimelineItemContent::Message(Message::from_event(c, relations)); + let content = + TimelineItemContent::Message(Message::from_event(c, relations, timeline_items)); Self::from_content(content) } diff --git a/crates/matrix-sdk/src/room/timeline/event_item/content.rs b/crates/matrix-sdk/src/room/timeline/event_item/content.rs index 43fc209f3..18aef7ba7 100644 --- a/crates/matrix-sdk/src/room/timeline/event_item/content.rs +++ b/crates/matrix-sdk/src/room/timeline/event_item/content.rs @@ -1,5 +1,6 @@ use std::{fmt, ops::Deref, sync::Arc}; +use imbl::{vector, Vector}; use indexmap::IndexMap; use matrix_sdk_base::deserialized_responses::TimelineEvent; use ruma::{ @@ -39,11 +40,13 @@ use ruma::{ }, OwnedDeviceId, OwnedEventId, OwnedMxcUri, OwnedTransactionId, OwnedUserId, UserId, }; -use tracing::error; +use tracing::{debug, error}; -use super::{Profile, TimelineDetails}; +use super::{EventTimelineItem, Profile, TimelineDetails}; use crate::{ - room::timeline::{inner::RoomDataProvider, Error as TimelineError, DEFAULT_SANITIZER_MODE}, + room::timeline::{ + inner::RoomDataProvider, Error as TimelineError, TimelineItem, DEFAULT_SANITIZER_MODE, + }, Result, }; @@ -130,6 +133,7 @@ impl Message { pub(in crate::room::timeline) fn from_event( c: RoomMessageEventContent, relations: BundledMessageLikeRelations, + timeline_items: &Vector>, ) -> Self { let edited = relations.has_replacement(); let edit = relations.replace.and_then(|r| match *r { @@ -150,7 +154,24 @@ impl Message { } }); - let in_reply_to = c.relates_to.and_then(InReplyToDetails::from_relation); + let in_reply_to = c.relates_to.and_then(|relation| match relation { + message::Relation::Reply { in_reply_to } => { + let event_id = in_reply_to.event_id; + let event = timeline_items + .iter() + .filter_map(|it| it.as_event()) + .find(|it| it.event_id() == Some(&*event_id)) + .and_then(RepliedToEvent::from_timeline_item) + .map(Box::new); + + Some(InReplyToDetails { + event_id, + event: TimelineDetails::from_initial_value(event), + }) + } + _ => None, + }); + let msgtype = match edit { Some(mut e) => { // Edit's content is never supposed to contain the reply fallback. @@ -231,17 +252,6 @@ pub struct InReplyToDetails { pub event: TimelineDetails>, } -impl InReplyToDetails { - pub(in crate::room::timeline) fn from_relation(relation: Relation) -> Option { - match relation { - message::Relation::Reply { in_reply_to } => { - Some(Self { event_id: in_reply_to.event_id, event: TimelineDetails::Unavailable }) - } - _ => None, - } - } -} - /// An event that is replied to. #[derive(Clone, Debug)] pub struct RepliedToEvent { @@ -266,6 +276,23 @@ impl RepliedToEvent { &self.sender_profile } + fn from_timeline_item(timeline_item: &EventTimelineItem) -> Option { + let message = match &timeline_item.content { + TimelineItemContent::Message(msg) => msg.to_owned(), + // FIXME: Handle redacted messages + _ => { + debug!("Replied-to event is not a message, discarding"); + return None; + } + }; + + Some(Self { + message, + sender: timeline_item.sender.clone(), + sender_profile: timeline_item.sender_profile.clone(), + }) + } + pub(in crate::room::timeline) async fn try_from_timeline_event( timeline_event: TimelineEvent, room_data_provider: &P, @@ -281,7 +308,7 @@ impl RepliedToEvent { return Err(TimelineError::UnsupportedEvent.into()); }; - let message = Message::from_event(c, event.relations()); + let message = Message::from_event(c, event.relations(), &vector![]); let sender = event.sender().to_owned(); let sender_profile = TimelineDetails::from_initial_value(room_data_provider.profile(&sender).await); diff --git a/crates/matrix-sdk/src/room/timeline/tests/basic.rs b/crates/matrix-sdk/src/room/timeline/tests/basic.rs index 55e97c650..a128f6caa 100644 --- a/crates/matrix-sdk/src/room/timeline/tests/basic.rs +++ b/crates/matrix-sdk/src/room/timeline/tests/basic.rs @@ -330,5 +330,6 @@ async fn reply() { let in_reply_to = message.in_reply_to().unwrap(); assert_eq!(in_reply_to.event_id, first_event_id); - assert_matches!(in_reply_to.event, TimelineDetails::Unavailable); + let replied_to_event = assert_matches!(&in_reply_to.event, TimelineDetails::Ready(msg) => msg); + assert_eq!(replied_to_event.sender(), *ALICE); } diff --git a/crates/matrix-sdk/tests/integration/room/timeline/mod.rs b/crates/matrix-sdk/tests/integration/room/timeline/mod.rs index 5bc8b515e..d258dfd50 100644 --- a/crates/matrix-sdk/tests/integration/room/timeline/mod.rs +++ b/crates/matrix-sdk/tests/integration/room/timeline/mod.rs @@ -662,7 +662,7 @@ async fn in_reply_to_details() { assert_matches!(second_event.content(), TimelineItemContent::Message(message) => message); let in_reply_to = message.in_reply_to().unwrap(); assert_eq!(in_reply_to.event_id, event_id!("$event1")); - assert_matches!(in_reply_to.event, TimelineDetails::Unavailable); + assert_matches!(in_reply_to.event, TimelineDetails::Ready(_)); // Fetch details locally first. timeline.fetch_event_details(second_event.event_id().unwrap()).await.unwrap();