mirror of
https://github.com/chatmail/core.git
synced 2026-04-27 18:36:30 +03:00
fix: Order of messages if outgoing reply is received earlier (#7169)
This fixes a scenario when an outgoing reply is received earlier than an incoming message for some reason like device B having `MvboxMove` enabled, sending the reply and going offline immediately, so the reply is in Inbox and it's processed by device A earlier because e.g. `MvboxMove` is disabled on it. Also if we add multi-transport later, this scenario will be just normal. This allows received outgoing messages to mingle with fresh incoming ones, i.e. sorts them together purely by timestamp by assigning `InFresh` state to received outgoing messages, but still returning `OutDelivered` by all APIs for compatibility. NB: We already do such a trick for `OutMdnRcvd`. As for messages sent locally, there's no need to make them more noticeable even if they are newer, so received outgoing messages are always added after them.
This commit is contained in:
@@ -36,7 +36,7 @@ pub enum EventType {
|
||||
/// Emitted when an IMAP message has been moved
|
||||
ImapMessageMoved(String),
|
||||
|
||||
/// Emitted before going into IDLE on the Inbox folder.
|
||||
/// Emitted before going into IDLE on any folder.
|
||||
ImapInboxIdle,
|
||||
|
||||
/// Emitted when an new file in the $BLOBDIR was created
|
||||
|
||||
@@ -85,7 +85,7 @@ impl MsgId {
|
||||
.sql
|
||||
.query_row_optional(
|
||||
concat!(
|
||||
"SELECT m.state, mdns.msg_id",
|
||||
"SELECT m.state, m.from_id, mdns.msg_id",
|
||||
" FROM msgs m LEFT JOIN msgs_mdns mdns ON mdns.msg_id=m.id",
|
||||
" WHERE id=?",
|
||||
" LIMIT 1",
|
||||
@@ -93,8 +93,9 @@ impl MsgId {
|
||||
(self,),
|
||||
|row| {
|
||||
let state: MessageState = row.get(0)?;
|
||||
let mdn_msg_id: Option<MsgId> = row.get(1)?;
|
||||
Ok(state.with_mdns(mdn_msg_id.is_some()))
|
||||
let from_id: ContactId = row.get(1)?;
|
||||
let mdn_msg_id: Option<MsgId> = row.get(2)?;
|
||||
Ok(state.with(from_id, mdn_msg_id.is_some()))
|
||||
},
|
||||
)
|
||||
.await?
|
||||
@@ -551,6 +552,7 @@ impl Message {
|
||||
}
|
||||
_ => String::new(),
|
||||
};
|
||||
let from_id = row.get("from_id")?;
|
||||
let msg = Message {
|
||||
id: row.get("id")?,
|
||||
rfc724_mid: row.get::<_, String>("rfc724mid")?,
|
||||
@@ -558,7 +560,7 @@ impl Message {
|
||||
.get::<_, Option<String>>("mime_in_reply_to")?
|
||||
.and_then(|in_reply_to| parse_message_id(&in_reply_to).ok()),
|
||||
chat_id: row.get("chat_id")?,
|
||||
from_id: row.get("from_id")?,
|
||||
from_id,
|
||||
to_id: row.get("to_id")?,
|
||||
timestamp_sort: row.get("timestamp")?,
|
||||
timestamp_sent: row.get("timestamp_sent")?,
|
||||
@@ -566,7 +568,7 @@ impl Message {
|
||||
ephemeral_timer: row.get("ephemeral_timer")?,
|
||||
ephemeral_timestamp: row.get("ephemeral_timestamp")?,
|
||||
viewtype: row.get("type").unwrap_or_default(),
|
||||
state: state.with_mdns(mdn_msg_id.is_some()),
|
||||
state: state.with(from_id, mdn_msg_id.is_some()),
|
||||
download_state: row.get("download_state")?,
|
||||
error: Some(row.get::<_, String>("error")?)
|
||||
.filter(|error| !error.is_empty()),
|
||||
@@ -1410,10 +1412,16 @@ impl MessageState {
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns adjusted message state if the message has MDNs.
|
||||
pub(crate) fn with_mdns(self, has_mdns: bool) -> Self {
|
||||
/// Returns adjusted message state.
|
||||
pub(crate) fn with(mut self, from_id: ContactId, has_mdns: bool) -> Self {
|
||||
if MessageState::InFresh <= self
|
||||
&& self <= MessageState::InSeen
|
||||
&& from_id == ContactId::SELF
|
||||
{
|
||||
self = MessageState::OutDelivered;
|
||||
}
|
||||
if self == MessageState::OutDelivered && has_mdns {
|
||||
return MessageState::OutMdnRcvd;
|
||||
self = MessageState::OutMdnRcvd;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
@@ -1694,7 +1694,7 @@ async fn add_parts(
|
||||
};
|
||||
|
||||
let state = if !mime_parser.incoming {
|
||||
MessageState::OutDelivered
|
||||
MessageState::InFresh
|
||||
} 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`.
|
||||
{
|
||||
@@ -1702,7 +1702,6 @@ async fn add_parts(
|
||||
} else {
|
||||
MessageState::InFresh
|
||||
};
|
||||
let in_fresh = state == MessageState::InFresh;
|
||||
|
||||
let sort_to_bottom = false;
|
||||
let received = true;
|
||||
@@ -1999,7 +1998,7 @@ async fn add_parts(
|
||||
save_mime_modified |= mime_parser.is_mime_modified && !part_is_empty && !hidden;
|
||||
let save_mime_modified = save_mime_modified && parts.peek().is_none();
|
||||
|
||||
let ephemeral_timestamp = if in_fresh {
|
||||
let ephemeral_timestamp = if state == MessageState::InFresh {
|
||||
0
|
||||
} else {
|
||||
match ephemeral_timer {
|
||||
@@ -2111,6 +2110,8 @@ RETURNING id
|
||||
ensure_and_debug_assert!(!row_id.is_special(), "Rowid {row_id} is special");
|
||||
created_db_entries.push(row_id);
|
||||
}
|
||||
let has_mdns = false;
|
||||
let state = state.with(from_id, has_mdns);
|
||||
|
||||
// Maybe set logging xdc and add gossip topics for webxdcs.
|
||||
for (part, msg_id) in mime_parser.parts.iter().zip(&created_db_entries) {
|
||||
|
||||
@@ -245,9 +245,9 @@ async fn test_old_message_4() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Alice is offline for some time.
|
||||
/// When they come online, first their sentbox is synced and then their inbox.
|
||||
/// This test tests that the messages are still in the right order.
|
||||
/// Alice's device#0 is offline for some time.
|
||||
/// When it comes online, it sees a message from another device and an incoming message. Messages
|
||||
/// may come from different folders.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_old_message_5() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
@@ -277,7 +277,11 @@ async fn test_old_message_5() -> Result<()> {
|
||||
.await?
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(msg_sent.sort_timestamp, msg_incoming.sort_timestamp);
|
||||
// If the messages come from the same folder and `msg_sent` is sent by Alice, it's better to
|
||||
// sort `msg_incoming` after it so that it's more visible. Messages coming from different
|
||||
// folders are a rare case now, but if Alice shares her account with someone else or has some
|
||||
// auto-reply bot, messages should be sorted just by "Date".
|
||||
assert!(msg_incoming.sort_timestamp < msg_sent.sort_timestamp);
|
||||
alice
|
||||
.golden_test_chat(msg_sent.chat_id, "test_old_message_5")
|
||||
.await;
|
||||
|
||||
@@ -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! √
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user