diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index ceb13752e..2624c202d 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -1122,8 +1122,8 @@ dc_array_t* dc_get_fresh_msgs (dc_context_t* context); * but are still waiting for being marked as "seen" using dc_markseen_msgs() * (IMAP/MDNs is not done for noticed messages). * - * Calling this function usually results in the event #DC_EVENT_MSGS_CHANGED. - * See also dc_marknoticed_all_chats(), dc_marknoticed_contact() and dc_markseen_msgs(). + * Calling this function usually results in the event #DC_EVENT_MSGS_NOTICED. + * See also dc_marknoticed_contact() and dc_markseen_msgs(). * * @memberof dc_context_t * @param context The context object as returned from dc_context_new(). @@ -1133,16 +1133,6 @@ dc_array_t* dc_get_fresh_msgs (dc_context_t* context); void dc_marknoticed_chat (dc_context_t* context, uint32_t chat_id); -/** - * Same as dc_marknoticed_chat() but for _all_ chats. - * - * @memberof dc_context_t - * @param context The context object as returned from dc_context_new(). - * @return None. - */ -void dc_marknoticed_all_chats (dc_context_t* context); - - /** * Returns all message IDs of the given types in a chat. * Typically used to show a gallery. @@ -1513,11 +1503,13 @@ void dc_forward_msgs (dc_context_t* context, const uint3 /** - * Mark all messages sent by the given contact - * as _noticed_. See also dc_marknoticed_chat() and - * dc_markseen_msgs() + * Mark all messages sent by the given contact as _noticed_. + * This function is typically used to ignore a user in the deaddrop temporarily ("Not now" button). * - * Calling this function usually results in the event #DC_EVENT_MSGS_CHANGED. + * The contact is expected to belong to the deaddrop; + * only one #DC_EVENT_MSGS_NOTICED with chat_id=DC_CHAT_ID_DEADDROP may be emitted. + * + * See also dc_marknoticed_chat() and dc_markseen_msgs() * * @memberof dc_context_t * @param context The context object. @@ -1536,6 +1528,8 @@ void dc_marknoticed_contact (dc_context_t* context, uint32_t co * Moreover, if messages belong to a chat with ephemeral messages enabled, * the ephemeral timer is started for these messages. * + * One #DC_EVENT_MSGS_NOTICED event is emitted per modified chat. + * * @memberof dc_context_t * @param context The context object. * @param msg_ids An array of uint32_t containing all the messages IDs that should be marked as seen. @@ -4607,6 +4601,20 @@ void dc_event_unref(dc_event_t* event); #define DC_EVENT_INCOMING_MSG 2005 +/** + * Messages were marked noticed or seen. + * The ui may update badge counters or stop showing a chatlist-item with a bold font. + * + * This event is emitted eg. when calling dc_markseen_msgs(), dc_marknoticed_chat() or dc_marknoticed_contact(). + * Do not try to derive the state of an item from just the fact you received the event; + * use eg. dc_msg_get_state() or dc_get_fresh_msg_cnt() for this purpose. + * + * @param data1 (int) chat_id + * @param data2 0 + */ +#define DC_EVENT_MSGS_NOTICED 2008 + + /** * A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to * DC_STATE_OUT_DELIVERED, see dc_msg_get_state(). diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index ab37466e9..213cb8b4e 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -362,6 +362,7 @@ pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc: | EventType::ErrorSelfNotInGroup(_) => 0, EventType::MsgsChanged { chat_id, .. } | EventType::IncomingMsg { chat_id, .. } + | EventType::MsgsNoticed(chat_id) | EventType::MsgDelivered { chat_id, .. } | EventType::MsgFailed { chat_id, .. } | EventType::MsgRead { chat_id, .. } @@ -407,6 +408,7 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc: | EventType::ConfigureProgress { .. } | EventType::ImexProgress(_) | EventType::ImexFileWritten(_) + | EventType::MsgsNoticed(_) | EventType::ChatModified(_) => 0, EventType::MsgsChanged { msg_id, .. } | EventType::IncomingMsg { msg_id, .. } @@ -446,6 +448,7 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut } EventType::MsgsChanged { .. } | EventType::IncomingMsg { .. } + | EventType::MsgsNoticed(_) | EventType::MsgDelivered { .. } | EventType::MsgFailed { .. } | EventType::MsgRead { .. } @@ -970,22 +973,6 @@ pub unsafe extern "C" fn dc_marknoticed_chat(context: *mut dc_context_t, chat_id }) } -#[no_mangle] -pub unsafe extern "C" fn dc_marknoticed_all_chats(context: *mut dc_context_t) { - if context.is_null() { - eprintln!("ignoring careless call to dc_marknoticed_all_chats()"); - return; - } - let ctx = &*context; - - block_on(async move { - chat::marknoticed_all_chats(&ctx) - .await - .log_err(ctx, "Failed marknoticed all chats") - .unwrap_or(()) - }) -} - fn from_prim(s: S) -> Option where T: FromPrimitive, diff --git a/python/tests/test_account.py b/python/tests/test_account.py index d54f6fb16..80dc0ce8c 100644 --- a/python/tests/test_account.py +++ b/python/tests/test_account.py @@ -882,7 +882,13 @@ class TestOnlineAccount: lp.sec("mark messages as seen on ac2, wait for changes on ac1") ac2.direct_imap.idle_start() ac1.direct_imap.idle_start() + ac2.mark_seen_messages([msg2, msg4]) + ev = ac2._evtracker.get_matching("DC_EVENT_MSGS_NOTICED") + assert msg2.chat.id == msg4.chat.id + assert ev.data1 == msg2.chat.id + assert ev.data2 == 0 + ac2.direct_imap.idle_check(terminate=True) lp.step("1") for i in range(2): diff --git a/src/chat.rs b/src/chat.rs index 08e03190b..e4a164f5b 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1795,42 +1795,7 @@ pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<(), ) .await?; - context.emit_event(EventType::MsgsChanged { - chat_id: ChatId::new(0), - msg_id: MsgId::new(0), - }); - - Ok(()) -} - -pub async fn marknoticed_all_chats(context: &Context) -> Result<(), Error> { - if !context - .sql - .exists( - "SELECT id - FROM msgs - WHERE state=10;", - paramsv![], - ) - .await? - { - return Ok(()); - } - - context - .sql - .execute( - "UPDATE msgs - SET state=13 - WHERE state=10;", - paramsv![], - ) - .await?; - - context.emit_event(EventType::MsgsChanged { - msg_id: MsgId::new(0), - chat_id: ChatId::new(0), - }); + context.emit_event(EventType::MsgsNoticed(chat_id)); Ok(()) } diff --git a/src/contact.rs b/src/contact.rs index 32059c2d0..8c8524e07 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -16,7 +16,7 @@ use crate::error::{bail, ensure, format_err, Result}; use crate::events::EventType; use crate::key::{DcKey, SignedPublicKey}; use crate::login_param::LoginParam; -use crate::message::{MessageState, MsgId}; +use crate::message::MessageState; use crate::mimeparser::AvatarAction; use crate::param::*; use crate::peerstate::*; @@ -260,10 +260,9 @@ impl Contact { Ok(contact_id) } - /// Mark all messages sent by the given contact - /// as *noticed*. See also dc_marknoticed_chat() and dc_markseen_msgs() - /// - /// Calling this function usually results in the event `#DC_EVENT_MSGS_CHANGED`. + /// Mark messages from a contact as noticed. + /// The contact is expected to belong to the deaddrop, + /// therefore, DC_EVENT_MSGS_NOTICED(DC_CHAT_ID_DEADDROP) is emitted. pub async fn mark_noticed(context: &Context, id: u32) { if context .sql @@ -274,10 +273,7 @@ impl Contact { .await .is_ok() { - context.emit_event(EventType::MsgsChanged { - chat_id: ChatId::new(0), - msg_id: MsgId::new(0), - }); + context.emit_event(EventType::MsgsNoticed(ChatId::new(DC_CHAT_ID_DEADDROP))); } } diff --git a/src/events.rs b/src/events.rs index 87006a49b..f884a7589 100644 --- a/src/events.rs +++ b/src/events.rs @@ -186,6 +186,11 @@ pub enum EventType { #[strum(props(id = "2005"))] IncomingMsg { chat_id: ChatId, msg_id: MsgId }, + /// Messages were seen or noticed. + /// chat id is always set. + #[strum(props(id = "2008"))] + MsgsNoticed(ChatId), + /// A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to /// DC_STATE_OUT_DELIVERED, see dc_msg_get_state(). #[strum(props(id = "2010"))] diff --git a/src/message.rs b/src/message.rs index e4311c3aa..9230313c5 100644 --- a/src/message.rs +++ b/src/message.rs @@ -19,6 +19,7 @@ use crate::mimeparser::{FailureReport, SystemMessage}; use crate::param::*; use crate::pgp::*; use crate::stock::StockMessage; +use std::collections::BTreeMap; // In practice, the user additionally cuts the string themselves // pixel-accurate. @@ -1226,6 +1227,7 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec) -> bool { .with_conn(move |conn| { let mut stmt = conn.prepare_cached(concat!( "SELECT", + " m.chat_id AS chat_id,", " m.state AS state,", " c.blocked AS blocked", " FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id", @@ -1236,6 +1238,7 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec) -> bool { for id in msg_ids.into_iter() { let query_res = stmt.query_row(paramsv![id], |row| { Ok(( + row.get::<_, ChatId>("chat_id")?, row.get::<_, MessageState>("state")?, row.get::<_, Option>("blocked")? .unwrap_or_default(), @@ -1244,8 +1247,8 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec) -> bool { if let Err(rusqlite::Error::QueryReturnedNoRows) = query_res { continue; } - let (state, blocked) = query_res.map_err(Into::::into)?; - msgs.push((id, state, blocked)); + let (chat_id, state, blocked) = query_res.map_err(Into::::into)?; + msgs.push((id, chat_id, state, blocked)); } Ok(msgs) @@ -1253,9 +1256,9 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec) -> bool { .await .unwrap_or_default(); - let mut send_event = false; + let mut updated_chat_ids = BTreeMap::new(); - for (id, curr_state, curr_blocked) in msgs.into_iter() { + for (id, curr_chat_id, curr_state, curr_blocked) in msgs.into_iter() { if let Err(err) = id.start_ephemeral_timer(context).await { error!( context, @@ -1274,19 +1277,16 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec) -> bool { job::Job::new(Action::MarkseenMsgOnImap, id.to_u32(), Params::new(), 0), ) .await; - send_event = true; + updated_chat_ids.insert(curr_chat_id, true); } } else if curr_state == MessageState::InFresh { update_msg_state(context, id, MessageState::InNoticed).await; - send_event = true; + updated_chat_ids.insert(ChatId::new(DC_CHAT_ID_DEADDROP), true); } } - if send_event { - context.emit_event(EventType::MsgsChanged { - chat_id: ChatId::new(0), - msg_id: MsgId::new(0), - }); + for updated_chat_id in updated_chat_ids.keys() { + context.emit_event(EventType::MsgsNoticed(*updated_chat_id)); } true