mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 09:26:29 +03:00
fix: apply_group_changes: Don't implicitly delete members locally, add absent ones instead (#4934)
This is another approach to provide group membership consistency for all members. Considerations: - Classical MUA users usually don't intend to remove users from an email thread, so if they removed a recipient then it was probably by accident. - DC users could miss new member additions and then better to handle this in the same way as for classical MUA messages. Moreover, if we remove a member implicitly, they will never know that and continue to think they're still here. But it shouldn't be a big problem if somebody missed a member removal, because they will likely recreate the member list from the next received message. The problem occurs only if that "somebody" managed to reply earlier. Really, it's a problem for big groups with high message rate, but let it be for now.
This commit is contained in:
@@ -1718,7 +1718,8 @@ async fn apply_group_changes(
|
|||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut chat_contacts = HashSet::from_iter(chat::get_chat_contacts(context, chat_id).await?);
|
let mut chat_contacts =
|
||||||
|
HashSet::<ContactId>::from_iter(chat::get_chat_contacts(context, chat_id).await?);
|
||||||
let is_from_in_chat =
|
let is_from_in_chat =
|
||||||
!chat_contacts.contains(&ContactId::SELF) || chat_contacts.contains(&from_id);
|
!chat_contacts.contains(&ContactId::SELF) || chat_contacts.contains(&from_id);
|
||||||
|
|
||||||
@@ -1854,33 +1855,31 @@ async fn apply_group_changes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !recreate_member_list {
|
if !recreate_member_list {
|
||||||
let diff: HashSet<ContactId> =
|
// Don't delete any members locally, but instead add absent ones to provide group
|
||||||
chat_contacts.difference(&new_members).copied().collect();
|
// membership consistency for all members:
|
||||||
// Only delete old contacts if the sender is not a classical MUA user:
|
// - Classical MUA users usually don't intend to remove users from an email thread, so
|
||||||
// Classical MUA users usually don't intend to remove users from an email
|
// if they removed a recipient then it was probably by accident.
|
||||||
// thread, so if they removed a recipient then it was probably by accident.
|
// - DC users could miss new member additions and then better to handle this in the same
|
||||||
if mime_parser.has_chat_version() {
|
// way as for classical MUA messages. Moreover, if we remove a member implicitly, they
|
||||||
// This is what provides group membership consistency: we remove group members
|
// will never know that and continue to think they're still here.
|
||||||
// locally if we see a discrepancy with the "To" list in the received message as it
|
// But it shouldn't be a big problem if somebody missed a member removal, because they
|
||||||
// is better for privacy than adding absent members locally. But it shouldn't be a
|
// will likely recreate the member list from the next received message. The problem
|
||||||
// big problem if somebody missed a member addition, because they will likely
|
// occurs only if that "somebody" managed to reply earlier. Really, it's a problem for
|
||||||
// recreate the member list from the next received message. The problem occurs only
|
// big groups with high message rate, but let it be for now.
|
||||||
// if that "somebody" managed to reply earlier. Really, it's a problem for big
|
let mut diff: HashSet<ContactId> =
|
||||||
// groups with high message rate, but let it be for now.
|
new_members.difference(&chat_contacts).copied().collect();
|
||||||
if !diff.is_empty() {
|
new_members = chat_contacts.clone();
|
||||||
warn!(context, "Implicit removal of {diff:?} from chat {chat_id}.");
|
new_members.extend(diff.clone());
|
||||||
}
|
if let Some(added_id) = added_id {
|
||||||
new_members = chat_contacts.difference(&diff).copied().collect();
|
diff.remove(&added_id);
|
||||||
} else {
|
}
|
||||||
new_members.extend(diff);
|
if !diff.is_empty() {
|
||||||
|
warn!(context, "Implicit addition of {diff:?} to chat {chat_id}.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(removed_id) = removed_id {
|
if let Some(removed_id) = removed_id {
|
||||||
new_members.remove(&removed_id);
|
new_members.remove(&removed_id);
|
||||||
}
|
}
|
||||||
if let Some(added_id) = added_id {
|
|
||||||
new_members.insert(added_id);
|
|
||||||
}
|
|
||||||
if recreate_member_list {
|
if recreate_member_list {
|
||||||
info!(
|
info!(
|
||||||
context,
|
context,
|
||||||
|
|||||||
@@ -3411,14 +3411,24 @@ async fn test_dont_recreate_contacts_on_add_remove() -> Result<()> {
|
|||||||
|
|
||||||
alice.recv_msg(&bob.pop_sent_msg().await).await;
|
alice.recv_msg(&bob.pop_sent_msg().await).await;
|
||||||
|
|
||||||
// Bob didn't receive the addition of Fiona, so Alice must remove Fiona from the members list
|
// Bob didn't receive the addition of Fiona, but Alice mustn't remove Fiona from the members
|
||||||
// back to make their group members view consistent.
|
// list back. Instead, Bob must add Fiona from the next Alice's message to make their group
|
||||||
assert_eq!(get_chat_contacts(&alice, alice_chat_id).await?.len(), 3);
|
// members view consistent.
|
||||||
|
assert_eq!(get_chat_contacts(&alice, alice_chat_id).await?.len(), 4);
|
||||||
|
|
||||||
// Just a dumb check for remove_contact_from_chat(). Let's have it in this only place.
|
// Just a dumb check for remove_contact_from_chat(). Let's have it in this only place.
|
||||||
remove_contact_from_chat(&bob, bob_chat_id, bob_blue).await?;
|
remove_contact_from_chat(&bob, bob_chat_id, bob_blue).await?;
|
||||||
alice.recv_msg(&bob.pop_sent_msg().await).await;
|
alice.recv_msg(&bob.pop_sent_msg().await).await;
|
||||||
assert_eq!(get_chat_contacts(&alice, alice_chat_id).await?.len(), 2);
|
assert_eq!(get_chat_contacts(&alice, alice_chat_id).await?.len(), 3);
|
||||||
|
|
||||||
|
send_text_msg(
|
||||||
|
&alice,
|
||||||
|
alice_chat_id,
|
||||||
|
"Finally add Fiona please".to_string(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
bob.recv_msg(&alice.pop_sent_msg().await).await;
|
||||||
|
assert_eq!(get_chat_contacts(&bob, bob_chat_id).await?.len(), 3);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -3502,8 +3512,11 @@ async fn test_dont_readd_with_normal_msg() -> Result<()> {
|
|||||||
|
|
||||||
bob.recv_msg(&alice.pop_sent_msg().await).await;
|
bob.recv_msg(&alice.pop_sent_msg().await).await;
|
||||||
|
|
||||||
// Alice didn't receive Bobs leave message, but bob shouldn't readded himself just because of that.
|
// Alice didn't receive Bob's leave message, so Bob must readd themselves otherwise other
|
||||||
assert!(!is_contact_in_chat(&bob, bob_chat_id, ContactId::SELF).await?);
|
// members would think Bob is still here while they aren't, and then retry to leave if they
|
||||||
|
// think that Alice didn't re-add them on purpose (which is possible if Alice uses a classical
|
||||||
|
// MUA).
|
||||||
|
assert!(is_contact_in_chat(&bob, bob_chat_id, ContactId::SELF).await?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user