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, 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 own_user_id(&self) -> &UserId;
102 fn room_version_rules(&self) -> RoomVersionRules;
103
104 fn crypto_context_info(&self)
105 -> impl Future<Output = CryptoContextInfo> + SendOutsideWasm + '_;
106
107 fn profile_from_user_id<'a>(
108 &'a self,
109 user_id: &'a UserId,
110 ) -> impl Future<Output = Option<Profile>> + SendOutsideWasm + 'a;
111
112 fn load_user_receipt<'a>(
114 &'a self,
115 receipt_type: ReceiptType,
116 thread: ReceiptThread,
117 user_id: &'a UserId,
118 ) -> impl Future<Output = Option<(OwnedEventId, Receipt)>> + SendOutsideWasm + 'a;
119
120 fn load_event_receipts<'a>(
122 &'a self,
123 event_id: &'a EventId,
124 receipt_thread: ReceiptThread,
125 ) -> impl Future<Output = IndexMap<OwnedUserId, Receipt>> + SendOutsideWasm + 'a;
126
127 fn load_fully_read_marker(&self) -> impl Future<Output = Option<OwnedEventId>> + '_;
129
130 fn send(
132 &self,
133 content: AnyMessageLikeEventContent,
134 ) -> impl Future<Output = Result<(), super::Error>> + SendOutsideWasm + '_;
135
136 fn redact<'a>(
138 &'a self,
139 event_id: &'a EventId,
140 reason: Option<&'a str>,
141 transaction_id: Option<OwnedTransactionId>,
142 ) -> impl Future<Output = Result<(), super::Error>> + SendOutsideWasm + 'a;
143
144 fn room_info(&self) -> Subscriber<RoomInfo>;
145
146 fn load_event<'a>(
148 &'a self,
149 event_id: &'a EventId,
150 ) -> impl Future<Output = Result<TimelineEvent>> + SendOutsideWasm + 'a;
151}
152
153impl RoomDataProvider for Room {
154 fn own_user_id(&self) -> &UserId {
155 (**self).own_user_id()
156 }
157
158 fn room_version_rules(&self) -> RoomVersionRules {
159 (**self).clone_info().room_version_rules_or_default()
160 }
161
162 async fn crypto_context_info(&self) -> CryptoContextInfo {
163 self.crypto_context_info().await
164 }
165
166 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 }
180 }
181
182 async fn load_user_receipt<'a>(
183 &'a self,
184 receipt_type: ReceiptType,
185 thread: ReceiptThread,
186 user_id: &'a UserId,
187 ) -> Option<(OwnedEventId, Receipt)> {
188 match self.load_user_receipt(receipt_type.clone(), thread.clone(), user_id).await {
189 Ok(receipt) => receipt,
190 Err(e) => {
191 error!(
192 ?receipt_type,
193 ?thread,
194 ?user_id,
195 "Failed to get read receipt for user: {e}"
196 );
197 None
198 }
199 }
200 }
201
202 async fn load_event_receipts<'a>(
203 &'a self,
204 event_id: &'a EventId,
205 receipt_thread: ReceiptThread,
206 ) -> IndexMap<OwnedUserId, Receipt> {
207 match self.load_event_receipts(ReceiptType::Read, receipt_thread.clone(), event_id).await {
208 Ok(receipts) => receipts.into_iter().collect(),
209 Err(e) => {
210 error!(?event_id, ?receipt_thread, "Failed to get read receipts for event: {e}");
211 IndexMap::new()
212 }
213 }
214 }
215
216 async fn load_fully_read_marker(&self) -> Option<OwnedEventId> {
217 match self.account_data_static::<FullyReadEventContent>().await {
218 Ok(Some(fully_read)) => match fully_read.deserialize() {
219 Ok(fully_read) => Some(fully_read.content.event_id),
220 Err(e) => {
221 error!("Failed to deserialize fully-read account data: {e}");
222 None
223 }
224 },
225 Err(e) => {
226 error!("Failed to get fully-read account data from the store: {e}");
227 None
228 }
229 _ => None,
230 }
231 }
232
233 async fn send(&self, content: AnyMessageLikeEventContent) -> Result<(), super::Error> {
234 let _ = self.send_queue().send(content).await?;
235 Ok(())
236 }
237
238 async fn redact<'a>(
239 &'a self,
240 event_id: &'a EventId,
241 reason: Option<&'a str>,
242 transaction_id: Option<OwnedTransactionId>,
243 ) -> Result<(), super::Error> {
244 let _ = self
245 .redact(event_id, reason, transaction_id)
246 .await
247 .map_err(RedactError::HttpError)
248 .map_err(super::Error::RedactError)?;
249 Ok(())
250 }
251
252 fn room_info(&self) -> Subscriber<RoomInfo> {
253 self.subscribe_info()
254 }
255
256 async fn load_event<'a>(&'a self, event_id: &'a EventId) -> Result<TimelineEvent> {
257 self.load_or_fetch_event(event_id, None).await
258 }
259}