1use std::future::Future;
16
17use eyeball::Subscriber;
18use indexmap::IndexMap;
19use matrix_sdk::{
20 Result, Room, SendOutsideWasm,
21 deserialized_responses::TimelineEvent,
22 paginators::{PaginableRoom, thread::PaginableThread},
23};
24use matrix_sdk_base::{RoomInfo, crypto::types::events::CryptoContextInfo};
25use ruma::{
26 EventId, OwnedEventId, OwnedTransactionId, OwnedUserId, RoomId, UserId,
27 events::{
28 AnyMessageLikeEventContent,
29 fully_read::FullyReadEventContent,
30 receipt::{Receipt, ReceiptThread, ReceiptType},
31 relation::RelationType,
32 },
33 room_version_rules::RoomVersionRules,
34};
35use tracing::error;
36
37use super::{Profile, RedactError, TimelineBuilder};
38use crate::timeline::{
39 self, Timeline, TimelineReadReceiptTracking, latest_event::LatestEventValue,
40 thread_list_service::ThreadListService,
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 fn thread_list_service(&self) -> ThreadListService;
73}
74
75impl RoomExt for Room {
76 async fn timeline(&self) -> Result<Timeline, timeline::Error> {
77 self.timeline_builder().build().await
78 }
79
80 fn timeline_builder(&self) -> TimelineBuilder {
81 TimelineBuilder::new(self)
82 .track_read_marker_and_receipts(TimelineReadReceiptTracking::AllEvents)
83 }
84
85 async fn latest_event(&self) -> LatestEventValue {
86 LatestEventValue::from_base_latest_event_value(
87 (**self).latest_event(),
88 self,
89 &self.client(),
90 )
91 .await
92 }
93
94 fn thread_list_service(&self) -> ThreadListService {
95 ThreadListService::new(self.clone())
96 }
97}
98
99pub(super) trait RoomDataProvider:
100 Clone + PaginableRoom + PaginableThread + 'static
101{
102 fn room_id(&self) -> &RoomId;
103 fn own_user_id(&self) -> &UserId;
104 fn room_version_rules(&self) -> RoomVersionRules;
105
106 fn crypto_context_info(&self)
107 -> impl Future<Output = CryptoContextInfo> + SendOutsideWasm + '_;
108
109 fn profile_from_user_id<'a>(
110 &'a self,
111 user_id: &'a UserId,
112 ) -> impl Future<Output = Option<Profile>> + SendOutsideWasm + 'a;
113
114 fn load_user_receipt<'a>(
116 &'a self,
117 receipt_type: ReceiptType,
118 thread: &'a ReceiptThread,
119 user_id: &'a UserId,
120 ) -> impl Future<Output = Option<(OwnedEventId, Receipt)>> + SendOutsideWasm + 'a;
121
122 fn load_event_receipts<'a>(
124 &'a self,
125 event_id: &'a EventId,
126 receipt_thread: &'a ReceiptThread,
127 ) -> impl Future<Output = IndexMap<OwnedUserId, Receipt>> + SendOutsideWasm + 'a;
128
129 fn load_fully_read_marker(&self) -> impl Future<Output = Option<OwnedEventId>> + '_;
131
132 fn send(
135 &self,
136 content: AnyMessageLikeEventContent,
137 extra_content: Option<serde_json::Map<String, serde_json::Value>>,
138 ) -> impl Future<Output = Result<(), super::Error>> + SendOutsideWasm + '_;
139
140 fn redact<'a>(
142 &'a self,
143 event_id: &'a EventId,
144 reason: Option<&'a str>,
145 transaction_id: Option<OwnedTransactionId>,
146 ) -> impl Future<Output = Result<(), super::Error>> + SendOutsideWasm + 'a;
147
148 fn room_info(&self) -> Subscriber<RoomInfo>;
149
150 fn load_event<'a>(
152 &'a self,
153 event_id: &'a EventId,
154 ) -> impl Future<Output = Result<TimelineEvent>> + SendOutsideWasm + 'a;
155
156 fn load_or_fetch_event_with_relations<'a>(
158 &'a self,
159 event_id: &'a EventId,
160 filter: Option<Vec<RelationType>>,
161 ) -> impl Future<Output = Result<(TimelineEvent, Vec<TimelineEvent>)>> + SendOutsideWasm + 'a;
162}
163
164impl RoomDataProvider for Room {
165 fn room_id(&self) -> &RoomId {
166 (**self).room_id()
167 }
168
169 fn own_user_id(&self) -> &UserId {
170 (**self).own_user_id()
171 }
172
173 fn room_version_rules(&self) -> RoomVersionRules {
174 (**self).clone_info().room_version_rules_or_default()
175 }
176
177 async fn crypto_context_info(&self) -> CryptoContextInfo {
178 self.crypto_context_info().await
179 }
180
181 async fn profile_from_user_id<'a>(&'a self, user_id: &'a UserId) -> Option<Profile> {
182 Profile::load(self, user_id).await
183 }
184
185 async fn load_user_receipt<'a>(
186 &'a self,
187 receipt_type: ReceiptType,
188 receipt_thread: &'a ReceiptThread,
189 user_id: &'a UserId,
190 ) -> Option<(OwnedEventId, Receipt)> {
191 match self.load_user_receipt(receipt_type.clone(), receipt_thread, user_id).await {
192 Ok(receipt) => receipt,
193 Err(e) => {
194 error!(
195 ?receipt_type,
196 ?receipt_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: &'a ReceiptThread,
209 ) -> IndexMap<OwnedUserId, Receipt> {
210 match self.load_event_receipts(ReceiptType::Read, receipt_thread, 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(
237 &self,
238 content: AnyMessageLikeEventContent,
239 extra_content: Option<serde_json::Map<String, serde_json::Value>>,
240 ) -> Result<(), super::Error> {
241 let queue = self.send_queue();
242 let send = queue.send(content);
243 let send = match extra_content {
244 Some(extra_content) => send.with_extra_content(extra_content),
245 None => send,
246 };
247 let _ = send.await?;
248 Ok(())
249 }
250
251 async fn redact<'a>(
252 &'a self,
253 event_id: &'a EventId,
254 reason: Option<&'a str>,
255 transaction_id: Option<OwnedTransactionId>,
256 ) -> Result<(), super::Error> {
257 let _ = self
258 .redact(event_id, reason, transaction_id)
259 .await
260 .map_err(RedactError::HttpError)
261 .map_err(super::Error::RedactError)?;
262 Ok(())
263 }
264
265 fn room_info(&self) -> Subscriber<RoomInfo> {
266 self.subscribe_info()
267 }
268
269 async fn load_event<'a>(&'a self, event_id: &'a EventId) -> Result<TimelineEvent> {
270 self.load_or_fetch_event(event_id, None).await
271 }
272
273 async fn load_or_fetch_event_with_relations<'a>(
274 &'a self,
275 event_id: &'a EventId,
276 filter: Option<Vec<RelationType>>,
277 ) -> Result<(TimelineEvent, Vec<TimelineEvent>)> {
278 self.load_or_fetch_event_with_relations(event_id, filter, None).await
279 }
280}