diff --git a/matrix_sdk/src/lib.rs b/matrix_sdk/src/lib.rs index 6bdfd0dc..809b8e20 100644 --- a/matrix_sdk/src/lib.rs +++ b/matrix_sdk/src/lib.rs @@ -40,6 +40,8 @@ pub use matrix_sdk_base::Error as BaseError; #[cfg(not(target_arch = "wasm32"))] pub use matrix_sdk_base::JsonStore; pub use matrix_sdk_base::{CustomOrRawEvent, EventEmitter, Room, Session, SyncRoom}; +#[cfg(feature = "messages")] +pub use matrix_sdk_base::{FullOrRedactedEvent, MessageQueue, MessageWrapper}; pub use matrix_sdk_base::{RoomState, StateStore}; pub use matrix_sdk_common::*; pub use reqwest::header::InvalidHeaderValue; diff --git a/matrix_sdk_base/src/client.rs b/matrix_sdk_base/src/client.rs index 1c38b90c..660bfe1f 100644 --- a/matrix_sdk_base/src/client.rs +++ b/matrix_sdk_base/src/client.rs @@ -1511,6 +1511,8 @@ impl BaseClient { } _ => {} }, + AnyRoomEventStub::RedactedState(_event) => {} + AnyRoomEventStub::RedactedMessage(_event) => {} } } @@ -1836,7 +1838,7 @@ impl BaseClient { #[cfg(test)] mod test { - use crate::identifiers::{RoomId, UserId}; + use crate::identifiers::{EventId, RoomId, UserId}; use crate::{BaseClient, BaseClientConfig, Session}; use matrix_sdk_common::events::{AnyRoomEventStub, EventJson}; use matrix_sdk_common_macros::async_trait; @@ -2402,12 +2404,17 @@ mod test { // check that the message has actually been redacted for room in client.joined_rooms().read().await.values() { let queue = &room.read().await.messages; - if let crate::events::AnyMessageEventContent::RoomRedaction(content) = - &queue.msgs[0].content + if let crate::models::FullOrRedactedEvent::Redacted( + crate::events::AnyRedactedMessageEventStub::RoomMessage(event), + ) = &queue.msgs[0].deref() { - assert_eq!(content.reason, Some("😀".to_string())); + // this is the id from the message event in the sync response + assert_eq!( + event.event_id, + EventId::try_from("$152037280074GZeOm:localhost").unwrap() + ) } else { - panic!("[pre store sync] message event in message queue should be redacted") + panic!("message event in message queue should be redacted") } } @@ -2425,10 +2432,15 @@ mod test { // properly for room in client.joined_rooms().read().await.values() { let queue = &room.read().await.messages; - if let crate::events::AnyMessageEventContent::RoomRedaction(content) = - &queue.msgs[0].content + if let crate::models::FullOrRedactedEvent::Redacted( + crate::events::AnyRedactedMessageEventStub::RoomMessage(event), + ) = &queue.msgs[0].deref() { - assert_eq!(content.reason, Some("😀".to_string())); + // this is the id from the message event in the sync response + assert_eq!( + event.event_id, + EventId::try_from("$152037280074GZeOm:localhost").unwrap() + ) } else { panic!("[post store sync] message event in message queue should be redacted") } diff --git a/matrix_sdk_base/src/event_emitter/mod.rs b/matrix_sdk_base/src/event_emitter/mod.rs index e0722eb1..bb7c6420 100644 --- a/matrix_sdk_base/src/event_emitter/mod.rs +++ b/matrix_sdk_base/src/event_emitter/mod.rs @@ -581,7 +581,7 @@ mod test { "unrecognized event", "redaction", "unrecognized event", - "unrecognized event", + // "unrecognized event", this is actually a redacted "m.room.messages" event "receipt event", "typing event" ], diff --git a/matrix_sdk_base/src/lib.rs b/matrix_sdk_base/src/lib.rs index 1ca09880..ab7f1e77 100644 --- a/matrix_sdk_base/src/lib.rs +++ b/matrix_sdk_base/src/lib.rs @@ -47,11 +47,16 @@ mod state; pub use client::{BaseClient, BaseClientConfig, RoomState, RoomStateType}; pub use event_emitter::{CustomOrRawEvent, EventEmitter, SyncRoom}; +pub use models::Room; +pub use state::{AllRooms, ClientState}; + #[cfg(feature = "encryption")] pub use matrix_sdk_crypto::{Device, TrustState}; -pub use models::Room; -pub use state::AllRooms; -pub use state::ClientState; + +#[cfg(feature = "messages")] +#[cfg_attr(docsrs, doc(cfg(feature = "messages")))] +pub use models::{FullOrRedactedEvent, MessageQueue, MessageWrapper}; + #[cfg(not(target_arch = "wasm32"))] pub use state::JsonStore; pub use state::StateStore; diff --git a/matrix_sdk_base/src/models/message.rs b/matrix_sdk_base/src/models/message.rs index 4bb0aae2..401b258a 100644 --- a/matrix_sdk_base/src/models/message.rs +++ b/matrix_sdk_base/src/models/message.rs @@ -3,17 +3,50 @@ //! The `Room` struct optionally holds a `MessageQueue` if the "messages" //! feature is enabled. -use std::cmp::Ordering; -use std::ops::{Deref, DerefMut}; -use std::vec::IntoIter; +use std::{ + cmp::Ordering, + ops::{Deref, DerefMut}, + time::SystemTime, + vec::IntoIter, +}; -use crate::events::{AnyMessageEventContent, AnyMessageEventStub, MessageEventStub}; +use matrix_sdk_common::identifiers::EventId; +use serde::{de, ser, Deserialize, Serialize}; -use serde::{de, ser, Serialize}; +use crate::events::{AnyMessageEventStub, AnyRedactedMessageEventStub}; + +/// Represents either a redacted event or a non-redacted event. +/// +/// Note: ruma may create types that solve this. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum FullOrRedactedEvent { + /// A non-redacted event. + Full(AnyMessageEventStub), + /// An event that has been redacted. + Redacted(AnyRedactedMessageEventStub), +} + +impl FullOrRedactedEvent { + /// Access the underlying events `event_id`. + pub fn event_id(&self) -> &EventId { + match self { + Self::Full(e) => e.event_id(), + Self::Redacted(e) => e.event_id(), + } + } + + /// Access the underlying events `origin_server_ts`. + pub fn origin_server_ts(&self) -> &SystemTime { + match self { + Self::Full(e) => e.origin_server_ts(), + Self::Redacted(e) => e.origin_server_ts(), + } + } +} const MESSAGE_QUEUE_CAP: usize = 35; -pub type SyncMessageEvent = MessageEventStub; +pub type SyncMessageEvent = FullOrRedactedEvent; /// A queue that holds the 35 most recent messages received from the server. #[derive(Clone, Debug, Default)] @@ -29,18 +62,6 @@ pub struct MessageQueue { #[derive(Clone, Debug, Serialize)] pub struct MessageWrapper(pub SyncMessageEvent); -impl MessageWrapper { - pub fn clone_into_any_content(event: &AnyMessageEventStub) -> SyncMessageEvent { - MessageEventStub { - content: event.content(), - sender: event.sender().clone(), - origin_server_ts: *event.origin_server_ts(), - event_id: event.event_id().clone(), - unsigned: event.unsigned().clone(), - } - } -} - impl Deref for MessageWrapper { type Target = SyncMessageEvent; @@ -57,7 +78,7 @@ impl DerefMut for MessageWrapper { impl PartialEq for MessageWrapper { fn eq(&self, other: &MessageWrapper) -> bool { - self.0.event_id == other.0.event_id + self.0.event_id() == other.0.event_id() } } @@ -65,7 +86,7 @@ impl Eq for MessageWrapper {} impl PartialOrd for MessageWrapper { fn partial_cmp(&self, other: &MessageWrapper) -> Option { - Some(self.0.origin_server_ts.cmp(&other.0.origin_server_ts)) + Some(self.0.origin_server_ts().cmp(&other.0.origin_server_ts())) } } @@ -82,7 +103,7 @@ impl PartialEq for MessageQueue { .msgs .iter() .zip(other.msgs.iter()) - .all(|(msg_a, msg_b)| msg_a.event_id == msg_b.event_id) + .all(|(msg_a, msg_b)| msg_a.event_id() == msg_b.event_id()) } } @@ -100,7 +121,7 @@ impl MessageQueue { pub fn push(&mut self, msg: SyncMessageEvent) -> bool { // only push new messages into the queue if let Some(latest) = self.msgs.last() { - if msg.origin_server_ts < latest.origin_server_ts && self.msgs.len() >= 10 { + if msg.origin_server_ts() < latest.origin_server_ts() && self.msgs.len() >= 10 { return false; } } @@ -120,10 +141,12 @@ impl MessageQueue { true } + /// Iterate over the messages in the queue. pub fn iter(&self) -> impl Iterator { self.msgs.iter() } + /// Iterate over each message mutably. pub fn iter_mut(&mut self) -> impl Iterator { self.msgs.iter_mut() } @@ -204,7 +227,9 @@ mod test { let mut room = Room::new(&id, &user); let json: &serde_json::Value = &test_json::MESSAGE_TEXT; - let msg = serde_json::from_value::(json.clone()).unwrap(); + let msg = FullOrRedactedEvent::Full( + serde_json::from_value::(json.clone()).unwrap(), + ); let mut msgs = MessageQueue::new(); msgs.push(msg.clone()); @@ -249,7 +274,9 @@ mod test { let mut room = Room::new(&id, &user); let json: &serde_json::Value = &test_json::MESSAGE_TEXT; - let msg = serde_json::from_value::(json.clone()).unwrap(); + let msg = FullOrRedactedEvent::Full( + serde_json::from_value::(json.clone()).unwrap(), + ); let mut msgs = MessageQueue::new(); msgs.push(msg.clone()); diff --git a/matrix_sdk_base/src/models/mod.rs b/matrix_sdk_base/src/models/mod.rs index 211e5b5d..4cf49a20 100644 --- a/matrix_sdk_base/src/models/mod.rs +++ b/matrix_sdk_base/src/models/mod.rs @@ -4,5 +4,8 @@ mod message; mod room; mod room_member; +#[cfg(feature = "messages")] +#[cfg_attr(docsrs, doc(cfg(feature = "messages")))] +pub use message::{FullOrRedactedEvent, MessageQueue, MessageWrapper}; pub use room::{Room, RoomName}; pub use room_member::RoomMember; diff --git a/matrix_sdk_base/src/models/room.rs b/matrix_sdk_base/src/models/room.rs index ae1d9c88..3be5980d 100644 --- a/matrix_sdk_base/src/models/room.rs +++ b/matrix_sdk_base/src/models/room.rs @@ -20,22 +20,21 @@ use serde::{Deserialize, Serialize}; use tracing::{debug, error, trace}; #[cfg(feature = "messages")] -use super::message::{MessageQueue, MessageWrapper}; +use super::message::{FullOrRedactedEvent, MessageQueue}; use super::RoomMember; use crate::api::r0::sync::sync_events::{RoomSummary, UnreadNotificationsCount}; -use crate::events::presence::{PresenceEvent, PresenceEventContent}; -use crate::events::room::{ - aliases::AliasesEventContent, - canonical_alias::CanonicalAliasEventContent, - encryption::EncryptionEventContent, - member::{MemberEventContent, MembershipChange, MembershipState}, - name::NameEventContent, - power_levels::{NotificationPowerLevels, PowerLevelsEventContent}, - tombstone::TombstoneEventContent, -}; - use crate::events::{ + presence::PresenceEvent, + room::{ + aliases::AliasesEventContent, + canonical_alias::CanonicalAliasEventContent, + encryption::EncryptionEventContent, + member::{MemberEventContent, MembershipChange}, + name::NameEventContent, + power_levels::{NotificationPowerLevels, PowerLevelsEventContent}, + tombstone::TombstoneEventContent, + }, Algorithm, AnyRoomEventStub, AnyStateEventStub, AnyStrippedStateEventStub, EventType, StateEventStub, StrippedStateEventStub, }; @@ -43,7 +42,7 @@ use crate::events::{ #[cfg(feature = "messages")] use crate::events::{ room::redaction::{RedactionEvent, RedactionEventStub}, - AnyMessageEventContent, AnyMessageEventStub, EventJson, + AnyMessageEventStub, EventJson, }; use crate::identifiers::{RoomAliasId, RoomId, UserId}; @@ -674,8 +673,7 @@ impl Room { #[cfg(feature = "messages")] #[cfg_attr(docsrs, doc(cfg(feature = "messages")))] pub fn handle_message(&mut self, event: &AnyMessageEventStub) -> bool { - let message = MessageWrapper::clone_into_any_content(event); - self.messages.push(message) + self.messages.push(FullOrRedactedEvent::Full(event.clone())) } /// Handle a room.redaction event and update the `MessageQueue`. @@ -686,17 +684,72 @@ impl Room { /// field. #[cfg(feature = "messages")] #[cfg_attr(docsrs, doc(cfg(feature = "messages")))] - pub fn handle_redaction(&mut self, event: &RedactionEventStub) -> bool { + pub fn handle_redaction(&mut self, redacted_event: &RedactionEventStub) -> bool { + use matrix_sdk_common::events::{ + AnyRedactedMessageEventStub, MessageEventStub, RedactedMessageEventStub, + }; + use std::ops::DerefMut; + if let Some(msg) = self .messages .iter_mut() - .find(|msg| event.redacts == msg.event_id) + .find(|msg| &redacted_event.redacts == msg.event_id()) { - msg.content = AnyMessageEventContent::RoomRedaction(event.content.clone()); - - let redaction = - redaction_event_from_redaction_stub(event.clone(), self.room_id.clone()); - msg.unsigned.redacted_because = Some(EventJson::from(redaction)); + match msg.deref_mut() { + FullOrRedactedEvent::Full(event) => match event { + AnyMessageEventStub::RoomMessage(event) => { + let MessageEventStub { + content, + event_id, + sender, + origin_server_ts, + mut unsigned, + } = event.clone(); + unsigned.redacted_because = + Some(EventJson::from(redaction_event_from_redaction_stub( + redacted_event.clone(), + self.room_id.clone(), + ))); + let redacted = content.redact(); + msg.0 = FullOrRedactedEvent::Redacted( + AnyRedactedMessageEventStub::RoomMessage(RedactedMessageEventStub { + content: redacted, + event_id, + origin_server_ts, + sender, + unsigned, + }), + ) + } + AnyMessageEventStub::Sticker(event) => { + let MessageEventStub { + content, + event_id, + sender, + origin_server_ts, + mut unsigned, + } = event.clone(); + unsigned.redacted_because = + Some(EventJson::from(redaction_event_from_redaction_stub( + redacted_event.clone(), + self.room_id.clone(), + ))); + let redacted = content.redact(); + msg.0 = FullOrRedactedEvent::Redacted(AnyRedactedMessageEventStub::Sticker( + RedactedMessageEventStub { + content: redacted, + event_id, + origin_server_ts, + sender, + unsigned, + }, + )) + } + // TODO handle the rest of the message events + _ => {} + }, + FullOrRedactedEvent::Redacted(_) => return false, + } true } else { false @@ -815,6 +868,7 @@ impl Room { AnyMessageEventStub::RoomRedaction(event) => self.handle_redaction(event), _ => false, }, + AnyRoomEventStub::RedactedMessage(_) | AnyRoomEventStub::RedactedState(_) => false, } } @@ -1661,10 +1715,15 @@ mod test { for room in client.joined_rooms().read().await.values() { let queue = &room.read().await.messages; - if let crate::events::AnyMessageEventContent::RoomRedaction(content) = - &queue.msgs[0].content + if let crate::models::message::FullOrRedactedEvent::Redacted( + crate::events::AnyRedactedMessageEventStub::RoomMessage(event), + ) = &queue.msgs[0].deref() { - assert_eq!(content.reason, Some("😀".to_string())); + // this is the id from the message event in the sync response + assert_eq!( + event.event_id, + EventId::try_from("$152037280074GZeOm:localhost").unwrap() + ) } else { panic!("message event in message queue should be redacted") } diff --git a/matrix_sdk_common/Cargo.toml b/matrix_sdk_common/Cargo.toml index 9700f812..b1ae4696 100644 --- a/matrix_sdk_common/Cargo.toml +++ b/matrix_sdk_common/Cargo.toml @@ -17,7 +17,7 @@ js_int = "0.1.8" [dependencies.ruma] git = "https://github.com/ruma/ruma" features = ["client-api"] -rev = "c19bcaab" +rev = "5e428ac" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] uuid = { version = "0.8.1", features = ["v4"] }