diff --git a/crates/matrix-sdk/src/room/timeline/event_item.rs b/crates/matrix-sdk/src/room/timeline/event_item.rs index 8f7620f50..96f836d40 100644 --- a/crates/matrix-sdk/src/room/timeline/event_item.rs +++ b/crates/matrix-sdk/src/room/timeline/event_item.rs @@ -301,6 +301,17 @@ pub enum TimelineItemContent { UnableToDecrypt(EncryptedMessage), } +impl TimelineItemContent { + /// If `self` is of the [`Message`][Self:Message] variant, return the inner + /// [`Message`]. + pub fn as_message(&self) -> Option<&Message> { + match self { + Self::Message(v) => Some(v), + _ => None, + } + } +} + /// An `m.room.message` event or extensible event, including edits. #[derive(Clone, Debug)] pub struct Message { @@ -318,6 +329,13 @@ impl Message { &self.msgtype } + /// Get a reference to the message body. + /// + /// Shorthand for `.msgtype().body()`. + pub fn body(&self) -> &str { + self.msgtype.body() + } + /// Get the event ID of the event this message is replying to, if any. pub fn in_reply_to(&self) -> Option<&EventId> { self.in_reply_to.as_deref() diff --git a/crates/matrix-sdk/src/room/timeline/tests.rs b/crates/matrix-sdk/src/room/timeline/tests.rs index fce69869c..a82fb89ad 100644 --- a/crates/matrix-sdk/src/room/timeline/tests.rs +++ b/crates/matrix-sdk/src/room/timeline/tests.rs @@ -26,9 +26,13 @@ use futures_util::StreamExt; use matrix_sdk_test::async_test; use once_cell::sync::Lazy; use ruma::{ + assign, events::{ reaction::{self, ReactionEventContent}, - room::{message::RoomMessageEventContent, redaction::OriginalSyncRoomRedactionEvent}, + room::{ + message::{self, Replacement, RoomMessageEventContent}, + redaction::OriginalSyncRoomRedactionEvent, + }, MessageLikeEventContent, OriginalSyncMessageLikeEvent, }, serde::Raw, @@ -70,6 +74,33 @@ async fn reaction_redaction() { assert_eq!(event.reactions().len(), 0); } +#[async_test] +async fn invalid_edit() { + let timeline = TestTimeline::new(&ALICE); + let mut stream = timeline.stream(); + + timeline.handle_live_message_event(&ALICE, RoomMessageEventContent::text_plain("test")); + let item = assert_matches!(stream.next().await, Some(VecDiff::Push { value }) => value); + let event = item.as_event().unwrap(); + let msg = event.content.as_message().unwrap(); + assert_eq!(msg.body(), "test"); + + let msg_event_id = event.event_id().unwrap(); + + let edit = assign!(RoomMessageEventContent::text_plain(" * fake"), { + relates_to: Some(message::Relation::Replacement(Replacement::new( + msg_event_id.to_owned(), + Box::new(RoomMessageEventContent::text_plain("fake")), + ))), + }); + // Edit is from a different user than the previous event + timeline.handle_live_message_event(&BOB, edit); + + // Can't easily test the non-arrival of an item using the stream. Instead + // just assert that there is still just a single item in the timeline. + assert_eq!(timeline.inner.items.lock_ref().len(), 1); +} + struct TestTimeline { own_user_id: OwnedUserId, inner: TimelineInner,