From ef309b5740f703ce59bfa70219c6d215f70646b5 Mon Sep 17 00:00:00 2001 From: Simon Laux Date: Fri, 16 Jan 2026 15:38:59 +0100 Subject: [PATCH 1/2] fix: emit `ChatModified` event when `can_send` changes during secure join Also adds a test for the event (for securejoin group and secure join contact). Closes #7634 --- src/securejoin/bob.rs | 5 +- src/securejoin/securejoin_tests.rs | 106 +++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/src/securejoin/bob.rs b/src/securejoin/bob.rs index fd92985625..e809faa56f 100644 --- a/src/securejoin/bob.rs +++ b/src/securejoin/bob.rs @@ -261,7 +261,10 @@ pub(super) async fn handle_auth_required( .await?; match invite { - QrInvite::Contact { .. } | QrInvite::Broadcast { .. } => {} + QrInvite::Contact { .. } | QrInvite::Broadcast { .. } => { + // This is to tell UI that Chat.can_send has changed + context.emit_event(EventType::ChatModified(chat_id)); + } QrInvite::Group { .. } => { // The message reads "Alice replied, waiting to be added to the group…", // so only show it when joining a group and not for a 1:1 chat or broadcast channel. diff --git a/src/securejoin/securejoin_tests.rs b/src/securejoin/securejoin_tests.rs index 6c309089e9..a9a6e3c36e 100644 --- a/src/securejoin/securejoin_tests.rs +++ b/src/securejoin/securejoin_tests.rs @@ -1370,3 +1370,109 @@ gU6dGXsFMe/RpRHrIAkMAaM5xkxMDRuRJDxiUdS/X+Y8 Ok(()) } + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_secure_join_group_can_send_and_event() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = tcm.alice().await; + let bob = tcm.bob().await; + + let alice_chatid = chat::create_group(&alice, "the chat").await?; + + tcm.section("Step 1: Generate QR-code, secure-join implied by chatid"); + let qr = get_securejoin_qr(&alice, Some(alice_chatid)).await.unwrap(); + + tcm.section("Step 2: Bob scans QR-code, sends vg-request"); + let bob_chatid = join_securejoin(&bob, &qr).await?; + assert_eq!( + Chat::load_from_db(&bob, bob_chatid) + .await? + .can_send(&bob) + .await?, + false + ); + + let sent = bob.pop_sent_msg().await; + let _ = alice.parse_msg(&sent).await; + + tcm.section("Step 3: Alice receives vg-request, sends vg-auth-required"); + alice.recv_msg_trash(&sent).await; + let sent = alice.pop_sent_msg().await; + let _ = bob.parse_msg(&sent).await; + + tcm.section("Step 4: Bob receives vg-auth-required, sends vg-request-with-auth"); + bob.recv_msg_trash(&sent).await; + let sent = bob.pop_sent_msg().await; + + tcm.section("Step 5+6: Alice receives vg-request-with-auth, sends vg-member-added"); + alice.recv_msg_trash(&sent).await; + let sent = alice.pop_sent_msg().await; + + bob.evtracker.clear_events(); + assert_eq!( + Chat::load_from_db(&bob, bob_chatid) + .await? + .can_send(&bob) + .await?, + false + ); + + tcm.section("Step 7: Bob receives vg-member-added"); + bob.recv_msg(&sent).await; + + bob.evtracker + .get_matching(|typ| typ == &EventType::ChatModified(bob_chatid)) + .await; + + assert_eq!( + Chat::load_from_db(&bob, bob_chatid) + .await? + .can_send(&bob) + .await?, + true + ); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_secure_join_contact_can_send_and_event() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = tcm.alice().await; + let bob = tcm.bob().await; + + tcm.section("Step 1: Generate QR-code, ChatId(0) indicates setup-contact"); + let qr = get_securejoin_qr(&alice, None).await?; + + tcm.section("Step 2: Bob scans QR-code, sends vc-request"); + let bob_chat_id = join_securejoin(&bob.ctx, &qr).await?; + + let bob_chat = Chat::load_from_db(&bob, bob_chat_id).await?; + assert_eq!( + bob_chat.why_cant_send(&bob).await?, + Some(CantSendReason::MissingKey) + ); + + let sent = bob.pop_sent_msg().await; + + tcm.section("Step 3: Alice receives vc-request, sends vc-auth-required"); + alice.recv_msg_trash(&sent).await; + + let sent = alice.pop_sent_msg().await; + let bob_chat = bob.get_chat(&alice).await; + assert_eq!(bob_chat.can_send(&bob).await?, false); + bob.evtracker.clear_events(); + + tcm.section("Step 4: Bob receives vc-auth-required, sends vc-request-with-auth"); + bob.recv_msg_trash(&sent).await; + + bob.evtracker + .get_matching(|typ| typ == &EventType::ChatModified(bob_chat_id)) + .await; + + let bob_chat = bob.get_chat(&alice).await; + assert_eq!(bob_chat.can_send(&bob).await?, true); + + // Rest of the steps (4-7) are irrelevant for this test + Ok(()) +} From f01b1d06012ad099427cabc0cc57ce7cd1e4e25e Mon Sep 17 00:00:00 2001 From: Simon Laux Date: Sat, 17 Jan 2026 16:26:58 +0100 Subject: [PATCH 2/2] don't emit changed on joining broadcast --- src/securejoin/bob.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/securejoin/bob.rs b/src/securejoin/bob.rs index e809faa56f..e0d54ceebf 100644 --- a/src/securejoin/bob.rs +++ b/src/securejoin/bob.rs @@ -261,7 +261,8 @@ pub(super) async fn handle_auth_required( .await?; match invite { - QrInvite::Contact { .. } | QrInvite::Broadcast { .. } => { + QrInvite::Broadcast { .. } => {} + QrInvite::Contact { .. } => { // This is to tell UI that Chat.can_send has changed context.emit_event(EventType::ChatModified(chat_id)); }