diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index ff323b63c3..062ebc04c8 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -29,7 +29,7 @@ use deltachat::context::{Context, ContextBuilder}; use deltachat::ephemeral::Timer as EphemeralTimer; use deltachat::imex::BackupProvider; use deltachat::key::preconfigure_keypair; -use deltachat::message::MsgId; +use deltachat::message::{MessageState, MsgId}; use deltachat::qr_code_generator::{create_qr_svg, generate_backup_qr, get_securejoin_qr_svg}; use deltachat::stock_str::StockMessage; use deltachat::webxdc::StatusUpdateSerial; @@ -3426,7 +3426,11 @@ pub unsafe extern "C" fn dc_msg_get_state(msg: *mut dc_msg_t) -> libc::c_int { return 0; } let ffi_msg = &*msg; - ffi_msg.message.get_state() as libc::c_int + let state = match ffi_msg.message.get_state() { + MessageState::OutRcvd => MessageState::OutDelivered, + s => s, + }; + state as libc::c_int } #[no_mangle] diff --git a/deltachat-ffi/src/lot.rs b/deltachat-ffi/src/lot.rs index e77483ef7d..7837d1315e 100644 --- a/deltachat-ffi/src/lot.rs +++ b/deltachat-ffi/src/lot.rs @@ -240,7 +240,7 @@ impl From for LotState { OutDraft => LotState::MsgOutDraft, OutPending => LotState::MsgOutPending, OutFailed => LotState::MsgOutFailed, - OutDelivered => LotState::MsgOutDelivered, + OutDelivered | OutRcvd => LotState::MsgOutDelivered, OutMdnRcvd => LotState::MsgOutMdnRcvd, } } diff --git a/deltachat-jsonrpc/src/api/types/message.rs b/deltachat-jsonrpc/src/api/types/message.rs index 398a6b018d..01547b6c3b 100644 --- a/deltachat-jsonrpc/src/api/types/message.rs +++ b/deltachat-jsonrpc/src/api/types/message.rs @@ -9,6 +9,7 @@ use deltachat::contact::Contact; use deltachat::context::Context; use deltachat::download; use deltachat::message::Message; +use deltachat::message::MessageState; use deltachat::message::MsgId; use deltachat::message::Viewtype; use deltachat::reaction::get_msg_reactions; @@ -149,6 +150,12 @@ impl MessageObject { let parent_id = message.parent(context).await?.map(|m| m.get_id().to_u32()); + let state = match message.get_state() { + MessageState::OutRcvd => MessageState::OutDelivered, + s => s, + } + .to_u32() + .context("state conversion to number failed")?; let download_state = message.download_state().into(); let quote = if let Some(quoted_text) = message.quoted_text() { @@ -212,10 +219,7 @@ impl MessageObject { has_location: message.has_location(), has_html: message.has_html(), view_type: message.get_viewtype().into(), - state: message - .get_state() - .to_u32() - .context("state conversion to number failed")?, + state, error: message.error(), timestamp: message.get_timestamp(), diff --git a/src/chat.rs b/src/chat.rs index e9d63e6090..336af65b68 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1450,17 +1450,21 @@ impl ChatId { // received message purely by timestamp. We could place it just before that seen // message, but anyway the user may not notice it. // - // NB: Received outgoing messages may break sorting of fresh incoming ones, but this - // shouldn't happen frequently. Seen incoming messages don't really break sorting of - // fresh ones, they rather mean that older incoming messages are actually seen as well. + // NB: Seen incoming messages don't really break sorting of fresh ones, they rather mean + // that older incoming messages are actually seen as well. context .sql .query_row_optional( "SELECT MAX(timestamp), MAX(IIF(state=?,timestamp_sent,0)) FROM msgs - WHERE chat_id=? AND hidden=0 AND state>? + WHERE chat_id=? AND hidden=0 AND state>? AND state!=? HAVING COUNT(*) > 0", - (MessageState::InSeen, self, MessageState::InFresh), + ( + MessageState::InSeen, + self, + MessageState::InFresh, + MessageState::OutRcvd, + ), |row| { let ts: i64 = row.get(0)?; let ts_sent_seen: i64 = row.get(1)?; @@ -4576,6 +4580,7 @@ pub async fn resend_msgs(context: &Context, msg_ids: &[MsgId]) -> Result<()> { MessageState::OutPending | MessageState::OutFailed | MessageState::OutDelivered + | MessageState::OutRcvd | MessageState::OutMdnRcvd => { message::update_msg_state(context, msg.id, MessageState::OutPending).await? } diff --git a/src/message.rs b/src/message.rs index cf4b84304d..00dc707543 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1447,6 +1447,9 @@ pub enum MessageState { /// the OutFailed state if we get such a hint from the server. OutDelivered = 26, + /// Received outgoing message sent by another MUA or device. + OutRcvd = 27, + /// Outgoing message read by the recipient (two checkmarks; this /// requires goodwill on the receiver's side). Not used in the db for new messages. OutMdnRcvd = 28, @@ -1467,6 +1470,7 @@ impl std::fmt::Display for MessageState { Self::OutPending => "Pending", Self::OutFailed => "Failed", Self::OutDelivered => "Delivered", + Self::OutRcvd => "Other MUA", Self::OutMdnRcvd => "Read", } ) @@ -1479,7 +1483,9 @@ impl MessageState { use MessageState::*; matches!( self, - OutPreparing | OutPending | OutDelivered | OutMdnRcvd // OutMdnRcvd can still fail because it could be a group message and only some recipients failed. + // OutMdnRcvd can still fail because it could be a group message and only some + // recipients failed. + OutPreparing | OutPending | OutDelivered | OutRcvd | OutMdnRcvd ) } @@ -1488,13 +1494,13 @@ impl MessageState { use MessageState::*; matches!( self, - OutPreparing | OutDraft | OutPending | OutFailed | OutDelivered | OutMdnRcvd + OutPreparing | OutDraft | OutPending | OutFailed | OutDelivered | OutRcvd | OutMdnRcvd ) } /// Returns adjusted message state if the message has MDNs. pub(crate) fn with_mdns(self, has_mdns: bool) -> Self { - if self == MessageState::OutDelivered && has_mdns { + if has_mdns && self >= MessageState::OutDelivered { return MessageState::OutMdnRcvd; } self diff --git a/src/mimeparser/mimeparser_tests.rs b/src/mimeparser/mimeparser_tests.rs index 73ea93b211..0074df89fa 100644 --- a/src/mimeparser/mimeparser_tests.rs +++ b/src/mimeparser/mimeparser_tests.rs @@ -1518,7 +1518,7 @@ async fn test_ignore_read_receipt_to_self() -> Result<()> { ) .await?; let msg = alice.get_last_msg().await; - assert_eq!(msg.state, MessageState::OutDelivered); + assert_eq!(msg.state, MessageState::OutRcvd); // Due to a bug in the old version running on the other device, Alice receives a read // receipt from self. @@ -1557,7 +1557,7 @@ async fn test_ignore_read_receipt_to_self() -> Result<()> { // Check that the state has not changed to `MessageState::OutMdnRcvd`. let msg = Message::load_from_db(&alice, msg.id).await?; - assert_eq!(msg.state, MessageState::OutDelivered); + assert_eq!(msg.state, MessageState::OutRcvd); Ok(()) } diff --git a/src/reaction.rs b/src/reaction.rs index 6b90e0947f..8742a90c54 100644 --- a/src/reaction.rs +++ b/src/reaction.rs @@ -477,7 +477,7 @@ Can we chat at 1pm pacific, today?" ) .await?; let msg = alice.get_last_msg().await; - assert_eq!(msg.state, MessageState::OutDelivered); + assert_eq!(msg.state, MessageState::OutRcvd); let reactions = get_msg_reactions(&alice, msg.id).await?; let contacts = reactions.contacts(); assert_eq!(contacts.len(), 0); diff --git a/src/receive_imf.rs b/src/receive_imf.rs index 760a14379d..671854b962 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -1750,7 +1750,7 @@ async fn add_parts( }; let state = if !mime_parser.incoming { - MessageState::OutDelivered + MessageState::OutRcvd } else if seen || is_mdn || chat_id_blocked == Blocked::Yes || group_changes.silent // No check for `hidden` because only reactions are such and they should be `InFresh`. { diff --git a/src/receive_imf/receive_imf_tests.rs b/src/receive_imf/receive_imf_tests.rs index 861bb984f3..bed414e5d1 100644 --- a/src/receive_imf/receive_imf_tests.rs +++ b/src/receive_imf/receive_imf_tests.rs @@ -685,7 +685,7 @@ async fn test_parse_ndn( if error_msg.is_some() { MessageState::OutFailed } else { - MessageState::OutDelivered + MessageState::OutRcvd } ); @@ -4398,7 +4398,7 @@ async fn test_outgoing_msg_forgery() -> Result<()> { let sent_msg = malice.send_text(malice_chat_id, "hi from malice").await; let msg = alice.recv_msg(&sent_msg).await; - assert_eq!(msg.state, MessageState::OutDelivered); + assert_eq!(msg.state, MessageState::OutRcvd); assert!(!msg.get_showpadlock()); Ok(()) diff --git a/src/tests/verified_chats.rs b/src/tests/verified_chats.rs index fbee49fc21..25a346f2e0 100644 --- a/src/tests/verified_chats.rs +++ b/src/tests/verified_chats.rs @@ -348,7 +348,7 @@ async fn test_old_message_5() -> Result<()> { .await? .unwrap(); - assert!(msg_sent.sort_timestamp == msg_incoming.sort_timestamp); + assert!(msg_incoming.sort_timestamp < msg_sent.sort_timestamp); alice .golden_test_chat(msg_sent.chat_id, "test_old_message_5") .await; diff --git a/test-data/golden/receive_imf_older_message_from_2nd_device b/test-data/golden/receive_imf_older_message_from_2nd_device index abc38ad339..04f2df83dd 100644 --- a/test-data/golden/receive_imf_older_message_from_2nd_device +++ b/test-data/golden/receive_imf_older_message_from_2nd_device @@ -1,5 +1,5 @@ Single#Chat#10: bob@example.net [bob@example.net] Icon: 4138c52e5bc1c576cda7dd44d088c07.png -------------------------------------------------------------------------------- Msg#10: Me (Contact#Contact#Self): We share this account √ -Msg#11: Me (Contact#Contact#Self): I'm Alice too √ +Msg#11: Me (Contact#Contact#Self): I'm Alice too -------------------------------------------------------------------------------- diff --git a/test-data/golden/test_old_message_5 b/test-data/golden/test_old_message_5 index 624838a43c..fc240473a0 100644 --- a/test-data/golden/test_old_message_5 +++ b/test-data/golden/test_old_message_5 @@ -1,5 +1,5 @@ Single#Chat#10: Bob [bob@example.net] Icon: 4138c52e5bc1c576cda7dd44d088c07.png -------------------------------------------------------------------------------- -Msg#10: Me (Contact#Contact#Self): Happy birthday, Bob! √ Msg#11: (Contact#Contact#10): Happy birthday to me, Alice! [FRESH] +Msg#10: Me (Contact#Contact#Self): Happy birthday, Bob! -------------------------------------------------------------------------------- diff --git a/test-data/golden/test_outgoing_encrypted_msg b/test-data/golden/test_outgoing_encrypted_msg index 06cecece6a..12ce23a21e 100644 --- a/test-data/golden/test_outgoing_encrypted_msg +++ b/test-data/golden/test_outgoing_encrypted_msg @@ -1,5 +1,5 @@ Single#Chat#10: bob@example.net [KEY bob@example.net] 🛡️ -------------------------------------------------------------------------------- Msg#10: info (Contact#Contact#Info): Messages are end-to-end encrypted. [NOTICED][INFO 🛡️] -Msg#11🔒: Me (Contact#Contact#Self): Test – This is encrypted, signed, and has an Autocrypt Header without prefer-encrypt=mutual. √ +Msg#11🔒: Me (Contact#Contact#Self): Test – This is encrypted, signed, and has an Autocrypt Header without prefer-encrypt=mutual. -------------------------------------------------------------------------------- diff --git a/test-data/golden/test_outgoing_mua_msg b/test-data/golden/test_outgoing_mua_msg index 5d4a0f2ee9..7674b38e81 100644 --- a/test-data/golden/test_outgoing_mua_msg +++ b/test-data/golden/test_outgoing_mua_msg @@ -1,4 +1,4 @@ Single#Chat#11: bob@example.net [bob@example.net] Icon: 4138c52e5bc1c576cda7dd44d088c07.png -------------------------------------------------------------------------------- -Msg#12: Me (Contact#Contact#Self): One classical MUA message √ +Msg#12: Me (Contact#Contact#Self): One classical MUA message --------------------------------------------------------------------------------