mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 01:16:31 +03:00
feat: Multi-device broadcast lists (#4953)
This commit is contained in:
48
src/chat.rs
48
src/chat.rs
@@ -3249,7 +3249,7 @@ pub async fn create_broadcast_list(context: &Context) -> Result<ChatId> {
|
|||||||
create_broadcast_list_ex(context, Sync, grpid, chat_name).await
|
create_broadcast_list_ex(context, Sync, grpid, chat_name).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_broadcast_list_ex(
|
pub(crate) async fn create_broadcast_list_ex(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
sync: sync::Sync,
|
sync: sync::Sync,
|
||||||
grpid: String,
|
grpid: String,
|
||||||
@@ -6514,6 +6514,47 @@ mod tests {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_broadcast_multidev() -> Result<()> {
|
||||||
|
let alices = [
|
||||||
|
TestContext::new_alice().await,
|
||||||
|
TestContext::new_alice().await,
|
||||||
|
];
|
||||||
|
let bob = TestContext::new_bob().await;
|
||||||
|
let a1b_contact_id = alices[1].add_or_lookup_contact(&bob).await.id;
|
||||||
|
|
||||||
|
let a0_broadcast_id = create_broadcast_list(&alices[0]).await?;
|
||||||
|
let a0_broadcast_chat = Chat::load_from_db(&alices[0], a0_broadcast_id).await?;
|
||||||
|
set_chat_name(&alices[0], a0_broadcast_id, "Broadcast list 42").await?;
|
||||||
|
let sent_msg = alices[0].send_text(a0_broadcast_id, "hi").await;
|
||||||
|
let msg = alices[1].recv_msg(&sent_msg).await;
|
||||||
|
let a1_broadcast_id = get_chat_id_by_grpid(&alices[1], &a0_broadcast_chat.grpid)
|
||||||
|
.await?
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
assert_eq!(msg.chat_id, a1_broadcast_id);
|
||||||
|
let a1_broadcast_chat = Chat::load_from_db(&alices[1], a1_broadcast_id).await?;
|
||||||
|
assert_eq!(a1_broadcast_chat.get_type(), Chattype::Broadcast);
|
||||||
|
assert_eq!(a1_broadcast_chat.get_name(), "Broadcast list 42");
|
||||||
|
assert!(get_chat_contacts(&alices[1], a1_broadcast_id)
|
||||||
|
.await?
|
||||||
|
.is_empty());
|
||||||
|
|
||||||
|
add_contact_to_chat(&alices[1], a1_broadcast_id, a1b_contact_id).await?;
|
||||||
|
set_chat_name(&alices[1], a1_broadcast_id, "Broadcast list 43").await?;
|
||||||
|
let sent_msg = alices[1].send_text(a1_broadcast_id, "hi").await;
|
||||||
|
let msg = alices[0].recv_msg(&sent_msg).await;
|
||||||
|
assert_eq!(msg.chat_id, a0_broadcast_id);
|
||||||
|
let a0_broadcast_chat = Chat::load_from_db(&alices[0], a0_broadcast_id).await?;
|
||||||
|
assert_eq!(a0_broadcast_chat.get_type(), Chattype::Broadcast);
|
||||||
|
assert_eq!(a0_broadcast_chat.get_name(), "Broadcast list 42");
|
||||||
|
assert!(get_chat_contacts(&alices[0], a0_broadcast_id)
|
||||||
|
.await?
|
||||||
|
.is_empty());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn test_create_for_contact_with_blocked() -> Result<()> {
|
async fn test_create_for_contact_with_blocked() -> Result<()> {
|
||||||
let t = TestContext::new().await;
|
let t = TestContext::new().await;
|
||||||
@@ -6947,9 +6988,8 @@ mod tests {
|
|||||||
let msg = bob.recv_msg(&sent_msg).await;
|
let msg = bob.recv_msg(&sent_msg).await;
|
||||||
let chat = Chat::load_from_db(&bob, msg.chat_id).await?;
|
let chat = Chat::load_from_db(&bob, msg.chat_id).await?;
|
||||||
assert_eq!(chat.get_type(), Chattype::Mailinglist);
|
assert_eq!(chat.get_type(), Chattype::Mailinglist);
|
||||||
// TODO: It doesn't work now for some reason, `msg.chat_id == DC_CHAT_ID_TRASH`.
|
let msg = alices[0].recv_msg(&sent_msg).await;
|
||||||
// let msg = alices[0].recv_msg(&sent_msg).await;
|
assert_eq!(msg.chat_id, a0_broadcast_id);
|
||||||
// assert_eq!(msg.chat_id, a0_broadcast_id);
|
|
||||||
remove_contact_from_chat(&alices[0], a0_broadcast_id, ab_contact_ids[0]).await?;
|
remove_contact_from_chat(&alices[0], a0_broadcast_id, ab_contact_ids[0]).await?;
|
||||||
sync(&alices).await?;
|
sync(&alices).await?;
|
||||||
assert!(get_chat_contacts(&alices[1], a1_broadcast_id)
|
assert!(get_chat_contacts(&alices[1], a1_broadcast_id)
|
||||||
|
|||||||
@@ -930,6 +930,22 @@ async fn add_parts(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if chat_id.is_none() {
|
||||||
|
// Check if the message belongs to a broadcast list.
|
||||||
|
if let Some(mailinglist_header) = mime_parser.get_mailinglist_header() {
|
||||||
|
let listid = mailinglist_header_listid(mailinglist_header)?;
|
||||||
|
chat_id = Some(
|
||||||
|
if let Some((id, ..)) = chat::get_chat_id_by_grpid(context, &listid).await? {
|
||||||
|
id
|
||||||
|
} else {
|
||||||
|
let name =
|
||||||
|
compute_mailinglist_name(mailinglist_header, &listid, mime_parser);
|
||||||
|
chat::create_broadcast_list_ex(context, Nosync, listid, name).await?
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if fetching_existing_messages && mime_parser.decrypting_failed {
|
if fetching_existing_messages && mime_parser.decrypting_failed {
|
||||||
@@ -1966,6 +1982,17 @@ async fn apply_group_changes(
|
|||||||
|
|
||||||
static LIST_ID_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(.+)<(.+)>$").unwrap());
|
static LIST_ID_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(.+)<(.+)>$").unwrap());
|
||||||
|
|
||||||
|
fn mailinglist_header_listid(list_id_header: &str) -> Result<String> {
|
||||||
|
Ok(match LIST_ID_REGEX.captures(list_id_header) {
|
||||||
|
Some(cap) => cap.get(2).context("no match??")?.as_str().trim(),
|
||||||
|
None => list_id_header
|
||||||
|
.trim()
|
||||||
|
.trim_start_matches('<')
|
||||||
|
.trim_end_matches('>'),
|
||||||
|
}
|
||||||
|
.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
/// Create or lookup a mailing list chat.
|
/// Create or lookup a mailing list chat.
|
||||||
///
|
///
|
||||||
/// `list_id_header` contains the Id that must be used for the mailing list
|
/// `list_id_header` contains the Id that must be used for the mailing list
|
||||||
@@ -1975,21 +2002,13 @@ static LIST_ID_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(.+)<(.+)>$").unw
|
|||||||
///
|
///
|
||||||
/// `mime_parser` is the corresponding message
|
/// `mime_parser` is the corresponding message
|
||||||
/// and is used to figure out the mailing list name from different header fields.
|
/// and is used to figure out the mailing list name from different header fields.
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
async fn create_or_lookup_mailinglist(
|
async fn create_or_lookup_mailinglist(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
allow_creation: bool,
|
allow_creation: bool,
|
||||||
list_id_header: &str,
|
list_id_header: &str,
|
||||||
mime_parser: &MimeMessage,
|
mime_parser: &MimeMessage,
|
||||||
) -> Result<Option<(ChatId, Blocked)>> {
|
) -> Result<Option<(ChatId, Blocked)>> {
|
||||||
let listid = match LIST_ID_REGEX.captures(list_id_header) {
|
let listid = mailinglist_header_listid(list_id_header)?;
|
||||||
Some(cap) => cap[2].trim().to_string(),
|
|
||||||
None => list_id_header
|
|
||||||
.trim()
|
|
||||||
.trim_start_matches('<')
|
|
||||||
.trim_end_matches('>')
|
|
||||||
.to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some((chat_id, _, blocked)) = chat::get_chat_id_by_grpid(context, &listid).await? {
|
if let Some((chat_id, _, blocked)) = chat::get_chat_id_by_grpid(context, &listid).await? {
|
||||||
return Ok(Some((chat_id, blocked)));
|
return Ok(Some((chat_id, blocked)));
|
||||||
|
|||||||
Reference in New Issue
Block a user