Skip to content

Commit 69afdb8

Browse files
committed
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.
1 parent 462985d commit 69afdb8

File tree

5 files changed

+30
-17
lines changed

5 files changed

+30
-17
lines changed

src/events/payload.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ pub enum EventType {
3535
/// Emitted when an IMAP message has been moved
3636
ImapMessageMoved(String),
3737

38-
/// Emitted before going into IDLE on the Inbox folder.
38+
/// Emitted before going into IDLE on any folder.
3939
ImapInboxIdle,
4040

4141
/// Emitted when an new file in the $BLOBDIR was created

src/message.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,17 @@ impl MsgId {
8787
.sql
8888
.query_row_optional(
8989
concat!(
90-
"SELECT m.state, mdns.msg_id",
90+
"SELECT m.state, m.from_id, mdns.msg_id",
9191
" FROM msgs m LEFT JOIN msgs_mdns mdns ON mdns.msg_id=m.id",
9292
" WHERE id=?",
9393
" LIMIT 1",
9494
),
9595
(self,),
9696
|row| {
9797
let state: MessageState = row.get(0)?;
98-
let mdn_msg_id: Option<MsgId> = row.get(1)?;
99-
Ok(state.with_mdns(mdn_msg_id.is_some()))
98+
let from_id: ContactId = row.get(1)?;
99+
let mdn_msg_id: Option<MsgId> = row.get(2)?;
100+
Ok(state.with(from_id, mdn_msg_id.is_some()))
100101
},
101102
)
102103
.await?
@@ -554,22 +555,23 @@ impl Message {
554555
}
555556
_ => String::new(),
556557
};
558+
let from_id = row.get("from_id")?;
557559
let msg = Message {
558560
id: row.get("id")?,
559561
rfc724_mid: row.get::<_, String>("rfc724mid")?,
560562
in_reply_to: row
561563
.get::<_, Option<String>>("mime_in_reply_to")?
562564
.and_then(|in_reply_to| parse_message_id(&in_reply_to).ok()),
563565
chat_id: row.get("chat_id")?,
564-
from_id: row.get("from_id")?,
566+
from_id,
565567
to_id: row.get("to_id")?,
566568
timestamp_sort: row.get("timestamp")?,
567569
timestamp_sent: row.get("timestamp_sent")?,
568570
timestamp_rcvd: row.get("timestamp_rcvd")?,
569571
ephemeral_timer: row.get("ephemeral_timer")?,
570572
ephemeral_timestamp: row.get("ephemeral_timestamp")?,
571573
viewtype: row.get("type")?,
572-
state: state.with_mdns(mdn_msg_id.is_some()),
574+
state: state.with(from_id, mdn_msg_id.is_some()),
573575
download_state: row.get("download_state")?,
574576
error: Some(row.get::<_, String>("error")?)
575577
.filter(|error| !error.is_empty()),
@@ -1490,10 +1492,16 @@ impl MessageState {
14901492
)
14911493
}
14921494

1493-
/// Returns adjusted message state if the message has MDNs.
1494-
pub(crate) fn with_mdns(self, has_mdns: bool) -> Self {
1495+
/// Returns adjusted message state.
1496+
pub(crate) fn with(mut self, from_id: ContactId, has_mdns: bool) -> Self {
1497+
if MessageState::InFresh <= self
1498+
&& self <= MessageState::InSeen
1499+
&& from_id == ContactId::SELF
1500+
{
1501+
self = MessageState::OutDelivered;
1502+
}
14951503
if self == MessageState::OutDelivered && has_mdns {
1496-
return MessageState::OutMdnRcvd;
1504+
self = MessageState::OutMdnRcvd;
14971505
}
14981506
self
14991507
}

src/receive_imf.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,15 +1689,14 @@ async fn add_parts(
16891689
};
16901690

16911691
let state = if !mime_parser.incoming {
1692-
MessageState::OutDelivered
1692+
MessageState::InFresh
16931693
} else if seen || is_mdn || chat_id_blocked == Blocked::Yes || group_changes.silent
16941694
// No check for `hidden` because only reactions are such and they should be `InFresh`.
16951695
{
16961696
MessageState::InSeen
16971697
} else {
16981698
MessageState::InFresh
16991699
};
1700-
let in_fresh = state == MessageState::InFresh;
17011700

17021701
let sort_to_bottom = false;
17031702
let received = true;
@@ -1993,7 +1992,7 @@ async fn add_parts(
19931992
save_mime_modified |= mime_parser.is_mime_modified && !part_is_empty && !hidden;
19941993
let save_mime_modified = save_mime_modified && parts.peek().is_none();
19951994

1996-
let ephemeral_timestamp = if in_fresh {
1995+
let ephemeral_timestamp = if state == MessageState::InFresh {
19971996
0
19981997
} else {
19991998
match ephemeral_timer {
@@ -2105,6 +2104,8 @@ RETURNING id
21052104
ensure_and_debug_assert!(!row_id.is_special(), "Rowid {row_id} is special");
21062105
created_db_entries.push(row_id);
21072106
}
2107+
let has_mdns = false;
2108+
let state = state.with(from_id, has_mdns);
21082109

21092110
// Maybe set logging xdc and add gossip topics for webxdcs.
21102111
for (part, msg_id) in mime_parser.parts.iter().zip(&created_db_entries) {

src/tests/verified_chats.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,9 @@ async fn test_old_message_4() -> Result<()> {
262262
Ok(())
263263
}
264264

265-
/// Alice is offline for some time.
266-
/// When they come online, first their sentbox is synced and then their inbox.
267-
/// This test tests that the messages are still in the right order.
265+
/// Alice's device#0 is offline for some time.
266+
/// When it comes online, it sees a message from another device and an incoming message. Messages
267+
/// may come from different folders.
268268
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
269269
async fn test_old_message_5() -> Result<()> {
270270
let alice = TestContext::new_alice().await;
@@ -294,7 +294,11 @@ async fn test_old_message_5() -> Result<()> {
294294
.await?
295295
.unwrap();
296296

297-
assert_eq!(msg_sent.sort_timestamp, msg_incoming.sort_timestamp);
297+
// If the messages come from the same folder and `msg_sent` is sent by Alice, it's better to
298+
// sort `msg_incoming` after it so that it's more visible. Messages coming from different
299+
// folders are a rare case now, but if Alice shares her account with someone else or has some
300+
// auto-reply bot, messages should be sorted just by "Date".
301+
assert!(msg_incoming.sort_timestamp < msg_sent.sort_timestamp);
298302
alice
299303
.golden_test_chat(msg_sent.chat_id, "test_old_message_5")
300304
.await;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Single#Chat#10: Bob [[email protected]] Icon: 4138c52e5bc1c576cda7dd44d088c07.png
22
--------------------------------------------------------------------------------
3-
Msg#10: Me (Contact#Contact#Self): Happy birthday, Bob! √
43
Msg#11: (Contact#Contact#10): Happy birthday to me, Alice! [FRESH]
4+
Msg#10: Me (Contact#Contact#Self): Happy birthday, Bob! √
55
--------------------------------------------------------------------------------

0 commit comments

Comments
 (0)