diff --git a/crates/matrix-sdk-base/src/lib.rs b/crates/matrix-sdk-base/src/lib.rs index e9740f69e..96754811e 100644 --- a/crates/matrix-sdk-base/src/lib.rs +++ b/crates/matrix-sdk-base/src/lib.rs @@ -43,7 +43,8 @@ pub use http; pub use matrix_sdk_crypto as crypto; pub use once_cell; pub use rooms::{ - DisplayName, Room, RoomInfo, RoomMember, RoomMemberships, RoomState, RoomStateFilter, + DisplayName, Room, RoomCreateWithCreatorEventContent, RoomInfo, RoomMember, RoomMemberships, + RoomState, RoomStateFilter, }; pub use store::{StateChanges, StateStore, StateStoreDataKey, StateStoreDataValue, StoreError}; pub use utils::{ diff --git a/crates/matrix-sdk-base/src/rooms/mod.rs b/crates/matrix-sdk-base/src/rooms/mod.rs index 3b9d5010f..1aa5bca7d 100644 --- a/crates/matrix-sdk-base/src/rooms/mod.rs +++ b/crates/matrix-sdk-base/src/rooms/mod.rs @@ -11,18 +11,24 @@ pub use normal::{Room, RoomInfo, RoomState, RoomStateFilter}; use ruma::{ assign, events::{ + macros::EventContent, room::{ - avatar::RoomAvatarEventContent, canonical_alias::RoomCanonicalAliasEventContent, - create::RoomCreateEventContent, encryption::RoomEncryptionEventContent, + avatar::RoomAvatarEventContent, + canonical_alias::RoomCanonicalAliasEventContent, + create::{PreviousRoom, RoomCreateEventContent}, + encryption::RoomEncryptionEventContent, guest_access::RoomGuestAccessEventContent, history_visibility::RoomHistoryVisibilityEventContent, - join_rules::RoomJoinRulesEventContent, member::MembershipState, - name::RoomNameEventContent, tombstone::RoomTombstoneEventContent, + join_rules::RoomJoinRulesEventContent, + member::MembershipState, + name::RoomNameEventContent, + tombstone::RoomTombstoneEventContent, topic::RoomTopicEventContent, }, - AnyStrippedStateEvent, AnySyncStateEvent, RedactContent, RedactedStateEventContent, - StaticStateEventContent, SyncStateEvent, + AnyStrippedStateEvent, AnySyncStateEvent, EmptyStateKey, RedactContent, + RedactedStateEventContent, StaticStateEventContent, SyncStateEvent, }, + room::RoomType, EventId, OwnedUserId, RoomVersionId, }; use serde::{Deserialize, Serialize}; @@ -69,7 +75,7 @@ pub struct BaseRoomInfo { /// The canonical alias of this room. canonical_alias: Option>, /// The `m.room.create` event content of this room. - create: Option>, + create: Option>, /// A list of user ids this room is considered as direct message, if this /// room is a DM. pub(crate) dm_targets: HashSet, @@ -311,6 +317,99 @@ fn calculate_room_name( } } +/// The content of an `m.room.create` event, with a required `creator` field. +/// +/// Starting with room version 11, the `creator` field should be removed and the +/// `sender` field of the event should be used instead. This is reflected on +/// [`RoomCreateEventContent`]. +/// +/// This type was created as an alternative for ease of use. When it is used in +/// the SDK, it is constructed by copying the `sender` of the original event as +/// the `creator`. +#[derive(Clone, Debug, Deserialize, Serialize, EventContent)] +#[ruma_event(type = "m.room.create", kind = State, state_key_type = EmptyStateKey, custom_redacted)] +pub struct RoomCreateWithCreatorEventContent { + /// The `user_id` of the room creator. + /// + /// This is set by the homeserver. + /// + /// While this should be optional since room version 11, we copy the sender + /// of the event so we can still access it. + pub creator: OwnedUserId, + + /// Whether or not this room's data should be transferred to other + /// homeservers. + #[serde( + rename = "m.federate", + default = "ruma::serde::default_true", + skip_serializing_if = "ruma::serde::is_true" + )] + pub federate: bool, + + /// The version of the room. + /// + /// Defaults to `RoomVersionId::V1`. + #[serde(default = "default_create_room_version_id")] + pub room_version: RoomVersionId, + + /// A reference to the room this room replaces, if the previous room was + /// upgraded. + #[serde(skip_serializing_if = "Option::is_none")] + pub predecessor: Option, + + /// The room type. + /// + /// This is currently only used for spaces. + #[serde(skip_serializing_if = "Option::is_none", rename = "type")] + pub room_type: Option, +} + +impl RoomCreateWithCreatorEventContent { + /// Constructs a `RoomCreateWithCreatorEventContent` with the given original + /// content and sender. + pub fn from_event_content(content: RoomCreateEventContent, sender: OwnedUserId) -> Self { + let RoomCreateEventContent { federate, room_version, predecessor, room_type, .. } = content; + Self { creator: sender, federate, room_version, predecessor, room_type } + } + + fn into_event_content(self) -> (RoomCreateEventContent, OwnedUserId) { + let Self { creator, federate, room_version, predecessor, room_type } = self; + + #[allow(deprecated)] + let content = assign!(RoomCreateEventContent::new_v11(), { + creator: Some(creator.clone()), + federate, + room_version, + predecessor, + room_type, + }); + + (content, creator) + } +} + +/// Redacted form of [`RoomCreateWithCreatorEventContent`]. +pub type RedactedRoomCreateWithCreatorEventContent = RoomCreateWithCreatorEventContent; + +impl RedactedStateEventContent for RedactedRoomCreateWithCreatorEventContent { + type StateKey = EmptyStateKey; +} + +impl RedactContent for RoomCreateWithCreatorEventContent { + type Redacted = RedactedRoomCreateWithCreatorEventContent; + + fn redact(self, version: &RoomVersionId) -> Self::Redacted { + let (content, sender) = self.into_event_content(); + // Use Ruma's redaction algorithm. + let content = content.redact(version); + Self::from_event_content(content, sender) + } +} + +fn default_create_room_version_id() -> RoomVersionId { + RoomVersionId::V1 +} + bitflags! { /// Room membership filter as a bitset. /// diff --git a/crates/matrix-sdk-base/src/rooms/normal.rs b/crates/matrix-sdk-base/src/rooms/normal.rs index 8548bb148..ab8ffa320 100644 --- a/crates/matrix-sdk-base/src/rooms/normal.rs +++ b/crates/matrix-sdk-base/src/rooms/normal.rs @@ -35,7 +35,6 @@ use ruma::{ ignored_user_list::IgnoredUserListEventContent, receipt::{Receipt, ReceiptThread, ReceiptType}, room::{ - create::RoomCreateEventContent, encryption::RoomEncryptionEventContent, guest_access::GuestAccess, history_visibility::HistoryVisibility, @@ -59,7 +58,7 @@ use tracing::{debug, field::debug, info, instrument, trace, warn}; use super::{ members::{MemberInfo, MemberRoomInfo}, - BaseRoomInfo, DisplayName, RoomMember, + BaseRoomInfo, DisplayName, RoomCreateWithCreatorEventContent, RoomMember, }; use crate::{ deserialized_responses::MemberEvent, @@ -257,10 +256,7 @@ impl Room { /// This usually isn't optional but some servers might not send an /// `m.room.create` event as the first event for a given room, thus this can /// be optional. - /// - /// It can also be redacted in current room versions, leaving only the - /// `creator` field. - pub fn create_content(&self) -> Option { + pub fn create_content(&self) -> Option { self.inner .read() .base_info @@ -995,10 +991,9 @@ impl RoomInfo { } fn creator(&self) -> Option<&UserId> { - #[allow(deprecated)] match self.base_info.create.as_ref()? { - MinimalStateEvent::Original(ev) => ev.content.creator.as_deref(), - MinimalStateEvent::Redacted(ev) => ev.content.creator.as_deref(), + MinimalStateEvent::Original(ev) => Some(&ev.content.creator), + MinimalStateEvent::Redacted(ev) => Some(&ev.content.creator), } } diff --git a/crates/matrix-sdk-base/src/utils.rs b/crates/matrix-sdk-base/src/utils.rs index 84ae66002..260de0739 100644 --- a/crates/matrix-sdk-base/src/utils.rs +++ b/crates/matrix-sdk-base/src/utils.rs @@ -4,7 +4,7 @@ use ruma::{ room::{ avatar::{RoomAvatarEventContent, StrippedRoomAvatarEvent}, canonical_alias::{RoomCanonicalAliasEventContent, StrippedRoomCanonicalAliasEvent}, - create::{RoomCreateEventContent, StrippedRoomCreateEvent}, + create::{StrippedRoomCreateEvent, SyncRoomCreateEvent}, guest_access::{ RedactedRoomGuestAccessEventContent, RoomGuestAccessEventContent, StrippedRoomGuestAccessEvent, @@ -28,6 +28,8 @@ use ruma::{ }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use crate::rooms::RoomCreateWithCreatorEventContent; + // #[serde(bound)] instead of DeserializeOwned in type where clause does not // work, it can only be a single bound that replaces the default and if a helper // trait is used, the compiler still complains about Deserialize not being @@ -181,6 +183,27 @@ where } } +impl From<&SyncRoomCreateEvent> for MinimalStateEvent { + fn from(ev: &SyncRoomCreateEvent) -> Self { + match ev { + SyncStateEvent::Original(ev) => Self::Original(OriginalMinimalStateEvent { + content: RoomCreateWithCreatorEventContent::from_event_content( + ev.content.clone(), + ev.sender.clone(), + ), + event_id: Some(ev.event_id.clone()), + }), + SyncStateEvent::Redacted(ev) => Self::Redacted(RedactedMinimalStateEvent { + content: RoomCreateWithCreatorEventContent::from_event_content( + ev.content.clone(), + ev.sender.clone(), + ), + event_id: Some(ev.event_id.clone()), + }), + } + } +} + impl From<&StrippedRoomAvatarEvent> for MinimalStateEvent { fn from(event: &StrippedRoomAvatarEvent) -> Self { let content = assign!(RoomAvatarEventContent::new(), { @@ -208,14 +231,15 @@ impl From<&StrippedRoomNameEvent> for MinimalStateEvent { } } -impl From<&StrippedRoomCreateEvent> for MinimalStateEvent { +impl From<&StrippedRoomCreateEvent> for MinimalStateEvent { fn from(event: &StrippedRoomCreateEvent) -> Self { - let content = assign!(RoomCreateEventContent::new_v1(event.sender.clone()), { + let content = RoomCreateWithCreatorEventContent { + creator: event.sender.clone(), federate: event.content.federate, room_version: event.content.room_version.clone(), predecessor: event.content.predecessor.clone(), room_type: event.content.room_type.clone(), - }); + }; Self::Original(OriginalMinimalStateEvent { content, event_id: None }) } } diff --git a/crates/matrix-sdk/src/lib.rs b/crates/matrix-sdk/src/lib.rs index eb344f546..6ae1c88b1 100644 --- a/crates/matrix-sdk/src/lib.rs +++ b/crates/matrix-sdk/src/lib.rs @@ -23,8 +23,9 @@ pub use matrix_sdk_base::crypto; pub use matrix_sdk_base::{ deserialized_responses, store::{DynStateStore, MemoryStore, StateStoreExt}, - DisplayName, Room as BaseRoom, RoomInfo, RoomMember as BaseRoomMember, RoomMemberships, - RoomState, SessionMeta, StateChanges, StateStore, StoreError, + DisplayName, Room as BaseRoom, RoomCreateWithCreatorEventContent, RoomInfo, + RoomMember as BaseRoomMember, RoomMemberships, RoomState, SessionMeta, StateChanges, + StateStore, StoreError, }; pub use matrix_sdk_common::*; pub use reqwest;