mirror of
https://github.com/chatmail/core.git
synced 2026-05-02 21:06:31 +03:00
fix: Return from dc_get_chatlist(DC_GCL_FOR_FORWARDING) only chats where we can send (#4616)
I.e. exclude from the list the following chats as well: - Read-only mailing lists. - Chats we're not a member of. But as for ProtectionBroken chats, we return them, 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.
This commit is contained in:
@@ -1255,6 +1255,7 @@ impl Chat {
|
|||||||
pub(crate) async fn why_cant_send(&self, context: &Context) -> Result<Option<CantSendReason>> {
|
pub(crate) async fn why_cant_send(&self, context: &Context) -> Result<Option<CantSendReason>> {
|
||||||
use CantSendReason::*;
|
use CantSendReason::*;
|
||||||
|
|
||||||
|
// NB: Don't forget to update Chatlist::try_load() when changing this function!
|
||||||
let reason = if self.id.is_special() {
|
let reason = if self.id.is_special() {
|
||||||
Some(SpecialChat)
|
Some(SpecialChat)
|
||||||
} else if self.is_device_talk() {
|
} else if self.is_device_talk() {
|
||||||
@@ -1263,7 +1264,7 @@ impl Chat {
|
|||||||
Some(ContactRequest)
|
Some(ContactRequest)
|
||||||
} else if self.is_protection_broken() {
|
} else if self.is_protection_broken() {
|
||||||
Some(ProtectionBroken)
|
Some(ProtectionBroken)
|
||||||
} 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)
|
Some(ReadOnlyMailingList)
|
||||||
} else if !self.is_self_in_chat(context).await? {
|
} else if !self.is_self_in_chat(context).await? {
|
||||||
Some(NotAMember)
|
Some(NotAMember)
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ use crate::constants::{
|
|||||||
use crate::contact::{Contact, ContactId};
|
use crate::contact::{Contact, ContactId};
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::message::{Message, MessageState, MsgId};
|
use crate::message::{Message, MessageState, MsgId};
|
||||||
|
use crate::param::{Param, Params};
|
||||||
use crate::stock_str;
|
use crate::stock_str;
|
||||||
use crate::summary::Summary;
|
use crate::summary::Summary;
|
||||||
|
use crate::tools::IsNoneOrEmpty;
|
||||||
|
|
||||||
/// An object representing a single chatlist in memory.
|
/// An object representing a single chatlist in memory.
|
||||||
///
|
///
|
||||||
@@ -204,15 +206,64 @@ impl Chatlist {
|
|||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
} else {
|
} else {
|
||||||
// show normal chatlist
|
let mut ids = if flag_for_forwarding {
|
||||||
let sort_id_up = if flag_for_forwarding {
|
let sort_id_up = ChatId::lookup_by_contact(context, ContactId::SELF)
|
||||||
ChatId::lookup_by_contact(context, ContactId::SELF)
|
|
||||||
.await?
|
.await?
|
||||||
.unwrap_or_default()
|
.unwrap_or_default();
|
||||||
} else {
|
let process_row = |row: &rusqlite::Row| {
|
||||||
ChatId::new(0)
|
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<MsgId> = row.get(3)?;
|
||||||
|
Ok((chat_id, typ, param, msg_id))
|
||||||
};
|
};
|
||||||
let mut ids = context.sql.query_map(
|
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::<std::result::Result<Vec<_>, _>>()
|
||||||
|
.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 {
|
||||||
|
// show normal chatlist
|
||||||
|
context.sql.query_map(
|
||||||
"SELECT c.id, m.id
|
"SELECT c.id, m.id
|
||||||
FROM chats c
|
FROM chats c
|
||||||
LEFT JOIN msgs m
|
LEFT JOIN msgs m
|
||||||
@@ -221,17 +272,18 @@ impl Chatlist {
|
|||||||
SELECT id
|
SELECT id
|
||||||
FROM msgs
|
FROM msgs
|
||||||
WHERE chat_id=c.id
|
WHERE chat_id=c.id
|
||||||
AND (hidden=0 OR state=?1)
|
AND (hidden=0 OR state=?)
|
||||||
ORDER BY timestamp DESC, id DESC LIMIT 1)
|
ORDER BY timestamp DESC, id DESC LIMIT 1)
|
||||||
WHERE c.id>9 AND c.id!=?2
|
WHERE c.id>9 AND c.id!=?
|
||||||
AND (c.blocked=0 OR (c.blocked=2 AND NOT ?3))
|
AND (c.blocked=0 OR c.blocked=2)
|
||||||
AND NOT c.archived=?4
|
AND NOT c.archived=?
|
||||||
GROUP BY c.id
|
GROUP BY c.id
|
||||||
ORDER BY c.id=?5 DESC, c.archived=?6 DESC, IFNULL(m.timestamp,c.created_timestamp) DESC, m.id DESC;",
|
ORDER BY c.id=0 DESC, c.archived=? 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),
|
(MessageState::OutDraft, skip_id, ChatVisibility::Archived, ChatVisibility::Pinned),
|
||||||
process_row,
|
process_row,
|
||||||
process_rows,
|
process_rows,
|
||||||
).await?;
|
).await?
|
||||||
|
};
|
||||||
if !flag_no_specials && get_archived_cnt(context).await? > 0 {
|
if !flag_no_specials && get_archived_cnt(context).await? > 0 {
|
||||||
if ids.is_empty() && flag_add_alldone_hint {
|
if ids.is_empty() && flag_add_alldone_hint {
|
||||||
ids.push((DC_CHAT_ID_ALLDONE_HINT, None));
|
ids.push((DC_CHAT_ID_ALLDONE_HINT, None));
|
||||||
@@ -388,7 +440,9 @@ pub async fn get_last_message_for_chat(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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::message::Viewtype;
|
||||||
use crate::receive_imf::receive_imf;
|
use crate::receive_imf::receive_imf;
|
||||||
use crate::stock_str::StockMessage;
|
use crate::stock_str::StockMessage;
|
||||||
@@ -473,6 +527,14 @@ mod tests {
|
|||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.is_self_talk());
|
.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)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::chat::{
|
|||||||
};
|
};
|
||||||
use crate::chat::{get_chat_msgs, ChatItem, ChatVisibility};
|
use crate::chat::{get_chat_msgs, ChatItem, ChatVisibility};
|
||||||
use crate::chatlist::Chatlist;
|
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::imap::prefetch_should_download;
|
||||||
use crate::message::Message;
|
use crate::message::Message;
|
||||||
use crate::test_utils::{get_chat_msg, TestContext, TestContextManager};
|
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?;
|
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await?;
|
||||||
assert_eq!(chats.len(), 1);
|
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?;
|
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"
|
assert_eq!(contacts.len(), 0); // mailing list recipients and senders do not count as "known contacts"
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ use anyhow::Result;
|
|||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use crate::chat::{Chat, ProtectionStatus};
|
use crate::chat::{Chat, ProtectionStatus};
|
||||||
|
use crate::chatlist::Chatlist;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
use crate::constants::DC_GCL_FOR_FORWARDING;
|
||||||
use crate::contact::VerifiedStatus;
|
use crate::contact::VerifiedStatus;
|
||||||
use crate::contact::{Contact, Origin};
|
use crate::contact::{Contact, Origin};
|
||||||
use crate::message::{Message, Viewtype};
|
use crate::message::{Message, Viewtype};
|
||||||
@@ -657,6 +659,8 @@ async fn test_break_protection_then_verify_again() -> Result<()> {
|
|||||||
|
|
||||||
alice.create_chat(&bob).await;
|
alice.create_chat(&bob).await;
|
||||||
assert_verified(&alice, &bob, ProtectionStatus::Protected).await;
|
assert_verified(&alice, &bob, ProtectionStatus::Protected).await;
|
||||||
|
let chats = Chatlist::try_load(&alice, DC_GCL_FOR_FORWARDING, None, None).await?;
|
||||||
|
assert!(chats.len() == 1);
|
||||||
|
|
||||||
tcm.section("Bob reinstalls DC");
|
tcm.section("Bob reinstalls DC");
|
||||||
drop(bob);
|
drop(bob);
|
||||||
@@ -678,6 +682,8 @@ async fn test_break_protection_then_verify_again() -> Result<()> {
|
|||||||
let chat = alice.get_chat(&bob_new).await;
|
let chat = alice.get_chat(&bob_new).await;
|
||||||
assert_eq!(chat.is_protected(), false);
|
assert_eq!(chat.is_protected(), false);
|
||||||
assert_eq!(chat.is_protection_broken(), true);
|
assert_eq!(chat.is_protection_broken(), true);
|
||||||
|
let chats = Chatlist::try_load(&alice, DC_GCL_FOR_FORWARDING, None, None).await?;
|
||||||
|
assert!(chats.len() == 1);
|
||||||
|
|
||||||
{
|
{
|
||||||
let alice_bob_chat = alice.get_chat(&bob_new).await;
|
let alice_bob_chat = alice.get_chat(&bob_new).await;
|
||||||
|
|||||||
Reference in New Issue
Block a user