feat(base): Properly handle redacted m.room.member events

This commit is contained in:
Jonas Platte
2022-05-06 18:39:57 +02:00
committed by Jonas Platte
parent a2fb420635
commit 0fad7b60a9
12 changed files with 181 additions and 129 deletions

View File

@@ -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

View File

@@ -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,
};

View File

@@ -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<MemberEvent>,
pub(crate) profile: Arc<Option<RoomMemberEventContent>>,
// 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<Option<MinimalRoomMemberEvent>>,
#[allow(dead_code)]
pub(crate) presence: Arc<Option<PresenceEvent>>,
pub(crate) power_levels: Arc<Option<SyncRoomPowerLevelsEvent>>,
@@ -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()
}

View File

@@ -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]

View File

@@ -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<AmbiguityMap>, Option<AmbiguityMap>)> {
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

View File

@@ -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]

View File

@@ -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<RwLock<Option<String>>>,
filters: Arc<DashMap<String, String>>,
account_data: Arc<DashMap<GlobalAccountDataEventType, Raw<AnyGlobalAccountDataEvent>>>,
members: Arc<DashMap<OwnedRoomId, DashMap<OwnedUserId, OriginalSyncRoomMemberEvent>>>,
profiles: Arc<DashMap<OwnedRoomId, DashMap<OwnedUserId, RoomMemberEventContent>>>,
members: Arc<DashMap<OwnedRoomId, DashMap<OwnedUserId, SyncRoomMemberEvent>>>,
profiles: Arc<DashMap<OwnedRoomId, DashMap<OwnedUserId, MinimalRoomMemberEvent>>>,
display_names: Arc<DashMap<OwnedRoomId, DashMap<String, BTreeSet<OwnedUserId>>>>,
joined_user_ids: Arc<DashMap<OwnedRoomId, DashSet<OwnedUserId>>>,
invited_user_ids: Arc<DashMap<OwnedRoomId, DashSet<OwnedUserId>>>,
@@ -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<Option<RoomMemberEventContent>> {
) -> Result<Option<MinimalRoomMemberEvent>> {
Ok(self.profiles.get(room_id).and_then(|p| p.get(user_id).map(|p| p.clone())))
}
@@ -508,7 +506,7 @@ impl MemoryStore {
) -> Result<Option<MemberEvent>> {
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<Option<RoomMemberEventContent>> {
) -> Result<Option<MinimalRoomMemberEvent>> {
self.get_profile(room_id, user_id).await
}

View File

@@ -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<Option<RoomMemberEventContent>>;
) -> Result<Option<MinimalRoomMemberEvent>>;
/// 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<OwnedUserId, Raw<PresenceEvent>>,
/// A mapping of `RoomId` to a map of users and their `SyncRoomMemberEvent`.
pub members: BTreeMap<OwnedRoomId, BTreeMap<OwnedUserId, SyncRoomMemberEvent>>,
/// A mapping of `RoomId` to a map of users and their
/// `OriginalRoomMemberEvent`.
pub members: BTreeMap<OwnedRoomId, BTreeMap<OwnedUserId, OriginalSyncRoomMemberEvent>>,
/// A mapping of `RoomId` to a map of users and their
/// `RoomMemberEventContent`.
pub profiles: BTreeMap<OwnedRoomId, BTreeMap<OwnedUserId, RoomMemberEventContent>>,
/// `MinimalRoomMemberEvent`.
pub profiles: BTreeMap<OwnedRoomId, BTreeMap<OwnedUserId, MinimalRoomMemberEvent>>,
/// A mapping of `RoomId` to a map of event type string to a state key and
/// `AnySyncStateEvent`.

View File

@@ -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<C: StateEventContent + RedactContent>
where
C::Redacted: StateEventContent + RedactedEventContent + DeserializeOwned,
{
/// An unredacted event.
Original(OriginalMinimalStateEvent<C>),
/// A redacted event.
Redacted(RedactedMinimalStateEvent<C::Redacted>),
}
/// An unredacted minimal state event.
///
/// For more details see [`MinimalStateEvent`].
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct OriginalMinimalStateEvent<C>
where
C: StateEventContent,
{
/// The event's content.
pub content: C,
/// The event's ID, if known.
pub event_id: Option<OwnedEventId>,
}
/// A redacted minimal state event.
///
/// For more details see [`MinimalStateEvent`].
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct RedactedMinimalStateEvent<C>
where
C: StateEventContent + RedactedEventContent,
{
/// The event's content.
pub content: C,
/// The event's ID, if known.
pub event_id: Option<OwnedEventId>,
}
@@ -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<C>> {
match self {
MinimalStateEvent::Original(ev) => Some(ev),
@@ -58,6 +79,18 @@ where
}
}
/// Converts `self` to the inner `OriginalMinimalStateEvent<C>`, if it isn't
/// redacted.
pub fn into_original(self) -> Option<OriginalMinimalStateEvent<C>> {
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<RoomMemberEventContent>;
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<C> From<&SyncStateEvent<C>> for MinimalStateEvent<C>
where
C: Clone + StateEventContent + RedactContent,

View File

@@ -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<OriginalRoomMemberEvent>,
pub chunk: Vec<RoomMemberEvent>,
/// Collection of ambiguity changes that room member events trigger.
pub ambiguity_changes: AmbiguityChanges,
}

View File

@@ -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<Option<RoomMemberEventContent>> {
) -> Result<Option<MinimalStateEvent<RoomMemberEventContent>>> {
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<Option<RoomMemberEventContent>> {
) -> StoreResult<Option<MinimalStateEvent<RoomMemberEventContent>>> {
self.get_profile(room_id, user_id).await.map_err(|e| e.into())
}

View File

@@ -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<Option<RoomMemberEventContent>> {
) -> Result<Option<MinimalStateEvent<RoomMemberEventContent>>> {
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<Option<RoomMemberEventContent>> {
) -> StoreResult<Option<MinimalStateEvent<RoomMemberEventContent>>> {
self.get_profile(room_id, user_id).await.map_err(Into::into)
}