diff --git a/deltachat-jsonrpc/src/api.rs b/deltachat-jsonrpc/src/api.rs index 2bdb9585c..9df9e1f9b 100644 --- a/deltachat-jsonrpc/src/api.rs +++ b/deltachat-jsonrpc/src/api.rs @@ -2775,6 +2775,15 @@ impl CommandApi { .map(JsonrpcAppSource::from_core_type), ) } + + /// Returns true if all accounts have empty outgoing message queue. + /// + /// This API is intended to be used by UIs + /// to request that operating system does not put the application in background + /// while there are still outgoing messages that are not sent out. + async fn is_sending_finished(&self) -> Result { + self.accounts.read().await.is_sending_finished().await + } } // Helper functions (to prevent code duplication) diff --git a/deltachat-rpc-client/src/deltachat_rpc_client/deltachat.py b/deltachat-rpc-client/src/deltachat_rpc_client/deltachat.py index 8bde4c586..9b976210d 100644 --- a/deltachat-rpc-client/src/deltachat_rpc_client/deltachat.py +++ b/deltachat-rpc-client/src/deltachat_rpc_client/deltachat.py @@ -67,3 +67,7 @@ class DeltaChat: def stop_sending_locations(self) -> None: """Stop sending locations to all chats.""" return self.rpc.stop_sending_locations() + + def is_sending_finished(self) -> bool: + """Return true if sending queues of all accounts are empty.""" + return self.rpc.is_sending_finished() diff --git a/deltachat-rpc-client/tests/test_something.py b/deltachat-rpc-client/tests/test_something.py index d67f7d72a..d5c0dabdf 100644 --- a/deltachat-rpc-client/tests/test_something.py +++ b/deltachat-rpc-client/tests/test_something.py @@ -1435,3 +1435,35 @@ def test_large_message(acf, rpcdata) -> None: assert msg.id == msgs_changed_event.msg_id snapshot = msg.get_snapshot() assert snapshot.text == "Hello World, this message is bigger than 5 bytes" + + +def test_is_sending_finished(dc, acf) -> None: + alice, bob = acf.get_online_accounts(2) + + alice_chat_bob = alice.create_chat(bob) + bob_chat_alice = bob.create_chat(alice) + + assert dc.is_sending_finished() + + alice_chat_bob.send_text("Hello!") + alice.wait_for_event(EventType.SMTP_MESSAGE_SENT) + + assert dc.is_sending_finished() + + alice.stop_io() + bob.stop_io() + + bob_chat_alice.send_text("Hello back!") + alice_chat_bob.send_text("Hello again!") + + assert not dc.is_sending_finished() + + alice.start_io() + alice.wait_for_event(EventType.SMTP_MESSAGE_SENT) + + assert not dc.is_sending_finished() + + bob.start_io() + bob.wait_for_event(EventType.SMTP_MESSAGE_SENT) + + assert dc.is_sending_finished() diff --git a/src/accounts.rs b/src/accounts.rs index 1efaa63d1..8e5fec8b0 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -26,6 +26,7 @@ use crate::events::{Event, EventEmitter, EventType, Events}; use crate::location; use crate::log::warn; use crate::push::PushSubscriber; +use crate::smtp; use crate::stock_str::StockStrings; /// Account manager, that can handle multiple accounts in a single place. @@ -509,6 +510,20 @@ impl Accounts { ) } + /// Returns true if there are no pending messages for sending. + /// + /// This is intended to be used by UIs to request not moving the app to background + /// when there are messages left in the queue. + pub async fn is_sending_finished(&self) -> Result { + let accounts: Vec = self.accounts.values().cloned().collect(); + for account in accounts { + if !smtp::is_queue_empty(&account).await? { + return Ok(false); + } + } + Ok(true) + } + /// Interrupts ongoing background_fetch() call, /// making it return early. /// diff --git a/src/smtp.rs b/src/smtp.rs index 9d26c7164..faf885384 100644 --- a/src/smtp.rs +++ b/src/smtp.rs @@ -751,3 +751,9 @@ pub(crate) async fn add_self_recipients( Ok(()) } + +/// Returns true if SMTP queue is empty. +pub(crate) async fn is_queue_empty(context: &Context) -> Result { + let sending_finished = !context.sql.exists("SELECT COUNT(*) FROM smtp", ()).await?; + Ok(sending_finished) +}