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, BTreeSet, HashSet},
31 sync::Arc,
32};
33
34pub use call::CallIntentConsensus;
35pub use create::*;
36pub use display_name::{RoomDisplayName, RoomHero};
37pub(crate) use display_name::{RoomSummary, UpdatedRoomDisplayName};
38pub use encryption::EncryptionState;
39use eyeball::{AsyncLock, SharedObservable};
40use futures_util::{Stream, StreamExt};
41pub use members::{RoomMember, RoomMembersUpdate, RoomMemberships};
42pub(crate) use room_info::SyncInfo;
43pub use room_info::{
44 BaseRoomInfo, RoomInfo, RoomInfoNotableUpdate, RoomInfoNotableUpdateReasons, RoomRecencyStamp,
45 apply_redaction,
46};
47use ruma::{
48 EventId, OwnedEventId, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomId,
49 RoomVersionId, UserId,
50 events::{
51 direct::OwnedDirectUserIdentifier,
52 receipt::{Receipt, ReceiptThread, ReceiptType},
53 room::{
54 avatar,
55 guest_access::GuestAccess,
56 history_visibility::HistoryVisibility,
57 join_rules::JoinRule,
58 power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent, RoomPowerLevelsSource},
59 },
60 },
61 room::RoomType,
62};
63use serde::{Deserialize, Serialize};
64pub use state::{RoomState, RoomStateFilter};
65pub(crate) use tags::RoomNotableTags;
66use tokio::sync::broadcast;
67pub use tombstone::{PredecessorRoom, SuccessorRoom};
68use tracing::{info, instrument, warn};
69
70use crate::{
71 Error,
72 deserialized_responses::MemberEvent,
73 notification_settings::RoomNotificationMode,
74 read_receipts::RoomReadReceipts,
75 store::{DynStateStore, Result as StoreResult, StateStoreExt},
76 sync::UnreadNotificationsCount,
77};
78
79#[derive(Debug, Clone)]
82pub struct Room {
83 pub(super) room_id: OwnedRoomId,
85
86 pub(super) own_user_id: OwnedUserId,
88
89 pub(super) info: SharedObservable<RoomInfo>,
90
91 pub(super) room_info_notable_update_sender: broadcast::Sender<RoomInfoNotableUpdate>,
95
96 pub(super) store: Arc<DynStateStore>,
98
99 pub seen_knock_request_ids_map:
103 SharedObservable<Option<BTreeMap<OwnedEventId, OwnedUserId>>, AsyncLock>,
104
105 pub room_member_updates_sender: broadcast::Sender<RoomMembersUpdate>,
107}
108
109impl Room {
110 pub(crate) fn new(
111 own_user_id: &UserId,
112 store: Arc<DynStateStore>,
113 room_id: &RoomId,
114 room_state: RoomState,
115 room_info_notable_update_sender: broadcast::Sender<RoomInfoNotableUpdate>,
116 ) -> Self {
117 let room_info = RoomInfo::new(room_id, room_state);
118 Self::restore(own_user_id, store, room_info, room_info_notable_update_sender)
119 }
120
121 pub(crate) fn restore(
122 own_user_id: &UserId,
123 store: Arc<DynStateStore>,
124 room_info: RoomInfo,
125 room_info_notable_update_sender: broadcast::Sender<RoomInfoNotableUpdate>,
126 ) -> Self {
127 let (room_member_updates_sender, _) = broadcast::channel(10);
128 Self {
129 own_user_id: own_user_id.into(),
130 room_id: room_info.room_id.clone(),
131 store,
132 info: SharedObservable::new(room_info),
133 room_info_notable_update_sender,
134 seen_knock_request_ids_map: SharedObservable::new_async(None),
135 room_member_updates_sender,
136 }
137 }
138
139 pub fn room_id(&self) -> &RoomId {
141 &self.room_id
142 }
143
144 pub fn creators(&self) -> Option<Vec<OwnedUserId>> {
146 self.info.read().creators()
147 }
148
149 pub fn own_user_id(&self) -> &UserId {
151 &self.own_user_id
152 }
153
154 pub fn is_space(&self) -> bool {
156 self.info.read().room_type().is_some_and(|t| *t == RoomType::Space)
157 }
158
159 pub fn is_call(&self) -> bool {
163 self.info.read().room_type().is_some_and(|t| *t == RoomType::Call)
164 }
165
166 pub fn room_type(&self) -> Option<RoomType> {
169 self.info.read().room_type().map(ToOwned::to_owned)
170 }
171
172 pub fn unread_notification_counts(&self) -> UnreadNotificationsCount {
181 self.info.read().notification_counts
182 }
183
184 pub fn num_unread_messages(&self) -> u64 {
189 self.info.read().read_receipts.num_unread
190 }
191
192 pub fn num_unread_notifications(&self) -> u64 {
197 self.info.read().read_receipts.num_notifications
198 }
199
200 pub fn num_unread_mentions(&self) -> u64 {
206 self.info.read().read_receipts.num_mentions
207 }
208
209 pub fn read_receipts(&self) -> RoomReadReceipts {
211 self.info.read().read_receipts.clone()
212 }
213
214 pub fn is_state_fully_synced(&self) -> bool {
222 self.info.read().sync_info == SyncInfo::FullySynced
223 }
224
225 pub fn is_state_partially_or_fully_synced(&self) -> bool {
229 self.info.read().sync_info != SyncInfo::NoState
230 }
231
232 pub fn last_prev_batch(&self) -> Option<String> {
235 self.info.read().last_prev_batch.clone()
236 }
237
238 pub fn avatar_url(&self) -> Option<OwnedMxcUri> {
240 self.info.read().avatar_url().map(ToOwned::to_owned)
241 }
242
243 pub fn avatar_info(&self) -> Option<avatar::ImageInfo> {
245 self.info.read().avatar_info().map(ToOwned::to_owned)
246 }
247
248 pub fn canonical_alias(&self) -> Option<OwnedRoomAliasId> {
250 self.info.read().canonical_alias().map(ToOwned::to_owned)
251 }
252
253 pub fn alt_aliases(&self) -> Vec<OwnedRoomAliasId> {
255 self.info.read().alt_aliases().to_owned()
256 }
257
258 pub fn create_content(&self) -> Option<RoomCreateWithCreatorEventContent> {
268 Some(self.info.read().base_info.create.as_ref()?.content.clone())
269 }
270
271 #[instrument(skip_all, fields(room_id = ?self.room_id))]
275 pub async fn is_direct(&self) -> StoreResult<bool> {
276 match self.state() {
277 RoomState::Joined | RoomState::Left | RoomState::Banned => {
278 Ok(!self.info.read().base_info.dm_targets.is_empty())
279 }
280
281 RoomState::Invited => {
282 let member = self.get_member(self.own_user_id()).await?;
283
284 match member {
285 None => {
286 info!("RoomMember not found for the user's own id");
287 Ok(false)
288 }
289 Some(member) => match member.event.as_ref() {
290 MemberEvent::Sync(_) => {
291 warn!("Got MemberEvent::Sync in an invited room");
292 Ok(false)
293 }
294 MemberEvent::Stripped(event) => {
295 Ok(event.content.is_direct.unwrap_or(false))
296 }
297 },
298 }
299 }
300
301 RoomState::Knocked => Ok(false),
303 }
304 }
305
306 pub fn direct_targets(&self) -> HashSet<OwnedDirectUserIdentifier> {
315 self.info.read().base_info.dm_targets.clone()
316 }
317
318 pub fn direct_targets_length(&self) -> usize {
321 self.info.read().base_info.dm_targets.len()
322 }
323
324 pub fn guest_access(&self) -> GuestAccess {
326 self.info.read().guest_access().clone()
327 }
328
329 pub fn history_visibility(&self) -> Option<HistoryVisibility> {
331 self.info.read().history_visibility().cloned()
332 }
333
334 pub fn history_visibility_or_default(&self) -> HistoryVisibility {
337 self.info.read().history_visibility_or_default().clone()
338 }
339
340 pub fn is_public(&self) -> Option<bool> {
344 self.info.read().join_rule().map(|join_rule| matches!(join_rule, JoinRule::Public))
345 }
346
347 pub fn join_rule(&self) -> Option<JoinRule> {
349 self.info.read().join_rule().cloned()
350 }
351
352 pub fn max_power_level(&self) -> i64 {
357 self.info.read().base_info.max_power_level
358 }
359
360 pub fn service_members(&self) -> Option<BTreeSet<OwnedUserId>> {
362 self.info.read().service_members().cloned()
363 }
364
365 pub async fn power_levels(&self) -> Result<RoomPowerLevels, Error> {
367 let power_levels_content = self
368 .store
369 .get_state_event_static::<RoomPowerLevelsEventContent>(self.room_id())
370 .await?
371 .ok_or(Error::InsufficientData)?
372 .deserialize()?;
373 let creators = self.creators().ok_or(Error::InsufficientData)?;
374 let rules = self.info.read().room_version_rules_or_default();
375
376 Ok(power_levels_content.power_levels(&rules.authorization, creators))
377 }
378
379 pub async fn power_levels_or_default(&self) -> RoomPowerLevels {
382 if let Ok(power_levels) = self.power_levels().await {
383 return power_levels;
384 }
385
386 let rules = self.info.read().room_version_rules_or_default();
388 RoomPowerLevels::new(
389 RoomPowerLevelsSource::None,
390 &rules.authorization,
391 self.creators().into_iter().flatten(),
392 )
393 }
394
395 pub fn name(&self) -> Option<String> {
400 self.info.read().name().map(ToOwned::to_owned)
401 }
402
403 pub fn topic(&self) -> Option<String> {
405 self.info.read().topic().map(ToOwned::to_owned)
406 }
407
408 pub fn update_cached_user_defined_notification_mode(&self, mode: RoomNotificationMode) {
414 self.info.update_if(|info| {
415 if info.cached_user_defined_notification_mode.as_ref() != Some(&mode) {
416 info.cached_user_defined_notification_mode = Some(mode);
417
418 true
419 } else {
420 false
421 }
422 });
423 }
424
425 pub fn cached_user_defined_notification_mode(&self) -> Option<RoomNotificationMode> {
430 self.info.read().cached_user_defined_notification_mode
431 }
432
433 pub fn clear_user_defined_notification_mode(&self) {
436 self.info.update_if(|info| {
437 if info.cached_user_defined_notification_mode.is_some() {
438 info.cached_user_defined_notification_mode = None;
439 true
440 } else {
441 false
442 }
443 })
444 }
445
446 pub async fn joined_user_ids(&self) -> StoreResult<Vec<OwnedUserId>> {
449 self.store.get_user_ids(self.room_id(), RoomMemberships::JOIN).await
450 }
451
452 pub fn heroes(&self) -> Vec<RoomHero> {
454 self.info.read().heroes().to_vec()
455 }
456
457 pub async fn load_user_receipt(
460 &self,
461 receipt_type: ReceiptType,
462 thread: ReceiptThread,
463 user_id: &UserId,
464 ) -> StoreResult<Option<(OwnedEventId, Receipt)>> {
465 self.store.get_user_room_receipt_event(self.room_id(), receipt_type, thread, user_id).await
466 }
467
468 pub async fn load_event_receipts(
472 &self,
473 receipt_type: ReceiptType,
474 thread: ReceiptThread,
475 event_id: &EventId,
476 ) -> StoreResult<Vec<(OwnedUserId, Receipt)>> {
477 self.store
478 .get_event_room_receipt_events(self.room_id(), receipt_type, thread, event_id)
479 .await
480 }
481
482 pub fn is_marked_unread(&self) -> bool {
485 self.info.read().base_info.is_marked_unread
486 }
487
488 pub fn version(&self) -> Option<RoomVersionId> {
490 self.info.read().room_version().cloned()
491 }
492
493 pub fn recency_stamp(&self) -> Option<RoomRecencyStamp> {
497 self.info.read().recency_stamp
498 }
499
500 pub fn pinned_event_ids_stream(&self) -> impl Stream<Item = Vec<OwnedEventId>> + use<> {
503 self.info
504 .subscribe()
505 .map(|i| i.base_info.pinned_events.and_then(|c| c.pinned).unwrap_or_default())
506 }
507
508 pub fn pinned_event_ids(&self) -> Option<Vec<OwnedEventId>> {
510 self.info.read().pinned_event_ids()
511 }
512}
513
514#[cfg(not(feature = "test-send-sync"))]
516unsafe impl Send for Room {}
517
518#[cfg(not(feature = "test-send-sync"))]
520unsafe impl Sync for Room {}
521
522#[cfg(feature = "test-send-sync")]
523#[test]
524fn test_send_sync_for_room() {
526 fn assert_send_sync<
527 T: matrix_sdk_common::SendOutsideWasm + matrix_sdk_common::SyncOutsideWasm,
528 >() {
529 }
530
531 assert_send_sync::<Room>();
532}
533
534#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
536pub(crate) enum AccountDataSource {
537 Stable,
539
540 #[default]
542 Unstable,
543}