Skip to content

Commit

Permalink
feat: Multi-device broadcast lists (#4953)
Browse files Browse the repository at this point in the history
  • Loading branch information
iequidoo committed Nov 12, 2023
1 parent 2938cf2 commit bccbaa9
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 13 deletions.
48 changes: 44 additions & 4 deletions src/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3212,7 +3212,7 @@ pub async fn create_broadcast_list(context: &Context) -> Result<ChatId> {
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,
sync: sync::Sync,
grpid: String,
Expand Down Expand Up @@ -6470,6 +6470,47 @@ mod tests {
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)]
async fn test_create_for_contact_with_blocked() -> Result<()> {
let t = TestContext::new().await;
Expand Down Expand Up @@ -6903,9 +6944,8 @@ mod tests {
let msg = bob.recv_msg(&sent_msg).await;
let chat = Chat::load_from_db(&bob, msg.chat_id).await?;
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;
// assert_eq!(msg.chat_id, a0_broadcast_id);
let msg = alices[0].recv_msg(&sent_msg).await;
assert_eq!(msg.chat_id, a0_broadcast_id);
remove_contact_from_chat(&alices[0], a0_broadcast_id, ab_contact_ids[0]).await?;
sync(&alices).await?;
assert!(get_chat_contacts(&alices[1], a1_broadcast_id)
Expand Down
38 changes: 29 additions & 9 deletions src/receive_imf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -1972,6 +1988,18 @@ async fn apply_group_changes(

static LIST_ID_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(.+)<(.+)>$").unwrap());

#[allow(clippy::indexing_slicing)]
fn mailinglist_header_listid(list_id_header: &str) -> String {
match LIST_ID_REGEX.captures(list_id_header) {
Some(cap) => cap[2].trim().to_string(),
None => list_id_header
.trim()
.trim_start_matches('<')
.trim_end_matches('>')
.to_string(),
}
}

/// Create or lookup a mailing list chat.
///
/// `list_id_header` contains the Id that must be used for the mailing list
Expand All @@ -1981,21 +2009,13 @@ static LIST_ID_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(.+)<(.+)>$").unw
///
/// `mime_parser` is the corresponding message
/// and is used to figure out the mailing list name from different header fields.
#[allow(clippy::indexing_slicing)]
async fn create_or_lookup_mailinglist(
context: &Context,
allow_creation: bool,
list_id_header: &str,
mime_parser: &MimeMessage,
) -> Result<Option<(ChatId, Blocked)>> {
let listid = match LIST_ID_REGEX.captures(list_id_header) {
Some(cap) => cap[2].trim().to_string(),
None => list_id_header
.trim()
.trim_start_matches('<')
.trim_end_matches('>')
.to_string(),
};
let listid = mailinglist_header_listid(list_id_header);

if let Some((chat_id, _, blocked)) = chat::get_chat_id_by_grpid(context, &listid).await? {
return Ok(Some((chat_id, blocked)));
Expand Down

0 comments on commit bccbaa9

Please sign in to comment.