diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 7032c5ff1..5d79f618f 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -4405,6 +4405,17 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); */ #define DC_EVENT_MSG_READ 2015 +/** + * Time left until the next message deletion has changed. + * + * The only parameter is the number of seconds left until next message + * deletion. It is rounded up. If the timer is 0, it means there are no + * messages to be deleted in the future. + * + * @param data1 (int) Timer in seconds + * @param data2 (int) 0 + */ +#define DC_EVENT_MSG_DELETE_TIMEOUT_CHANGED 2016 /** * Chat changed. The name or the image of a chat group was changed or members were added or removed. diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 3d1f78bd3..4cd0ba3d0 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -167,6 +167,9 @@ impl ContextWrapper { msg_id.to_u32() as uintptr_t, ); } + Event::MsgDeleteTimeoutChanged { timer } => { + ffi_cb(self, event_id, timer as uintptr_t, 0); + } Event::ChatModified(chat_id) => { ffi_cb(self, event_id, chat_id.to_u32() as uintptr_t, 0); } diff --git a/src/chat.rs b/src/chat.rs index 3d7dc3f22..fee9b03bd 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1,6 +1,6 @@ //! # Chat module -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; use std::path::{Path, PathBuf}; use std::time::{Duration, SystemTime}; @@ -931,6 +931,7 @@ impl Chat { } else { error!(context, "Cannot send message, not configured.",); } + update_autodelete_timeout(context); Ok(MsgId::new(msg_id)) } @@ -1600,11 +1601,48 @@ pub fn marknoticed_all_chats(context: &Context) -> Result<(), Error> { Ok(()) } +/// Emits an event to update the time until the next local deletion. +/// +/// This takes into account only per-chat timeouts, because global device +/// timeouts are at least one hour long and deletion is triggered often enough +/// by user actions. +pub fn update_autodelete_timeout(context: &Context) { + let autodelete_timestamp: Option = match context.sql.query_get_value_result( + "SELECT autodelete_timestamp \ + FROM msgs \ + WHERE autodelete_timestamp != 0 \ + ORDER BY autodelete_timestamp ASC \ + LIMIT 1", + rusqlite::NO_PARAMS, + ) { + Err(err) => { + warn!(context, "Can't calculate next autodelete timeout: {}", err); + return; + } + Ok(autodelete_timestamp) => autodelete_timestamp, + }; + + let timer = if let Some(next_autodelete) = autodelete_timestamp { + let now = time(); + if now > next_autodelete { + 1 + } else { + (next_autodelete - now) + .try_into() + .unwrap_or(u32::MAX) + .saturating_add(1) + } + } else { + 0 + }; + emit_event!(context, Event::MsgDeleteTimeoutChanged { timer }); +} + /// Deletes messages which are expired according to "delete_device_after" setting. /// /// Returns true if any message is deleted, so event can be emitted. If nothing /// has been deleted, returns false. -pub fn delete_device_expired_messages(context: &Context) -> Result { +pub(crate) fn delete_device_expired_messages(context: &Context) -> Result { let now = time(); let threshold_timestamp = match context.get_config_delete_device_after() { @@ -1650,7 +1688,11 @@ pub fn delete_device_expired_messages(context: &Context) -> Result ], )?; - Ok(rows_modified > 0) + let updated = rows_modified > 0; + if updated { + update_autodelete_timeout(context); + } + Ok(updated) } pub fn get_chat_media( diff --git a/src/events.rs b/src/events.rs index 79f053337..2b7073414 100644 --- a/src/events.rs +++ b/src/events.rs @@ -132,6 +132,14 @@ pub enum Event { #[strum(props(id = "2015"))] MsgRead { chat_id: ChatId, msg_id: MsgId }, + /// Time left until the next message deletion has changed. + /// + /// The only parameter is the number of seconds left until next message + /// deletion. It is rounded up. If the timer is 0, it means there are no + /// messages to be deleted in the future. + #[strum(props(id = "2016"))] + MsgDeleteTimeoutChanged { timer: u32 }, + /// Chat changed. The name or the image of a chat group was changed or members were added or removed. /// Or the verify state of a chat has changed. /// See dc_set_chat_name(), dc_set_chat_profile_image(), dc_add_contact_to_chat() diff --git a/src/message.rs b/src/message.rs index b518012e8..ac9b6261f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -6,7 +6,7 @@ use deltachat_derive::{FromSql, ToSql}; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; -use crate::chat::{self, Chat, ChatId}; +use crate::chat::{self, update_autodelete_timeout, Chat, ChatId}; use crate::constants::*; use crate::contact::*; use crate::context::*; @@ -157,10 +157,10 @@ impl MsgId { WHERE (autodelete_timestamp == 0 OR autodelete_timestamp > ?) \ AND id = ?", params![autodelete_timestamp, autodelete_timestamp, self], - ) - } else { - Ok(()) + )?; + update_autodelete_timeout(context); } + Ok(()) } /// Bad evil escape hatch.