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 },
32 room_version_rules::RoomVersionRules,
33};
34use tracing::error;
35
36use super::{Profile, RedactError, TimelineBuilder};
37use crate::timeline::{
38 self, Timeline, TimelineReadReceiptTracking, latest_event::LatestEventValue,
39 thread_list_service::ThreadListService,
40};
41
42pub trait RoomExt {
43 fn timeline(&self)
51 -> impl Future<Output = Result<Timeline, timeline::Error>> + SendOutsideWasm;
52
53 fn timeline_builder(&self) -> TimelineBuilder;
62
63 fn latest_event(&self) -> impl Future<Output = LatestEventValue>;
65
66 fn thread_list_service(&self) -> ThreadListService;
72}
73
74impl RoomExt for Room {
75 async fn timeline(&self) -> Result<Timeline, timeline::Error> {
76 self.timeline_builder().build().await
77 }
78
79 fn timeline_builder(&self) -> TimelineBuilder {
80 TimelineBuilder::new(self)
81 .track_read_marker_and_receipts(TimelineReadReceiptTracking::AllEvents)
82 }
83
84 async fn latest_event(&self) -> LatestEventValue {
85 LatestEventValue::from_base_latest_event_value(
86 (**self).latest_event(),
87 self,
88 &self.client(),
89 )
90 .await
91 }
92
93 fn thread_list_service(&self) -> ThreadListService {
94 ThreadListService::new(self.clone())
95 }
96}
97
98pub(super) trait RoomDataProvider:
99 Clone + PaginableRoom + PaginableThread + 'static
100{
101 fn room_id(&self) -> &RoomId;
102 fn own_user_id(&self) -> &UserId;
103 fn room_version_rules(&self) -> RoomVersionRules;
104
105 fn crypto_context_info(&self)
106 -> impl Future<Output = CryptoContextInfo> + SendOutsideWasm + '_;
107
108 fn profile_from_user_id<'a>(
109 &'a self,
110 user_id: &'a UserId,
111 ) -> impl Future<Output = Option<Profile>> + SendOutsideWasm + 'a;
112
113 fn load_user_receipt<'a>(
115 &'a self,
116 receipt_type: ReceiptType,
117 thread: &'a ReceiptThread,
118 user_id: &'a UserId,
119 ) -> impl Future<Output = Option<(OwnedEventId, Receipt)>> + SendOutsideWasm + 'a;
120
121 fn load_event_receipts<'a>(
123 &'a self,
124 event_id: &'a EventId,
125 receipt_thread: &'a ReceiptThread,
126 ) -> impl Future<Output = IndexMap<OwnedUserId, Receipt>> + SendOutsideWasm + 'a;
127
128 fn load_fully_read_marker(&self) -> impl Future<Output = Option<OwnedEventId>> + '_;
130
131 fn send(
134 &self,
135 content: AnyMessageLikeEventContent,
136 extra_content: Option<serde_json::Map<String, serde_json::Value>>,
137 ) -> impl Future<Output = Result<(), super::Error>> + SendOutsideWasm + '_;
138
139 fn redact<'a>(
141 &'a self,
142 event_id: &'a EventId,
143 reason: Option<&'a str>,
144 transaction_id: Option<OwnedTransactionId>,
145 ) -> impl Future<Output = Result<(), super::Error>> + SendOutsideWasm + 'a;
146
147 fn room_info(&self) -> Subscriber<RoomInfo>;
148
149 fn load_event<'a>(
151 &'a self,
152 event_id: &'a EventId,
153 ) -> impl Future<Output = Result<TimelineEvent>> + SendOutsideWasm + 'a;
154}
155
156impl RoomDataProvider for Room {
157 fn room_id(&self) -> &RoomId {
158 (**self).room_id()
159 }
160
161 fn own_user_id(&self) -> &UserId {
162 (**self).own_user_id()
163 }
164
165 fn room_version_rules(&self) -> RoomVersionRules {
166 (**self).clone_info().room_version_rules_or_default()
167 }
168
169 async fn crypto_context_info(&self) -> CryptoContextInfo {
170 self.crypto_context_info().await
171 }
172
173 async fn profile_from_user_id<'a>(&'a self, user_id: &'a UserId) -> Option<Profile> {
174 Profile::load(self, user_id).await
175 }
176
177 async fn load_user_receipt<'a>(
178 &'a self,
179 receipt_type: ReceiptType,
180 receipt_thread: &'a ReceiptThread,
181 user_id: &'a UserId,
182 ) -> Option<(OwnedEventId, Receipt)> {
183 match self.load_user_receipt(receipt_type.clone(), receipt_thread, user_id).await {
184 Ok(receipt) => receipt,
185 Err(e) => {
186 error!(
187 ?receipt_type,
188 ?receipt_thread,
189 ?user_id,
190 "Failed to get read receipt for user: {e}"
191 );
192 None
193 }
194 }
195 }
196
197 async fn load_event_receipts<'a>(
198 &'a self,
199 event_id: &'a EventId,
200 receipt_thread: &'a ReceiptThread,
201 ) -> IndexMap<OwnedUserId, Receipt> {
202 match self.load_event_receipts(ReceiptType::Read, receipt_thread, event_id).await {
203 Ok(receipts) => receipts.into_iter().collect(),
204 Err(e) => {
205 error!(?event_id, ?receipt_thread, "Failed to get read receipts for event: {e}");
206 IndexMap::new()
207 }
208 }
209 }
210
211 async fn load_fully_read_marker(&self) -> Option<OwnedEventId> {
212 match self.account_data_static::<FullyReadEventContent>().await {
213 Ok(Some(fully_read)) => match fully_read.deserialize() {
214 Ok(fully_read) => Some(fully_read.content.event_id),
215 Err(e) => {
216 error!("Failed to deserialize fully-read account data: {e}");
217 None
218 }
219 },
220 Err(e) => {
221 error!("Failed to get fully-read account data from the store: {e}");
222 None
223 }
224 _ => None,
225 }
226 }
227
228 async fn send(
229 &self,
230 content: AnyMessageLikeEventContent,
231 extra_content: Option<serde_json::Map<String, serde_json::Value>>,
232 ) -> Result<(), super::Error> {
233 let queue = self.send_queue();
234 let send = queue.send(content);
235 let send = match extra_content {
236 Some(extra_content) => send.with_extra_content(extra_content),
237 None => send,
238 };
239 let _ = send.await?;
240 Ok(())
241 }
242
243 async fn redact<'a>(
244 &'a self,
245 event_id: &'a EventId,
246 reason: Option<&'a str>,
247 transaction_id: Option<OwnedTransactionId>,
248 ) -> Result<(), super::Error> {
249 let _ = self
250 .redact(event_id, reason, transaction_id)
251 .await
252 .map_err(RedactError::HttpError)
253 .map_err(super::Error::RedactError)?;
254 Ok(())
255 }
256
257 fn room_info(&self) -> Subscriber<RoomInfo> {
258 self.subscribe_info()
259 }
260
261 async fn load_event<'a>(&'a self, event_id: &'a EventId) -> Result<TimelineEvent> {
262 self.load_or_fetch_event(event_id, None).await
263 }
264}