Skip to content

Commit 7cea5be

Browse files
committed
refactor(timeline): allow creating a TimelineItemContent from a sdk::TimelineEvent
1 parent 32e9626 commit 7cea5be

5 files changed

Lines changed: 85 additions & 155 deletions

File tree

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

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,18 @@
1515
use std::sync::Arc;
1616

1717
use as_variant::as_variant;
18+
use matrix_sdk::{Room, deserialized_responses::TimelineEvent};
1819
use matrix_sdk_base::crypto::types::events::UtdCause;
1920
use ruma::{
2021
OwnedDeviceId, OwnedEventId, OwnedMxcUri, OwnedUserId, UserId,
2122
events::{
22-
AnyStateEventContentChange, Mentions, MessageLikeEventType, StateEventContentChange,
23-
StateEventType,
23+
AnyMessageLikeEventContent, AnyStateEventContentChange, Mentions, MessageLikeEventType,
24+
StateEventContentChange, StateEventType,
2425
policy::rule::{
2526
room::PolicyRuleRoomEventContent, server::PolicyRuleServerEventContent,
2627
user::PolicyRuleUserEventContent,
2728
},
29+
relation::Replacement,
2830
room::{
2931
aliases::RoomAliasesEventContent,
3032
avatar::RoomAvatarEventContent,
@@ -36,7 +38,7 @@ use ruma::{
3638
history_visibility::RoomHistoryVisibilityEventContent,
3739
join_rules::RoomJoinRulesEventContent,
3840
member::{Change, RoomMemberEventContent},
39-
message::MessageType,
41+
message::{MessageType, RoomMessageEventContent},
4042
name::RoomNameEventContent,
4143
pinned_events::RoomPinnedEventsEventContent,
4244
power_levels::RoomPowerLevelsEventContent,
@@ -77,6 +79,7 @@ pub use self::{
7779
reply::{EmbeddedEvent, InReplyToDetails},
7880
};
7981
use super::ReactionsByKeyBySender;
82+
use crate::timeline::event_handler::{HandleAggregationKind, TimelineAction};
8083

8184
/// The content of an [`EventTimelineItem`][super::EventTimelineItem].
8285
#[allow(clippy::large_enum_variant)]
@@ -147,6 +150,49 @@ impl TimelineItemContent {
147150
}
148151
}
149152

153+
/// Create a raw [`TimelineItemContent`] for a given [`TimelineEvent`],
154+
/// without providing extra information (about thread root, replied-to
155+
/// information, UTD info, and so on).
156+
pub async fn from_event(room: &Room, timeline_event: TimelineEvent) -> Option<Self> {
157+
let raw_event = timeline_event.into_raw();
158+
let deserialized_event = raw_event.deserialize().ok()?;
159+
160+
match TimelineAction::from_event(
161+
deserialized_event,
162+
&raw_event,
163+
room,
164+
None,
165+
None,
166+
None,
167+
None,
168+
)
169+
.await
170+
{
171+
Some(TimelineAction::AddItem { content }) => Some(content),
172+
173+
// Aggregated event: only edits are supported at the moment.
174+
Some(TimelineAction::HandleAggregation {
175+
kind: HandleAggregationKind::Edit { replacement: Replacement { new_content, .. } },
176+
..
177+
}) => {
178+
// Map the edit to a regular message.
179+
match TimelineAction::from_content(
180+
AnyMessageLikeEventContent::RoomMessage(RoomMessageEventContent::new(
181+
new_content.msgtype,
182+
)),
183+
None,
184+
None,
185+
None,
186+
) {
187+
TimelineAction::AddItem { content } => Some(content),
188+
_ => None,
189+
}
190+
}
191+
192+
_ => None,
193+
}
194+
}
195+
150196
pub fn as_msglike(&self) -> Option<&MsgLikeContent> {
151197
as_variant!(self, TimelineItemContent::MsgLike)
152198
}

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use std::{
2020
use as_variant::as_variant;
2121
use indexmap::IndexMap;
2222
use matrix_sdk::{
23-
Error,
23+
Error, Room,
2424
deserialized_responses::{EncryptionInfo, ShieldState},
2525
send_queue::{SendHandle, SendReactionHandle},
2626
};
@@ -32,6 +32,7 @@ use ruma::{
3232
room_version_rules::RedactionRules,
3333
serde::Raw,
3434
};
35+
use tracing::error;
3536
use unicode_segmentation::UnicodeSegmentation;
3637

3738
mod content;
@@ -643,6 +644,24 @@ pub struct Profile {
643644
pub avatar_url: Option<OwnedMxcUri>,
644645
}
645646

647+
impl Profile {
648+
pub async fn load(room: &Room, user_id: &UserId) -> Option<Self> {
649+
match room.get_member_no_sync(user_id).await {
650+
Ok(Some(member)) => Some(Profile {
651+
display_name: member.display_name().map(ToOwned::to_owned),
652+
display_name_ambiguous: member.name_ambiguous(),
653+
avatar_url: member.avatar_url().map(ToOwned::to_owned),
654+
}),
655+
Ok(None) if room.are_members_synced() => Some(Profile::default()),
656+
Ok(None) => None,
657+
Err(e) => {
658+
error!(%user_id, "Failed to fetch room member information: {e}");
659+
None
660+
}
661+
}
662+
}
663+
}
664+
646665
/// Some details of an [`EventTimelineItem`] that may require server requests
647666
/// other than just the regular
648667
/// [`sync_events`][ruma::api::client::sync::sync_events].

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

Lines changed: 10 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,11 @@
1414

1515
use matrix_sdk::{Client, Room, latest_events::LocalLatestEventValue};
1616
use matrix_sdk_base::latest_event::LatestEventValue as BaseLatestEventValue;
17-
use ruma::{
18-
MilliSecondsSinceUnixEpoch, OwnedUserId,
19-
events::{
20-
AnyMessageLikeEventContent, relation::Replacement, room::message::RoomMessageEventContent,
21-
},
22-
};
17+
use ruma::{MilliSecondsSinceUnixEpoch, OwnedUserId};
2318
use tracing::trace;
2419

2520
use crate::timeline::{
26-
Profile, TimelineDetails, TimelineItemContent,
27-
event_handler::{HandleAggregationKind, TimelineAction},
28-
traits::RoomDataProvider,
21+
Profile, TimelineDetails, TimelineItemContent, event_handler::TimelineAction,
2922
};
3023

3124
/// A simplified version of [`matrix_sdk_base::latest_event::LatestEventValue`]
@@ -111,77 +104,17 @@ impl LatestEventValue {
111104
};
112105
let is_own = client.user_id().map(|user_id| user_id == sender).unwrap_or(false);
113106

114-
let raw_any_sync_timeline_event = timeline_event.into_raw();
115-
let Ok(any_sync_timeline_event) = raw_any_sync_timeline_event.deserialize() else {
116-
return Self::None;
117-
};
118-
let profile = room
119-
.profile_from_user_id(&sender)
120-
.await
121-
.map(TimelineDetails::Ready)
122-
.unwrap_or(TimelineDetails::Unavailable);
123-
124-
match TimelineAction::from_event(
125-
any_sync_timeline_event,
126-
&raw_any_sync_timeline_event,
127-
room,
128-
None,
129-
None,
130-
None,
131-
None,
132-
)
133-
.await
134-
{
135-
// Easy path: no aggregation, direct event.
136-
Some(TimelineAction::AddItem { content }) => {
137-
Self::Remote { timestamp, sender, is_own, profile, content }
138-
}
139-
140-
// Aggregated event.
141-
//
142-
// Only edits are supported for the moment.
143-
Some(TimelineAction::HandleAggregation {
144-
kind:
145-
HandleAggregationKind::Edit { replacement: Replacement { new_content, .. } },
146-
..
147-
}) => {
148-
// Let's map the edit into a regular message.
149-
match TimelineAction::from_content(
150-
AnyMessageLikeEventContent::RoomMessage(RoomMessageEventContent::new(
151-
new_content.msgtype,
152-
)),
153-
// We don't care about the `InReplyToDetails` in the context of a
154-
// `LatestEventValue`.
155-
None,
156-
// We don't care about the thread information in the context of a
157-
// `LatestEventValue`.
158-
None,
159-
None,
160-
) {
161-
// The expected case.
162-
TimelineAction::AddItem { content } => {
163-
Self::Remote { timestamp, sender, is_own, profile, content }
164-
}
165-
166-
// Supposedly unreachable, but let's pretend there is no
167-
// `LatestEventValue` if it happens.
168-
_ => {
169-
trace!("latest event was an edit that failed to be un-aggregated");
170-
171-
Self::None
172-
}
173-
}
174-
}
107+
let profile =
108+
TimelineDetails::from_initial_value(Profile::load(room, &sender).await);
175109

176-
_ => Self::None,
110+
match TimelineItemContent::from_event(room, timeline_event).await {
111+
Some(content) => Self::Remote { timestamp, sender, is_own, profile, content },
112+
None => Self::None,
177113
}
178114
}
179115
BaseLatestEventValue::RemoteInvite { timestamp, inviter, .. } => {
180116
let inviter_profile = if let Some(inviter_id) = &inviter {
181-
room.profile_from_user_id(inviter_id)
182-
.await
183-
.map(TimelineDetails::Ready)
184-
.unwrap_or(TimelineDetails::Unavailable)
117+
TimelineDetails::from_initial_value(Profile::load(room, inviter_id).await)
185118
} else {
186119
TimelineDetails::Unavailable
187120
};
@@ -199,11 +132,8 @@ impl LatestEventValue {
199132

200133
let sender =
201134
client.user_id().expect("The `Client` is supposed to be logged").to_owned();
202-
let profile = room
203-
.profile_from_user_id(&sender)
204-
.await
205-
.map(TimelineDetails::Ready)
206-
.unwrap_or(TimelineDetails::Unavailable);
135+
let profile =
136+
TimelineDetails::from_initial_value(Profile::load(room, &sender).await);
207137

208138
match TimelineAction::from_content(message_like_event_content, None, None, None) {
209139
TimelineAction::AddItem { content } => Self::Local {

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

Lines changed: 5 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,11 @@ use matrix_sdk::{
2828
task_monitor::BackgroundTaskHandle,
2929
};
3030
use matrix_sdk_common::serde_helpers::extract_thread_root;
31-
use ruma::{
32-
MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedUserId,
33-
events::{
34-
AnyMessageLikeEventContent, relation::Replacement, room::message::RoomMessageEventContent,
35-
},
36-
};
31+
use ruma::{MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedUserId};
3732
use tokio::sync::Mutex as AsyncMutex;
3833
use tracing::{error, trace, warn};
3934

40-
use crate::timeline::{
41-
Profile, TimelineDetails, TimelineItemContent,
42-
event_handler::{HandleAggregationKind, TimelineAction},
43-
traits::RoomDataProvider,
44-
};
35+
use crate::timeline::{Profile, TimelineDetails, TimelineItemContent, traits::RoomDataProvider};
4536

4637
/// Each `ThreadListItem` represents one thread root event in the room. The
4738
/// fields are pre-resolved from the raw homeserver response: the sender's
@@ -379,53 +370,9 @@ impl ThreadListService {
379370
let timestamp = timeline_event.timestamp()?;
380371
let sender = timeline_event.sender()?;
381372
let is_own = room.own_user_id() == sender;
382-
383-
let raw = timeline_event.into_raw();
384-
let deserialized = match raw.deserialize() {
385-
Ok(ev) => ev,
386-
Err(e) => {
387-
error!("Failed deserializing thread event for latest_event: {e}");
388-
return None;
389-
}
390-
};
391-
392-
let sender_profile = room
393-
.profile_from_user_id(&sender)
394-
.await
395-
.map(TimelineDetails::Ready)
396-
.unwrap_or(TimelineDetails::Unavailable);
397-
398-
let content: Option<TimelineItemContent> = match TimelineAction::from_event(
399-
deserialized,
400-
&raw,
401-
room,
402-
None,
403-
None,
404-
None,
405-
None,
406-
)
407-
.await
408-
{
409-
Some(TimelineAction::AddItem { content }) => Some(content),
410-
Some(TimelineAction::HandleAggregation {
411-
kind: HandleAggregationKind::Edit { replacement: Replacement { new_content, .. } },
412-
..
413-
}) => {
414-
match TimelineAction::from_content(
415-
AnyMessageLikeEventContent::RoomMessage(RoomMessageEventContent::new(
416-
new_content.msgtype,
417-
)),
418-
None,
419-
None,
420-
None,
421-
) {
422-
TimelineAction::AddItem { content } => Some(content),
423-
_ => None,
424-
}
425-
}
426-
_ => None,
427-
};
428-
373+
let sender_profile =
374+
TimelineDetails::from_initial_value(Profile::load(room, &sender).await);
375+
let content = TimelineItemContent::from_event(room, timeline_event).await;
429376
Some(ThreadListItemEvent { event_id, timestamp, sender, is_own, sender_profile, content })
430377
}
431378

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

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -164,19 +164,7 @@ impl RoomDataProvider for Room {
164164
}
165165

166166
async fn profile_from_user_id<'a>(&'a self, user_id: &'a UserId) -> Option<Profile> {
167-
match self.get_member_no_sync(user_id).await {
168-
Ok(Some(member)) => Some(Profile {
169-
display_name: member.display_name().map(ToOwned::to_owned),
170-
display_name_ambiguous: member.name_ambiguous(),
171-
avatar_url: member.avatar_url().map(ToOwned::to_owned),
172-
}),
173-
Ok(None) if self.are_members_synced() => Some(Profile::default()),
174-
Ok(None) => None,
175-
Err(e) => {
176-
error!(%user_id, "Failed to fetch room member information: {e}");
177-
None
178-
}
179-
}
167+
Profile::load(self, user_id).await
180168
}
181169

182170
async fn load_user_receipt<'a>(

0 commit comments

Comments
 (0)