diff --git a/src/chat.rs b/src/chat.rs index 387593e90..e93fefabe 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1241,13 +1241,14 @@ impl Chat { pub(crate) async fn why_cant_send(&self, context: &Context) -> Result> { use CantSendReason::*; + // NB: Don't forget to update Chatlist::try_load() when changing this function! let reason = if self.id.is_special() { Some(SpecialChat) } else if self.is_device_talk() { Some(DeviceChat) } else if self.is_contact_request() { Some(ContactRequest) - } else if self.is_mailing_list() && self.param.get(Param::ListPost).is_none_or_empty() { + } else if self.is_mailing_list() && self.get_mailinglist_addr().is_none_or_empty() { Some(ReadOnlyMailingList) } else if !self.is_self_in_chat(context).await? { Some(NotAMember) diff --git a/src/chatlist.rs b/src/chatlist.rs index df8daac35..c7a2e4b8a 100644 --- a/src/chatlist.rs +++ b/src/chatlist.rs @@ -10,8 +10,10 @@ use crate::constants::{ use crate::contact::{Contact, ContactId}; use crate::context::Context; use crate::message::{Message, MessageState, MsgId}; +use crate::param::{Param, Params}; use crate::stock_str; use crate::summary::Summary; +use crate::tools::IsNoneOrEmpty; /// An object representing a single chatlist in memory. /// @@ -204,34 +206,84 @@ impl Chatlist { ) .await? } else { - // show normal chatlist - let sort_id_up = if flag_for_forwarding { - ChatId::lookup_by_contact(context, ContactId::SELF) + let mut ids = if flag_for_forwarding { + let sort_id_up = ChatId::lookup_by_contact(context, ContactId::SELF) .await? - .unwrap_or_default() + .unwrap_or_default(); + let process_row = |row: &rusqlite::Row| { + let chat_id: ChatId = row.get(0)?; + let typ: Chattype = row.get(1)?; + let param: Params = row.get::<_, String>(2)?.parse().unwrap_or_default(); + let msg_id: Option = row.get(3)?; + Ok((chat_id, typ, param, msg_id)) + }; + let process_rows = |rows: rusqlite::MappedRows<_>| { + rows.filter_map(|row: std::result::Result<(_, _, Params, _), _>| match row { + Ok((chat_id, typ, param, msg_id)) => { + if typ == Chattype::Mailinglist + && param.get(Param::ListPost).is_none_or_empty() + { + None + } else { + Some(Ok((chat_id, msg_id))) + } + } + Err(e) => Some(Err(e)), + }) + .collect::, _>>() + .map_err(Into::into) + }; + // Return ProtectionBroken chats also, as that may happen to a verified chat at any + // time. It may be confusing if a chat that is normally in the list disappears + // suddenly. The UI need to deal with that case anyway. + context.sql.query_map( + "SELECT c.id, c.type, c.param, m.id + FROM chats c + LEFT JOIN msgs m + ON c.id=m.chat_id + AND m.id=( + SELECT id + FROM msgs + WHERE chat_id=c.id + AND (hidden=0 OR state=?) + ORDER BY timestamp DESC, id DESC LIMIT 1) + WHERE c.id>9 AND c.id!=? + AND c.blocked=0 + AND NOT c.archived=? + AND (c.type!=? OR c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?)) + GROUP BY c.id + ORDER BY c.id=? DESC, c.archived=? DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;", + ( + MessageState::OutDraft, skip_id, ChatVisibility::Archived, + Chattype::Group, ContactId::SELF, + sort_id_up, ChatVisibility::Pinned, + ), + process_row, + process_rows, + ).await? } else { - ChatId::new(0) + // show normal chatlist + context.sql.query_map( + "SELECT c.id, m.id + FROM chats c + LEFT JOIN msgs m + ON c.id=m.chat_id + AND m.id=( + SELECT id + FROM msgs + WHERE chat_id=c.id + AND (hidden=0 OR state=?) + ORDER BY timestamp DESC, id DESC LIMIT 1) + WHERE c.id>9 AND c.id!=? + AND (c.blocked=0 OR c.blocked=2) + AND NOT c.archived=? + GROUP BY c.id + ORDER BY c.id=0 DESC, c.archived=? DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;", + (MessageState::OutDraft, skip_id, ChatVisibility::Archived, ChatVisibility::Pinned), + process_row, + process_rows, + ).await? }; - let mut ids = context.sql.query_map( - "SELECT c.id, m.id - FROM chats c - LEFT JOIN msgs m - ON c.id=m.chat_id - AND m.id=( - SELECT id - FROM msgs - WHERE chat_id=c.id - AND (hidden=0 OR state=?1) - ORDER BY timestamp DESC, id DESC LIMIT 1) - WHERE c.id>9 AND c.id!=?2 - AND (c.blocked=0 OR (c.blocked=2 AND NOT ?3)) - AND NOT c.archived=?4 - GROUP BY c.id - ORDER BY c.id=?5 DESC, c.archived=?6 DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;", - (MessageState::OutDraft, skip_id, flag_for_forwarding, ChatVisibility::Archived, sort_id_up, ChatVisibility::Pinned), - process_row, - process_rows, - ).await?; if !flag_no_specials && get_archived_cnt(context).await? > 0 { if ids.is_empty() && flag_add_alldone_hint { ids.push((DC_CHAT_ID_ALLDONE_HINT, None)); @@ -388,7 +440,9 @@ pub async fn get_last_message_for_chat( #[cfg(test)] mod tests { use super::*; - use crate::chat::{create_group_chat, get_chat_contacts, ProtectionStatus}; + use crate::chat::{ + create_group_chat, get_chat_contacts, remove_contact_from_chat, ProtectionStatus, + }; use crate::message::Viewtype; use crate::receive_imf::receive_imf; use crate::stock_str::StockMessage; @@ -473,6 +527,14 @@ mod tests { .await .unwrap() .is_self_talk()); + + remove_contact_from_chat(&t, chats.get_chat_id(1).unwrap(), ContactId::SELF) + .await + .unwrap(); + let chats = Chatlist::try_load(&t, DC_GCL_FOR_FORWARDING, None, None) + .await + .unwrap(); + assert!(chats.len() == 1); } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] diff --git a/src/receive_imf/tests.rs b/src/receive_imf/tests.rs index 75f973274..c610961e1 100644 --- a/src/receive_imf/tests.rs +++ b/src/receive_imf/tests.rs @@ -8,7 +8,7 @@ use crate::chat::{ }; use crate::chat::{get_chat_msgs, ChatItem, ChatVisibility}; use crate::chatlist::Chatlist; -use crate::constants::DC_GCL_NO_SPECIALS; +use crate::constants::{DC_GCL_FOR_FORWARDING, DC_GCL_NO_SPECIALS}; use crate::imap::prefetch_should_download; use crate::message::Message; use crate::test_utils::{get_chat_msg, TestContext, TestContextManager}; @@ -793,6 +793,8 @@ async fn test_github_mailing_list() -> Result<()> { let chats = Chatlist::try_load(&t.ctx, 0, None, None).await?; assert_eq!(chats.len(), 1); + let chats = Chatlist::try_load(&t.ctx, DC_GCL_FOR_FORWARDING, None, None).await?; + assert_eq!(chats.len(), 0); let contacts = Contact::get_all(&t.ctx, 0, None).await?; assert_eq!(contacts.len(), 0); // mailing list recipients and senders do not count as "known contacts"