fix: do not sort received messages below the last seen one

This commit is contained in:
link2xt
2026-03-25 02:09:28 +01:00
parent be2b2bd561
commit 58aafef935
6 changed files with 20 additions and 59 deletions

View File

@@ -1228,15 +1228,11 @@ SELECT id, rfc724_mid, pre_rfc724_mid, timestamp, ?, 1 FROM msgs WHERE chat_id=?
/// corresponding event in case of a system message (usually the current system time).
/// `always_sort_to_bottom` makes this adjust the returned timestamp up so that the message goes
/// to the chat bottom.
/// `received` -- whether the message is received. Otherwise being sent.
/// `incoming` -- whether the message is incoming.
pub(crate) async fn calc_sort_timestamp(
self,
context: &Context,
message_timestamp: i64,
always_sort_to_bottom: bool,
received: bool,
incoming: bool,
) -> Result<i64> {
let mut sort_timestamp = cmp::min(message_timestamp, smeared_time(context));
@@ -1256,38 +1252,6 @@ SELECT id, rfc724_mid, pre_rfc724_mid, timestamp, ?, 1 FROM msgs WHERE chat_id=?
(self, MessageState::OutDraft),
)
.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.
//
// 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.
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),
|row| {
let ts: i64 = row.get(0)?;
let ts_sent_seen: i64 = row.get(1)?;
Ok((ts, ts_sent_seen))
},
)
.await?
.and_then(|(ts, ts_sent_seen)| {
match incoming || ts_sent_seen <= message_timestamp {
true => Some(ts),
false => None,
}
})
} else {
None
};
@@ -4963,15 +4927,8 @@ pub(crate) async fn add_info_msg_with_cmd(
ts
} else {
let sort_to_bottom = true;
let (received, incoming) = (false, false);
chat_id
.calc_sort_timestamp(
context,
smeared_time(context),
sort_to_bottom,
received,
incoming,
)
.calc_sort_timestamp(context, smeared_time(context), sort_to_bottom)
.await?
};

View File

@@ -1835,6 +1835,16 @@ async fn add_parts(
}
}
// Sort message to the bottom if we are not in the chat
// so if we are added via QR code scan
// the message about our addition goes after all the info messages.
// Info messages are sorted by local smeared_timestamp()
// which advances quickly during SecureJoin,
// while "member added" message may have older timestamp
// corresponding to the sender clock.
// In practice inviter clock may even be slightly in the past.
let sort_to_bottom = !chat.is_self_in_chat(context).await?;
let is_location_kml = mime_parser.location_kml.is_some();
let mut group_changes = match chat.typ {
_ if chat.id.is_special() => GroupChangesInfo::default(),
@@ -1885,16 +1895,8 @@ async fn add_parts(
};
let in_fresh = state == MessageState::InFresh;
let sort_to_bottom = false;
let received = true;
let sort_timestamp = chat_id
.calc_sort_timestamp(
context,
mime_parser.timestamp_sent,
sort_to_bottom,
received,
mime_parser.incoming,
)
.calc_sort_timestamp(context, mime_parser.timestamp_sent, sort_to_bottom)
.await?;
// Apply ephemeral timer changes to the chat.

View File

@@ -64,9 +64,11 @@ DKIM Results: Passed=true";
async fn check_parse_receive_headers_integration(raw: &[u8], expected: &str) {
let t = TestContext::new_alice().await;
receive_imf(&t, raw, false).await.unwrap();
let msg = t.get_last_msg().await;
let msg_info = msg.id.get_info(&t).await.unwrap();
let received = receive_imf(&t, raw, false).await.unwrap().unwrap();
assert_eq!(received.msg_ids.len(), 1);
let msg_id = received.msg_ids[0];
let msg_info = msg_id.get_info(&t).await.unwrap();
// Ignore the first rows of the msg_info because they contain a
// received time that depends on the test time which makes it impossible to

View File

@@ -2,9 +2,9 @@ Group#Chat#1001: Group [5 member(s)]
--------------------------------------------------------------------------------
Msg#1001: info (Contact#Contact#Info): Messages are end-to-end encrypted. [NOTICED][INFO]
Msg#1002🔒: Me (Contact#Contact#Self): populate √
Msg#1007🔒: (Contact#Contact#1001): Member fiona@example.net removed by bob@example.net. [FRESH][INFO]
Msg#1003: info (Contact#Contact#Info): Member dom@example.net added. [NOTICED][INFO]
Msg#1004: info (Contact#Contact#Info): Member fiona@example.net removed. [NOTICED][INFO]
Msg#1005🔒: (Contact#Contact#1001): Member elena@example.net added by bob@example.net. [FRESH][INFO]
Msg#1006🔒: Me (Contact#Contact#Self): You added member fiona@example.net. [INFO] o
Msg#1007🔒: (Contact#Contact#1001): Member fiona@example.net removed by bob@example.net. [FRESH][INFO]
--------------------------------------------------------------------------------

View File

@@ -1,5 +1,5 @@
Single#Chat#1001: bob@example.net [bob@example.net] Icon: 4138c52e5bc1c576cda7dd44d088c07.png
--------------------------------------------------------------------------------
Msg#1001: Me (Contact#Contact#Self): We share this account √
Msg#1002: Me (Contact#Contact#Self): I'm Alice too √
Msg#1001: Me (Contact#Contact#Self): We share this account √
--------------------------------------------------------------------------------

View File

@@ -1,7 +1,7 @@
OutBroadcast#Chat#1001: Channel [0 member(s)]
--------------------------------------------------------------------------------
Msg#1001: info (Contact#Contact#Info): Messages are end-to-end encrypted. [NOTICED][INFO]
Msg#1006🔒: Me (Contact#Contact#Self): Member bob@example.net added. [INFO] √
Msg#1008🔒: Me (Contact#Contact#Self): hi √
Msg#1006🔒: Me (Contact#Contact#Self): Member bob@example.net added. [INFO] √
Msg#1009🔒: Me (Contact#Contact#Self): You removed member bob@example.net. [INFO] √
--------------------------------------------------------------------------------