Skip to content

Commit

Permalink
sync: Refactor rawroominfo to different struct
Browse files Browse the repository at this point in the history
  • Loading branch information
timokoesters committed Mar 4, 2024
1 parent 3498433 commit 7de60a1
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 381 deletions.
144 changes: 128 additions & 16 deletions crates/matrix-sdk-base/src/rooms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ use std::{

use bitflags::bitflags;
pub use members::RoomMember;
pub use normal::{Room, RoomInfo, RoomInfoUpdate, RoomState, RoomStateFilter};
pub use normal::{RawRoomInfo, Room, RoomInfo, RoomInfoUpdate, RoomState, RoomStateFilter};
use ruma::{
assign,
canonical_json::redact_in_place,
events::{
call::member::CallMemberEventContent,
call::member::{CallMemberEvent, CallMemberEventContent},
macros::EventContent,
room::{
avatar::RoomAvatarEventContent,
Expand All @@ -32,7 +33,7 @@ use ruma::{
},
tag::{TagName, Tags},
AnyStrippedStateEvent, AnySyncStateEvent, EmptyStateKey, RedactContent,
RedactedStateEventContent, StaticStateEventContent, SyncStateEvent,
RedactedStateEventContent, StateEventType, StaticStateEventContent, SyncStateEvent,
},
room::RoomType,
serde::Raw,
Expand Down Expand Up @@ -78,44 +79,33 @@ impl fmt::Display for DisplayName {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BaseRoomInfo {
/// The avatar URL of this room.
#[serde(skip_serializing)]
pub(crate) avatar: Option<MinimalStateEvent<RoomAvatarEventContent>>,
/// The canonical alias of this room.
#[serde(skip_serializing)]
pub(crate) canonical_alias: Option<MinimalStateEvent<RoomCanonicalAliasEventContent>>,
/// The `m.room.create` event content of this room.
#[serde(skip_serializing)]
pub(crate) create: Option<MinimalStateEvent<RoomCreateWithCreatorEventContent>>,
/// A list of user ids this room is considered as direct message, if this
/// room is a DM.
pub(crate) dm_targets: HashSet<OwnedUserId>,
/// The `m.room.encryption` event content that enabled E2EE in this room.
#[serde(skip_serializing)]
pub(crate) encryption: Option<RoomEncryptionEventContent>,
/// The guest access policy of this room.
#[serde(skip_serializing)]
pub(crate) guest_access: Option<MinimalStateEvent<RoomGuestAccessEventContent>>,
/// The history visibility policy of this room.
#[serde(skip_serializing)]
pub(crate) history_visibility: Option<MinimalStateEvent<RoomHistoryVisibilityEventContent>>,
/// The join rule policy of this room.
#[serde(skip_serializing)]
pub(crate) join_rules: Option<MinimalStateEvent<RoomJoinRulesEventContent>>,
/// The maximal power level that can be found in this room.
pub(crate) max_power_level: i64,
/// The `m.room.name` of this room.
#[serde(skip_serializing)]
pub(crate) name: Option<MinimalStateEvent<RoomNameEventContent>>,
/// The `m.room.tombstone` event content of this room.
#[serde(skip_serializing)]
pub(crate) tombstone: Option<MinimalStateEvent<RoomTombstoneEventContent>>,
/// The topic of this room.
#[serde(skip_serializing)]
pub(crate) topic: Option<MinimalStateEvent<RoomTopicEventContent>>,
/// All minimal state events that containing one or more running matrixRTC
/// memberships.
// #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
#[serde(skip_serializing, default)]
#[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
pub(crate) rtc_member: BTreeMap<OwnedUserId, MinimalStateEvent<CallMemberEventContent>>,
/// Whether this room has been manually marked as unread.
#[serde(default)]
Expand All @@ -128,14 +118,18 @@ pub struct BaseRoomInfo {
pub(crate) notable_tags: RoomNotableTags,
}

/// This contains the same fields as BaseRoomInfo, but events are in the raw, unparsed form.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct RawRoomInfo {
pub struct RawBaseRoomInfo {
/// The avatar URL of this room.
pub avatar: Option<Raw<AnySyncStateEvent>>,
/// The canonical alias of this room.
pub canonical_alias: Option<Raw<AnySyncStateEvent>>,
/// The `m.room.create` event content of this room.
pub create: Option<Raw<AnySyncStateEvent>>,
/// A list of user ids this room is considered as direct message, if this
/// room is a DM.
pub dm_targets: HashSet<OwnedUserId>,
/// The `m.room.encryption` event content that enabled E2EE in this room.
pub encryption: Option<Raw<AnySyncStateEvent>>,
/// The guest access policy of this room.
Expand All @@ -144,6 +138,8 @@ pub struct RawRoomInfo {
pub history_visibility: Option<Raw<AnySyncStateEvent>>,
/// The join rule policy of this room.
pub join_rules: Option<Raw<AnySyncStateEvent>>,
/// The maximal power level that can be found in this room.
pub max_power_level: i64,
/// The `m.room.name` of this room.
pub name: Option<Raw<AnySyncStateEvent>>,
/// The `m.room.tombstone` event content of this room.
Expand All @@ -154,6 +150,95 @@ pub struct RawRoomInfo {
/// memberships.
#[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
pub rtc_member: BTreeMap<OwnedUserId, Raw<AnySyncStateEvent>>,
/// Whether this room has been manually marked as unread.
#[serde(default)]
pub is_marked_unread: bool,
/// Some notable tags.
///
/// We are not interested by all the tags. Some tags are more important than
/// others, and this field collects them.
#[serde(skip_serializing_if = "RoomNotableTags::is_empty", default)]
pub notable_tags: RoomNotableTags,
}

impl RawBaseRoomInfo {
pub fn map_events(
&mut self,
f: &dyn Fn(&mut Option<Raw<AnySyncStateEvent>>, StateEventType, &str),
f_rtc_member: &dyn Fn(&mut BTreeMap<OwnedUserId, Raw<AnySyncStateEvent>>),
) {
f(&mut self.avatar, StateEventType::RoomAvatar, "");
f(&mut self.canonical_alias, StateEventType::RoomCanonicalAlias, "");
f(&mut self.create, StateEventType::RoomCreate, "");
f(&mut self.encryption, StateEventType::RoomEncryption, "");
f(&mut self.guest_access, StateEventType::RoomGuestAccess, "");
f(&mut self.history_visibility, StateEventType::RoomHistoryVisibility, "");
f(&mut self.join_rules, StateEventType::RoomJoinRules, "");
f(&mut self.name, StateEventType::RoomName, "");
f(&mut self.tombstone, StateEventType::RoomTombstone, "");
f(&mut self.topic, StateEventType::RoomTopic, "");
f_rtc_member(&mut self.rtc_member);
}

pub fn handle_redaction(&mut self, event_id: &EventId, room_version: &RoomVersionId) {
self.map_events(
// Clean up normal events
&|event, _event_type, _state_key| {
if event
.as_ref()
.and_then(|a| a.deserialize().ok())
.is_some_and(|e| e.event_id() == event_id)
{
if let Ok(mut object) = event.as_ref().unwrap().deserialize_as() {
if redact_in_place(&mut object, room_version, None).is_ok() {
*event = Some(Raw::new(&object).unwrap().cast());
}
}
}
},
// Clean up rtc_members
&|map| {
for event in map.values_mut() {
if event.deserialize().is_ok_and(|e| e.event_id() == event_id) {
if let Ok(mut object) = event.deserialize_as() {
if redact_in_place(&mut object, room_version, None).is_ok() {
*event = Raw::new(&object).unwrap().cast();
}
}
}
}
},
);
}
pub fn extend_with_changes(
&mut self,
changes: &BTreeMap<StateEventType, BTreeMap<String, Raw<AnySyncStateEvent>>>,
) {
self.map_events(
&|event, event_type, state_key| {
if let Some(e) = changes.get(&event_type).and_then(|c| c.get(state_key)) {
*event = Some(e.clone());
}
},
&|map| {
if let Some(rtc_changes) = changes.get(&StateEventType::CallMember) {
map.extend(
rtc_changes
.clone()
.into_iter()
.filter_map(|(u, r)| OwnedUserId::try_from(u).ok().map(|u| (u, r))),
);
// Remove all events that don't contain any memberships anymore.
map.retain(|_, ev| {
ev.deserialize_as::<CallMemberEvent>().is_ok_and(|c| {
c.as_original()
.is_some_and(|o| !o.content.active_memberships(None).is_empty())
})
});
}
},
);
}
}

impl BaseRoomInfo {
Expand All @@ -162,6 +247,33 @@ impl BaseRoomInfo {
Self::default()
}

pub fn into_raw_lossy(self) -> RawBaseRoomInfo {
RawBaseRoomInfo {
avatar: self.avatar.and_then(|e| Raw::new(&e).ok()).map(|r| r.cast()),
canonical_alias: self.canonical_alias.and_then(|e| Raw::new(&e).ok()).map(|r| r.cast()),
create: self.create.and_then(|e| Raw::new(&e).ok()).map(|r| r.cast()),
dm_targets: self.dm_targets,
encryption: self.encryption.and_then(|e| Raw::new(&e).ok()).map(|r| r.cast()),
guest_access: self.guest_access.and_then(|e| Raw::new(&e).ok()).map(|r| r.cast()),
history_visibility: self
.history_visibility
.and_then(|e| Raw::new(&e).ok())
.map(|r| r.cast()),
join_rules: self.join_rules.and_then(|e| Raw::new(&e).ok()).map(|r| r.cast()),
max_power_level: self.max_power_level,
name: self.name.and_then(|e| Raw::new(&e).ok()).map(|r| r.cast()),
tombstone: self.tombstone.and_then(|e| Raw::new(&e).ok()).map(|r| r.cast()),
topic: self.topic.and_then(|e| Raw::new(&e).ok()).map(|r| r.cast()),
rtc_member: self
.rtc_member
.into_iter()
.filter_map(|(u, e)| Raw::new(&e).ok().map(|r| (u, r.cast())))
.collect(),
is_marked_unread: self.is_marked_unread,
notable_tags: self.notable_tags,
}
}

pub(crate) fn calculate_room_name(
&self,
joined_member_count: u64,
Expand Down
110 changes: 68 additions & 42 deletions crates/matrix-sdk-base/src/rooms/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ use tracing::{debug, field::debug, info, instrument, trace, warn};

use super::{
members::{MemberInfo, MemberRoomInfo},
BaseRoomInfo, DisplayName, RoomCreateWithCreatorEventContent, RoomMember, RoomNotableTags,
BaseRoomInfo, DisplayName, RawBaseRoomInfo, RoomCreateWithCreatorEventContent, RoomMember,
RoomNotableTags,
};
#[cfg(feature = "experimental-sliding-sync")]
use crate::latest_event::LatestEvent;
Expand All @@ -68,7 +69,7 @@ use crate::{
read_receipts::RoomReadReceipts,
store::{DynStateStore, Result as StoreResult, StateStoreExt},
sync::UnreadNotificationsCount,
MinimalStateEvent, OriginalMinimalStateEvent, RawRoomInfo, RoomMemberships,
MinimalStateEvent, OriginalMinimalStateEvent, RoomMemberships,
};

/// A summary of changes to room information.
Expand Down Expand Up @@ -808,7 +809,7 @@ impl Room {
/// The underlying pure data structure for joined and left rooms.
///
/// Holds all the info needed to persist a room into the state store.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Deserialize)]
pub struct RoomInfo {
/// The unique room id of the room.
pub(crate) room_id: OwnedRoomId,
Expand Down Expand Up @@ -850,8 +851,51 @@ pub struct RoomInfo {
pub(crate) base_info: Box<BaseRoomInfo>,
}

/// This is the same as RoomInfo, except it contains RawBaseRoomInfo and implements Serialize.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RawRoomInfo {
/// The unique room id of the room.
pub room_id: OwnedRoomId,

/// The state of the room.
pub room_state: RoomState,

/// The unread notifications counts, as returned by the server.
///
/// These might be incorrect for encrypted rooms, since the server doesn't
/// have access to the content of the encrypted events.
pub notification_counts: UnreadNotificationsCount,

/// The summary of this room.
pub summary: RoomSummary,

/// Flag remembering if the room members are synced.
pub members_synced: bool,

/// The prev batch of this room we received during the last sync.
pub last_prev_batch: Option<String>,

/// How much we know about this room.
pub sync_info: SyncInfo,

/// Whether or not the encryption info was been synced.
pub encryption_state_synced: bool,

/// The last event send by sliding sync
#[cfg(feature = "experimental-sliding-sync")]
pub latest_event: Option<Box<LatestEvent>>,

/// Information about read receipts for this room.
#[serde(default)]
pub read_receipts: RoomReadReceipts,

/// Base room info which holds some basic event contents important for the
/// room state.
pub base_info: Box<RawBaseRoomInfo>,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub(crate) enum SyncInfo {
pub enum SyncInfo {
/// We only know the room exists and whether it is in invite / joined / left
/// state.
///
Expand Down Expand Up @@ -886,41 +930,19 @@ impl RoomInfo {
}
}

pub fn replace_by_raw(&mut self, raws: RawRoomInfo) {
if let Some(event) = raws.avatar.and_then(|e| e.deserialize_as().ok()) {
self.base_info.avatar = Some(event);
}
if let Some(event) = raws.canonical_alias.and_then(|e| e.deserialize_as().ok()) {
self.base_info.canonical_alias = Some(event);
}
if let Some(event) = raws.create.and_then(|e| e.deserialize_as().ok()) {
self.base_info.create = Some(event);
}
if let Some(event) = raws.encryption.and_then(|e| e.deserialize_as().ok()) {
self.base_info.encryption = Some(event);
}
if let Some(event) = raws.guest_access.and_then(|e| e.deserialize_as().ok()) {
self.base_info.guest_access = Some(event);
}
if let Some(event) = raws.history_visibility.and_then(|e| e.deserialize_as().ok()) {
self.base_info.history_visibility = Some(event);
}
if let Some(event) = raws.join_rules.and_then(|e| e.deserialize_as().ok()) {
self.base_info.join_rules = Some(event);
}
if let Some(event) = raws.name.and_then(|e| e.deserialize_as().ok()) {
self.base_info.name = Some(event);
}
if let Some(event) = raws.tombstone.and_then(|e| e.deserialize_as().ok()) {
self.base_info.tombstone = Some(event);
}
if let Some(event) = raws.topic.and_then(|e| e.deserialize_as().ok()) {
self.base_info.topic = Some(event);
}
for (user, event) in raws.rtc_member {
if let Ok(event) = event.deserialize_as() {
self.base_info.rtc_member.insert(user, event);
}
pub fn into_raw_lossy(self) -> RawRoomInfo {
RawRoomInfo {
room_id: self.room_id,
room_state: self.room_state,
notification_counts: self.notification_counts,
summary: self.summary,
members_synced: self.members_synced,
last_prev_batch: self.last_prev_batch,
sync_info: self.sync_info,
encryption_state_synced: self.encryption_state_synced,
latest_event: self.latest_event,
read_receipts: self.read_receipts,
base_info: Box::new(self.base_info.into_raw_lossy()),
}
}

Expand Down Expand Up @@ -1396,9 +1418,13 @@ mod tests {
// serialized format for `RoomInfo`.

use super::RoomSummary;
use crate::{rooms::BaseRoomInfo, sync::UnreadNotificationsCount};
use crate::{
rooms::{BaseRoomInfo, RawBaseRoomInfo},
sync::UnreadNotificationsCount,
RawRoomInfo,
};

let info = RoomInfo {
let info = RawRoomInfo {
room_id: room_id!("!gda78o:server.tld").into(),
room_state: RoomState::Invited,
notification_counts: UnreadNotificationsCount {
Expand All @@ -1417,7 +1443,7 @@ mod tests {
latest_event: Some(Box::new(LatestEvent::new(
Raw::from_json_string(json!({"sender": "@u:i.uk"}).to_string()).unwrap().into(),
))),
base_info: Box::new(BaseRoomInfo::new()),
base_info: Box::new(RawBaseRoomInfo::default()),
read_receipts: Default::default(),
};

Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk-indexeddb/src/state_store/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ async fn migrate_to_v8(db: IdbDatabase, store_cipher: Option<&StoreCipher>) -> R
let room_info = room_info_v1.migrate(create.as_ref());
room_infos_store.put_key_val(
&encode_key(store_cipher, keys::ROOM_INFOS, room_info.room_id()),
&serialize_event(store_cipher, &room_info)?,
&serialize_event(store_cipher, &room_info.into_raw_lossy())?,
)?;
}

Expand Down
Loading

0 comments on commit 7de60a1

Please sign in to comment.