1
1
//! # Messages and their identifiers.
2
2
3
3
use std:: collections:: BTreeSet ;
4
+ use std:: collections:: HashSet ;
4
5
use std:: path:: { Path , PathBuf } ;
5
6
use std:: str;
6
7
@@ -31,6 +32,7 @@ use crate::pgp::split_armored_data;
31
32
use crate :: reaction:: get_msg_reactions;
32
33
use crate :: sql;
33
34
use crate :: summary:: Summary ;
35
+ use crate :: sync:: SyncData ;
34
36
use crate :: tools:: {
35
37
buf_compress, buf_decompress, get_filebytes, get_filemeta, gm2local_offset, read_file,
36
38
sanitize_filename, time, timestamp_to_str, truncate,
@@ -1651,35 +1653,80 @@ pub async fn get_mime_headers(context: &Context, msg_id: MsgId) -> Result<Vec<u8
1651
1653
Ok ( headers)
1652
1654
}
1653
1655
1656
+ /// Delete a single message from the database, including references in other tables.
1657
+ /// This may be called in batches; the final events are emitted in delete_msgs_locally_done() then.
1658
+ pub ( crate ) async fn delete_msg_locally ( context : & Context , msg : & Message ) -> Result < ( ) > {
1659
+ if msg. location_id > 0 {
1660
+ delete_poi_location ( context, msg. location_id ) . await ?;
1661
+ }
1662
+ let on_server = true ;
1663
+ msg. id
1664
+ . trash ( context, on_server)
1665
+ . await
1666
+ . with_context ( || format ! ( "Unable to trash message {}" , msg. id) ) ?;
1667
+
1668
+ context. emit_event ( EventType :: MsgDeleted {
1669
+ chat_id : msg. chat_id ,
1670
+ msg_id : msg. id ,
1671
+ } ) ;
1672
+
1673
+ if msg. viewtype == Viewtype :: Webxdc {
1674
+ context. emit_event ( EventType :: WebxdcInstanceDeleted { msg_id : msg. id } ) ;
1675
+ }
1676
+
1677
+ let logging_xdc_id = context
1678
+ . debug_logging
1679
+ . read ( )
1680
+ . expect ( "RwLock is poisoned" )
1681
+ . as_ref ( )
1682
+ . map ( |dl| dl. msg_id ) ;
1683
+ if let Some ( id) = logging_xdc_id {
1684
+ if id == msg. id {
1685
+ set_debug_logging_xdc ( context, None ) . await ?;
1686
+ }
1687
+ }
1688
+
1689
+ Ok ( ( ) )
1690
+ }
1691
+
1692
+ /// Do final events and jobs after batch deletion using calls to delete_msg_locally().
1693
+ /// To avoid additional database queries, collecting data is up to the caller.
1694
+ pub ( crate ) async fn delete_msgs_locally_done (
1695
+ context : & Context ,
1696
+ msg_ids : & [ MsgId ] ,
1697
+ modified_chat_ids : HashSet < ChatId > ,
1698
+ ) -> Result < ( ) > {
1699
+ for modified_chat_id in modified_chat_ids {
1700
+ context. emit_msgs_changed_without_msg_id ( modified_chat_id) ;
1701
+ chatlist_events:: emit_chatlist_item_changed ( context, modified_chat_id) ;
1702
+ }
1703
+ if !msg_ids. is_empty ( ) {
1704
+ context. emit_msgs_changed_without_ids ( ) ;
1705
+ chatlist_events:: emit_chatlist_changed ( context) ;
1706
+ // Run housekeeping to delete unused blobs.
1707
+ context
1708
+ . set_config_internal ( Config :: LastHousekeeping , None )
1709
+ . await ?;
1710
+ }
1711
+ Ok ( ( ) )
1712
+ }
1713
+
1654
1714
/// Deletes requested messages
1655
1715
/// by moving them to the trash chat
1656
1716
/// and scheduling for deletion on IMAP.
1657
1717
pub async fn delete_msgs ( context : & Context , msg_ids : & [ MsgId ] ) -> Result < ( ) > {
1658
- let mut modified_chat_ids = BTreeSet :: new ( ) ;
1718
+ let mut modified_chat_ids = HashSet :: new ( ) ;
1719
+ let mut deleted_rfc724_mid = Vec :: new ( ) ;
1659
1720
let mut res = Ok ( ( ) ) ;
1660
1721
1661
1722
for & msg_id in msg_ids {
1662
1723
let msg = Message :: load_from_db ( context, msg_id) . await ?;
1663
- if msg. location_id > 0 {
1664
- delete_poi_location ( context, msg. location_id ) . await ?;
1665
- }
1666
- let on_server = true ;
1667
- msg_id
1668
- . trash ( context, on_server)
1669
- . await
1670
- . with_context ( || format ! ( "Unable to trash message {msg_id}" ) ) ?;
1671
-
1672
- context. emit_event ( EventType :: MsgDeleted {
1673
- chat_id : msg. chat_id ,
1674
- msg_id,
1675
- } ) ;
1676
-
1677
- if msg. viewtype == Viewtype :: Webxdc {
1678
- context. emit_event ( EventType :: WebxdcInstanceDeleted { msg_id } ) ;
1679
- }
1724
+ delete_msg_locally ( context, & msg) . await ?;
1680
1725
1681
1726
modified_chat_ids. insert ( msg. chat_id ) ;
1682
1727
1728
+ deleted_rfc724_mid. push ( msg. rfc724_mid . clone ( ) ) ;
1729
+
1683
1730
let target = context. get_delete_msgs_target ( ) . await ?;
1684
1731
let update_db = |trans : & mut rusqlite:: Transaction | {
1685
1732
trans. execute (
@@ -1694,38 +1741,20 @@ pub async fn delete_msgs(context: &Context, msg_ids: &[MsgId]) -> Result<()> {
1694
1741
res = Err ( e) ;
1695
1742
continue ;
1696
1743
}
1697
-
1698
- let logging_xdc_id = context
1699
- . debug_logging
1700
- . read ( )
1701
- . expect ( "RwLock is poisoned" )
1702
- . as_ref ( )
1703
- . map ( |dl| dl. msg_id ) ;
1704
-
1705
- if let Some ( id) = logging_xdc_id {
1706
- if id == msg_id {
1707
- set_debug_logging_xdc ( context, None ) . await ?;
1708
- }
1709
- }
1710
1744
}
1711
1745
res?;
1712
1746
1713
- for modified_chat_id in modified_chat_ids {
1714
- context. emit_msgs_changed_without_msg_id ( modified_chat_id) ;
1715
- chatlist_events:: emit_chatlist_item_changed ( context, modified_chat_id) ;
1716
- }
1747
+ delete_msgs_locally_done ( context, msg_ids, modified_chat_ids) . await ?;
1717
1748
1718
- if !msg_ids. is_empty ( ) {
1719
- context. emit_msgs_changed_without_ids ( ) ;
1720
- chatlist_events:: emit_chatlist_changed ( context) ;
1721
- // Run housekeeping to delete unused blobs.
1722
- context
1723
- . set_config_internal ( Config :: LastHousekeeping , None )
1724
- . await ?;
1725
- }
1749
+ context
1750
+ . add_sync_item ( SyncData :: DeleteMessages {
1751
+ msgs : deleted_rfc724_mid,
1752
+ } )
1753
+ . await ?;
1726
1754
1727
- // Interrupt Inbox loop to start message deletion and run housekeeping.
1755
+ // Interrupt Inbox loop to start message deletion, run housekeeping and call send_sync_msg() .
1728
1756
context. scheduler . interrupt_inbox ( ) . await ;
1757
+
1729
1758
Ok ( ( ) )
1730
1759
}
1731
1760
0 commit comments