diff --git a/crates/matrix-sdk-base/src/client.rs b/crates/matrix-sdk-base/src/client.rs index 24d47e98a..9008bd7fb 100644 --- a/crates/matrix-sdk-base/src/client.rs +++ b/crates/matrix-sdk-base/src/client.rs @@ -56,16 +56,16 @@ use ruma::{ api::client::{self as api, push::get_notifications::v3::Notification}, events::{ push_rules::PushRulesEvent, - room::member::{MembershipState, OriginalSyncRoomMemberEvent, RoomMemberEvent}, + room::member::{MembershipState, SyncRoomMemberEvent}, AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, AnyStrippedStateEvent, AnySyncEphemeralRoomEvent, AnySyncRoomEvent, AnySyncStateEvent, GlobalAccountDataEventType, - StateEventType, SyncStateEvent, + StateEventType, }, push::{Action, PushConditionRoomCtx, Ruleset}, serde::Raw, MilliSecondsSinceUnixEpoch, OwnedUserId, RoomId, UInt, UserId, }; -use tracing::{info, trace, warn}; +use tracing::{debug, info, trace, warn}; #[cfg(feature = "e2e-encryption")] use crate::error::Error; @@ -281,15 +281,15 @@ impl BaseClient { #[allow(clippy::single_match)] match &e { AnySyncRoomEvent::State(s) => match s { - AnySyncStateEvent::RoomMember(SyncStateEvent::Original(member)) => { + AnySyncStateEvent::RoomMember(member) => { ambiguity_cache.handle_event(changes, room_id, member).await?; - match member.content.membership { + match member.membership() { MembershipState::Join | MembershipState::Invite => { - user_ids.insert(member.state_key.clone()); + user_ids.insert(member.state_key().to_owned()); } _ => { - user_ids.remove(&member.state_key); + user_ids.remove(member.state_key()); } } @@ -297,19 +297,19 @@ impl BaseClient { // of profiles that the member set themselves to avoid // having confusing profile changes when a member gets // kicked/banned. - if member.state_key == member.sender { + if member.state_key() == member.sender() { changes .profiles .entry(room_id.to_owned()) .or_default() - .insert(member.sender.clone(), member.content.clone()); + .insert(member.sender().to_owned(), member.into()); } changes .members .entry(room_id.to_owned()) .or_default() - .insert(member.state_key.clone(), member.to_owned()); + .insert(member.state_key().to_owned(), member.clone()); } _ => { room_info.handle_state_event(s); @@ -470,12 +470,12 @@ impl BaseClient { room_info.handle_state_event(&event); - if let AnySyncStateEvent::RoomMember(SyncStateEvent::Original(member)) = event { + if let AnySyncStateEvent::RoomMember(member) = event { ambiguity_cache.handle_event(changes, &room_id, &member).await?; - match member.content.membership { + match member.membership() { MembershipState::Join | MembershipState::Invite => { - user_ids.insert(member.state_key.clone()); + user_ids.insert(member.state_key().to_owned()); } _ => (), } @@ -484,11 +484,11 @@ impl BaseClient { // of profiles that the member set themselves to avoid // having confusing profile changes when a member gets // kicked/banned. - if member.state_key == member.sender { - profiles.insert(member.sender.clone(), member.content.clone()); + if member.state_key() == member.sender() { + profiles.insert(member.sender().to_owned(), (&member).into()); } - members.insert(member.state_key.clone(), member); + members.insert(member.state_key().to_owned(), member); } else { state_events .entry(event.event_type()) @@ -859,12 +859,12 @@ impl BaseClient { let members: Vec<_> = response .chunk .iter() - .filter_map(|e| e.deserialize().ok()) - .filter_map(|ev| match ev { - RoomMemberEvent::Original(ev) => Some(ev), - // FIXME: don't filter these out - // https://github.com/matrix-org/matrix-rust-sdk/issues/607 - RoomMemberEvent::Redacted(_) => None, + .filter_map(|event| match event.deserialize() { + Ok(ev) => Some(ev), + Err(e) => { + debug!(?event, "Failed to deserialize m.room.member event: {}", e); + None + } }) .collect(); @@ -880,32 +880,32 @@ impl BaseClient { let mut user_ids = BTreeSet::new(); for member in &members { - let member: OriginalSyncRoomMemberEvent = member.clone().into(); + let member: SyncRoomMemberEvent = member.clone().into(); - if self.store.get_member_event(room_id, &member.state_key).await?.is_none() { + if self.store.get_member_event(room_id, member.state_key()).await?.is_none() { #[cfg(feature = "e2e-encryption")] - match member.content.membership { + match member.membership() { MembershipState::Join | MembershipState::Invite => { - user_ids.insert(member.state_key.clone()); + user_ids.insert(member.state_key().to_owned()); } _ => (), } ambiguity_cache.handle_event(&changes, room_id, &member).await?; - if member.state_key == member.sender { + if member.state_key() == member.sender() { changes .profiles .entry(room_id.to_owned()) .or_default() - .insert(member.sender.clone(), member.content.clone()); + .insert(member.sender().to_owned(), (&member).into()); } changes .members .entry(room_id.to_owned()) .or_default() - .insert(member.state_key.clone(), member); + .insert(member.state_key().to_owned(), member); } } @@ -1238,7 +1238,10 @@ impl BaseClient { let user_display_name = if let Some(member) = changes.members.get(room_id).and_then(|members| members.get(user_id)) { - member.content.displayname.clone().unwrap_or_else(|| user_id.localpart().to_owned()) + member + .as_original() + .and_then(|ev| ev.content.displayname.clone()) + .unwrap_or_else(|| user_id.localpart().to_owned()) } else if let Some(member) = room.get_member(user_id).await? { member.name().to_owned() } else { @@ -1291,8 +1294,10 @@ impl BaseClient { if let Some(member) = changes.members.get(&**room_id).and_then(|members| members.get(user_id)) { - push_rules.user_display_name = - member.content.displayname.clone().unwrap_or_else(|| user_id.localpart().to_owned()) + push_rules.user_display_name = member + .as_original() + .and_then(|ev| ev.content.displayname.clone()) + .unwrap_or_else(|| user_id.localpart().to_owned()) } if let Some(AnySyncStateEvent::RoomPowerLevels(event)) = changes diff --git a/crates/matrix-sdk-base/src/lib.rs b/crates/matrix-sdk-base/src/lib.rs index baefd3b34..f32253e60 100644 --- a/crates/matrix-sdk-base/src/lib.rs +++ b/crates/matrix-sdk-base/src/lib.rs @@ -43,5 +43,6 @@ pub use http; pub use matrix_sdk_crypto as crypto; pub use rooms::{DisplayName, Room, RoomInfo, RoomMember, RoomType}; pub use store::{StateChanges, StateStore, Store, StoreError}; -#[allow(unused_imports)] -pub(crate) use utils::{MinimalStateEvent, OriginalMinimalStateEvent, RedactedMinimalStateEvent}; +pub use utils::{ + MinimalRoomMemberEvent, MinimalStateEvent, OriginalMinimalStateEvent, RedactedMinimalStateEvent, +}; diff --git a/crates/matrix-sdk-base/src/rooms/members.rs b/crates/matrix-sdk-base/src/rooms/members.rs index 77bc8b4ab..aa967a0a8 100644 --- a/crates/matrix-sdk-base/src/rooms/members.rs +++ b/crates/matrix-sdk-base/src/rooms/members.rs @@ -17,21 +17,21 @@ use std::sync::Arc; use ruma::{ events::{ presence::PresenceEvent, - room::{ - member::{MembershipState, RoomMemberEventContent}, - power_levels::SyncRoomPowerLevelsEvent, - }, + room::{member::MembershipState, power_levels::SyncRoomPowerLevelsEvent}, }, MxcUri, UserId, }; -use crate::deserialized_responses::MemberEvent; +use crate::{deserialized_responses::MemberEvent, MinimalRoomMemberEvent}; /// A member of a room. #[derive(Clone, Debug)] pub struct RoomMember { pub(crate) event: Arc, - pub(crate) profile: Arc>, + // The latest member event sent by the member themselves. + // Stored in addition to the latest member event overall to get displayname + // and avatar from, which should be ignored on events sent by others. + pub(crate) profile: Arc>, #[allow(dead_code)] pub(crate) presence: Arc>, pub(crate) power_levels: Arc>, @@ -49,7 +49,7 @@ impl RoomMember { /// Get the display name of the member if there is one. pub fn display_name(&self) -> Option<&str> { if let Some(p) = self.profile.as_ref() { - p.displayname.as_deref() + p.as_original().and_then(|e| e.content.displayname.as_deref()) } else { self.event.original_content()?.displayname.as_deref() } @@ -69,9 +69,10 @@ impl RoomMember { /// Get the avatar url of the member, if there is one. pub fn avatar_url(&self) -> Option<&MxcUri> { - match self.profile.as_ref() { - Some(p) => p.avatar_url.as_deref(), - None => self.event.original_content()?.avatar_url.as_deref(), + if let Some(p) = self.profile.as_ref() { + p.as_original().and_then(|e| e.content.avatar_url.as_deref()) + } else { + self.event.original_content()?.avatar_url.as_deref() } } @@ -106,7 +107,7 @@ impl RoomMember { /// Get the membership state of this member. pub fn membership(&self) -> &MembershipState { if let Some(p) = self.profile.as_ref() { - &p.membership + p.membership() } else { self.event.membership() } diff --git a/crates/matrix-sdk-base/src/rooms/normal.rs b/crates/matrix-sdk-base/src/rooms/normal.rs index c8fd0accc..f54cbdfc6 100644 --- a/crates/matrix-sdk-base/src/rooms/normal.rs +++ b/crates/matrix-sdk-base/src/rooms/normal.rs @@ -813,7 +813,7 @@ mod test { canonical_alias::RoomCanonicalAliasEventContent, member::{ MembershipState, OriginalSyncRoomMemberEvent, RoomMemberEventContent, - StrippedRoomMemberEvent, + StrippedRoomMemberEvent, SyncRoomMemberEvent, }, name::RoomNameEventContent, }, @@ -846,8 +846,8 @@ mod test { } } - fn make_member_event(user_id: &UserId, name: &str) -> OriginalSyncRoomMemberEvent { - OriginalSyncRoomMemberEvent { + fn make_member_event(user_id: &UserId, name: &str) -> SyncRoomMemberEvent { + SyncRoomMemberEvent::Original(OriginalSyncRoomMemberEvent { content: assign!(RoomMemberEventContent::new(MembershipState::Join), { displayname: Some(name.to_owned()) }), @@ -856,7 +856,7 @@ mod test { event_id: event_id!("$h29iv0s1:example.com").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(208u32.into()), unsigned: StateUnsigned::default(), - } + }) } #[tokio::test] diff --git a/crates/matrix-sdk-base/src/store/ambiguity_map.rs b/crates/matrix-sdk-base/src/store/ambiguity_map.rs index 355967f18..73d9cb8e1 100644 --- a/crates/matrix-sdk-base/src/store/ambiguity_map.rs +++ b/crates/matrix-sdk-base/src/store/ambiguity_map.rs @@ -16,7 +16,7 @@ use std::collections::{BTreeMap, BTreeSet}; use matrix_sdk_common::deserialized_responses::{AmbiguityChange, MemberEvent}; use ruma::{ - events::room::member::{MembershipState, OriginalSyncRoomMemberEvent, SyncRoomMemberEvent}, + events::room::member::{MembershipState, SyncRoomMemberEvent}, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId, }; use tracing::trace; @@ -75,7 +75,7 @@ impl AmbiguityCache { &mut self, changes: &StateChanges, room_id: &RoomId, - member_event: &OriginalSyncRoomMemberEvent, + member_event: &SyncRoomMemberEvent, ) -> Result<()> { // Synapse seems to have a bug where it puts the same event into the // state and the timeline sometimes. @@ -89,7 +89,7 @@ impl AmbiguityCache { if self .changes .get(room_id) - .map(|c| c.contains_key(&member_event.event_id)) + .map(|c| c.contains_key(member_event.event_id())) .unwrap_or(false) { return Ok(()); @@ -106,9 +106,10 @@ impl AmbiguityCache { return Ok(()); } - let disambiguated_member = old_map.as_mut().and_then(|o| o.remove(&member_event.state_key)); + let disambiguated_member = + old_map.as_mut().and_then(|o| o.remove(member_event.state_key())); let ambiguated_member = - new_map.as_mut().and_then(|n| n.add(member_event.state_key.clone())); + new_map.as_mut().and_then(|n| n.add(member_event.state_key().clone())); let ambiguous = new_map.as_ref().map(|n| n.is_ambiguous()).unwrap_or(false); self.update(room_id, old_map, new_map); @@ -119,9 +120,9 @@ impl AmbiguityCache { member_ambiguous: ambiguous, }; - trace!("Handling display name ambiguity for {}: {:#?}", member_event.state_key, change); + trace!("Handling display name ambiguity for {}: {:#?}", member_event.state_key(), change); - self.add_change(room_id, member_event.event_id.clone(), change); + self.add_change(room_id, member_event.event_id().to_owned(), change); Ok(()) } @@ -151,16 +152,16 @@ impl AmbiguityCache { &mut self, changes: &StateChanges, room_id: &RoomId, - member_event: &OriginalSyncRoomMemberEvent, + member_event: &SyncRoomMemberEvent, ) -> Result<(Option, Option)> { use MembershipState::*; let old_event = if let Some(m) = - changes.members.get(room_id).and_then(|m| m.get(&member_event.state_key)) + changes.members.get(room_id).and_then(|m| m.get(member_event.state_key())) { - Some(MemberEvent::Sync(SyncRoomMemberEvent::Original(m.clone()))) + Some(MemberEvent::Sync(m.clone())) } else { - self.store.get_member_event(room_id, &member_event.state_key).await? + self.store.get_member_event(room_id, member_event.state_key()).await? }; let old_display_name = if let Some(event) = old_event { @@ -168,15 +169,17 @@ impl AmbiguityCache { let display_name = if let Some(d) = changes .profiles .get(room_id) - .and_then(|p| p.get(&member_event.state_key)) - .and_then(|p| p.displayname.as_deref()) + .and_then(|p| p.get(member_event.state_key())) + .and_then(|p| p.as_original()) + .and_then(|p| p.content.displayname.as_deref()) { Some(d.to_owned()) } else if let Some(d) = self .store - .get_profile(room_id, &member_event.state_key) + .get_profile(room_id, member_event.state_key()) .await? - .and_then(|c| c.displayname) + .and_then(|p| p.into_original()) + .and_then(|p| p.content.displayname) { Some(d) } else { @@ -204,17 +207,16 @@ impl AmbiguityCache { None }; - let new_map = if matches!(member_event.content.membership, Join | Invite) { + let new_map = if matches!(member_event.membership(), Join | Invite) { let new = member_event - .content - .displayname - .as_deref() - .unwrap_or_else(|| member_event.state_key.localpart()); + .as_original() + .and_then(|ev| ev.content.displayname.as_deref()) + .unwrap_or_else(|| member_event.state_key().localpart()); // We don't allow other users to set the display name, so if we // have a more trusted version of the display // name use that. - let new_display_name = if member_event.sender.as_str() == member_event.state_key { + let new_display_name = if member_event.sender().as_str() == member_event.state_key() { new } else if let Some(old) = old_display_name.as_deref() { old diff --git a/crates/matrix-sdk-base/src/store/integration_tests.rs b/crates/matrix-sdk-base/src/store/integration_tests.rs index 94331df4d..91c8873cc 100644 --- a/crates/matrix-sdk-base/src/store/integration_tests.rs +++ b/crates/matrix-sdk-base/src/store/integration_tests.rs @@ -53,8 +53,8 @@ macro_rules! statestore_integration_tests { presence::PresenceEvent, room::{ member::{ - MembershipState, OriginalSyncRoomMemberEvent, RoomMemberEventContent, - StrippedRoomMemberEvent, + MembershipState, OriginalSyncRoomMemberEvent, SyncRoomMemberEvent, + RoomMemberEventContent, StrippedRoomMemberEvent, }, power_levels::RoomPowerLevelsEventContent, MediaSource, @@ -173,14 +173,15 @@ macro_rules! statestore_integration_tests { let mut room_members = BTreeMap::new(); let member_json: &JsonValue = &test_json::MEMBER; - let member_event: OriginalSyncRoomMemberEvent = + let member_event: SyncRoomMemberEvent = serde_json::from_value(member_json.clone()).unwrap(); - let member_event_content = member_event.content.clone(); + let displayname = + member_event.as_original().unwrap().content.displayname.clone().unwrap(); room_ambiguity_map.insert( - member_event_content.displayname.clone().unwrap(), + displayname.clone(), BTreeSet::from([user_id.to_owned()]), ); - room_profiles.insert(user_id.to_owned(), member_event.content.clone()); + room_profiles.insert(user_id.to_owned(), (&member_event).into()); room_members.insert(user_id.to_owned(), member_event); let member_state_raw = @@ -190,13 +191,13 @@ macro_rules! statestore_integration_tests { let invited_member_json: &JsonValue = &test_json::MEMBER_INVITE; // FIXME: Should be stripped room member event - let invited_member_event: OriginalSyncRoomMemberEvent = + let invited_member_event: SyncRoomMemberEvent = serde_json::from_value(invited_member_json.clone()).unwrap(); room_ambiguity_map - .entry(member_event_content.displayname.clone().unwrap()) + .entry(displayname) .or_default() .insert(invited_user_id.to_owned()); - room_profiles.insert(invited_user_id.to_owned(), invited_member_event.content.clone()); + room_profiles.insert(invited_user_id.to_owned(), (&invited_member_event).into()); room_members.insert(invited_user_id.to_owned(), invited_member_event); let invited_member_state_raw = @@ -279,22 +280,22 @@ macro_rules! statestore_integration_tests { } } - fn membership_event() -> OriginalSyncRoomMemberEvent { + fn membership_event() -> SyncRoomMemberEvent { custom_membership_event(user_id(), event_id!("$h29iv0s8:example.com").to_owned()) } fn custom_membership_event( user_id: &UserId, event_id: OwnedEventId, - ) -> OriginalSyncRoomMemberEvent { - OriginalSyncRoomMemberEvent { + ) -> SyncRoomMemberEvent { + SyncRoomMemberEvent::Original(OriginalSyncRoomMemberEvent { event_id, content: RoomMemberEventContent::new(MembershipState::Join), sender: user_id.to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(198u32.into()), state_key: user_id.to_owned(), unsigned: StateUnsigned::default(), - } + }) } #[async_test] diff --git a/crates/matrix-sdk-base/src/store/memory_store.rs b/crates/matrix-sdk-base/src/store/memory_store.rs index ea4a45a73..a93fa9bcb 100644 --- a/crates/matrix-sdk-base/src/store/memory_store.rs +++ b/crates/matrix-sdk-base/src/store/memory_store.rs @@ -30,10 +30,7 @@ use ruma::{ events::{ presence::PresenceEvent, receipt::Receipt, - room::member::{ - MembershipState, OriginalSyncRoomMemberEvent, RoomMemberEventContent, - StrippedRoomMemberEvent, SyncRoomMemberEvent, - }, + room::member::{MembershipState, StrippedRoomMemberEvent, SyncRoomMemberEvent}, AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, AnyStrippedStateEvent, AnySyncStateEvent, GlobalAccountDataEventType, RoomAccountDataEventType, StateEventType, }, @@ -54,6 +51,7 @@ use super::{Result, RoomInfo, StateChanges, StateStore}; use crate::{ deserialized_responses::MemberEvent, media::{MediaRequest, UniqueKey}, + MinimalRoomMemberEvent, }; #[cfg(feature = "experimental-timeline")] use crate::{deserialized_responses::SyncRoomEvent, StoreError}; @@ -67,8 +65,8 @@ pub struct MemoryStore { sync_token: Arc>>, filters: Arc>, account_data: Arc>>, - members: Arc>>, - profiles: Arc>>, + members: Arc>>, + profiles: Arc>>, display_names: Arc>>>, joined_user_ids: Arc>>, invited_user_ids: Arc>>, @@ -156,43 +154,43 @@ impl MemoryStore { for (room, events) in &changes.members { for event in events.values() { - match event.content.membership { + match event.membership() { MembershipState::Join => { self.joined_user_ids .entry(room.clone()) .or_default() - .insert(event.state_key.clone()); + .insert(event.state_key().to_owned()); self.invited_user_ids .entry(room.clone()) .or_default() - .remove(&event.state_key); + .remove(event.state_key()); } MembershipState::Invite => { self.invited_user_ids .entry(room.clone()) .or_default() - .insert(event.state_key.clone()); + .insert(event.state_key().to_owned()); self.joined_user_ids .entry(room.clone()) .or_default() - .remove(&event.state_key); + .remove(event.state_key()); } _ => { self.joined_user_ids .entry(room.clone()) .or_default() - .remove(&event.state_key); + .remove(event.state_key()); self.invited_user_ids .entry(room.clone()) .or_default() - .remove(&event.state_key); + .remove(event.state_key()); } } self.members .entry(room.clone()) .or_default() - .insert(event.state_key.clone(), event.clone()); + .insert(event.state_key().to_owned(), event.clone()); } } @@ -497,7 +495,7 @@ impl MemoryStore { &self, room_id: &RoomId, user_id: &UserId, - ) -> Result> { + ) -> Result> { Ok(self.profiles.get(room_id).and_then(|p| p.get(user_id).map(|p| p.clone()))) } @@ -508,7 +506,7 @@ impl MemoryStore { ) -> Result> { if let Some(e) = self.members.get(room_id).and_then(|m| m.get(state_key).map(|m| m.clone())) { - Ok(Some(MemberEvent::Sync(SyncRoomMemberEvent::Original(e)))) + Ok(Some(MemberEvent::Sync(e))) } else if let Some(e) = self.stripped_members.get(room_id).and_then(|m| m.get(state_key).map(|m| m.clone())) { @@ -743,7 +741,7 @@ impl StateStore for MemoryStore { &self, room_id: &RoomId, user_id: &UserId, - ) -> Result> { + ) -> Result> { self.get_profile(room_id, user_id).await } diff --git a/crates/matrix-sdk-base/src/store/mod.rs b/crates/matrix-sdk-base/src/store/mod.rs index 684c9e0b2..b6492c3f3 100644 --- a/crates/matrix-sdk-base/src/store/mod.rs +++ b/crates/matrix-sdk-base/src/store/mod.rs @@ -42,9 +42,7 @@ use ruma::{ events::{ presence::PresenceEvent, receipt::{Receipt, ReceiptEventContent}, - room::member::{ - OriginalSyncRoomMemberEvent, RoomMemberEventContent, StrippedRoomMemberEvent, - }, + room::member::{StrippedRoomMemberEvent, SyncRoomMemberEvent}, AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, AnyStrippedStateEvent, AnySyncStateEvent, GlobalAccountDataEventType, RoomAccountDataEventType, StateEventType, }, @@ -62,7 +60,7 @@ use crate::{ deserialized_responses::MemberEvent, media::MediaRequest, rooms::{RoomInfo, RoomType}, - Room, Session, + MinimalRoomMemberEvent, Room, Session, }; pub(crate) mod ambiguity_map; @@ -185,7 +183,7 @@ pub trait StateStore: AsyncTraitDeps { &self, room_id: &RoomId, user_id: &UserId, - ) -> Result>; + ) -> Result>; /// Get the `MemberEvent` for the given state key in the given room id. /// @@ -493,12 +491,11 @@ pub struct StateChanges { /// A mapping of `UserId` to `PresenceEvent`. pub presence: BTreeMap>, + /// A mapping of `RoomId` to a map of users and their `SyncRoomMemberEvent`. + pub members: BTreeMap>, /// A mapping of `RoomId` to a map of users and their - /// `OriginalRoomMemberEvent`. - pub members: BTreeMap>, - /// A mapping of `RoomId` to a map of users and their - /// `RoomMemberEventContent`. - pub profiles: BTreeMap>, + /// `MinimalRoomMemberEvent`. + pub profiles: BTreeMap>, /// A mapping of `RoomId` to a map of event type string to a state key and /// `AnySyncStateEvent`. diff --git a/crates/matrix-sdk-base/src/utils.rs b/crates/matrix-sdk-base/src/utils.rs index 8624b1641..96cb78ad6 100644 --- a/crates/matrix-sdk-base/src/utils.rs +++ b/crates/matrix-sdk-base/src/utils.rs @@ -1,5 +1,6 @@ use ruma::{ events::{ + room::member::{MembershipState, RoomMemberEventContent}, RedactContent, RedactedEventContent, StateEventContent, StrippedStateEvent, SyncStateEvent, }, EventId, OwnedEventId, RoomVersionId, @@ -12,30 +13,48 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; // implemented for C::Redacted. // // It is unclear why a Serialize bound on C::Redacted is not also required. + +/// A minimal state event. +/// +/// This type can holding an possibly-redacted state event with an optional +/// event ID. The event ID is optional so this type can also hold events from +/// invited rooms, where event IDs are not available. #[derive(Clone, Debug, Deserialize, Serialize)] pub enum MinimalStateEvent where C::Redacted: StateEventContent + RedactedEventContent + DeserializeOwned, { + /// An unredacted event. Original(OriginalMinimalStateEvent), + /// A redacted event. Redacted(RedactedMinimalStateEvent), } +/// An unredacted minimal state event. +/// +/// For more details see [`MinimalStateEvent`]. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct OriginalMinimalStateEvent where C: StateEventContent, { + /// The event's content. pub content: C, + /// The event's ID, if known. pub event_id: Option, } +/// A redacted minimal state event. +/// +/// For more details see [`MinimalStateEvent`]. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct RedactedMinimalStateEvent where C: StateEventContent + RedactedEventContent, { + /// The event's content. pub content: C, + /// The event's ID, if known. pub event_id: Option, } @@ -44,6 +63,7 @@ where C: StateEventContent + RedactContent, C::Redacted: StateEventContent + RedactedEventContent + DeserializeOwned, { + /// Get the inner event's ID. pub fn event_id(&self) -> Option<&EventId> { match self { MinimalStateEvent::Original(ev) => ev.event_id.as_deref(), @@ -51,6 +71,7 @@ where } } + /// Returns the inner event, if it isn't redacted. pub fn as_original(&self) -> Option<&OriginalMinimalStateEvent> { match self { MinimalStateEvent::Original(ev) => Some(ev), @@ -58,6 +79,18 @@ where } } + /// Converts `self` to the inner `OriginalMinimalStateEvent`, if it isn't + /// redacted. + pub fn into_original(self) -> Option> { + match self { + MinimalStateEvent::Original(ev) => Some(ev), + MinimalStateEvent::Redacted(_) => None, + } + } + + /// Redacts this event. + /// + /// Does nothing if it is already redacted. pub fn redact(&mut self, room_version: &RoomVersionId) where C: Clone, @@ -71,6 +104,20 @@ where } } +/// A minimal `m.room.member` event. +pub type MinimalRoomMemberEvent = MinimalStateEvent; + +impl MinimalRoomMemberEvent { + /// Obtain the membership state, regardless of whether this event is + /// redacted. + pub fn membership(&self) -> &MembershipState { + match self { + MinimalStateEvent::Original(ev) => &ev.content.membership, + MinimalStateEvent::Redacted(ev) => &ev.content.membership, + } + } +} + impl From<&SyncStateEvent> for MinimalStateEvent where C: Clone + StateEventContent + RedactContent, diff --git a/crates/matrix-sdk-common/src/deserialized_responses.rs b/crates/matrix-sdk-common/src/deserialized_responses.rs index b805f3c96..3c0cdb94b 100644 --- a/crates/matrix-sdk-common/src/deserialized_responses.rs +++ b/crates/matrix-sdk-common/src/deserialized_responses.rs @@ -10,8 +10,8 @@ use ruma::{ }, events::{ room::member::{ - MembershipState, OriginalRoomMemberEvent, RoomMemberEventContent, - StrippedRoomMemberEvent, SyncRoomMemberEvent, + MembershipState, RoomMemberEvent, RoomMemberEventContent, StrippedRoomMemberEvent, + SyncRoomMemberEvent, }, AnyRoomEvent, AnySyncRoomEvent, }, @@ -336,7 +336,7 @@ impl MemberEvent { #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct MembersResponse { /// The list of members events. - pub chunk: Vec, + pub chunk: Vec, /// Collection of ambiguity changes that room member events trigger. pub ambiguity_changes: AmbiguityChanges, } diff --git a/crates/matrix-sdk-indexeddb/src/state_store.rs b/crates/matrix-sdk-indexeddb/src/state_store.rs index 44a74606c..5bd8255c0 100644 --- a/crates/matrix-sdk-indexeddb/src/state_store.rs +++ b/crates/matrix-sdk-indexeddb/src/state_store.rs @@ -23,7 +23,7 @@ use matrix_sdk_base::{ deserialized_responses::MemberEvent, media::{MediaRequest, UniqueKey}, store::{Result as StoreResult, StateChanges, StateStore, StoreError}, - RoomInfo, + MinimalStateEvent, RoomInfo, }; #[cfg(feature = "experimental-timeline")] use matrix_sdk_base::{deserialized_responses::SyncRoomEvent, store::BoxStream}; @@ -557,20 +557,20 @@ impl IndexeddbStore { let profile_changes = changes.profiles.get(room); for event in events.values() { - let key = (room, &event.state_key); + let key = (room, event.state_key()); - match event.content.membership { + match event.membership() { MembershipState::Join => { joined.put_key_val_owned( &self.encode_key(KEYS::JOINED_USER_IDS, key), - &self.serialize_event(&event.state_key)?, + &self.serialize_event(event.state_key())?, )?; invited.delete(&self.encode_key(KEYS::INVITED_USER_IDS, key))?; } MembershipState::Invite => { invited.put_key_val_owned( &self.encode_key(KEYS::INVITED_USER_IDS, key), - &self.serialize_event(&event.state_key)?, + &self.serialize_event(event.state_key())?, )?; joined.delete(&self.encode_key(KEYS::JOINED_USER_IDS, key))?; } @@ -585,7 +585,7 @@ impl IndexeddbStore { &self.serialize_event(&event)?, )?; - if let Some(profile) = profile_changes.and_then(|p| p.get(&event.state_key)) { + if let Some(profile) = profile_changes.and_then(|p| p.get(event.state_key())) { profiles_store.put_key_val_owned( &self.encode_key(KEYS::PROFILES, key), &self.serialize_event(&profile)?, @@ -888,7 +888,7 @@ impl IndexeddbStore { &self, room_id: &RoomId, user_id: &UserId, - ) -> Result> { + ) -> Result>> { self.inner .transaction_on_one_with_mode(KEYS::PROFILES, IdbTransactionMode::Readonly)? .object_store(KEYS::PROFILES)? @@ -1335,7 +1335,7 @@ impl StateStore for IndexeddbStore { &self, room_id: &RoomId, user_id: &UserId, - ) -> StoreResult> { + ) -> StoreResult>> { self.get_profile(room_id, user_id).await.map_err(|e| e.into()) } diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index 3d9eb9c61..9207d62db 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -28,7 +28,7 @@ use matrix_sdk_base::{ deserialized_responses::MemberEvent, media::{MediaRequest, UniqueKey}, store::{Result as StoreResult, StateChanges, StateStore, StoreError}, - RoomInfo, + MinimalStateEvent, RoomInfo, }; #[cfg(feature = "experimental-timeline")] use matrix_sdk_base::{deserialized_responses::SyncRoomEvent, store::BoxStream}; @@ -459,20 +459,20 @@ impl SledStore { let profile_changes = changes.profiles.get(room); for event in events.values() { - let key = (room, &event.state_key); + let key = (room, event.state_key()); - match event.content.membership { + match event.membership() { MembershipState::Join => { joined.insert( self.encode_key(JOINED_USER_ID, &key), - event.state_key.as_str(), + event.state_key().as_str(), )?; invited.remove(self.encode_key(INVITED_USER_ID, &key))?; } MembershipState::Invite => { invited.insert( self.encode_key(INVITED_USER_ID, &key), - event.state_key.as_str(), + event.state_key().as_str(), )?; joined.remove(self.encode_key(JOINED_USER_ID, &key))?; } @@ -489,7 +489,7 @@ impl SledStore { )?; if let Some(profile) = - profile_changes.and_then(|p| p.get(&event.state_key)) + profile_changes.and_then(|p| p.get(event.state_key())) { profiles.insert( self.encode_key(PROFILE, &key), @@ -730,7 +730,7 @@ impl SledStore { &self, room_id: &RoomId, user_id: &UserId, - ) -> Result> { + ) -> Result>> { let db = self.clone(); let key = self.encode_key(PROFILE, (room_id, user_id)); spawn_blocking(move || db.profiles.get(key)?.map(|p| db.deserialize_event(&p)).transpose()) @@ -1433,7 +1433,7 @@ impl StateStore for SledStore { &self, room_id: &RoomId, user_id: &UserId, - ) -> StoreResult> { + ) -> StoreResult>> { self.get_profile(room_id, user_id).await.map_err(Into::into) }