android, desktop, ios: show chat name in delete / leave / clear confirmation dialogs (#7021)

* android, desktop, ios: show chat name in delete / leave / clear confirmation dialogs

The dialogs confirming delete contact, delete/leave group/channel and
clear chat now show the chat's name on its own line above the existing
warning, so the user can see which chat the destructive action will
affect.

Pure code change: no new translation strings, no signature changes, no
new helpers. The name is read via existing displayName accessors on
GroupInfo / Contact / ChatInfo.

clearNoteFolderDialog is intentionally unchanged - the notes folder is a
single-instance object and its existing warning already identifies it.

* android, desktop, ios: also show chat name when deleting pending connection

deleteContactConnectionAlert was missed in the original inventory.
Same pattern as the other dispatchers - prepend the connection's
displayName on its own line above the existing warning - so a user
who set a custom name on a pending connection can see which one
they are about to delete.

* android: use <br> instead of \n for newline in delete confirmation dialog body

On Android, the alert body goes through HtmlCompat.fromHtml which
treats the input as HTML and collapses literal \n to a single space -
so "Tech Talk\n\nGroup will be deleted..." rendered as
"Tech Talk Group will be deleted...". Switch to <br><br>, which both
HtmlCompat (Android) and the Desktop parser at Utils.desktop.kt:75
correctly render as a newline.

* android, desktop: skip HTML parsing for delete confirmation dialog text

Add parseHtml: Boolean = true parameter to showAlertDialog and
showAlertDialogButtonsColumn; when false, the body text is wrapped as
AnnotatedString and routed through the existing AnnotatedString
AlertContent overload, bypassing escapedHtmlToAnnotatedString entirely.

The 8 dispatchers that embed the user-controlled chat displayName now
opt out (parseHtml = false). This means:
 - displayName is rendered as literal text - no HTML interpretation,
   so a contact whose alias is "<b>X</b>" or "<font color=..." no
   longer renders bold or coloured in the confirmation dialog
 - the platforms align: both iOS and Kotlin now use plain "\n\n" for
   the separator (no more <br><br> Kotlin-only workaround)
 - removes coupling to escapedHtmlToAnnotatedString in this path

* android, desktop: shorten parseHtml comment in AlertManager

* remove extra text

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
Narasimha-sc
2026-06-08 12:13:06 +01:00
committed by GitHub
co-authored by Evgeny Poberezkin
parent 589d834f83
commit 503d091ec4
9 changed files with 620 additions and 21 deletions
@@ -248,6 +248,8 @@ fun deleteContactDialog(chat: Chat, chatModel: ChatModel, close: (() -> Unit)? =
private fun deleteContactOrConversationDialog(chat: Chat, contact: Contact, chatModel: ChatModel, close: (() -> Unit)?) {
AlertManager.shared.showAlertDialogButtonsColumn(
title = generalGetString(MR.strings.delete_contact_question),
text = contact.displayName,
parseHtml = false,
buttons = {
Column {
// Only delete conversation
@@ -306,7 +308,8 @@ private fun deleteActiveContactDialog(chat: Chat, contact: Contact, chatModel: C
AlertManager.shared.showAlertDialogButtonsColumn(
title = generalGetString(MR.strings.delete_contact_question),
text = generalGetString(MR.strings.delete_contact_cannot_undo_warning),
text = "${contact.displayName}\n\n${generalGetString(MR.strings.delete_contact_cannot_undo_warning)}",
parseHtml = false,
buttons = {
Column {
// Keep conversation toggle
@@ -361,7 +364,8 @@ private fun deleteActiveContactDialog(chat: Chat, contact: Contact, chatModel: C
private fun deleteContactWithoutConversation(chat: Chat, chatModel: ChatModel, close: (() -> Unit)?) {
AlertManager.shared.showAlertDialogButtonsColumn(
title = generalGetString(MR.strings.confirm_delete_contact_question),
text = generalGetString(MR.strings.delete_contact_cannot_undo_warning),
text = "${chat.chatInfo.displayName}\n\n${generalGetString(MR.strings.delete_contact_cannot_undo_warning)}",
parseHtml = false,
buttons = {
Column {
// Delete and notify contact
@@ -417,7 +421,8 @@ private fun deleteContactWithoutConversation(chat: Chat, chatModel: ChatModel, c
private fun deleteNotReadyContact(chat: Chat, chatModel: ChatModel, close: (() -> Unit)?) {
AlertManager.shared.showAlertDialogButtonsColumn(
title = generalGetString(MR.strings.confirm_delete_contact_question),
text = generalGetString(MR.strings.delete_contact_cannot_undo_warning),
text = "${chat.chatInfo.displayName}\n\n${generalGetString(MR.strings.delete_contact_cannot_undo_warning)}",
parseHtml = false,
buttons = {
// Confirm
SectionItemView({
@@ -492,7 +497,8 @@ fun deleteContact(chat: Chat, chatModel: ChatModel, close: (() -> Unit)?, chatDe
fun clearChatDialog(chat: Chat, close: (() -> Unit)? = null) {
AlertManager.shared.showAlertDialog(
title = generalGetString(MR.strings.clear_chat_question),
text = generalGetString(MR.strings.clear_chat_warning),
text = "${chat.chatInfo.displayName}\n\n${generalGetString(MR.strings.clear_chat_warning)}",
parseHtml = false,
confirmText = generalGetString(MR.strings.clear_verb),
onConfirm = { controller.clearChat(chat, close) },
destructive = true,
@@ -199,7 +199,8 @@ fun deleteGroupDialog(chat: Chat, groupInfo: GroupInfo, chatModel: ChatModel, cl
}
AlertManager.shared.showAlertDialog(
title = generalGetString(titleId),
text = generalGetString(messageId),
text = "${groupInfo.displayName}\n\n${generalGetString(messageId)}",
parseHtml = false,
confirmText = generalGetString(MR.strings.delete_verb),
onConfirm = {
withBGApi {
@@ -233,7 +234,8 @@ fun leaveGroupDialog(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel, cl
MR.strings.you_will_stop_receiving_messages_from_this_chat_chat_history_will_be_preserved
AlertManager.shared.showAlertDialog(
title = generalGetString(titleId),
text = generalGetString(messageId),
text = "${groupInfo.displayName}\n\n${generalGetString(messageId)}",
parseHtml = false,
confirmText = generalGetString(MR.strings.leave_group_button),
onConfirm = {
withLongRunningApi(60_000) {
@@ -772,10 +772,11 @@ fun rejectContactRequest(rhId: Long?, contactRequestId: Long, chatModel: ChatMod
fun deleteContactConnectionAlert(rhId: Long?, connection: PendingContactConnection, chatModel: ChatModel, onSuccess: () -> Unit) {
AlertManager.shared.showAlertDialog(
title = generalGetString(MR.strings.delete_pending_connection__question),
text = generalGetString(
text = "${connection.displayName}\n\n" + generalGetString(
if (connection.initiated) MR.strings.contact_you_shared_link_with_wont_be_able_to_connect
else MR.strings.connection_you_accepted_will_be_cancelled
),
parseHtml = false,
confirmText = generalGetString(MR.strings.delete_verb),
onConfirm = {
withBGApi {
@@ -75,6 +75,8 @@ class AlertManager {
onDismissRequest: (() -> Unit)? = null,
hostDevice: Pair<Long?, String>? = null,
belowTextContent: @Composable (() -> Unit) = {},
// When false, [text] is rendered as literal text — use for user-controlled content.
parseHtml: Boolean = true,
buttons: @Composable () -> Unit,
) {
showAlert {
@@ -82,8 +84,14 @@ class AlertManager {
onDismissRequest = { onDismissRequest?.invoke(); if (dismissible) hideAlert() },
title = alertTitle(title),
buttons = {
AlertContent(text, hostDevice, extraPadding = true, textAlign = textAlign, belowTextContent = belowTextContent) {
buttons()
if (parseHtml) {
AlertContent(text, hostDevice, extraPadding = true, textAlign = textAlign, belowTextContent = belowTextContent) {
buttons()
}
} else {
AlertContent(text?.let { AnnotatedString(it) }, hostDevice, extraPadding = true) {
buttons()
}
}
},
shape = RoundedCornerShape(corner = CornerSize(25.dp))
@@ -122,13 +130,15 @@ class AlertManager {
onDismissRequest: (() -> Unit)? = null,
destructive: Boolean = false,
hostDevice: Pair<Long?, String>? = null,
// When false, [text] is rendered as literal text — use for user-controlled content.
parseHtml: Boolean = true,
) {
showAlert {
AlertDialog(
onDismissRequest = { onDismissRequest?.invoke(); hideAlert() },
title = alertTitle(title),
buttons = {
AlertContent(text, hostDevice, true) {
val buttonRow: @Composable () -> Unit = {
Row(
Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING),
horizontalArrangement = Arrangement.SpaceBetween
@@ -149,6 +159,11 @@ class AlertManager {
}, Modifier.focusRequester(focusRequester)) { Text(confirmText, color = if (destructive) MaterialTheme.colors.error else Color.Unspecified) }
}
}
if (parseHtml) {
AlertContent(text, hostDevice, true, content = buttonRow)
} else {
AlertContent(text?.let { AnnotatedString(it) }, hostDevice, true, content = buttonRow)
}
},
shape = RoundedCornerShape(corner = CornerSize(25.dp))
)