diff --git a/bindings/matrix-sdk-ffi/src/timeline/mod.rs b/bindings/matrix-sdk-ffi/src/timeline/mod.rs index 3b1ace9d3d..99b9032d49 100644 --- a/bindings/matrix-sdk-ffi/src/timeline/mod.rs +++ b/bindings/matrix-sdk-ffi/src/timeline/mod.rs @@ -81,6 +81,7 @@ use crate::{ mod content; pub use content::MessageContent; +use matrix_sdk::utils::formatted_body_from; use crate::error::QueueWedgeError; @@ -289,6 +290,8 @@ impl Timeline { progress_watcher: Option>, use_send_queue: bool, ) -> Arc { + let formatted_caption = + formatted_body_from(caption.as_deref(), formatted_caption.map(Into::into)); SendAttachmentJoinHandle::new(RUNTIME.spawn(async move { let base_image_info = BaseImageInfo::try_from(&image_info) .map_err(|_| RoomError::InvalidAttachmentData)?; @@ -297,7 +300,7 @@ impl Timeline { let attachment_config = build_thumbnail_info(thumbnail_url, image_info.thumbnail_info)? .info(attachment_info) .caption(caption) - .formatted_caption(formatted_caption.map(Into::into)); + .formatted_caption(formatted_caption); self.send_attachment( url, @@ -321,6 +324,8 @@ impl Timeline { progress_watcher: Option>, use_send_queue: bool, ) -> Arc { + let formatted_caption = + formatted_body_from(caption.as_deref(), formatted_caption.map(Into::into)); SendAttachmentJoinHandle::new(RUNTIME.spawn(async move { let base_video_info: BaseVideoInfo = BaseVideoInfo::try_from(&video_info) .map_err(|_| RoomError::InvalidAttachmentData)?; @@ -351,6 +356,8 @@ impl Timeline { progress_watcher: Option>, use_send_queue: bool, ) -> Arc { + let formatted_caption = + formatted_body_from(caption.as_deref(), formatted_caption.map(Into::into)); SendAttachmentJoinHandle::new(RUNTIME.spawn(async move { let base_audio_info: BaseAudioInfo = BaseAudioInfo::try_from(&audio_info) .map_err(|_| RoomError::InvalidAttachmentData)?; @@ -383,6 +390,8 @@ impl Timeline { progress_watcher: Option>, use_send_queue: bool, ) -> Arc { + let formatted_caption = + formatted_body_from(caption.as_deref(), formatted_caption.map(Into::into)); SendAttachmentJoinHandle::new(RUNTIME.spawn(async move { let base_audio_info: BaseAudioInfo = BaseAudioInfo::try_from(&audio_info) .map_err(|_| RoomError::InvalidAttachmentData)?; diff --git a/crates/matrix-sdk/src/utils.rs b/crates/matrix-sdk/src/utils.rs index 327c041017..b9da26e3ae 100644 --- a/crates/matrix-sdk/src/utils.rs +++ b/crates/matrix-sdk/src/utils.rs @@ -21,6 +21,8 @@ use std::sync::{Arc, RwLock}; use futures_core::Stream; #[cfg(feature = "e2e-encryption")] use futures_util::StreamExt; +#[cfg(feature = "markdown")] +use ruma::events::room::message::FormattedBody; use ruma::{ events::{AnyMessageLikeEventContent, AnyStateEventContent}, serde::Raw, @@ -218,8 +220,32 @@ pub fn is_room_alias_format_valid(alias: String) -> bool { has_valid_format && is_lowercase && RoomAliasId::parse(alias).is_ok() } +/// Given a pair of optional `body` and `formatted_body` parameters, +/// returns a formatted body. +/// +/// Return the formatted body if available, or interpret the `body` parameter as +/// markdown, if provided. +#[cfg(feature = "markdown")] +pub fn formatted_body_from( + body: Option<&str>, + formatted_body: Option, +) -> Option { + if formatted_body.is_some() { + formatted_body + } else { + body.and_then(FormattedBody::markdown) + } +} + #[cfg(test)] mod test { + #[cfg(feature = "markdown")] + use assert_matches2::{assert_let, assert_matches}; + #[cfg(feature = "markdown")] + use ruma::events::room::message::FormattedBody; + + #[cfg(feature = "markdown")] + use crate::utils::formatted_body_from; use crate::utils::is_room_alias_format_valid; #[cfg(feature = "e2e-encryption")] @@ -282,4 +308,50 @@ mod test { fn test_is_room_alias_format_valid_when_has_valid_format() { assert!(is_room_alias_format_valid("#alias.test:domain.org".to_owned())) } + + #[test] + #[cfg(feature = "markdown")] + fn test_formatted_body_from_nothing_returns_none() { + assert_matches!(formatted_body_from(None, None), None); + } + + #[test] + #[cfg(feature = "markdown")] + fn test_formatted_body_from_only_formatted_body_returns_the_formatted_body() { + let formatted_body = FormattedBody::html(r"

Hello!

"); + + assert_let!( + Some(result_formatted_body) = formatted_body_from(None, Some(formatted_body.clone())) + ); + + assert_eq!(formatted_body.body, result_formatted_body.body); + assert_eq!(result_formatted_body.format, result_formatted_body.format); + } + + #[test] + #[cfg(feature = "markdown")] + fn test_formatted_body_from_markdown_body_returns_a_processed_formatted_body() { + let markdown_body = Some(r"# Parsed"); + + assert_let!(Some(result_formatted_body) = formatted_body_from(markdown_body, None)); + + let expected_formatted_body = FormattedBody::html("

Parsed

\n".to_owned()); + assert_eq!(expected_formatted_body.body, result_formatted_body.body); + assert_eq!(expected_formatted_body.format, result_formatted_body.format); + } + + #[test] + #[cfg(feature = "markdown")] + fn test_formatted_body_from_body_and_formatted_body_returns_the_formatted_body() { + let markdown_body = Some(r"# Markdown"); + let formatted_body = FormattedBody::html(r"

HTML

"); + + assert_let!( + Some(result_formatted_body) = + formatted_body_from(markdown_body, Some(formatted_body.clone())) + ); + + assert_eq!(formatted_body.body, result_formatted_body.body); + assert_eq!(formatted_body.format, result_formatted_body.format); + } }