mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 01:16:31 +03:00
feat: Add info messages about implicit membership changes if group member list is recreated (#6314)
This commit is contained in:
@@ -2216,9 +2216,7 @@ async fn apply_group_changes(
|
|||||||
if let Some(contact_id) =
|
if let Some(contact_id) =
|
||||||
Contact::lookup_id_by_addr(context, added_addr, Origin::Unknown).await?
|
Contact::lookup_id_by_addr(context, added_addr, Origin::Unknown).await?
|
||||||
{
|
{
|
||||||
if !recreate_member_list {
|
|
||||||
added_id = Some(contact_id);
|
added_id = Some(contact_id);
|
||||||
}
|
|
||||||
is_new_member = !chat_contacts.contains(&contact_id);
|
is_new_member = !chat_contacts.contains(&contact_id);
|
||||||
} else {
|
} else {
|
||||||
warn!(context, "Added {added_addr:?} has no contact id.");
|
warn!(context, "Added {added_addr:?} has no contact id.");
|
||||||
@@ -2286,12 +2284,16 @@ async fn apply_group_changes(
|
|||||||
new_members.insert(from_id);
|
new_members.insert(from_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These are for adding info messages about implicit membership changes, so they are only
|
||||||
|
// filled when such messages are needed.
|
||||||
|
let mut added_ids = HashSet::<ContactId>::new();
|
||||||
|
let mut removed_ids = HashSet::<ContactId>::new();
|
||||||
|
|
||||||
if !recreate_member_list {
|
if !recreate_member_list {
|
||||||
let mut diff = HashSet::<ContactId>::new();
|
|
||||||
if sync_member_list {
|
if sync_member_list {
|
||||||
diff = new_members.difference(&chat_contacts).copied().collect();
|
added_ids = new_members.difference(&chat_contacts).copied().collect();
|
||||||
} else if let Some(added_id) = added_id {
|
} else if let Some(added_id) = added_id {
|
||||||
diff.insert(added_id);
|
added_ids.insert(added_id);
|
||||||
}
|
}
|
||||||
new_members.clone_from(&chat_contacts);
|
new_members.clone_from(&chat_contacts);
|
||||||
// Don't delete any members locally, but instead add absent ones to provide group
|
// Don't delete any members locally, but instead add absent ones to provide group
|
||||||
@@ -2305,36 +2307,15 @@ async fn apply_group_changes(
|
|||||||
// will likely recreate the member list from the next received message. The problem
|
// 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
|
// 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.
|
// big groups with high message rate, but let it be for now.
|
||||||
new_members.extend(diff.clone());
|
new_members.extend(added_ids.clone());
|
||||||
if let Some(added_id) = added_id {
|
|
||||||
diff.remove(&added_id);
|
|
||||||
}
|
|
||||||
if !diff.is_empty() {
|
|
||||||
warn!(context, "Implicit addition of {diff:?} to chat {chat_id}.");
|
|
||||||
}
|
|
||||||
group_changes_msgs.reserve(diff.len());
|
|
||||||
for contact_id in diff {
|
|
||||||
let contact = Contact::get_by_id(context, contact_id).await?;
|
|
||||||
group_changes_msgs.push(
|
|
||||||
stock_str::msg_add_member_local(
|
|
||||||
context,
|
|
||||||
contact.get_addr(),
|
|
||||||
ContactId::UNDEFINED,
|
|
||||||
)
|
|
||||||
.await,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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 recreate_member_list {
|
if recreate_member_list {
|
||||||
info!(
|
if self_added {
|
||||||
context,
|
// ... then `better_msg` is already set.
|
||||||
"Recreating chat {chat_id} member list with {new_members:?}.",
|
} else if chat.blocked == Blocked::Request || !chat_contacts.contains(&ContactId::SELF)
|
||||||
);
|
|
||||||
if !self_added
|
|
||||||
&& (chat.blocked == Blocked::Request || !chat_contacts.contains(&ContactId::SELF))
|
|
||||||
{
|
{
|
||||||
warn!(context, "Implicit addition of SELF to chat {chat_id}.");
|
warn!(context, "Implicit addition of SELF to chat {chat_id}.");
|
||||||
group_changes_msgs.push(
|
group_changes_msgs.push(
|
||||||
@@ -2345,9 +2326,46 @@ async fn apply_group_changes(
|
|||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
added_ids = new_members.difference(&chat_contacts).copied().collect();
|
||||||
|
removed_ids = chat_contacts.difference(&new_members).copied().collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(added_id) = added_id {
|
||||||
|
added_ids.remove(&added_id);
|
||||||
|
}
|
||||||
|
if let Some(removed_id) = removed_id {
|
||||||
|
removed_ids.remove(&removed_id);
|
||||||
|
}
|
||||||
|
if !added_ids.is_empty() {
|
||||||
|
warn!(
|
||||||
|
context,
|
||||||
|
"Implicit addition of {added_ids:?} to chat {chat_id}."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if !removed_ids.is_empty() {
|
||||||
|
warn!(
|
||||||
|
context,
|
||||||
|
"Implicit removal of {removed_ids:?} from chat {chat_id}."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
group_changes_msgs.reserve(added_ids.len() + removed_ids.len());
|
||||||
|
for contact_id in added_ids {
|
||||||
|
let contact = Contact::get_by_id(context, contact_id).await?;
|
||||||
|
group_changes_msgs.push(
|
||||||
|
stock_str::msg_add_member_local(context, contact.get_addr(), ContactId::UNDEFINED)
|
||||||
|
.await,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for contact_id in removed_ids {
|
||||||
|
let contact = Contact::get_by_id(context, contact_id).await?;
|
||||||
|
group_changes_msgs.push(
|
||||||
|
stock_str::msg_del_member_local(context, contact.get_addr(), ContactId::UNDEFINED)
|
||||||
|
.await,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if new_members != chat_contacts {
|
if new_members != chat_contacts {
|
||||||
chat::update_chat_contacts_table(context, chat_id, &new_members).await?;
|
chat::update_chat_contacts_table(context, chat_id, &new_members).await?;
|
||||||
chat_contacts = new_members;
|
chat_contacts = new_members;
|
||||||
|
|||||||
@@ -4137,7 +4137,7 @@ async fn test_dont_recreate_contacts_on_add_remove() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn test_recreate_contact_list_on_missing_message() -> Result<()> {
|
async fn test_recreate_contact_list_on_missing_messages() -> Result<()> {
|
||||||
let alice = TestContext::new_alice().await;
|
let alice = TestContext::new_alice().await;
|
||||||
let bob = TestContext::new_bob().await;
|
let bob = TestContext::new_bob().await;
|
||||||
let chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "Group").await?;
|
let chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "Group").await?;
|
||||||
@@ -4162,25 +4162,33 @@ async fn test_recreate_contact_list_on_missing_message() -> Result<()> {
|
|||||||
remove_contact_from_chat(&bob, bob_chat_id, bob_contact_fiona).await?;
|
remove_contact_from_chat(&bob, bob_chat_id, bob_contact_fiona).await?;
|
||||||
let remove_msg = bob.pop_sent_msg().await;
|
let remove_msg = bob.pop_sent_msg().await;
|
||||||
|
|
||||||
// bob adds a new member
|
// bob adds new members
|
||||||
let bob_blue = Contact::create(&bob, "blue", "blue@example.net").await?;
|
let bob_blue = Contact::create(&bob, "blue", "blue@example.net").await?;
|
||||||
add_contact_to_chat(&bob, bob_chat_id, bob_blue).await?;
|
add_contact_to_chat(&bob, bob_chat_id, bob_blue).await?;
|
||||||
|
bob.pop_sent_msg().await;
|
||||||
|
let bob_orange = Contact::create(&bob, "orange", "orange@example.net").await?;
|
||||||
|
add_contact_to_chat(&bob, bob_chat_id, bob_orange).await?;
|
||||||
let add_msg = bob.pop_sent_msg().await;
|
let add_msg = bob.pop_sent_msg().await;
|
||||||
|
|
||||||
// alice only receives the addition of the member
|
// alice only receives the second member addition
|
||||||
alice.recv_msg(&add_msg).await;
|
alice.recv_msg(&add_msg).await;
|
||||||
|
|
||||||
// since we missed a message, a new contact list should be build
|
// since we missed messages, a new contact list should be build
|
||||||
assert_eq!(get_chat_contacts(&alice, chat_id).await?.len(), 3);
|
assert_eq!(get_chat_contacts(&alice, chat_id).await?.len(), 4);
|
||||||
|
|
||||||
// re-add fiona
|
// re-add fiona
|
||||||
add_contact_to_chat(&alice, chat_id, alice_fiona).await?;
|
add_contact_to_chat(&alice, chat_id, alice_fiona).await?;
|
||||||
|
|
||||||
// delayed removal of fiona shouldn't remove her
|
// delayed removal of fiona shouldn't remove her
|
||||||
alice.recv_msg_trash(&remove_msg).await;
|
alice.recv_msg_trash(&remove_msg).await;
|
||||||
assert_eq!(get_chat_contacts(&alice, chat_id).await?.len(), 4);
|
assert_eq!(get_chat_contacts(&alice, chat_id).await?.len(), 5);
|
||||||
|
|
||||||
|
alice
|
||||||
|
.golden_test_chat(
|
||||||
|
chat_id,
|
||||||
|
"receive_imf_recreate_contact_list_on_missing_messages",
|
||||||
|
)
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -431,6 +431,9 @@ pub enum StockMessage {
|
|||||||
#[strum(props(fallback = "%1$s reacted %2$s to \"%3$s\""))]
|
#[strum(props(fallback = "%1$s reacted %2$s to \"%3$s\""))]
|
||||||
MsgReactedBy = 177,
|
MsgReactedBy = 177,
|
||||||
|
|
||||||
|
#[strum(props(fallback = "Member %1$s removed."))]
|
||||||
|
MsgDelMember = 178,
|
||||||
|
|
||||||
#[strum(props(fallback = "Establishing guaranteed end-to-end encryption, please wait…"))]
|
#[strum(props(fallback = "Establishing guaranteed end-to-end encryption, please wait…"))]
|
||||||
SecurejoinWait = 190,
|
SecurejoinWait = 190,
|
||||||
|
|
||||||
@@ -711,7 +714,11 @@ pub(crate) async fn msg_del_member_local(
|
|||||||
.unwrap_or_else(|_| addr.to_string()),
|
.unwrap_or_else(|_| addr.to_string()),
|
||||||
_ => addr.to_string(),
|
_ => addr.to_string(),
|
||||||
};
|
};
|
||||||
if by_contact == ContactId::SELF {
|
if by_contact == ContactId::UNDEFINED {
|
||||||
|
translated(context, StockMessage::MsgDelMember)
|
||||||
|
.await
|
||||||
|
.replace1(whom)
|
||||||
|
} else if by_contact == ContactId::SELF {
|
||||||
translated(context, StockMessage::MsgYouDelMember)
|
translated(context, StockMessage::MsgYouDelMember)
|
||||||
.await
|
.await
|
||||||
.replace1(whom)
|
.replace1(whom)
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
Group#Chat#10: Group [5 member(s)]
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
Msg#10: Me (Contact#Contact#Self): populate √
|
||||||
|
Msg#11: info (Contact#Contact#Info): Member blue@example.net added. [NOTICED][INFO]
|
||||||
|
Msg#12: info (Contact#Contact#Info): Member fiona (fiona@example.net) removed. [NOTICED][INFO]
|
||||||
|
Msg#13: bob (Contact#Contact#11): Member orange@example.net added by bob (bob@example.net). [FRESH][INFO]
|
||||||
|
Msg#14: Me (Contact#Contact#Self): You added member fiona (fiona@example.net). [INFO] o
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
Reference in New Issue
Block a user