diff --git a/src/chat.rs b/src/chat.rs index 0526eed3d0..789105d477 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1211,37 +1211,34 @@ impl ChatId { ) .await? } else if received { - // Received messages shouldn't mingle with just sent ones and appear somewhere in the - // middle of the chat, so we go after the newest non fresh message. - // - // But if a received outgoing message is older than some seen message, better sort the - // received message purely by timestamp. We could place it just before that seen - // message, but anyway the user may not notice it. + // Received incoming messages shouldn't mingle with just sent ones and appear somewhere + // in the middle of the chat, so we go after the newest non fresh message. Received + // outgoing messages are allowed to mingle with seen messages though to avoid seen + // replies appearing before messages sent from another device (cases like the user + // sharing the account with others or bots are rare, so let them break sometimes). // // 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. + let states = match incoming { + true => "13, 16, 18, 20, 24, 26", // `> MessageState::InFresh` + false => "18, 20, 24, 26", // `> MessageState::InSeen` + }; context .sql .query_row_optional( - "SELECT MAX(timestamp), MAX(IIF(state=?,timestamp_sent,0)) - FROM msgs - WHERE chat_id=? AND hidden=0 AND state>? - HAVING COUNT(*) > 0", - (MessageState::InSeen, self, MessageState::InFresh), + &format!( + "SELECT MAX(timestamp) FROM msgs + WHERE state IN ({states}) AND hidden=0 AND chat_id=? + HAVING COUNT(*) > 0" + ), + (self,), |row| { let ts: i64 = row.get(0)?; - let ts_sent_seen: i64 = row.get(1)?; - Ok((ts, ts_sent_seen)) + Ok(ts) }, ) .await? - .and_then(|(ts, ts_sent_seen)| { - match incoming || ts_sent_seen <= message_timestamp { - true => Some(ts), - false => None, - } - }) } else { None }; diff --git a/src/message.rs b/src/message.rs index 91374e3cc8..f52ac9ab27 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1361,7 +1361,7 @@ pub enum MessageState { OutDelivered = 26, /// Outgoing message read by the recipient (two checkmarks; this - /// requires goodwill on the receiver's side). Not used in the db for new messages. + /// requires goodwill on the receiver's side). API-only, not used in the db. OutMdnRcvd = 28, } diff --git a/src/sql/migrations.rs b/src/sql/migrations.rs index 6bd56c91c6..eaf1dfd709 100644 --- a/src/sql/migrations.rs +++ b/src/sql/migrations.rs @@ -1328,6 +1328,18 @@ CREATE INDEX gossip_timestamp_index ON gossip_timestamp (chat_id, fingerprint); .await?; } + inc_and_check(&mut migration_version, 138)?; + if dbversion < migration_version { + // Tweak the index for `chat::calc_sort_timestamp()`. + sql.execute_migration( + "UPDATE msgs SET state=26 WHERE state=28; + DROP INDEX IF EXISTS msgs_index7; + CREATE INDEX msgs_index7 ON msgs (state, hidden, chat_id, timestamp);", + migration_version, + ) + .await?; + } + let new_version = sql .get_raw_config_int(VERSION_CFG) .await? diff --git a/src/tests/verified_chats.rs b/src/tests/verified_chats.rs index c120d73baa..4b1da1745b 100644 --- a/src/tests/verified_chats.rs +++ b/src/tests/verified_chats.rs @@ -277,7 +277,7 @@ async fn test_old_message_5() -> Result<()> { .await? .unwrap(); - assert!(msg_sent.sort_timestamp == msg_incoming.sort_timestamp); + assert_eq!(msg_sent.sort_timestamp, msg_incoming.sort_timestamp); alice .golden_test_chat(msg_sent.chat_id, "test_old_message_5") .await; diff --git a/test-data/golden/test_outgoing_encrypted_msg b/test-data/golden/test_outgoing_encrypted_msg index 4b26297c2d..984032e74c 100644 --- a/test-data/golden/test_outgoing_encrypted_msg +++ b/test-data/golden/test_outgoing_encrypted_msg @@ -1,5 +1,5 @@ Single#Chat#1001: bob@example.net [KEY bob@example.net] -------------------------------------------------------------------------------- -Msg#1001: info (Contact#Contact#Info): Messages are end-to-end encrypted. [NOTICED][INFO] Msg#1002🔒: Me (Contact#Contact#Self): Test – This is encrypted, signed, and has an Autocrypt Header without prefer-encrypt=mutual. √ +Msg#1001: info (Contact#Contact#Info): Messages are end-to-end encrypted. [NOTICED][INFO] --------------------------------------------------------------------------------