From 586aae690cc8529a9301818db650858ede385150 Mon Sep 17 00:00:00 2001 From: iequidoo Date: Wed, 15 Nov 2023 02:29:58 -0300 Subject: [PATCH] feat: Sync chats deletion across devices Currently broadcast lists creation is synced across devices. Groups creation, in some means, too -- on a group promotion by a first message. Otoh, messages deletion is synced now as well. So, for feature-completeness, sync chats deletion too. --- src/chat.rs | 28 ++++++++++++++++++++++------ src/chat/chat_tests.rs | 37 +++++++++++++++++++++++++++++++++++++ src/test_utils.rs | 9 +++++++++ 3 files changed, 68 insertions(+), 6 deletions(-) diff --git a/src/chat.rs b/src/chat.rs index b9b8e9050..a62c0ce1d 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -434,7 +434,7 @@ impl ChatId { .ok(); } if delete { - self.delete(context).await?; + self.delete_ex(context, Nosync).await?; } Ok(()) } @@ -773,6 +773,10 @@ impl ChatId { /// Deletes a chat. pub async fn delete(self, context: &Context) -> Result<()> { + self.delete_ex(context, Sync).await + } + + pub(crate) async fn delete_ex(self, context: &Context, sync: sync::Sync) -> Result<()> { ensure!( !self.is_special(), "bad chat_id, can not be a special chat: {}", @@ -780,6 +784,10 @@ impl ChatId { ); let chat = Chat::load_from_db(context, self).await?; + let sync_id = match sync { + Nosync => None, + Sync => chat.get_sync_id(context).await?, + }; context .sql @@ -796,12 +804,13 @@ impl ChatId { .await?; context.emit_msgs_changed_without_ids(); - chatlist_events::emit_chatlist_changed(context); - context - .set_config_internal(Config::LastHousekeeping, None) - .await?; - context.scheduler.interrupt_inbox().await; + if let Some(id) = sync_id { + self::sync(context, id, SyncAction::Delete) + .await + .log_err(context) + .ok(); + } if chat.is_self_talk() { let mut msg = Message::new_text(stock_str::self_deleted_msg_body(context).await); @@ -809,6 +818,11 @@ impl ChatId { } chatlist_events::emit_chatlist_changed(context); + context + .set_config_internal(Config::LastHousekeeping, None) + .await?; + context.scheduler.interrupt_inbox().await; + Ok(()) } @@ -4875,6 +4889,7 @@ pub(crate) enum SyncAction { Rename(String), /// Set chat contacts by their addresses. SetContacts(Vec), + Delete, } impl Context { @@ -4934,6 +4949,7 @@ impl Context { } SyncAction::Rename(to) => rename_ex(self, Nosync, chat_id, to).await, SyncAction::SetContacts(addrs) => set_contacts_by_addrs(self, chat_id, addrs).await, + SyncAction::Delete => chat_id.delete_ex(self, Nosync).await, } } diff --git a/src/chat/chat_tests.rs b/src/chat/chat_tests.rs index 4fe4011c4..ee5c62e96 100644 --- a/src/chat/chat_tests.rs +++ b/src/chat/chat_tests.rs @@ -3030,6 +3030,39 @@ async fn test_sync_block_before_first_msg() -> Result<()> { Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_sync_delete_chat() -> Result<()> { + let alice0 = &TestContext::new_alice().await; + let alice1 = &TestContext::new_alice().await; + for a in [alice0, alice1] { + a.set_config_bool(Config::SyncMsgs, true).await?; + } + let bob = TestContext::new_bob().await; + + let ba_chat = bob.create_chat(alice0).await; + let sent_msg = bob.send_text(ba_chat.id, "hi").await; + let a0b_chat_id = alice0.recv_msg(&sent_msg).await.chat_id; + let a1b_chat_id = alice1.recv_msg(&sent_msg).await.chat_id; + a0b_chat_id.accept(alice0).await?; + sync(alice0, alice1).await; + a0b_chat_id.delete(alice0).await?; + sync(alice0, alice1).await; + alice1.assert_no_chat(a1b_chat_id).await; + + let bob_grp_chat_id = bob + .create_group_with_members(ProtectionStatus::Unprotected, "grp", &[alice0]) + .await; + let sent_msg = bob.send_text(bob_grp_chat_id, "hi").await; + let a0_grp_chat_id = alice0.recv_msg(&sent_msg).await.chat_id; + let a1_grp_chat_id = alice1.recv_msg(&sent_msg).await.chat_id; + a0_grp_chat_id.accept(alice0).await?; + sync(alice0, alice1).await; + a0_grp_chat_id.delete(alice0).await?; + sync(alice0, alice1).await; + alice1.assert_no_chat(a1_grp_chat_id).await; + Ok(()) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_sync_adhoc_grp() -> Result<()> { let alice0 = &TestContext::new_alice().await; @@ -3212,6 +3245,10 @@ async fn test_sync_broadcast() -> Result<()> { assert!(get_past_chat_contacts(alice1, a1_broadcast_id) .await? .is_empty()); + + a0_broadcast_id.delete(alice0).await?; + sync(alice0, alice1).await; + alice1.assert_no_chat(a1_broadcast_id).await; Ok(()) } diff --git a/src/test_utils.rs b/src/test_utils.rs index e645af492..243903141 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -743,6 +743,15 @@ impl TestContext { Chat::load_from_db(self, chat_id).await.unwrap() } + pub async fn assert_no_chat(&self, id: ChatId) { + assert!(Chat::load_from_db(self, id).await.is_err()); + assert!(!self + .sql + .exists("SELECT COUNT(*) FROM chats WHERE id=?", (id,)) + .await + .unwrap()); + } + /// Sends out the text message. /// /// This is not hooked up to any SMTP-IMAP pipeline, so the other account must call