diff --git a/deltachat-jsonrpc/src/api/mod.rs b/deltachat-jsonrpc/src/api/mod.rs index 6b9e8d5ff7..be73ed7cf5 100644 --- a/deltachat-jsonrpc/src/api/mod.rs +++ b/deltachat-jsonrpc/src/api/mod.rs @@ -1750,10 +1750,15 @@ impl CommandApi { account_id: u32, chat_id: u32, sticker_path: String, + force: bool, ) -> Result { let ctx = self.get_context(account_id).await?; - let mut msg = Message::new(Viewtype::Sticker); + let mut msg = Message::new(if force { + Viewtype::ForceSticker + } else { + Viewtype::Sticker + }); msg.set_file(&sticker_path, None); let message_id = deltachat::chat::send_msg(&ctx, ChatId::new(chat_id), &mut msg).await?; diff --git a/deltachat-jsonrpc/src/api/types/message.rs b/deltachat-jsonrpc/src/api/types/message.rs index 4bd691e3ad..041161050d 100644 --- a/deltachat-jsonrpc/src/api/types/message.rs +++ b/deltachat-jsonrpc/src/api/types/message.rs @@ -147,6 +147,7 @@ impl MessageObject { image: if quote.get_viewtype() == Viewtype::Image || quote.get_viewtype() == Viewtype::Gif || quote.get_viewtype() == Viewtype::Sticker + || quote.get_viewtype() == Viewtype::ForceSticker { match quote.get_file(context) { Some(path_buf) => path_buf.to_str().map(|s| s.to_owned()), @@ -257,6 +258,12 @@ pub enum MessageViewtype { /// A click on a sticker will offer to install the sticker set in some future. Sticker, + /// Message containing a sticker, similar to image. + /// If possible, the ui should display the image without borders in a transparent way. + /// A click on a sticker will offer to install the sticker set in some future. + /// This stick is guaranteed to be displayed as a stick in the ui. + ForceSticker, + /// Message containing an Audio file. Audio, @@ -285,6 +292,7 @@ impl From for MessageViewtype { Viewtype::Image => MessageViewtype::Image, Viewtype::Gif => MessageViewtype::Gif, Viewtype::Sticker => MessageViewtype::Sticker, + Viewtype::ForceSticker => MessageViewtype::ForceSticker, Viewtype::Audio => MessageViewtype::Audio, Viewtype::Voice => MessageViewtype::Voice, Viewtype::Video => MessageViewtype::Video, @@ -303,6 +311,7 @@ impl From for Viewtype { MessageViewtype::Image => Viewtype::Image, MessageViewtype::Gif => Viewtype::Gif, MessageViewtype::Sticker => Viewtype::Sticker, + MessageViewtype::ForceSticker => Viewtype::ForceSticker, MessageViewtype::Audio => Viewtype::Audio, MessageViewtype::Voice => Viewtype::Voice, MessageViewtype::Video => Viewtype::Video, diff --git a/src/chat.rs b/src/chat.rs index 7687e554ef..61794d982c 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -2207,11 +2207,12 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> { .await? .with_context(|| format!("attachment missing for message of type #{}", msg.viewtype))?; - let mut maybe_sticker = msg.viewtype == Viewtype::Sticker; + let mut maybe_sticker = + msg.viewtype == Viewtype::Sticker || msg.viewtype == Viewtype::ForceSticker; if msg.viewtype == Viewtype::Image || maybe_sticker { blob.recode_to_image_size(context, &mut maybe_sticker) .await?; - if !maybe_sticker { + if !maybe_sticker && msg.viewtype != Viewtype::ForceSticker { msg.viewtype = Viewtype::Image; } } @@ -3573,7 +3574,7 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId) // by not marking own forwarded messages as such, // however, this turned out to be to confusing and unclear. - if msg.get_viewtype() != Viewtype::Sticker { + if msg.get_viewtype() != Viewtype::Sticker && msg.get_viewtype() != Viewtype::ForceSticker { msg.param .set_int(Param::Forwarded, src_msg_id.to_u32() as i32); } @@ -5618,6 +5619,7 @@ mod tests { async fn test_sticker( filename: &str, bytes: &[u8], + viewtype: Viewtype, res_viewtype: Viewtype, w: i32, h: i32, @@ -5630,12 +5632,12 @@ mod tests { let file = alice.get_blobdir().join(filename); tokio::fs::write(&file, bytes).await?; - let mut msg = Message::new(Viewtype::Sticker); + let mut msg = Message::new(viewtype); msg.set_file(file.to_str().unwrap(), None); let sent_msg = alice.send_msg(alice_chat.id, &mut msg).await; let mime = sent_msg.payload(); - if res_viewtype == Viewtype::Sticker { + if res_viewtype == Viewtype::Sticker || res_viewtype == Viewtype::ForceSticker { assert_eq!(mime.match_indices("Chat-Content: sticker").count(), 1); } @@ -5644,7 +5646,7 @@ mod tests { assert_eq!(msg.get_viewtype(), res_viewtype); let msg_filename = msg.get_filename().unwrap(); match res_viewtype { - Viewtype::Sticker => assert_eq!(msg_filename, filename), + Viewtype::Sticker | Viewtype::ForceSticker => assert_eq!(msg_filename, filename), Viewtype::Image => assert!(msg_filename.starts_with("image_")), _ => panic!("Not implemented"), } @@ -5661,6 +5663,7 @@ mod tests { "sticker.png", include_bytes!("../test-data/image/logo.png"), Viewtype::Sticker, + Viewtype::Sticker, 135, 135, ) @@ -5672,6 +5675,7 @@ mod tests { test_sticker( "sticker.jpg", include_bytes!("../test-data/image/avatar1000x1000.jpg"), + Viewtype::Sticker, Viewtype::Image, 1000, 1000, @@ -5679,12 +5683,26 @@ mod tests { .await } + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_sticker_jpeg_force() -> Result<()> { + test_sticker( + "sticker.jpg", + include_bytes!("../test-data/image/avatar1000x1000.jpg"), + Viewtype::ForceSticker, + Viewtype::Sticker, + 1000, + 1000, + ) + .await + } + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_sticker_gif() -> Result<()> { test_sticker( "sticker.gif", include_bytes!("../test-data/image/logo.gif"), Viewtype::Sticker, + Viewtype::Sticker, 135, 135, ) diff --git a/src/message.rs b/src/message.rs index 7e86f8c998..57d858fac6 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1858,6 +1858,12 @@ pub enum Viewtype { /// A click on a sticker will offer to install the sticker set in some future. Sticker = 23, + /// Message containing a sticker, similar to image. + /// If possible, the ui should display the image without borders in a transparent way. + /// A click on a sticker will offer to install the sticker set in some future. + /// This stick is guaranteed to be displayed as a stick in the ui. + ForceSticker = 24, + /// Message containing an Audio file. /// File and duration are set via dc_msg_set_file(), dc_msg_set_duration() /// and retrieved via dc_msg_get_file(), dc_msg_get_duration(). @@ -1898,6 +1904,7 @@ impl Viewtype { Viewtype::Image => true, Viewtype::Gif => true, Viewtype::Sticker => true, + Viewtype::ForceSticker => true, Viewtype::Audio => true, Viewtype::Voice => true, Viewtype::Video => true, diff --git a/src/mimefactory.rs b/src/mimefactory.rs index 46d4b1ed0e..b8bd84f8c7 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -1118,7 +1118,7 @@ impl<'a> MimeFactory<'a> { .push(Header::new("Chat-Group-Avatar".into(), filename_as_sent)); } - if self.msg.viewtype == Viewtype::Sticker { + if self.msg.viewtype == Viewtype::Sticker || self.msg.viewtype == Viewtype::ForceSticker { headers .protected .push(Header::new("Chat-Content".into(), "sticker".into())); diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 502a4fa101..e465c4ffe5 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -552,6 +552,7 @@ impl MimeMessage { Viewtype::Image | Viewtype::Gif | Viewtype::Sticker + | Viewtype::ForceSticker | Viewtype::Audio | Viewtype::Voice | Viewtype::Video diff --git a/src/summary.rs b/src/summary.rs index dfeab5f59e..68b3bd8e34 100644 --- a/src/summary.rs +++ b/src/summary.rs @@ -95,6 +95,7 @@ impl Summary { let thumbnail_path = if msg.viewtype == Viewtype::Image || msg.viewtype == Viewtype::Gif || msg.viewtype == Viewtype::Sticker + || msg.viewtype == Viewtype::ForceSticker { msg.get_file(context) .and_then(|path| path.to_str().map(|p| p.to_owned())) @@ -124,7 +125,7 @@ impl Message { let prefix = match self.viewtype { Viewtype::Image => stock_str::image(context).await, Viewtype::Gif => stock_str::gif(context).await, - Viewtype::Sticker => stock_str::sticker(context).await, + Viewtype::Sticker | Viewtype::ForceSticker => stock_str::sticker(context).await, Viewtype::Video => stock_str::video(context).await, Viewtype::Voice => stock_str::voice_message(context).await, Viewtype::Audio | Viewtype::File => {