Set timestamps for system messages

Previously system messages were always added to the end of the chat,
even if the message triggering them was sent earlier.  This is
especially important for messages about disappearing timer reset
triggered by classic email messages, as they should be placed right
after the message resetting the timer.
This commit is contained in:
link2xt
2021-08-07 14:36:04 +00:00
parent 5a5b80c960
commit 20bf41b4e6
9 changed files with 67 additions and 25 deletions

View File

@@ -381,7 +381,14 @@ impl ChatId {
msg.param.set_cmd(cmd); msg.param.set_cmd(cmd);
send_msg(context, self, &mut msg).await?; send_msg(context, self, &mut msg).await?;
} else { } else {
add_info_msg_with_cmd(context, self, msg_text, cmd).await?; add_info_msg_with_cmd(
context,
self,
msg_text,
cmd,
dc_create_smeared_timestamp(context).await,
)
.await?;
} }
Ok(()) Ok(())
@@ -2981,6 +2988,7 @@ pub(crate) async fn add_info_msg_with_cmd(
chat_id: ChatId, chat_id: ChatId,
text: impl AsRef<str>, text: impl AsRef<str>,
cmd: SystemMessage, cmd: SystemMessage,
timestamp: i64,
) -> Result<MsgId> { ) -> Result<MsgId> {
let rfc724_mid = dc_create_outgoing_rfc724_mid(None, "@device"); let rfc724_mid = dc_create_outgoing_rfc724_mid(None, "@device");
let ephemeral_timer = chat_id.get_ephemeral_timer(context).await?; let ephemeral_timer = chat_id.get_ephemeral_timer(context).await?;
@@ -2997,7 +3005,7 @@ pub(crate) async fn add_info_msg_with_cmd(
chat_id, chat_id,
DC_CONTACT_ID_INFO, DC_CONTACT_ID_INFO,
DC_CONTACT_ID_INFO, DC_CONTACT_ID_INFO,
dc_create_smeared_timestamp(context).await, timestamp,
Viewtype::Text, Viewtype::Text,
MessageState::InNoticed, MessageState::InNoticed,
text.as_ref().to_string(), text.as_ref().to_string(),
@@ -3012,8 +3020,16 @@ pub(crate) async fn add_info_msg_with_cmd(
Ok(msg_id) Ok(msg_id)
} }
pub(crate) async fn add_info_msg(context: &Context, chat_id: ChatId, text: impl AsRef<str>) { /// Adds info message with a given text and `timestamp` to the chat.
if let Err(e) = add_info_msg_with_cmd(context, chat_id, text, SystemMessage::Unknown).await { pub(crate) async fn add_info_msg(
context: &Context,
chat_id: ChatId,
text: impl AsRef<str>,
timestamp: i64,
) {
if let Err(e) =
add_info_msg_with_cmd(context, chat_id, text, SystemMessage::Unknown, timestamp).await
{
warn!(context, "Could not add info msg: {}", e); warn!(context, "Could not add info msg: {}", e);
} }
} }
@@ -3637,7 +3653,7 @@ mod tests {
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo") let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo")
.await .await
.unwrap(); .unwrap();
add_info_msg(&t, chat_id, "foo info").await; add_info_msg(&t, chat_id, "foo info", 200000).await;
let msg = t.get_last_msg_in(chat_id).await; let msg = t.get_last_msg_in(chat_id).await;
assert_eq!(msg.get_chat_id(), chat_id); assert_eq!(msg.get_chat_id(), chat_id);
@@ -3658,6 +3674,7 @@ mod tests {
chat_id, chat_id,
"foo bar info", "foo bar info",
SystemMessage::EphemeralTimerChanged, SystemMessage::EphemeralTimerChanged,
10000,
) )
.await .await
.unwrap(); .unwrap();

View File

@@ -798,6 +798,12 @@ async fn add_parts(
let location_kml_is = mime_parser.location_kml.is_some(); let location_kml_is = mime_parser.location_kml.is_some();
// correct message_timestamp, it should not be used before,
// however, we cannot do this earlier as we need from_id to be set
let in_fresh = state == MessageState::InFresh;
let rcvd_timestamp = time();
let sort_timestamp = calc_sort_timestamp(context, *sent_timestamp, chat_id, in_fresh).await?;
// Apply ephemeral timer changes to the chat. // Apply ephemeral timer changes to the chat.
// //
// Only non-hidden timers are applied now. Timers from hidden // Only non-hidden timers are applied now. Timers from hidden
@@ -825,6 +831,7 @@ async fn add_parts(
context, context,
chat_id, chat_id,
stock_ephemeral_timer_changed(context, ephemeral_timer, from_id).await, stock_ephemeral_timer_changed(context, ephemeral_timer, from_id).await,
sort_timestamp,
) )
.await; .await;
} }
@@ -868,6 +875,7 @@ async fn add_parts(
context, context,
chat_id, chat_id,
format!("Cannot set protection: {}", e), format!("Cannot set protection: {}", e),
sort_timestamp,
) )
.await; .await;
return Ok(chat_id); // do not return an error as this would result in retrying the message return Ok(chat_id); // do not return an error as this would result in retrying the message
@@ -883,12 +891,6 @@ async fn add_parts(
} }
} }
// correct message_timestamp, it should not be used before,
// however, we cannot do this earlier as we need from_id to be set
let in_fresh = state == MessageState::InFresh;
let rcvd_timestamp = time();
let sort_timestamp = calc_sort_timestamp(context, *sent_timestamp, chat_id, in_fresh).await?;
// Ensure replies to messages are sorted after the parent message. // Ensure replies to messages are sorted after the parent message.
// //
// This is useful in a case where sender clocks are not // This is useful in a case where sender clocks are not

View File

@@ -178,7 +178,9 @@ pub async fn try_decrypt(
let mut signatures = HashSet::default(); let mut signatures = HashSet::default();
if let Some(ref mut peerstate) = peerstate { if let Some(ref mut peerstate) = peerstate {
peerstate.handle_fingerprint_change(context).await?; peerstate
.handle_fingerprint_change(context, message_time)
.await?;
if let Some(key) = &peerstate.public_key { if let Some(key) = &peerstate.public_key {
public_keyring_for_validate.add(key.clone()); public_keyring_for_validate.add(key.clone());
} else if let Some(key) = &peerstate.gossip_key { } else if let Some(key) = &peerstate.gossip_key {

View File

@@ -190,7 +190,7 @@ impl Kml {
} }
} }
// location streaming /// Enables location streaming in chat identified by `chat_id` for `seconds` seconds.
pub async fn send_locations_to_chat(context: &Context, chat_id: ChatId, seconds: i64) { pub async fn send_locations_to_chat(context: &Context, chat_id: ChatId, seconds: i64) {
let now = time(); let now = time();
if !(seconds < 0 || chat_id.is_special()) { if !(seconds < 0 || chat_id.is_special()) {
@@ -221,7 +221,7 @@ pub async fn send_locations_to_chat(context: &Context, chat_id: ChatId, seconds:
.unwrap_or_default(); .unwrap_or_default();
} else if 0 == seconds && is_sending_locations_before { } else if 0 == seconds && is_sending_locations_before {
let stock_str = stock_str::msg_location_disabled(context).await; let stock_str = stock_str::msg_location_disabled(context).await;
chat::add_info_msg(context, chat_id, stock_str).await; chat::add_info_msg(context, chat_id, stock_str, now).await;
} }
context.emit_event(EventType::ChatModified(chat_id)); context.emit_event(EventType::ChatModified(chat_id));
if 0 != seconds { if 0 != seconds {
@@ -716,7 +716,8 @@ pub(crate) async fn job_maybe_send_locations_ended(
.await .await
); );
if !(send_begin != 0 && time() <= send_until) { let now = time();
if !(send_begin != 0 && now <= send_until) {
// still streaming - // still streaming -
// may happen as several calls to dc_send_locations_to_chat() // may happen as several calls to dc_send_locations_to_chat()
// do not un-schedule pending DC_MAYBE_SEND_LOC_ENDED jobs // do not un-schedule pending DC_MAYBE_SEND_LOC_ENDED jobs
@@ -735,7 +736,7 @@ pub(crate) async fn job_maybe_send_locations_ended(
); );
let stock_str = stock_str::msg_location_disabled(context).await; let stock_str = stock_str::msg_location_disabled(context).await;
chat::add_info_msg(context, chat_id, stock_str).await; chat::add_info_msg(context, chat_id, stock_str, now).await;
context.emit_event(EventType::ChatModified(chat_id)); context.emit_event(EventType::ChatModified(chat_id));
} }
} }

View File

@@ -19,8 +19,8 @@ use crate::constants::{
use crate::contact::{Contact, Origin}; use crate::contact::{Contact, Origin};
use crate::context::Context; use crate::context::Context;
use crate::dc_tools::{ use crate::dc_tools::{
dc_get_filebytes, dc_get_filemeta, dc_gm2local_offset, dc_read_file, dc_timestamp_to_str, dc_create_smeared_timestamp, dc_get_filebytes, dc_get_filemeta, dc_gm2local_offset,
dc_truncate, time, dc_read_file, dc_timestamp_to_str, dc_truncate, time,
}; };
use crate::ephemeral::Timer as EphemeralTimer; use crate::ephemeral::Timer as EphemeralTimer;
use crate::events::EventType; use crate::events::EventType;
@@ -1805,7 +1805,13 @@ async fn ndn_maybe_add_info_msg(
// Tell the user which of the recipients failed if we know that (because in // Tell the user which of the recipients failed if we know that (because in
// a group, this might otherwise be unclear) // a group, this might otherwise be unclear)
let text = stock_str::failed_sending_to(context, contact.get_display_name()).await; let text = stock_str::failed_sending_to(context, contact.get_display_name()).await;
chat::add_info_msg(context, chat_id, text).await; chat::add_info_msg(
context,
chat_id,
text,
dc_create_smeared_timestamp(context).await,
)
.await;
context.emit_event(EventType::ChatModified(chat_id)); context.emit_event(EventType::ChatModified(chat_id));
} }
} }

View File

@@ -1347,7 +1347,9 @@ async fn update_gossip_peerstates(
peerstate = Some(p); peerstate = Some(p);
} }
if let Some(peerstate) = peerstate { if let Some(peerstate) = peerstate {
peerstate.handle_fingerprint_change(context).await?; peerstate
.handle_fingerprint_change(context, message_time)
.await?;
} }
gossipped_addr.insert(header.addr.clone()); gossipped_addr.insert(header.addr.clone());

View File

@@ -260,7 +260,11 @@ impl Peerstate {
} }
/// Adds a warning to the chat corresponding to peerstate if fingerprint has changed. /// Adds a warning to the chat corresponding to peerstate if fingerprint has changed.
pub(crate) async fn handle_fingerprint_change(&self, context: &Context) -> Result<()> { pub(crate) async fn handle_fingerprint_change(
&self,
context: &Context,
timestamp: i64,
) -> Result<()> {
if self.fingerprint_changed { if self.fingerprint_changed {
if let Some(contact_id) = context if let Some(contact_id) = context
.sql .sql
@@ -273,7 +277,7 @@ impl Peerstate {
let msg = stock_str::contact_setup_changed(context, self.addr.clone()).await; let msg = stock_str::contact_setup_changed(context, self.addr.clone()).await;
chat::add_info_msg(context, chat_id, msg).await; chat::add_info_msg(context, chat_id, msg, timestamp).await;
emit_event!(context, EventType::ChatModified(chat_id)); emit_event!(context, EventType::ChatModified(chat_id));
} else { } else {
bail!("contact with peerstate.addr {:?} not found", &self.addr); bail!("contact with peerstate.addr {:?} not found", &self.addr);

View File

@@ -11,6 +11,7 @@ use crate::config::Config;
use crate::constants::Blocked; use crate::constants::Blocked;
use crate::contact::{addr_normalize, may_be_valid_addr, Contact, Origin}; use crate::contact::{addr_normalize, may_be_valid_addr, Contact, Origin};
use crate::context::Context; use crate::context::Context;
use crate::dc_tools::time;
use crate::key::Fingerprint; use crate::key::Fingerprint;
use crate::log::LogExt; use crate::log::LogExt;
use crate::lot::{Lot, LotState}; use crate::lot::{Lot, LotState};
@@ -160,7 +161,13 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Lot {
.await .await
.log_err(context, "Failed to create (new) chat for contact") .log_err(context, "Failed to create (new) chat for contact")
{ {
chat::add_info_msg(context, chat.id, format!("{} verified.", peerstate.addr)).await; chat::add_info_msg(
context,
chat.id,
format!("{} verified.", peerstate.addr),
time(),
)
.await;
} }
} else if let Some(addr) = addr { } else if let Some(addr) = addr {
lot.state = LotState::QrFprMismatch; lot.state = LotState::QrFprMismatch;

View File

@@ -14,6 +14,7 @@ use crate::config::Config;
use crate::constants::{Blocked, Viewtype, DC_CONTACT_ID_LAST_SPECIAL}; use crate::constants::{Blocked, Viewtype, DC_CONTACT_ID_LAST_SPECIAL};
use crate::contact::{Contact, Origin, VerifiedStatus}; use crate::contact::{Contact, Origin, VerifiedStatus};
use crate::context::Context; use crate::context::Context;
use crate::dc_tools::time;
use crate::e2ee::ensure_secret_key_exists; use crate::e2ee::ensure_secret_key_exists;
use crate::events::EventType; use crate::events::EventType;
use crate::headerdef::HeaderDef; use crate::headerdef::HeaderDef;
@@ -859,7 +860,7 @@ async fn secure_connection_established(
"?" "?"
}; };
let msg = stock_str::contact_verified(context, addr).await; let msg = stock_str::contact_verified(context, addr).await;
chat::add_info_msg(context, contact_chat_id, msg).await; chat::add_info_msg(context, contact_chat_id, msg, time()).await;
emit_event!(context, EventType::ChatModified(contact_chat_id)); emit_event!(context, EventType::ChatModified(contact_chat_id));
info!(context, "StockMessage::ContactVerified posted to 1:1 chat"); info!(context, "StockMessage::ContactVerified posted to 1:1 chat");
@@ -883,7 +884,7 @@ async fn could_not_establish_secure_connection(
) )
.await; .await;
chat::add_info_msg(context, contact_chat_id, &msg).await; chat::add_info_msg(context, contact_chat_id, &msg, time()).await;
error!( error!(
context, context,
"StockMessage::ContactNotVerified posted to 1:1 chat ({})", details "StockMessage::ContactNotVerified posted to 1:1 chat ({})", details