mirror of
https://github.com/chatmail/core.git
synced 2026-05-09 01:46:30 +03:00
feat: Assign message to ad-hoc group with matching name and members (#5385)
This should fix ad-hoc groups splitting when messages are fetched out of order from different folders or otherwise reordered, or some messages are missing so that the messages reference chain is broken, or a member was removed from the thread and readded later, etc. Even if this way two different threads are merged, it looks acceptable, having many threads with the same name/subject and members isn't a common use case.
This commit is contained in:
@@ -35,7 +35,7 @@ use crate::peerstate::Peerstate;
|
|||||||
use crate::reaction::{set_msg_reaction, Reaction};
|
use crate::reaction::{set_msg_reaction, Reaction};
|
||||||
use crate::securejoin::{self, handle_securejoin_handshake, observe_securejoin_on_other_device};
|
use crate::securejoin::{self, handle_securejoin_handshake, observe_securejoin_on_other_device};
|
||||||
use crate::simplify;
|
use crate::simplify;
|
||||||
use crate::sql;
|
use crate::sql::{self, params_iter};
|
||||||
use crate::stock_str;
|
use crate::stock_str;
|
||||||
use crate::sync::Sync::*;
|
use crate::sync::Sync::*;
|
||||||
use crate::tools::{self, buf_compress, remove_subject_prefix};
|
use crate::tools::{self, buf_compress, remove_subject_prefix};
|
||||||
@@ -1829,9 +1829,6 @@ async fn lookup_chat_or_create_adhoc_group(
|
|||||||
{
|
{
|
||||||
return Ok(Some((new_chat_id, new_chat_id_blocked)));
|
return Ok(Some((new_chat_id, new_chat_id_blocked)));
|
||||||
}
|
}
|
||||||
if !allow_creation {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
// Partial download may be an encrypted message with protected Subject header. We do not want to
|
// Partial download may be an encrypted message with protected Subject header. We do not want to
|
||||||
// create a group with "..." or "Encrypted message" as a subject. The same is for undecipherable
|
// create a group with "..." or "Encrypted message" as a subject. The same is for undecipherable
|
||||||
// messages. Instead, assign the message to 1:1 chat with the sender.
|
// messages. Instead, assign the message to 1:1 chat with the sender.
|
||||||
@@ -1854,6 +1851,48 @@ async fn lookup_chat_or_create_adhoc_group(
|
|||||||
.get_subject()
|
.get_subject()
|
||||||
.map(|s| remove_subject_prefix(&s))
|
.map(|s| remove_subject_prefix(&s))
|
||||||
.unwrap_or_else(|| "👥📧".to_string());
|
.unwrap_or_else(|| "👥📧".to_string());
|
||||||
|
let mut contact_ids = Vec::with_capacity(to_ids.len() + 1);
|
||||||
|
contact_ids.extend(to_ids);
|
||||||
|
if !contact_ids.contains(&from_id) {
|
||||||
|
contact_ids.push(from_id);
|
||||||
|
}
|
||||||
|
if let Some((chat_id, blocked)) = context
|
||||||
|
.sql
|
||||||
|
.query_row_optional(
|
||||||
|
&format!(
|
||||||
|
"SELECT c.id, c.blocked
|
||||||
|
FROM chats c INNER JOIN msgs m ON c.id=m.chat_id
|
||||||
|
WHERE m.hidden=0 AND c.grpid='' AND c.name=?
|
||||||
|
AND (SELECT COUNT(*) FROM chats_contacts
|
||||||
|
WHERE chat_id=c.id)=?
|
||||||
|
AND (SELECT COUNT(*) FROM chats_contacts
|
||||||
|
WHERE chat_id=c.id
|
||||||
|
AND contact_id NOT IN ({}))=0
|
||||||
|
ORDER BY m.timestamp DESC",
|
||||||
|
sql::repeat_vars(contact_ids.len()),
|
||||||
|
),
|
||||||
|
rusqlite::params_from_iter(
|
||||||
|
params_iter(&[&grpname])
|
||||||
|
.chain(params_iter(&[contact_ids.len()]))
|
||||||
|
.chain(params_iter(&contact_ids)),
|
||||||
|
),
|
||||||
|
|row| {
|
||||||
|
let id: ChatId = row.get(0)?;
|
||||||
|
let blocked: Blocked = row.get(1)?;
|
||||||
|
Ok((id, blocked))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
info!(
|
||||||
|
context,
|
||||||
|
"Assigning message to ad-hoc group {chat_id} with matching name and members."
|
||||||
|
);
|
||||||
|
return Ok(Some((chat_id, blocked)));
|
||||||
|
}
|
||||||
|
if !allow_creation {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
create_adhoc_group(
|
create_adhoc_group(
|
||||||
context,
|
context,
|
||||||
mime_parser,
|
mime_parser,
|
||||||
|
|||||||
@@ -204,6 +204,71 @@ async fn test_adhoc_group_show_all() {
|
|||||||
assert_eq!(chat::get_chat_contacts(&t, chat_id).await.unwrap().len(), 3);
|
assert_eq!(chat::get_chat_contacts(&t, chat_id).await.unwrap().len(), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_adhoc_groups_merge() -> Result<()> {
|
||||||
|
let mut tcm = TestContextManager::new();
|
||||||
|
let alice = &tcm.alice().await;
|
||||||
|
receive_imf(
|
||||||
|
alice,
|
||||||
|
b"From: bob@example.net\n\
|
||||||
|
To: alice@example.org, claire@example.com\n\
|
||||||
|
Message-ID: <1111@example.net>\n\
|
||||||
|
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\
|
||||||
|
Subject: New thread\n\
|
||||||
|
\n\
|
||||||
|
The first of us should create a thread as discussed\n",
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
receive_imf(
|
||||||
|
alice,
|
||||||
|
b"From: alice@example.org\n\
|
||||||
|
To: bob@example.net, claire@example.com\n\
|
||||||
|
Message-ID: <2222@example.org>\n\
|
||||||
|
Date: Sun, 22 Mar 2020 22:37:58 +0000\n\
|
||||||
|
Subject: New thread\n\
|
||||||
|
\n\
|
||||||
|
The first of us should create a thread as discussed\n",
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let chats = Chatlist::try_load(alice, 0, None, None).await?;
|
||||||
|
assert_eq!(chats.len(), 1);
|
||||||
|
let chat_id = chats.get_chat_id(0)?;
|
||||||
|
assert_eq!(chat_id.get_msg_cnt(alice).await?, 2);
|
||||||
|
|
||||||
|
// If member list doesn't match, threads aren't merged.
|
||||||
|
receive_imf(
|
||||||
|
alice,
|
||||||
|
b"From: bob@example.net\n\
|
||||||
|
To: alice@example.org, claire@example.com, fiona@example.net\n\
|
||||||
|
Message-ID: <3333@example.net>\n\
|
||||||
|
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\
|
||||||
|
Subject: New thread\n\
|
||||||
|
\n\
|
||||||
|
This is another thread, with Fiona\n",
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let chats = Chatlist::try_load(alice, 0, None, None).await?;
|
||||||
|
assert_eq!(chats.len(), 2);
|
||||||
|
receive_imf(
|
||||||
|
alice,
|
||||||
|
b"From: bob@example.net\n\
|
||||||
|
To: alice@example.org, fiona@example.net\n\
|
||||||
|
Message-ID: <4444@example.net>\n\
|
||||||
|
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\
|
||||||
|
Subject: New thread\n\
|
||||||
|
\n\
|
||||||
|
This is yet another thread, with Fiona and 0 Claires\n",
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let chats = Chatlist::try_load(alice, 0, None, None).await?;
|
||||||
|
assert_eq!(chats.len(), 3);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn test_read_receipt_and_unarchive() -> Result<()> {
|
async fn test_read_receipt_and_unarchive() -> Result<()> {
|
||||||
// create alice's account
|
// create alice's account
|
||||||
|
|||||||
@@ -1028,6 +1028,15 @@ CREATE INDEX msgs_status_updates_index2 ON msgs_status_updates (uid);
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inc_and_check(&mut migration_version, 121)?;
|
||||||
|
if dbversion < migration_version {
|
||||||
|
sql.execute_migration(
|
||||||
|
"CREATE INDEX chats_index4 ON chats (name)",
|
||||||
|
migration_version,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
let new_version = sql
|
let new_version = sql
|
||||||
.get_raw_config_int(VERSION_CFG)
|
.get_raw_config_int(VERSION_CFG)
|
||||||
.await?
|
.await?
|
||||||
|
|||||||
Reference in New Issue
Block a user