Skip to content

Commit d74cc9d

Browse files
committed
refactor(timeline): deduplicate parsing of events when making a RepliedToEvent
1 parent 73df64e commit d74cc9d

File tree

5 files changed

+89
-136
lines changed

5 files changed

+89
-136
lines changed

bindings/matrix-sdk-ffi/src/timeline/mod.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use matrix_sdk::{
3232
},
3333
};
3434
use matrix_sdk_ui::timeline::{
35-
self, AttachmentSource, EventItemOrigin, Profile, RepliedToEvent, TimelineDetails,
35+
self, AttachmentSource, EventItemOrigin, Profile, TimelineDetails,
3636
TimelineUniqueId as SdkTimelineUniqueId,
3737
};
3838
use mime::Mime;
@@ -687,9 +687,7 @@ impl Timeline {
687687
let event_id = EventId::parse(&event_id_str)?;
688688

689689
let replied_to = match self.inner.room().load_or_fetch_event(&event_id, None).await {
690-
Ok(event) => RepliedToEvent::try_from_timeline_event_for_room(event, self.inner.room())
691-
.await
692-
.map_err(ClientError::from),
690+
Ok(event) => self.inner.make_replied_to(event).await.map_err(ClientError::from),
693691
Err(e) => Err(ClientError::from(e)),
694692
};
695693

crates/matrix-sdk-ui/src/timeline/controller/mod.rs

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,23 @@ impl<P: RoomDataProvider, D: Decryptor> TimelineController<P, D> {
13141314
);
13151315
txn.commit();
13161316
}
1317+
1318+
pub(super) async fn make_replied_to(
1319+
&self,
1320+
event: TimelineEvent,
1321+
) -> Result<RepliedToEvent, Error> {
1322+
// Reborrow, to avoid that the automatic deref borrows the entire guard (and we
1323+
// can't borrow both items and meta).
1324+
let state = &mut *self.state.write().await;
1325+
1326+
RepliedToEvent::try_from_timeline_event(
1327+
event,
1328+
&self.room_data_provider,
1329+
&state.items,
1330+
&mut state.meta,
1331+
)
1332+
.await
1333+
}
13171334
}
13181335

13191336
impl TimelineController {
@@ -1325,8 +1342,8 @@ impl TimelineController {
13251342
/// replying to, if applicable.
13261343
#[instrument(skip(self))]
13271344
pub(super) async fn fetch_in_reply_to_details(&self, event_id: &EventId) -> Result<(), Error> {
1328-
let state = self.state.write().await;
1329-
let (index, item) = rfind_event_by_id(&state.items, event_id)
1345+
let state_guard = self.state.write().await;
1346+
let (index, item) = rfind_event_by_id(&state_guard.items, event_id)
13301347
.ok_or(Error::EventNotInTimeline(TimelineEventItemId::EventId(event_id.to_owned())))?;
13311348
let remote_item = item
13321349
.as_remote()
@@ -1353,7 +1370,8 @@ impl TimelineController {
13531370
let internal_id = item.internal_id.to_owned();
13541371
let item = item.clone();
13551372
let event = fetch_replied_to_event(
1356-
state,
1373+
state_guard,
1374+
&self.state,
13571375
index,
13581376
&item,
13591377
internal_id,
@@ -1507,15 +1525,16 @@ impl<P: RoomDataProvider> TimelineController<P, (OlmMachine, OwnedRoomId)> {
15071525
}
15081526

15091527
async fn fetch_replied_to_event(
1510-
mut state: RwLockWriteGuard<'_, TimelineState>,
1528+
mut state_guard: RwLockWriteGuard<'_, TimelineState>,
1529+
state_lock: &RwLock<TimelineState>,
15111530
index: usize,
15121531
item: &EventTimelineItem,
15131532
internal_id: TimelineUniqueId,
15141533
msglike: &MsgLikeContent,
15151534
in_reply_to: &EventId,
15161535
room: &Room,
15171536
) -> Result<TimelineDetails<Box<RepliedToEvent>>, Error> {
1518-
if let Some((_, item)) = rfind_event_by_id(&state.items, in_reply_to) {
1537+
if let Some((_, item)) = rfind_event_by_id(&state_guard.items, in_reply_to) {
15191538
let details = TimelineDetails::Ready(Box::new(RepliedToEvent::from_timeline_item(&item)));
15201539
trace!("Found replied-to event locally");
15211540
return Ok(details);
@@ -1531,16 +1550,25 @@ async fn fetch_replied_to_event(
15311550
.with_content(TimelineItemContent::MsgLike(msglike.with_in_reply_to(in_reply_to_details)));
15321551

15331552
let new_timeline_item = TimelineItem::new(event_item, internal_id);
1534-
state.items.replace(index, new_timeline_item);
1553+
state_guard.items.replace(index, new_timeline_item);
15351554

1536-
// Don't hold the state lock while the network request is made
1537-
drop(state);
1555+
// Don't hold the state lock while the network request is made.
1556+
drop(state_guard);
15381557

15391558
trace!("Fetching replied-to event");
15401559
let res = match room.load_or_fetch_event(in_reply_to, None).await {
1541-
Ok(timeline_event) => TimelineDetails::Ready(Box::new(
1542-
RepliedToEvent::try_from_timeline_event(timeline_event, room).await?,
1543-
)),
1560+
Ok(timeline_event) => {
1561+
let state = &mut *state_lock.write().await;
1562+
TimelineDetails::Ready(Box::new(
1563+
RepliedToEvent::try_from_timeline_event(
1564+
timeline_event,
1565+
room,
1566+
&state.items,
1567+
&mut state.meta,
1568+
)
1569+
.await?,
1570+
))
1571+
}
15441572
Err(e) => TimelineDetails::Error(Arc::new(e)),
15451573
};
15461574
Ok(res)

crates/matrix-sdk-ui/src/timeline/event_item/content/reply.rs

Lines changed: 34 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,17 @@
1515
use std::sync::Arc;
1616

1717
use imbl::Vector;
18-
use matrix_sdk::{
19-
crypto::types::events::UtdCause,
20-
deserialized_responses::{TimelineEvent, TimelineEventKind},
21-
Room,
22-
};
23-
use ruma::{
24-
events::{
25-
poll::unstable_start::UnstablePollStartEventContent, AnyMessageLikeEventContent,
26-
AnySyncTimelineEvent,
27-
},
28-
html::RemoveReplyFallback,
29-
OwnedEventId, OwnedUserId, UserId,
30-
};
18+
use matrix_sdk::deserialized_responses::TimelineEvent;
19+
use ruma::{OwnedEventId, OwnedUserId, UserId};
3120
use tracing::{debug, instrument, warn};
3221

3322
use super::TimelineItemContent;
3423
use crate::timeline::{
35-
event_item::{
36-
content::{MsgLikeContent, MsgLikeKind},
37-
extract_room_msg_edit_content, EventTimelineItem, Profile, TimelineDetails,
38-
},
24+
controller::TimelineMetadata,
25+
event_handler::TimelineAction,
26+
event_item::{content::MsgLikeContent, EventTimelineItem, Profile, TimelineDetails},
3927
traits::RoomDataProvider,
40-
EncryptedMessage, Error as TimelineError, Message, PollState, ReactionsByKeyBySender, Sticker,
41-
TimelineItem,
28+
Error as TimelineError, TimelineItem,
4229
};
4330

4431
/// Details about an event being replied to.
@@ -104,26 +91,23 @@ impl RepliedToEvent {
10491
}
10592
}
10693

107-
/// Try to create a `RepliedToEvent` from a `TimelineEvent` by providing the
108-
/// room.
109-
pub async fn try_from_timeline_event_for_room(
110-
timeline_event: TimelineEvent,
111-
room_data_provider: &Room,
112-
) -> Result<Self, TimelineError> {
113-
Self::try_from_timeline_event(timeline_event, room_data_provider).await
114-
}
115-
11694
#[instrument(skip_all)]
11795
pub(in crate::timeline) async fn try_from_timeline_event<P: RoomDataProvider>(
11896
timeline_event: TimelineEvent,
11997
room_data_provider: &P,
98+
timeline_items: &Vector<Arc<TimelineItem>>,
99+
meta: &mut TimelineMetadata,
120100
) -> Result<Self, TimelineError> {
121-
let event = match timeline_event.raw().deserialize() {
122-
Ok(AnySyncTimelineEvent::MessageLike(event)) => event,
123-
Ok(_) => {
124-
warn!("can't get details, event isn't a message-like event");
125-
return Err(TimelineError::UnsupportedEvent);
126-
}
101+
let (raw_event, unable_to_decrypt_info) = match timeline_event.kind {
102+
matrix_sdk::deserialized_responses::TimelineEventKind::UnableToDecrypt {
103+
utd_info,
104+
event,
105+
} => (event, Some(utd_info)),
106+
_ => (timeline_event.kind.into_raw(), None),
107+
};
108+
109+
let event = match raw_event.deserialize() {
110+
Ok(event) => event,
127111
Err(err) => {
128112
warn!("can't get details, event couldn't be deserialized: {err}");
129113
return Err(TimelineError::UnsupportedEvent);
@@ -132,94 +116,24 @@ impl RepliedToEvent {
132116

133117
debug!(event_type = %event.event_type(), "got deserialized event");
134118

135-
let content = match event.original_content() {
136-
Some(content) => match content {
137-
AnyMessageLikeEventContent::RoomMessage(c) => {
138-
// Assume we're not interested in aggregations in this context:
139-
// this is information for an embedded (replied-to) event, that will usually not
140-
// include detailed information like reactions.
141-
let reactions = ReactionsByKeyBySender::default();
142-
let thread_root = None;
143-
let in_reply_to = None;
144-
let thread_summary = None;
145-
146-
TimelineItemContent::MsgLike(MsgLikeContent {
147-
kind: MsgLikeKind::Message(Message::from_event(
148-
c.msgtype,
149-
c.mentions,
150-
extract_room_msg_edit_content(event.relations()),
151-
RemoveReplyFallback::Yes,
152-
)),
153-
reactions,
154-
thread_root,
155-
in_reply_to,
156-
thread_summary,
157-
})
158-
}
159-
160-
AnyMessageLikeEventContent::Sticker(content) => {
161-
// Assume we're not interested in aggregations in this context.
162-
// (See above an explanation as to why that's the case.)
163-
let reactions = Default::default();
164-
let thread_root = None;
165-
let in_reply_to = None;
166-
let thread_summary = None;
167-
168-
TimelineItemContent::MsgLike(MsgLikeContent {
169-
kind: MsgLikeKind::Sticker(Sticker { content }),
170-
reactions,
171-
thread_root,
172-
in_reply_to,
173-
thread_summary,
174-
})
175-
}
176-
177-
AnyMessageLikeEventContent::RoomEncrypted(content) => {
178-
let utd_cause = match &timeline_event.kind {
179-
TimelineEventKind::UnableToDecrypt { utd_info, .. } => UtdCause::determine(
180-
timeline_event.raw(),
181-
room_data_provider.crypto_context_info().await,
182-
utd_info,
183-
),
184-
_ => UtdCause::Unknown,
185-
};
186-
187-
TimelineItemContent::MsgLike(MsgLikeContent::unable_to_decrypt(
188-
EncryptedMessage::from_content(content, utd_cause),
189-
))
190-
}
191-
192-
AnyMessageLikeEventContent::UnstablePollStart(
193-
UnstablePollStartEventContent::New(content),
194-
) => {
195-
// Assume we're not interested in aggregations in this context.
196-
// (See above an explanation as to why that's the case.)
197-
let reactions = Default::default();
198-
let thread_root = None;
199-
let in_reply_to = None;
200-
let thread_summary = None;
201-
202-
// TODO: could we provide the bundled edit here?
203-
let poll_state = PollState::new(content);
204-
TimelineItemContent::MsgLike(MsgLikeContent {
205-
kind: MsgLikeKind::Poll(poll_state),
206-
reactions,
207-
thread_root,
208-
in_reply_to,
209-
thread_summary,
210-
})
211-
}
212-
213-
_ => {
214-
warn!("unsupported event type");
215-
return Err(TimelineError::UnsupportedEvent);
216-
}
217-
},
218-
219-
None => TimelineItemContent::MsgLike(MsgLikeContent::redacted()),
119+
let sender = event.sender().to_owned();
120+
let action = TimelineAction::from_event(
121+
event,
122+
&raw_event,
123+
room_data_provider,
124+
unable_to_decrypt_info,
125+
timeline_items,
126+
meta,
127+
)
128+
.await;
129+
130+
let content = if let Some(TimelineAction::AddItem { content, .. }) = action {
131+
content
132+
} else {
133+
// TODO: need some kind of "unsupported" event here: return an option
134+
TimelineItemContent::MsgLike(MsgLikeContent::redacted())
220135
};
221136

222-
let sender = event.sender().to_owned();
223137
let sender_profile = TimelineDetails::from_initial_value(
224138
room_data_provider.profile_from_user_id(&sender).await,
225139
);

crates/matrix-sdk-ui/src/timeline/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use futures_core::Stream;
2525
use imbl::Vector;
2626
use matrix_sdk::{
2727
attachment::AttachmentConfig,
28+
deserialized_responses::TimelineEvent,
2829
event_cache::{EventCacheDropHandles, RoomEventCache},
2930
event_handler::EventHandlerHandle,
3031
executor::JoinHandle,
@@ -667,6 +668,12 @@ impl Timeline {
667668
Ok(false)
668669
}
669670
}
671+
672+
/// Create a [`RepliedToEvent`] from an arbitrary event, be it in the
673+
/// timeline or not.
674+
pub async fn make_replied_to(&self, event: TimelineEvent) -> Result<RepliedToEvent, Error> {
675+
self.controller.make_replied_to(event).await
676+
}
670677
}
671678

672679
/// Test helpers, likely not very useful in production.

crates/matrix-sdk-ui/tests/integration/timeline/replies.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,13 @@ async fn test_fetch_details_utd() {
350350
let in_reply_to = in_reply_to.clone().unwrap();
351351
assert_let!(TimelineDetails::Ready(replied_to) = &in_reply_to.event);
352352
assert_eq!(replied_to.sender(), *ALICE);
353-
assert!(replied_to.content().is_unable_to_decrypt());
353+
assert_matches!(
354+
replied_to.content(),
355+
TimelineItemContent::MsgLike(MsgLikeContent {
356+
kind: MsgLikeKind::UnableToDecrypt(_),
357+
..
358+
})
359+
);
354360
}
355361
}
356362

0 commit comments

Comments
 (0)