From f02888e3cc7e2242f0ec52d5d0074bce90f2d492 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 27 Nov 2022 11:37:24 +0000 Subject: [PATCH 1/4] Add API to get next fresh message Rust: Context.get_next_fresh_msg() C: dc_get_next_fresh_msg() JSON-RPC: get_next_fresh_msg method --- CHANGELOG.md | 1 + deltachat-ffi/deltachat.h | 10 ++++++++++ deltachat-ffi/src/lib.rs | 14 ++++++++++++++ deltachat-jsonrpc/src/api/mod.rs | 9 +++++++++ src/context.rs | 26 ++++++++++++++++++++++++-- 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1f003c4c0..4ec7ba8f4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### API-Changes - Add Python API to send reactions #3762 +- Add API to get next fresh message ### Fixes - Make sure malformed messsages will never block receiving further messages anymore #3771 diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 95e4cf45f8..175ee411d0 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -1278,6 +1278,16 @@ int dc_estimate_deletion_cnt (dc_context_t* context, int from_ser dc_array_t* dc_get_fresh_msgs (dc_context_t* context); +/** + * Returns the ID of the fresh message of any chat. + * + * @memberof dc_context_t + * @param context The context object as returned from dc_context_new(). + * @return Message ID of the next fresh message. Returns 0 on error. + */ +dc_array_t* dc_get_next_fresh_msg (dc_context_t* context); + + /** * Mark all messages in a chat as _noticed_. * _Noticed_ messages are no longer _fresh_ and do not count as being unseen diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 68c7c8d27b..130bb11faf 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -1243,6 +1243,20 @@ pub unsafe extern "C" fn dc_get_fresh_msgs( }) } +#[no_mangle] +pub unsafe extern "C" fn dc_get_next_fresh_msg(context: *mut dc_context_t) -> u32 { + if context.is_null() { + eprintln!("ignoring careless call to dc_get_next_fresh_msg()"); + return 0; + } + let ctx = &*context; + + block_on(ctx.get_next_fresh_msg()) + .log_err(ctx, "Failed to get next fresh message") + .unwrap_or_default() + .to_u32() +} + #[no_mangle] pub unsafe extern "C" fn dc_marknoticed_chat(context: *mut dc_context_t, chat_id: u32) { if context.is_null() { diff --git a/deltachat-jsonrpc/src/api/mod.rs b/deltachat-jsonrpc/src/api/mod.rs index e00db72173..6ef7f67dc8 100644 --- a/deltachat-jsonrpc/src/api/mod.rs +++ b/deltachat-jsonrpc/src/api/mod.rs @@ -372,6 +372,15 @@ impl CommandApi { .collect()) } + /// Returns the message ID of the oldest fresh message. + /// + /// If there are no such messages, waits until there is one. + async fn get_next_fresh_msg(&self, account_id: u32) -> Result { + let ctx = self.get_context(account_id).await?; + let msg_id = ctx.get_next_fresh_msg().await?; + Ok(msg_id.to_u32()) + } + /// Get the number of _fresh_ messages in a chat. /// Typically used to implement a badge with a number in the chatlist. /// diff --git a/src/context.rs b/src/context.rs index eea2479d4e..5cd56e5162 100644 --- a/src/context.rs +++ b/src/context.rs @@ -9,7 +9,7 @@ use std::time::{Duration, Instant, SystemTime}; use anyhow::{ensure, Result}; use async_channel::{self as channel, Receiver, Sender}; -use tokio::sync::{Mutex, RwLock}; +use tokio::sync::{Mutex, Notify, RwLock}; use crate::chat::{get_chat_cnt, ChatId}; use crate::config::Config; @@ -205,6 +205,11 @@ pub struct InnerContext { pub(crate) translated_stockstrings: StockStrings, pub(crate) events: Events, + /// Notify about fresh messages. + /// + /// This notification is used only internally for [`InternalContext.get_next_fresh_msg()`]. + pub(crate) notify_fresh_message: Notify, + pub(crate) scheduler: RwLock>, pub(crate) ratelimit: RwLock, @@ -354,6 +359,7 @@ impl Context { wrong_pw_warning_mutex: Mutex::new(()), translated_stockstrings: stockstrings, events, + notify_fresh_message: Notify::new(), scheduler: RwLock::new(None), ratelimit: RwLock::new(Ratelimit::new(Duration::new(60, 0), 6.0)), // Allow to send 6 messages immediately, no more than once every 10 seconds. quota: RwLock::new(None), @@ -451,9 +457,12 @@ impl Context { self.emit_event(EventType::MsgsChanged { chat_id, msg_id }); } - /// Emits an IncomingMsg event with specified chat and message ids + /// Emits an IncomingMsg event with specified chat and message ids. + /// + /// Notifies `get_next_fresh_msg()` that there might be a new fresh message. pub fn emit_incoming_msg(&self, chat_id: ChatId, msg_id: MsgId) { self.emit_event(EventType::IncomingMsg { chat_id, msg_id }); + self.notify_fresh_message.notify_one(); } /// Returns a receiver for emitted events. @@ -751,6 +760,19 @@ impl Context { Ok(list) } + /// Returns oldest fresh message in unmuted and unblocked chats. + /// + /// If there are no such messages, waits until there is one. + pub async fn get_next_fresh_msg(&self) -> Result { + loop { + if let Some(msg_id) = self.get_fresh_msgs().await?.last() { + return Ok(*msg_id); + } else { + self.notify_fresh_message.notified().await; + } + } + } + /// Searches for messages containing the query string. /// /// If `chat_id` is provided this searches only for messages in this chat, if `chat_id` From d22f62005ca327b71388bb9d4b527dd4d8b46fb0 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 27 Nov 2022 11:38:45 +0000 Subject: [PATCH 2/4] Add PR number to changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ec7ba8f4e..eafec0b65d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ ### API-Changes - Add Python API to send reactions #3762 -- Add API to get next fresh message +- Add API to get next fresh message #3777 ### Fixes - Make sure malformed messsages will never block receiving further messages anymore #3771 From 751edd477278805a09dab6efae703d44568c4133 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 27 Nov 2022 12:37:35 +0000 Subject: [PATCH 3/4] Comment that the function blocks if there are no fresh messages --- deltachat-ffi/deltachat.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 175ee411d0..cb12882715 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -1281,6 +1281,8 @@ dc_array_t* dc_get_fresh_msgs (dc_context_t* context); /** * Returns the ID of the fresh message of any chat. * + * If there is no such message, the function blocks until there is one. + * * @memberof dc_context_t * @param context The context object as returned from dc_context_new(). * @return Message ID of the next fresh message. Returns 0 on error. From 97465bd0e64a58609222daa78e8c1cb3c6ab1ddf Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 27 Nov 2022 14:46:28 +0000 Subject: [PATCH 4/4] Add python bindings for get_next_fresh_message() --- python/src/deltachat/account.py | 5 +++++ python/tests/test_1_online.py | 1 + 2 files changed, 6 insertions(+) diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index d02f37348b..be189c4063 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -371,6 +371,11 @@ def get_fresh_messages(self) -> Generator[Message, None, None]: dc_array = ffi.gc(lib.dc_get_fresh_msgs(self._dc_context), lib.dc_array_unref) yield from iter_array(dc_array, lambda x: Message.from_db(self, x)) + def get_next_fresh_message(self) -> Message: + """Returns the oldest fresh message or waits for a new one to arrive.""" + msg_id = lib.dc_get_next_fresh_msg(self._dc_context) + return Message.from_db(self, msg_id) + def create_chat(self, obj) -> Chat: """Create a 1:1 chat with Account, Contact or e-mail address.""" return self.create_contact(obj).create_chat() diff --git a/python/tests/test_1_online.py b/python/tests/test_1_online.py index 39cbae48cd..b8dd62228b 100644 --- a/python/tests/test_1_online.py +++ b/python/tests/test_1_online.py @@ -710,6 +710,7 @@ def test_send_and_receive_will_encrypt_decrypt(acfactory, lp): fresh_msgs = list(ac1.get_fresh_messages()) assert len(fresh_msgs) == 1 assert fresh_msgs[0] == msg3 + assert ac1.get_next_fresh_message() == msg3 msg3.mark_seen() assert not list(ac1.get_fresh_messages())