1#![allow(clippy::assign_op_pattern)] mod call;
18mod create;
19mod display_name;
20mod encryption;
21mod knock;
22mod latest_event;
23mod members;
24mod room_info;
25mod state;
26mod tags;
27mod tombstone;
28
29use std::{
30 collections::{BTreeMap, HashSet},
31 sync::Arc,
32};
33
34pub use create::*;
35pub use display_name::{RoomDisplayName, RoomHero};
36pub(crate) use display_name::{RoomSummary, UpdatedRoomDisplayName};
37pub use encryption::EncryptionState;
38use eyeball::{AsyncLock, SharedObservable};
39use futures_util::{Stream, StreamExt};
40pub use members::{RoomMember, RoomMembersUpdate, RoomMemberships};
41pub(crate) use room_info::SyncInfo;
42pub use room_info::{
43 BaseRoomInfo, InviteAcceptanceDetails, RoomInfo, RoomInfoNotableUpdate,
44 RoomInfoNotableUpdateReasons, RoomRecencyStamp, apply_redaction,
45};
46use ruma::{
47 EventId, OwnedEventId, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomId,
48 RoomVersionId, UserId,
49 events::{
50 direct::OwnedDirectUserIdentifier,
51 receipt::{Receipt, ReceiptThread, ReceiptType},
52 room::{
53 avatar,
54 guest_access::GuestAccess,
55 history_visibility::HistoryVisibility,
56 join_rules::JoinRule,
57 power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent, RoomPowerLevelsSource},
58 },
59 },
60 room::RoomType,
61};
62use serde::{Deserialize, Serialize};
63pub use state::{RoomState, RoomStateFilter};
64pub(crate) use tags::RoomNotableTags;
65use tokio::sync::broadcast;
66pub use tombstone::{PredecessorRoom, SuccessorRoom};
67use tracing::{info, instrument, warn};
68
69use crate::{
70 Error, MinimalStateEvent,
71 deserialized_responses::MemberEvent,
72 notification_settings::RoomNotificationMode,
73 read_receipts::RoomReadReceipts,
74 store::{DynStateStore, Result as StoreResult, StateStoreExt},
75 sync::UnreadNotificationsCount,
76};
77
78#[derive(Debug, Clone)]
81pub struct Room {
82 pub(super) room_id: OwnedRoomId,
84
85 pub(super) own_user_id: OwnedUserId,
87
88 pub(super) info: SharedObservable<RoomInfo>,
89 pub(super) room_info_notable_update_sender: broadcast::Sender<RoomInfoNotableUpdate>,
90 pub(super) store: Arc<DynStateStore>,
91
92 pub seen_knock_request_ids_map:
96 SharedObservable<Option<BTreeMap<OwnedEventId, OwnedUserId>>, AsyncLock>,
97
98 pub room_member_updates_sender: broadcast::Sender<RoomMembersUpdate>,
100}
101
102impl Room {
103 pub(crate) fn new(
104 own_user_id: &UserId,
105 store: Arc<DynStateStore>,
106 room_id: &RoomId,
107 room_state: RoomState,
108 room_info_notable_update_sender: broadcast::Sender<RoomInfoNotableUpdate>,
109 ) -> Self {
110 let room_info = RoomInfo::new(room_id, room_state);
111 Self::restore(own_user_id, store, room_info, room_info_notable_update_sender)
112 }
113
114 pub(crate) fn restore(
115 own_user_id: &UserId,
116 store: Arc<DynStateStore>,
117 room_info: RoomInfo,
118 room_info_notable_update_sender: broadcast::Sender<RoomInfoNotableUpdate>,
119 ) -> Self {
120 let (room_member_updates_sender, _) = broadcast::channel(10);
121 Self {
122 own_user_id: own_user_id.into(),
123 room_id: room_info.room_id.clone(),
124 store,
125 info: SharedObservable::new(room_info),
126 room_info_notable_update_sender,
127 seen_knock_request_ids_map: SharedObservable::new_async(None),
128 room_member_updates_sender,
129 }
130 }
131
132 pub fn room_id(&self) -> &RoomId {
134 &self.room_id
135 }
136
137 pub fn creators(&self) -> Option<Vec<OwnedUserId>> {
139 self.info.read().creators()
140 }
141
142 pub fn own_user_id(&self) -> &UserId {
144 &self.own_user_id
145 }
146
147 pub fn is_space(&self) -> bool {
149 self.info.read().room_type().is_some_and(|t| *t == RoomType::Space)
150 }
151
152 pub fn room_type(&self) -> Option<RoomType> {
155 self.info.read().room_type().map(ToOwned::to_owned)
156 }
157
158 pub fn unread_notification_counts(&self) -> UnreadNotificationsCount {
160 self.info.read().notification_counts
161 }
162
163 pub fn num_unread_messages(&self) -> u64 {
168 self.info.read().read_receipts.num_unread
169 }
170
171 pub fn read_receipts(&self) -> RoomReadReceipts {
173 self.info.read().read_receipts.clone()
174 }
175
176 pub fn num_unread_notifications(&self) -> u64 {
181 self.info.read().read_receipts.num_notifications
182 }
183
184 pub fn num_unread_mentions(&self) -> u64 {
190 self.info.read().read_receipts.num_mentions
191 }
192
193 pub fn is_state_fully_synced(&self) -> bool {
201 self.info.read().sync_info == SyncInfo::FullySynced
202 }
203
204 pub fn is_state_partially_or_fully_synced(&self) -> bool {
208 self.info.read().sync_info != SyncInfo::NoState
209 }
210
211 pub fn last_prev_batch(&self) -> Option<String> {
214 self.info.read().last_prev_batch.clone()
215 }
216
217 pub fn avatar_url(&self) -> Option<OwnedMxcUri> {
219 self.info.read().avatar_url().map(ToOwned::to_owned)
220 }
221
222 pub fn avatar_info(&self) -> Option<avatar::ImageInfo> {
224 self.info.read().avatar_info().map(ToOwned::to_owned)
225 }
226
227 pub fn canonical_alias(&self) -> Option<OwnedRoomAliasId> {
229 self.info.read().canonical_alias().map(ToOwned::to_owned)
230 }
231
232 pub fn alt_aliases(&self) -> Vec<OwnedRoomAliasId> {
234 self.info.read().alt_aliases().to_owned()
235 }
236
237 pub fn create_content(&self) -> Option<RoomCreateWithCreatorEventContent> {
247 match self.info.read().base_info.create.as_ref()? {
248 MinimalStateEvent::Original(ev) => Some(ev.content.clone()),
249 MinimalStateEvent::Redacted(ev) => Some(ev.content.clone()),
250 }
251 }
252
253 #[instrument(skip_all, fields(room_id = ?self.room_id))]
257 pub async fn is_direct(&self) -> StoreResult<bool> {
258 match self.state() {
259 RoomState::Joined | RoomState::Left | RoomState::Banned => {
260 Ok(!self.info.read().base_info.dm_targets.is_empty())
261 }
262
263 RoomState::Invited => {
264 let member = self.get_member(self.own_user_id()).await?;
265
266 match member {
267 None => {
268 info!("RoomMember not found for the user's own id");
269 Ok(false)
270 }
271 Some(member) => match member.event.as_ref() {
272 MemberEvent::Sync(_) => {
273 warn!("Got MemberEvent::Sync in an invited room");
274 Ok(false)
275 }
276 MemberEvent::Stripped(event) => {
277 Ok(event.content.is_direct.unwrap_or(false))
278 }
279 },
280 }
281 }
282
283 RoomState::Knocked => Ok(false),
285 }
286 }
287
288 pub fn direct_targets(&self) -> HashSet<OwnedDirectUserIdentifier> {
297 self.info.read().base_info.dm_targets.clone()
298 }
299
300 pub fn direct_targets_length(&self) -> usize {
303 self.info.read().base_info.dm_targets.len()
304 }
305
306 pub fn guest_access(&self) -> GuestAccess {
308 self.info.read().guest_access().clone()
309 }
310
311 pub fn history_visibility(&self) -> Option<HistoryVisibility> {
313 self.info.read().history_visibility().cloned()
314 }
315
316 pub fn history_visibility_or_default(&self) -> HistoryVisibility {
319 self.info.read().history_visibility_or_default().clone()
320 }
321
322 pub fn is_public(&self) -> Option<bool> {
326 self.info.read().join_rule().map(|join_rule| matches!(join_rule, JoinRule::Public))
327 }
328
329 pub fn join_rule(&self) -> Option<JoinRule> {
331 self.info.read().join_rule().cloned()
332 }
333
334 pub fn max_power_level(&self) -> i64 {
339 self.info.read().base_info.max_power_level
340 }
341
342 pub async fn power_levels(&self) -> Result<RoomPowerLevels, Error> {
344 let power_levels_content = self
345 .store
346 .get_state_event_static::<RoomPowerLevelsEventContent>(self.room_id())
347 .await?
348 .ok_or(Error::InsufficientData)?
349 .deserialize()?;
350 let creators = self.creators().ok_or(Error::InsufficientData)?;
351 let rules = self.info.read().room_version_rules_or_default();
352
353 Ok(power_levels_content.power_levels(&rules.authorization, creators))
354 }
355
356 pub async fn power_levels_or_default(&self) -> RoomPowerLevels {
359 if let Ok(power_levels) = self.power_levels().await {
360 return power_levels;
361 }
362
363 let rules = self.info.read().room_version_rules_or_default();
365 RoomPowerLevels::new(
366 RoomPowerLevelsSource::None,
367 &rules.authorization,
368 self.creators().into_iter().flatten(),
369 )
370 }
371
372 pub fn name(&self) -> Option<String> {
377 self.info.read().name().map(ToOwned::to_owned)
378 }
379
380 pub fn topic(&self) -> Option<String> {
382 self.info.read().topic().map(ToOwned::to_owned)
383 }
384
385 pub fn update_cached_user_defined_notification_mode(&self, mode: RoomNotificationMode) {
391 self.info.update_if(|info| {
392 if info.cached_user_defined_notification_mode.as_ref() != Some(&mode) {
393 info.cached_user_defined_notification_mode = Some(mode);
394
395 true
396 } else {
397 false
398 }
399 });
400 }
401
402 pub fn cached_user_defined_notification_mode(&self) -> Option<RoomNotificationMode> {
407 self.info.read().cached_user_defined_notification_mode
408 }
409
410 pub fn clear_user_defined_notification_mode(&self) {
413 self.info.update_if(|info| {
414 if info.cached_user_defined_notification_mode.is_some() {
415 info.cached_user_defined_notification_mode = None;
416 true
417 } else {
418 false
419 }
420 })
421 }
422
423 pub async fn joined_user_ids(&self) -> StoreResult<Vec<OwnedUserId>> {
426 self.store.get_user_ids(self.room_id(), RoomMemberships::JOIN).await
427 }
428
429 pub fn heroes(&self) -> Vec<RoomHero> {
431 self.info.read().heroes().to_vec()
432 }
433
434 pub async fn load_user_receipt(
437 &self,
438 receipt_type: ReceiptType,
439 thread: ReceiptThread,
440 user_id: &UserId,
441 ) -> StoreResult<Option<(OwnedEventId, Receipt)>> {
442 self.store.get_user_room_receipt_event(self.room_id(), receipt_type, thread, user_id).await
443 }
444
445 pub async fn load_event_receipts(
449 &self,
450 receipt_type: ReceiptType,
451 thread: ReceiptThread,
452 event_id: &EventId,
453 ) -> StoreResult<Vec<(OwnedUserId, Receipt)>> {
454 self.store
455 .get_event_room_receipt_events(self.room_id(), receipt_type, thread, event_id)
456 .await
457 }
458
459 pub fn is_marked_unread(&self) -> bool {
462 self.info.read().base_info.is_marked_unread
463 }
464
465 pub fn version(&self) -> Option<RoomVersionId> {
467 self.info.read().room_version().cloned()
468 }
469
470 pub fn recency_stamp(&self) -> Option<RoomRecencyStamp> {
474 self.info.read().recency_stamp
475 }
476
477 pub fn invite_acceptance_details(&self) -> Option<InviteAcceptanceDetails> {
485 self.info.read().invite_acceptance_details.clone()
486 }
487
488 pub fn pinned_event_ids_stream(&self) -> impl Stream<Item = Vec<OwnedEventId>> + use<> {
491 self.info
492 .subscribe()
493 .map(|i| i.base_info.pinned_events.map(|c| c.pinned).unwrap_or_default())
494 }
495
496 pub fn pinned_event_ids(&self) -> Option<Vec<OwnedEventId>> {
498 self.info.read().pinned_event_ids()
499 }
500}
501
502#[cfg(not(feature = "test-send-sync"))]
504unsafe impl Send for Room {}
505
506#[cfg(not(feature = "test-send-sync"))]
508unsafe impl Sync for Room {}
509
510#[cfg(feature = "test-send-sync")]
511#[test]
512fn test_send_sync_for_room() {
514 fn assert_send_sync<
515 T: matrix_sdk_common::SendOutsideWasm + matrix_sdk_common::SyncOutsideWasm,
516 >() {
517 }
518
519 assert_send_sync::<Room>();
520}
521
522#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
524pub(crate) enum AccountDataSource {
525 Stable,
527
528 #[default]
530 Unstable,
531}