diff --git a/src/chat.rs b/src/chat.rs index 7537e1c417..805f040a8d 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -4207,7 +4207,7 @@ pub async fn remove_contact_from_chat( if chat.typ == Chattype::Group && chat.is_promoted() { let addr = contact.get_addr(); - let res = send_member_removal_msg(context, chat_id, contact_id, addr).await; + let res = send_member_removal_msg(context, &chat, contact_id, addr).await; if contact_id == ContactId::SELF { res?; @@ -4231,7 +4231,7 @@ pub async fn remove_contact_from_chat( // For incoming broadcast channels, it's not possible to remove members, // but it's possible to leave: let self_addr = context.get_primary_self_addr().await?; - send_member_removal_msg(context, chat_id, contact_id, &self_addr).await?; + send_member_removal_msg(context, &chat, contact_id, &self_addr).await?; } else { bail!("Cannot remove members from non-group chats."); } @@ -4241,14 +4241,18 @@ pub async fn remove_contact_from_chat( async fn send_member_removal_msg( context: &Context, - chat_id: ChatId, + chat: &Chat, contact_id: ContactId, addr: &str, ) -> Result { let mut msg = Message::new(Viewtype::Text); if contact_id == ContactId::SELF { - msg.text = stock_str::msg_group_left_local(context, ContactId::SELF).await; + if chat.typ == Chattype::InBroadcast { + msg.text = stock_str::msg_you_left_broadcast(context).await; + } else { + msg.text = stock_str::msg_group_left_local(context, ContactId::SELF).await; + } } else { msg.text = stock_str::msg_del_member_local(context, contact_id, ContactId::SELF).await; } @@ -4258,7 +4262,7 @@ async fn send_member_removal_msg( msg.param .set(Param::ContactAddedRemoved, contact_id.to_u32()); - send_msg(context, chat_id, &mut msg).await + send_msg(context, chat.id, &mut msg).await } async fn set_group_explicitly_left(context: &Context, grpid: &str) -> Result<()> { diff --git a/src/chat/chat_tests.rs b/src/chat/chat_tests.rs index 611826a1b3..e018e6de5e 100644 --- a/src/chat/chat_tests.rs +++ b/src/chat/chat_tests.rs @@ -3026,7 +3026,7 @@ async fn test_leave_broadcast() -> Result<()> { } /// Tests that if Bob leaves a broadcast channel with one device, -/// the other device shows a correct info message "You left.". +/// the other device shows a correct info message "You left the channel.". #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_leave_broadcast_multidevice() -> Result<()> { let mut tcm = TestContextManager::new(); @@ -3061,10 +3061,7 @@ async fn test_leave_broadcast_multidevice() -> Result<()> { assert_eq!(rcvd.chat_id, bob1_hello.chat_id); assert!(rcvd.is_info()); assert_eq!(rcvd.get_info_type(), SystemMessage::MemberRemovedFromGroup); - assert_eq!( - rcvd.text, - stock_str::msg_group_left_local(bob1, ContactId::SELF).await - ); + assert_eq!(rcvd.text, stock_str::msg_you_left_broadcast(bob1).await); Ok(()) } diff --git a/src/qr_code_generator.rs b/src/qr_code_generator.rs index d8d6d4327a..a8456e04fc 100644 --- a/src/qr_code_generator.rs +++ b/src/qr_code_generator.rs @@ -1,6 +1,6 @@ //! # QR code generation module. -use anyhow::Result; +use anyhow::{Result, bail}; use base64::Engine as _; use qrcodegen::{QrCode, QrCodeEcc}; @@ -108,8 +108,18 @@ async fn generate_join_group_qr_code(context: &Context, chat_id: ChatId) -> Resu None => None, }; + let qrcode_description = match chat.typ { + crate::constants::Chattype::Group => { + stock_str::secure_join_group_qr_description(context, &chat).await + } + crate::constants::Chattype::OutBroadcast => { + stock_str::secure_join_broadcast_qr_description(context, &chat).await + } + _ => bail!("Unexpected chat type {}", chat.typ), + }; + inner_generate_secure_join_qr_code( - &stock_str::secure_join_group_qr_description(context, &chat).await, + &qrcode_description, &securejoin::get_securejoin_qr(context, Some(chat_id)).await?, &color_int_to_hex_string(chat.get_color(context).await?), avatar, diff --git a/src/receive_imf.rs b/src/receive_imf.rs index 3e9e3d762d..60975d564b 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -3532,8 +3532,7 @@ async fn apply_in_broadcast_changes( // The only member added/removed message that is ever sent is "I left.", // so, this is the only case we need to handle here if from_id == ContactId::SELF { - better_msg - .get_or_insert(stock_str::msg_group_left_local(context, ContactId::SELF).await); + better_msg.get_or_insert(stock_str::msg_you_left_broadcast(context).await); } } diff --git a/src/stock_str.rs b/src/stock_str.rs index 0ff247332c..c93e958f7b 100644 --- a/src/stock_str.rs +++ b/src/stock_str.rs @@ -279,7 +279,7 @@ pub enum StockMessage { #[strum(props(fallback = "Member %1$s removed by %2$s."))] MsgDelMemberBy = 131, - #[strum(props(fallback = "You left."))] + #[strum(props(fallback = "You left the group."))] MsgYouLeftGroup = 132, #[strum(props(fallback = "Group left by %1$s."))] @@ -439,6 +439,12 @@ https://delta.chat/donate"))] #[strum(props(fallback = "Missed Call"))] MissedCall = 198, + + #[strum(props(fallback = "You left the channel."))] + MsgYouLeftBroadcast = 200, + + #[strum(props(fallback = "Scan to join channel %1$s"))] + SecureJoinBrodcastQRDescription = 201, } impl StockMessage { @@ -711,7 +717,7 @@ pub(crate) async fn msg_group_left_remote(context: &Context) -> String { translated(context, StockMessage::MsgILeftGroup).await } -/// Stock string: `You left.` or `Group left by %1$s.`. +/// Stock string: `You left the group.` or `Group left by %1$s.`. pub(crate) async fn msg_group_left_local(context: &Context, by_contact: ContactId) -> String { if by_contact == ContactId::SELF { translated(context, StockMessage::MsgYouLeftGroup).await @@ -722,6 +728,11 @@ pub(crate) async fn msg_group_left_local(context: &Context, by_contact: ContactI } } +/// Stock string: `You left the channel.` +pub(crate) async fn msg_you_left_broadcast(context: &Context) -> String { + translated(context, StockMessage::MsgYouLeftBroadcast).await +} + /// Stock string: `You reacted %1$s to "%2$s"` or `%1$s reacted %2$s to "%3$s"`. pub(crate) async fn msg_reacted( context: &Context, @@ -857,13 +868,20 @@ pub(crate) async fn setup_contact_qr_description( .replace1(&name) } -/// Stock string: `Scan to join %1$s`. +/// Stock string: `Scan to join group %1$s`. pub(crate) async fn secure_join_group_qr_description(context: &Context, chat: &Chat) -> String { translated(context, StockMessage::SecureJoinGroupQRDescription) .await .replace1(chat.get_name()) } +/// Stock string: `Scan to join channel %1$s`. +pub(crate) async fn secure_join_broadcast_qr_description(context: &Context, chat: &Chat) -> String { + translated(context, StockMessage::SecureJoinBrodcastQRDescription) + .await + .replace1(chat.get_name()) +} + /// Stock string: `%1$s verified.`. #[allow(dead_code)] pub(crate) async fn contact_verified(context: &Context, contact: &Contact) -> String { diff --git a/test-data/golden/chat_test_parallel_member_remove b/test-data/golden/chat_test_parallel_member_remove index b2442e855a..c71bcf0d53 100644 --- a/test-data/golden/chat_test_parallel_member_remove +++ b/test-data/golden/chat_test_parallel_member_remove @@ -2,7 +2,7 @@ Group#Chat#10: Group chat [3 member(s)] -------------------------------------------------------------------------------- Msg#10: info (Contact#Contact#Info): Messages are end-to-end encrypted. [NOTICED][INFO] Msg#11🔒: (Contact#Contact#10): Hi! I created a group. [FRESH] -Msg#12🔒: Me (Contact#Contact#Self): You left. [INFO] √ +Msg#12🔒: Me (Contact#Contact#Self): You left the group. [INFO] √ Msg#13🔒: (Contact#Contact#10): Member charlie@example.net added by alice@example.org. [FRESH][INFO] Msg#14🔒: (Contact#Contact#10): What a silence! [FRESH] --------------------------------------------------------------------------------