fix: Don't mark messages added to the db later as noticed when handling self-MDN

The device which issued the MDN might not have these messages at that moment, or the user might not
see them there, so only mark messages having lower ids as noticed, regardless of `timestamp`. This
doesn't fix the problem completely because messages may be received by devices in different order,
but should fix it in most cases.
This commit is contained in:
iequidoo
2026-05-21 15:27:30 -03:00
parent 862a90580a
commit f832f25828
2 changed files with 47 additions and 5 deletions

View File

@@ -974,25 +974,27 @@ UPDATE config SET value=? WHERE keyname='configured_addr' AND value!=?1
&& msg.chat_visibility == ChatVisibility::Archived;
updated_chats
.entry(msg.chat_id)
.and_modify(|pos| *pos = cmp::max(*pos, (msg.timestamp_sort, msg.id)))
.or_insert((msg.timestamp_sort, msg.id));
.and_modify(|val| *val = cmp::max(*val, msg.id))
.or_insert(msg.id);
}
}
for (chat_id, (timestamp_sort, msg_id)) in updated_chats {
for (chat_id, msg_id) in updated_chats {
context
.sql
.execute(
// Only mark messages added to the db earlier as noticed, regardless of
// `timestamp`. We assume that messages arrive in the same order on devices,
// and the user notices messages in this order.
"
UPDATE msgs SET state=? WHERE
state=? AND
hidden=0 AND
chat_id=? AND
(timestamp,id)<(?,?)",
id<?",
(
MessageState::InNoticed,
MessageState::InFresh,
chat_id,
timestamp_sort,
msg_id,
),
)

View File

@@ -15,6 +15,7 @@ use crate::imap::prefetch_should_download;
use crate::imex::{ImexMode, imex};
use crate::key;
use crate::securejoin::get_securejoin_qr;
use crate::smtp;
use crate::test_utils;
use crate::test_utils::{
TestContext, TestContextManager, alice_keypair, get_chat_msg, mark_as_verified,
@@ -2724,6 +2725,45 @@ async fn test_read_receipts_dont_unmark_bots() -> Result<()> {
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_self_mdn_vs_delayed_msg() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob1 = &tcm.bob().await;
let bob2 = &tcm.bob().await;
let alice_chat = alice.create_chat(bob1).await;
let sent1 = alice.send_text(alice_chat.id, "1").await;
SystemTime::shift(Duration::from_secs(1));
let sent2 = alice.send_text(alice_chat.id, "2").await;
let msg2_id = bob1.recv_msg(&sent2).await.id;
let msg1_id = bob1.recv_msg(&sent1).await.id;
let bob2_chat_id = bob2.recv_msg(&sent2).await.chat_id;
bob2.recv_msg(&sent1).await;
message::markseen_msgs(bob1, vec![msg2_id]).await?;
assert_eq!(msg1_id.get_state(bob1).await?, MessageState::InFresh);
smtp::queue_mdn(bob1).await?;
let sent = bob1.pop_sent_msg().await;
bob2.recv_msg_trash(&sent).await;
let mut msgs = get_chat_msgs(bob2, bob2_chat_id).await?;
let ChatItem::Message { msg_id } = msgs.pop().unwrap() else {
unreachable!();
};
let msg = Message::load_from_db(bob2, msg_id).await?;
assert_eq!(msg.text, "2");
assert_eq!(msg.state, MessageState::InSeen);
let ChatItem::Message { msg_id } = msgs.pop().unwrap() else {
unreachable!();
};
let msg = Message::load_from_db(bob2, msg_id).await?;
assert_eq!(msg.text, "1");
assert_eq!(msg.state, MessageState::InFresh);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_gmx_forwarded_msg() -> Result<()> {
let t = TestContext::new_alice().await;