1use std::future::Future;
16
17use eyeball::Subscriber;
18use indexmap::IndexMap;
19use matrix_sdk::{
20 BoxFuture, Result, Room, SendOutsideWasm,
21 config::RequestConfig,
22 deserialized_responses::TimelineEvent,
23 paginators::{PaginableRoom, thread::PaginableThread},
24};
25use matrix_sdk_base::{RoomInfo, crypto::types::events::CryptoContextInfo};
26use ruma::{
27 EventId, OwnedEventId, OwnedTransactionId, OwnedUserId, UserId,
28 events::{
29 AnyMessageLikeEventContent,
30 fully_read::FullyReadEventContent,
31 receipt::{Receipt, ReceiptThread, ReceiptType},
32 relation::RelationType,
33 },
34 room_version_rules::RoomVersionRules,
35};
36use tracing::error;
37
38use super::{Profile, RedactError, TimelineBuilder};
39use crate::timeline::{
40 self, Timeline, TimelineReadReceiptTracking, latest_event::LatestEventValue,
41};
42
43pub trait RoomExt {
44 fn timeline(&self)
52 -> impl Future<Output = Result<Timeline, timeline::Error>> + SendOutsideWasm;
53
54 fn timeline_builder(&self) -> TimelineBuilder;
63
64 fn latest_event(&self) -> impl Future<Output = LatestEventValue>;
66}
67
68impl RoomExt for Room {
69 async fn timeline(&self) -> Result<Timeline, timeline::Error> {
70 self.timeline_builder().build().await
71 }
72
73 fn timeline_builder(&self) -> TimelineBuilder {
74 TimelineBuilder::new(self)
75 .track_read_marker_and_receipts(TimelineReadReceiptTracking::AllEvents)
76 }
77
78 async fn latest_event(&self) -> LatestEventValue {
79 LatestEventValue::from_base_latest_event_value(
80 (**self).latest_event(),
81 self,
82 &self.client(),
83 )
84 .await
85 }
86}
87
88pub(super) trait RoomDataProvider:
89 Clone + PaginableRoom + PaginableThread + 'static
90{
91 fn own_user_id(&self) -> &UserId;
92 fn room_version_rules(&self) -> RoomVersionRules;
93
94 fn crypto_context_info(&self)
95 -> impl Future<Output = CryptoContextInfo> + SendOutsideWasm + '_;
96
97 fn profile_from_user_id<'a>(
98 &'a self,
99 user_id: &'a UserId,
100 ) -> impl Future<Output = Option<Profile>> + SendOutsideWasm + 'a;
101
102 fn load_user_receipt<'a>(
104 &'a self,
105 receipt_type: ReceiptType,
106 thread: ReceiptThread,
107 user_id: &'a UserId,
108 ) -> impl Future<Output = Option<(OwnedEventId, Receipt)>> + SendOutsideWasm + 'a;
109
110 fn load_event_receipts<'a>(
112 &'a self,
113 event_id: &'a EventId,
114 receipt_thread: ReceiptThread,
115 ) -> impl Future<Output = IndexMap<OwnedUserId, Receipt>> + SendOutsideWasm + 'a;
116
117 fn load_fully_read_marker(&self) -> impl Future<Output = Option<OwnedEventId>> + '_;
119
120 fn send(
122 &self,
123 content: AnyMessageLikeEventContent,
124 ) -> impl Future<Output = Result<(), super::Error>> + SendOutsideWasm + '_;
125
126 fn redact<'a>(
128 &'a self,
129 event_id: &'a EventId,
130 reason: Option<&'a str>,
131 transaction_id: Option<OwnedTransactionId>,
132 ) -> impl Future<Output = Result<(), super::Error>> + SendOutsideWasm + 'a;
133
134 fn room_info(&self) -> Subscriber<RoomInfo>;
135
136 fn load_event<'a>(
138 &'a self,
139 event_id: &'a EventId,
140 ) -> impl Future<Output = Result<TimelineEvent>> + SendOutsideWasm + 'a;
141
142 fn load_event_with_relations<'a>(
149 &'a self,
150 event_id: &'a EventId,
151 request_config: Option<RequestConfig>,
152 related_event_filters: Option<Vec<RelationType>>,
153 ) -> BoxFuture<'a, Result<(TimelineEvent, Vec<TimelineEvent>), matrix_sdk::Error>>;
154}
155
156impl RoomDataProvider for Room {
157 fn own_user_id(&self) -> &UserId {
158 (**self).own_user_id()
159 }
160
161 fn room_version_rules(&self) -> RoomVersionRules {
162 (**self).clone_info().room_version_rules_or_default()
163 }
164
165 async fn crypto_context_info(&self) -> CryptoContextInfo {
166 self.crypto_context_info().await
167 }
168
169 async fn profile_from_user_id<'a>(&'a self, user_id: &'a UserId) -> Option<Profile> {
170 match self.get_member_no_sync(user_id).await {
171 Ok(Some(member)) => Some(Profile {
172 display_name: member.display_name().map(ToOwned::to_owned),
173 display_name_ambiguous: member.name_ambiguous(),
174 avatar_url: member.avatar_url().map(ToOwned::to_owned),
175 }),
176 Ok(None) if self.are_members_synced() => Some(Profile::default()),
177 Ok(None) => None,
178 Err(e) => {
179 error!(%user_id, "Failed to fetch room member information: {e}");
180 None
181 }
182 }
183 }
184
185 async fn load_user_receipt<'a>(
186 &'a self,
187 receipt_type: ReceiptType,
188 thread: ReceiptThread,
189 user_id: &'a UserId,
190 ) -> Option<(OwnedEventId, Receipt)> {
191 match self.load_user_receipt(receipt_type.clone(), thread.clone(), user_id).await {
192 Ok(receipt) => receipt,
193 Err(e) => {
194 error!(
195 ?receipt_type,
196 ?thread,
197 ?user_id,
198 "Failed to get read receipt for user: {e}"
199 );
200 None
201 }
202 }
203 }
204
205 async fn load_event_receipts<'a>(
206 &'a self,
207 event_id: &'a EventId,
208 receipt_thread: ReceiptThread,
209 ) -> IndexMap<OwnedUserId, Receipt> {
210 match self.load_event_receipts(ReceiptType::Read, receipt_thread.clone(), event_id).await {
211 Ok(receipts) => receipts.into_iter().collect(),
212 Err(e) => {
213 error!(?event_id, ?receipt_thread, "Failed to get read receipts for event: {e}");
214 IndexMap::new()
215 }
216 }
217 }
218
219 async fn load_fully_read_marker(&self) -> Option<OwnedEventId> {
220 match self.account_data_static::<FullyReadEventContent>().await {
221 Ok(Some(fully_read)) => match fully_read.deserialize() {
222 Ok(fully_read) => Some(fully_read.content.event_id),
223 Err(e) => {
224 error!("Failed to deserialize fully-read account data: {e}");
225 None
226 }
227 },
228 Err(e) => {
229 error!("Failed to get fully-read account data from the store: {e}");
230 None
231 }
232 _ => None,
233 }
234 }
235
236 async fn send(&self, content: AnyMessageLikeEventContent) -> Result<(), super::Error> {
237 let _ = self.send_queue().send(content).await?;
238 Ok(())
239 }
240
241 async fn redact<'a>(
242 &'a self,
243 event_id: &'a EventId,
244 reason: Option<&'a str>,
245 transaction_id: Option<OwnedTransactionId>,
246 ) -> Result<(), super::Error> {
247 let _ = self
248 .redact(event_id, reason, transaction_id)
249 .await
250 .map_err(RedactError::HttpError)
251 .map_err(super::Error::RedactError)?;
252 Ok(())
253 }
254
255 fn room_info(&self) -> Subscriber<RoomInfo> {
256 self.subscribe_info()
257 }
258
259 async fn load_event<'a>(&'a self, event_id: &'a EventId) -> Result<TimelineEvent> {
260 self.load_or_fetch_event(event_id, None).await
261 }
262
263 fn load_event_with_relations<'a>(
264 &'a self,
265 event_id: &'a EventId,
266 request_config: Option<RequestConfig>,
267 related_event_filters: Option<Vec<RelationType>>,
268 ) -> BoxFuture<'a, Result<(TimelineEvent, Vec<TimelineEvent>), matrix_sdk::Error>> {
269 Box::pin(self.load_or_fetch_event_with_relations(
270 event_id,
271 related_event_filters,
272 request_config,
273 ))
274 }
275}