mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 17:36:29 +03:00
feat: remove non-sticker heuristics and force_sticker()
This causes problems for Delta Chat Desktop at <https://github.com/deltachat/deltachat-desktop/pull/6278> even though the logic was originally introduced for iOS. If the problem remains on iOS, heuristics can be added into iOS UI.
This commit is contained in:
@@ -2379,9 +2379,6 @@ impl CommandApi {
|
|||||||
let mut msg = Message::new(Viewtype::Sticker);
|
let mut msg = Message::new(Viewtype::Sticker);
|
||||||
msg.set_file_and_deduplicate(&ctx, Path::new(&sticker_path), None, None)?;
|
msg.set_file_and_deduplicate(&ctx, Path::new(&sticker_path), None, None)?;
|
||||||
|
|
||||||
// JSON-rpc does not need heuristics to turn [Viewtype::Sticker] into [Viewtype::Image]
|
|
||||||
msg.force_sticker();
|
|
||||||
|
|
||||||
let message_id = deltachat::chat::send_msg(&ctx, ChatId::new(chat_id), &mut msg).await?;
|
let message_id = deltachat::chat::send_msg(&ctx, ChatId::new(chat_id), &mut msg).await?;
|
||||||
Ok(message_id.to_u32())
|
Ok(message_id.to_u32())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -287,8 +287,6 @@ pub enum MessageViewtype {
|
|||||||
Gif,
|
Gif,
|
||||||
|
|
||||||
/// Message containing a sticker, similar to image.
|
/// Message containing a sticker, similar to image.
|
||||||
/// NB: When sending, the message viewtype may be changed to `Image` by some heuristics like
|
|
||||||
/// checking for transparent pixels. Use `Message::force_sticker()` to disable them.
|
|
||||||
///
|
///
|
||||||
/// If possible, the ui should display the image without borders in a transparent way.
|
/// 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.
|
/// A click on a sticker will offer to install the sticker set in some future.
|
||||||
|
|||||||
@@ -284,10 +284,6 @@ impl<'a> BlobObject<'a> {
|
|||||||
///
|
///
|
||||||
/// Recoding is only done for [`Viewtype::Image`]. For [`Viewtype::File`], if it's a correct
|
/// Recoding is only done for [`Viewtype::Image`]. For [`Viewtype::File`], if it's a correct
|
||||||
/// image, `*viewtype` is set to [`Viewtype::Image`].
|
/// image, `*viewtype` is set to [`Viewtype::Image`].
|
||||||
///
|
|
||||||
/// On some platforms images are passed to Core as [`Viewtype::Sticker`]. We recheck if the
|
|
||||||
/// image is a true sticker assuming that it must have at least one fully transparent corner,
|
|
||||||
/// otherwise `*viewtype` is set to [`Viewtype::Image`].
|
|
||||||
pub async fn check_or_recode_image(
|
pub async fn check_or_recode_image(
|
||||||
&mut self,
|
&mut self,
|
||||||
context: &Context,
|
context: &Context,
|
||||||
|
|||||||
@@ -445,7 +445,6 @@ async fn test_recode_image_balanced_png() {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// This will be sent as Image, see [`BlobObject::check_or_recode_image()`] for explanation.
|
|
||||||
SendImageCheckMediaquality {
|
SendImageCheckMediaquality {
|
||||||
viewtype: Viewtype::Sticker,
|
viewtype: Viewtype::Sticker,
|
||||||
media_quality_config: "0",
|
media_quality_config: "0",
|
||||||
@@ -453,6 +452,7 @@ async fn test_recode_image_balanced_png() {
|
|||||||
extension: "png",
|
extension: "png",
|
||||||
original_width: 1920,
|
original_width: 1920,
|
||||||
original_height: 1080,
|
original_height: 1080,
|
||||||
|
res_viewtype: Some(Viewtype::Sticker),
|
||||||
compressed_width: 1920,
|
compressed_width: 1920,
|
||||||
compressed_height: 1080,
|
compressed_height: 1080,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -734,8 +734,6 @@ async fn test_send_gif_as_sticker() -> Result<()> {
|
|||||||
let chat = alice.get_self_chat().await;
|
let chat = alice.get_self_chat().await;
|
||||||
let sent = alice.send_msg(chat.id, &mut msg).await;
|
let sent = alice.send_msg(chat.id, &mut msg).await;
|
||||||
let msg = Message::load_from_db(alice, sent.sender_msg_id).await?;
|
let msg = Message::load_from_db(alice, sent.sender_msg_id).await?;
|
||||||
// Message::force_sticker() wasn't used, still Viewtype::Sticker is preserved because of the
|
|
||||||
// extension.
|
|
||||||
assert_eq!(msg.get_viewtype(), Viewtype::Sticker);
|
assert_eq!(msg.get_viewtype(), Viewtype::Sticker);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
17
src/chat.rs
17
src/chat.rs
@@ -2467,10 +2467,7 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> {
|
|||||||
.with_context(|| format!("attachment missing for message of type #{}", msg.viewtype))?;
|
.with_context(|| format!("attachment missing for message of type #{}", msg.viewtype))?;
|
||||||
let mut maybe_image = false;
|
let mut maybe_image = false;
|
||||||
|
|
||||||
if msg.viewtype == Viewtype::File
|
if msg.viewtype == Viewtype::File || msg.viewtype == Viewtype::Image {
|
||||||
|| msg.viewtype == Viewtype::Image
|
|
||||||
|| msg.viewtype == Viewtype::Sticker && !msg.param.exists(Param::ForceSticker)
|
|
||||||
{
|
|
||||||
// Correct the type, take care not to correct already very special
|
// Correct the type, take care not to correct already very special
|
||||||
// formats as GIF or VOICE.
|
// formats as GIF or VOICE.
|
||||||
//
|
//
|
||||||
@@ -2478,12 +2475,7 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> {
|
|||||||
// - from FILE to AUDIO/VIDEO/IMAGE
|
// - from FILE to AUDIO/VIDEO/IMAGE
|
||||||
// - from FILE/IMAGE to GIF */
|
// - from FILE/IMAGE to GIF */
|
||||||
if let Some((better_type, _)) = message::guess_msgtype_from_suffix(msg) {
|
if let Some((better_type, _)) = message::guess_msgtype_from_suffix(msg) {
|
||||||
if msg.viewtype == Viewtype::Sticker {
|
if better_type == Viewtype::Image {
|
||||||
if better_type != Viewtype::Image {
|
|
||||||
// UIs don't want conversions of `Sticker` to anything other than `Image`.
|
|
||||||
msg.param.set_int(Param::ForceSticker, 1);
|
|
||||||
}
|
|
||||||
} else if better_type == Viewtype::Image {
|
|
||||||
maybe_image = true;
|
maybe_image = true;
|
||||||
} else if better_type != Viewtype::Webxdc
|
} else if better_type != Viewtype::Webxdc
|
||||||
|| context
|
|| context
|
||||||
@@ -2503,10 +2495,7 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> {
|
|||||||
if msg.viewtype == Viewtype::Vcard {
|
if msg.viewtype == Viewtype::Vcard {
|
||||||
msg.try_set_vcard(context, &blob.to_abs_path()).await?;
|
msg.try_set_vcard(context, &blob.to_abs_path()).await?;
|
||||||
}
|
}
|
||||||
if msg.viewtype == Viewtype::File && maybe_image
|
if msg.viewtype == Viewtype::File && maybe_image || msg.viewtype == Viewtype::Image {
|
||||||
|| msg.viewtype == Viewtype::Image
|
|
||||||
|| msg.viewtype == Viewtype::Sticker && !msg.param.exists(Param::ForceSticker)
|
|
||||||
{
|
|
||||||
let new_name = blob
|
let new_name = blob
|
||||||
.check_or_recode_image(context, msg.get_filename(), &mut msg.viewtype)
|
.check_or_recode_image(context, msg.get_filename(), &mut msg.viewtype)
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@@ -2075,13 +2075,7 @@ async fn test_chat_get_color_encrypted() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn test_sticker(
|
async fn test_sticker(filename: &str, bytes: &[u8], w: i32, h: i32) -> Result<()> {
|
||||||
filename: &str,
|
|
||||||
bytes: &[u8],
|
|
||||||
res_viewtype: Viewtype,
|
|
||||||
w: i32,
|
|
||||||
h: i32,
|
|
||||||
) -> Result<()> {
|
|
||||||
let alice = TestContext::new_alice().await;
|
let alice = TestContext::new_alice().await;
|
||||||
let bob = TestContext::new_bob().await;
|
let bob = TestContext::new_bob().await;
|
||||||
let alice_chat = alice.create_chat(&bob).await;
|
let alice_chat = alice.create_chat(&bob).await;
|
||||||
@@ -2097,7 +2091,7 @@ async fn test_sticker(
|
|||||||
|
|
||||||
let msg = bob.recv_msg(&sent_msg).await;
|
let msg = bob.recv_msg(&sent_msg).await;
|
||||||
assert_eq!(msg.chat_id, bob_chat.id);
|
assert_eq!(msg.chat_id, bob_chat.id);
|
||||||
assert_eq!(msg.get_viewtype(), res_viewtype);
|
assert_eq!(msg.get_viewtype(), Viewtype::Sticker);
|
||||||
assert_eq!(msg.get_filename().unwrap(), filename);
|
assert_eq!(msg.get_filename().unwrap(), filename);
|
||||||
assert_eq!(msg.get_width(), w);
|
assert_eq!(msg.get_width(), w);
|
||||||
assert_eq!(msg.get_height(), h);
|
assert_eq!(msg.get_height(), h);
|
||||||
@@ -2111,7 +2105,6 @@ async fn test_sticker_png() -> Result<()> {
|
|||||||
test_sticker(
|
test_sticker(
|
||||||
"sticker.png",
|
"sticker.png",
|
||||||
include_bytes!("../../test-data/image/logo.png"),
|
include_bytes!("../../test-data/image/logo.png"),
|
||||||
Viewtype::Sticker,
|
|
||||||
135,
|
135,
|
||||||
135,
|
135,
|
||||||
)
|
)
|
||||||
@@ -2123,7 +2116,6 @@ async fn test_sticker_jpeg() -> Result<()> {
|
|||||||
test_sticker(
|
test_sticker(
|
||||||
"sticker.jpg",
|
"sticker.jpg",
|
||||||
include_bytes!("../../test-data/image/avatar1000x1000.jpg"),
|
include_bytes!("../../test-data/image/avatar1000x1000.jpg"),
|
||||||
Viewtype::Image,
|
|
||||||
1000,
|
1000,
|
||||||
1000,
|
1000,
|
||||||
)
|
)
|
||||||
@@ -2131,10 +2123,33 @@ async fn test_sticker_jpeg() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn test_sticker_jpeg_force() {
|
async fn test_sticker_gif() -> Result<()> {
|
||||||
let alice = TestContext::new_alice().await;
|
test_sticker(
|
||||||
let bob = TestContext::new_bob().await;
|
"sticker.gif",
|
||||||
let alice_chat = alice.create_chat(&bob).await;
|
include_bytes!("../../test-data/image/logo.gif"),
|
||||||
|
135,
|
||||||
|
135,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests that stickers are sent as stickers.
|
||||||
|
///
|
||||||
|
/// Previously there was heuristic that stickers
|
||||||
|
/// were sometimes turned into non-stickers,
|
||||||
|
/// e.g. when it looked like UI sent
|
||||||
|
/// a screenshot dragged from the gallery into chat
|
||||||
|
/// as a sticker.
|
||||||
|
///
|
||||||
|
/// We have no such heuristic anymore,
|
||||||
|
/// if such heuristic is needed on some platform,
|
||||||
|
/// UI code should implement it.
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_sticker_no_heuristics() {
|
||||||
|
let mut tcm = TestContextManager::new();
|
||||||
|
let alice = &tcm.alice().await;
|
||||||
|
let bob = &tcm.bob().await;
|
||||||
|
let alice_chat = alice.create_chat(bob).await;
|
||||||
|
|
||||||
let file = alice.get_blobdir().join("sticker.jpg");
|
let file = alice.get_blobdir().join("sticker.jpg");
|
||||||
tokio::fs::write(
|
tokio::fs::write(
|
||||||
@@ -2144,53 +2159,38 @@ async fn test_sticker_jpeg_force() {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Images without force_sticker should be turned into [Viewtype::Image]
|
// Send a sticker.
|
||||||
let mut msg = Message::new(Viewtype::Sticker);
|
let mut msg = Message::new(Viewtype::Sticker);
|
||||||
msg.set_file_and_deduplicate(&alice, &file, Some("sticker.jpg"), None)
|
msg.set_file_and_deduplicate(alice, &file, Some("sticker.jpg"), None)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let file = msg.get_file(&alice).unwrap();
|
let file = msg.get_file(alice).unwrap();
|
||||||
let sent_msg = alice.send_msg(alice_chat.id, &mut msg).await;
|
|
||||||
let msg = bob.recv_msg(&sent_msg).await;
|
|
||||||
assert_eq!(msg.get_viewtype(), Viewtype::Image);
|
|
||||||
|
|
||||||
// Images with `force_sticker = true` should keep [Viewtype::Sticker]
|
|
||||||
let mut msg = Message::new(Viewtype::Sticker);
|
|
||||||
msg.set_file_and_deduplicate(&alice, &file, Some("sticker.jpg"), None)
|
|
||||||
.unwrap();
|
|
||||||
msg.force_sticker();
|
|
||||||
let sent_msg = alice.send_msg(alice_chat.id, &mut msg).await;
|
let sent_msg = alice.send_msg(alice_chat.id, &mut msg).await;
|
||||||
let msg = bob.recv_msg(&sent_msg).await;
|
let msg = bob.recv_msg(&sent_msg).await;
|
||||||
assert_eq!(msg.get_viewtype(), Viewtype::Sticker);
|
assert_eq!(msg.get_viewtype(), Viewtype::Sticker);
|
||||||
|
|
||||||
// Images with `force_sticker = true` should keep [Viewtype::Sticker]
|
// Send a sticker reusing the file.
|
||||||
// even on drafted messages
|
|
||||||
let mut msg = Message::new(Viewtype::Sticker);
|
let mut msg = Message::new(Viewtype::Sticker);
|
||||||
msg.set_file_and_deduplicate(&alice, &file, Some("sticker.jpg"), None)
|
msg.set_file_and_deduplicate(alice, &file, Some("sticker.jpg"), None)
|
||||||
|
.unwrap();
|
||||||
|
let sent_msg = alice.send_msg(alice_chat.id, &mut msg).await;
|
||||||
|
let msg = bob.recv_msg(&sent_msg).await;
|
||||||
|
assert_eq!(msg.get_viewtype(), Viewtype::Sticker);
|
||||||
|
|
||||||
|
// Set sticker as a draft, then send it.
|
||||||
|
let mut msg = Message::new(Viewtype::Sticker);
|
||||||
|
msg.set_file_and_deduplicate(alice, &file, Some("sticker.jpg"), None)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
msg.force_sticker();
|
|
||||||
alice_chat
|
alice_chat
|
||||||
.id
|
.id
|
||||||
.set_draft(&alice, Some(&mut msg))
|
.set_draft(alice, Some(&mut msg))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut msg = alice_chat.id.get_draft(&alice).await.unwrap().unwrap();
|
let mut msg = alice_chat.id.get_draft(alice).await.unwrap().unwrap();
|
||||||
let sent_msg = alice.send_msg(alice_chat.id, &mut msg).await;
|
let sent_msg = alice.send_msg(alice_chat.id, &mut msg).await;
|
||||||
let msg = bob.recv_msg(&sent_msg).await;
|
let msg = bob.recv_msg(&sent_msg).await;
|
||||||
assert_eq!(msg.get_viewtype(), Viewtype::Sticker);
|
assert_eq!(msg.get_viewtype(), Viewtype::Sticker);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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,
|
|
||||||
135,
|
|
||||||
135,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn test_sticker_forward() -> Result<()> {
|
async fn test_sticker_forward() -> Result<()> {
|
||||||
// create chats
|
// create chats
|
||||||
|
|||||||
@@ -795,12 +795,6 @@ impl Message {
|
|||||||
self.viewtype
|
self.viewtype
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Forces the message to **keep** [Viewtype::Sticker]
|
|
||||||
/// e.g the message will not be converted to a [Viewtype::Image].
|
|
||||||
pub fn force_sticker(&mut self) {
|
|
||||||
self.param.set_int(Param::ForceSticker, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the state of the message.
|
/// Returns the state of the message.
|
||||||
pub fn get_state(&self) -> MessageState {
|
pub fn get_state(&self) -> MessageState {
|
||||||
self.state
|
self.state
|
||||||
@@ -2322,8 +2316,6 @@ pub enum Viewtype {
|
|||||||
Gif = 21,
|
Gif = 21,
|
||||||
|
|
||||||
/// Message containing a sticker, similar to image.
|
/// Message containing a sticker, similar to image.
|
||||||
/// NB: When sending, the message viewtype may be changed to `Image` by some heuristics like
|
|
||||||
/// checking for transparent pixels. Use `Message::force_sticker()` to disable them.
|
|
||||||
///
|
///
|
||||||
/// If possible, the ui should display the image without borders in a transparent way.
|
/// 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.
|
/// A click on a sticker will offer to install the sticker set in some future.
|
||||||
|
|||||||
@@ -247,9 +247,6 @@ pub enum Param {
|
|||||||
/// For Webxdc Message Instances: Chat to integrate the Webxdc for.
|
/// For Webxdc Message Instances: Chat to integrate the Webxdc for.
|
||||||
WebxdcIntegrateFor = b'2',
|
WebxdcIntegrateFor = b'2',
|
||||||
|
|
||||||
/// For messages: Whether [crate::message::Viewtype::Sticker] should be forced.
|
|
||||||
ForceSticker = b'X',
|
|
||||||
|
|
||||||
/// For messages: Message is a deletion request. The value is a list of rfc724_mid of the messages to delete.
|
/// For messages: Message is a deletion request. The value is a list of rfc724_mid of the messages to delete.
|
||||||
DeleteRequestFor = b'M',
|
DeleteRequestFor = b'M',
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user