From 95eaaf7017b505b7d1f37961fdfabc5f47fd6234 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Wed, 13 May 2026 15:24:58 +0200 Subject: [PATCH 1/7] fix links Signed-off-by: sowjanyakch --- .../talk/chat/ui/model/ChatMessageUi.kt | 22 ++++++++++++++++--- .../nextcloud/talk/ui/chat/MarkdownText.kt | 15 ++++++++++--- .../talk/ui/chat/MentionEnrichedText.kt | 2 -- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt b/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt index e5d97b436d..858172da07 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt @@ -243,7 +243,18 @@ fun resolveStatusIcon( else -> MessageStatusIcon.SENT } + +private val validHttpsLinkRegex = Regex( + """(? appendLinkedToken(token, token, linkColor) } lastIndex = range.last + 1 From 7b72f3d68346f317700d53e70aa537728826f8cb Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 19 May 2026 14:23:14 +0200 Subject: [PATCH 2/7] fix quoted links - don't underline Signed-off-by: sowjanyakch --- .../nextcloud/talk/chat/ui/model/ChatMessageUi.kt | 10 +++------- .../nextcloud/talk/ui/chat/ChatMessageScaffold.kt | 14 +++++++++++++- .../com/nextcloud/talk/ui/chat/MarkdownText.kt | 3 +-- .../nextcloud/talk/ui/chat/MentionEnrichedText.kt | 2 ++ 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt b/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt index 858172da07..87fca7624e 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt @@ -243,18 +243,15 @@ fun resolveStatusIcon( else -> MessageStatusIcon.SENT } - private val validHttpsLinkRegex = Regex( """(? append(token) } lastIndex = range.last + 1 From fd27a641a0a542d8a158d254be091555021bba16 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 19 May 2026 15:56:36 +0200 Subject: [PATCH 3/7] make entire quoted text clickable Signed-off-by: sowjanyakch --- .../talk/ui/chat/ChatMessageScaffold.kt | 10 +++-- .../com/nextcloud/talk/ui/chat/MentionChip.kt | 38 +++++++++++++------ .../talk/ui/chat/MentionEnrichedText.kt | 17 ++++++--- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageScaffold.kt b/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageScaffold.kt index c302970561..d397651994 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageScaffold.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageScaffold.kt @@ -711,14 +711,14 @@ fun CommonMessageQuote(message: ChatMessageUi) { iconRes = R.drawable.baseline_location_pin_24, label = c.name ) - else -> QuoteTextContent(message) + else -> QuoteTextContent(message, onClick = {onQuotedMessageClick(message.id)} ) } } } @Composable -private fun QuoteTextContent(message: ChatMessageUi) { - Column { +private fun QuoteTextContent(message: ChatMessageUi, onClick: () -> Unit) { + Column ( modifier = Modifier.combinedClickable(onClick = onClick)){ Text( message.actorDisplayName, fontSize = authorTextSize, @@ -739,7 +739,9 @@ private fun QuoteTextContent(message: ChatMessageUi) { color = colorScheme.onSurface, lineHeight = fontSize * LINE_SPACING ), - maxLines = 4 + maxLines = 4, + enableMentionClicks = false, + onDisabledMentionClick = onClick ) } } diff --git a/app/src/main/java/com/nextcloud/talk/ui/chat/MentionChip.kt b/app/src/main/java/com/nextcloud/talk/ui/chat/MentionChip.kt index f85bc3ddc2..70215ae7db 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/chat/MentionChip.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/chat/MentionChip.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity @@ -34,7 +35,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.role -import androidx.compose.ui.draw.clip import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.Placeholder @@ -88,7 +88,8 @@ fun parseMentionChipModel( messageParameters: Map>, activeUserId: String?, activeUserBaseUrl: String?, - roomToken: String? + roomToken: String?, + enableMentionClicks: Boolean = true ): MentionChipModel? = messageParameters[key] ?.takeIf { it["type"] in mentionParameterTypes } @@ -117,7 +118,7 @@ fun parseMentionChipModel( type = type, isFederated = isFederated, isSelfMention = isSelfMention, - isClickableUserMention = type == "user" && !isSelfMention && !isFederated, + isClickableUserMention = enableMentionClicks && type == "user" && !isSelfMention && !isFederated, avatarUrl = avatarUrl ) } @@ -170,7 +171,8 @@ fun estimateMentionChipWidthInEm(label: String, fontSizeSp: Float): Float { fun buildMentionInlineContent( mention: MentionChipModel, textStyle: TextStyle, - isMultilineLayout: Boolean + isMultilineLayout: Boolean, + onDisabledMentionClick: (() -> Unit)? = null ): InlineTextContent { val fontSizeSp = if (textStyle.fontSize.isSpecified) textStyle.fontSize.value else CHIP_FALLBACK_FONT_SIZE_SP val width = estimateMentionChipWidthInEm(mention.name, fontSizeSp) @@ -192,7 +194,8 @@ fun buildMentionInlineContent( MentionChip( mention = mention, textStyle = textStyle, - isMultilineLayout = isMultilineLayout + isMultilineLayout = isMultilineLayout, + onDisabledMentionClick = onDisabledMentionClick ) } } @@ -203,9 +206,10 @@ fun AnnotatedString.Builder.appendMentionChip( mention: MentionChipModel, inlineContent: MutableMap, textStyle: TextStyle, - isMultilineLayout: Boolean + isMultilineLayout: Boolean, + onDisabledMentionClick: (() -> Unit)? = null ) { - inlineContent[inlineId] = buildMentionInlineContent(mention, textStyle, isMultilineLayout) + inlineContent[inlineId] = buildMentionInlineContent(mention, textStyle, isMultilineLayout, onDisabledMentionClick) appendInlineContent(inlineId, "@${mention.name}") } @@ -215,8 +219,9 @@ fun AnnotatedString.Builder.appendBoldToken(text: String) { addStyle(SpanStyle(fontWeight = FontWeight.Bold), start, length) } + @Composable -fun MentionChip(mention: MentionChipModel, textStyle: TextStyle, isMultilineLayout: Boolean) { +fun MentionChip(mention: MentionChipModel, textStyle: TextStyle, isMultilineLayout: Boolean, onDisabledMentionClick: (() -> Unit)? = null) { val context = LocalContext.current val viewThemeUtils = LocalViewThemeUtils.current val density = LocalDensity.current @@ -246,6 +251,19 @@ fun MentionChip(mention: MentionChipModel, textStyle: TextStyle, isMultilineLayo mention.name } + + val clickModifier = when { + mention.isClickableUserMention -> Modifier.clickable { + EventBus.getDefault().post(UserMentionClickEvent(mention.id)) + } + + onDisabledMentionClick != null -> Modifier.clickable { + onDisabledMentionClick() + } + + else -> Modifier + } + Row( modifier = Modifier .semantics { @@ -254,9 +272,7 @@ fun MentionChip(mention: MentionChipModel, textStyle: TextStyle, isMultilineLayo } .clip(RoundedCornerShape(chipCornerRadius)) .background(backgroundColor) - .clickable(enabled = mention.isClickableUserMention) { - EventBus.getDefault().post(UserMentionClickEvent(mention.id)) - } + .then(clickModifier) .padding(start = verticalPadding, top = verticalPadding, end = 4.dp, bottom = verticalPadding), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp) diff --git a/app/src/main/java/com/nextcloud/talk/ui/chat/MentionEnrichedText.kt b/app/src/main/java/com/nextcloud/talk/ui/chat/MentionEnrichedText.kt index b0aaffce97..57214e2a22 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/chat/MentionEnrichedText.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/chat/MentionEnrichedText.kt @@ -39,7 +39,9 @@ fun MentionEnrichedText( modifier: Modifier = Modifier, textStyle: TextStyle, maxLines: Int = Int.MAX_VALUE, - highlightSearchTerm: String? = null + highlightSearchTerm: String? = null, + enableMentionClicks: Boolean = true, + onDisabledMentionClick: (() -> Unit)? = null ) { var isMultilineLayout by remember(message.id, message.message) { mutableStateOf(message.message.contains("\n") || message.message.contains("\r")) @@ -58,7 +60,9 @@ fun MentionEnrichedText( linkColor = linkColor, codeBackground = codeBackground, textStyle = textStyle, - isMultilineLayout = isMultilineLayout + isMultilineLayout = isMultilineLayout, + enableMentionClicks = enableMentionClicks, + onDisabledMentionClick = onDisabledMentionClick ) } val highlightedText = remember(richText.annotated, highlightSearchTerm) { @@ -91,7 +95,9 @@ private fun buildMentionRichText( linkColor: Color, codeBackground: Color, textStyle: TextStyle, - isMultilineLayout: Boolean + isMultilineLayout: Boolean, + enableMentionClicks: Boolean, + onDisabledMentionClick: (() -> Unit)? ): MentionRichText { val inlineContent = linkedMapOf() var mentionCounter = 0 @@ -113,14 +119,15 @@ private fun buildMentionRichText( messageParameters = message.messageParameters, activeUserId = message.activeUserId, activeUserBaseUrl = message.activeUserBaseUrl, - roomToken = message.roomToken + roomToken = message.roomToken, + enableMentionClicks = enableMentionClicks ) if (mention == null) { appendFallbackParameter(token, message.messageParameters) } else { val inlineId = "mention-${message.id}-$mentionCounter" mentionCounter += 1 - appendMentionChip(inlineId, mention, inlineContent, textStyle, isMultilineLayout) + appendMentionChip(inlineId, mention, inlineContent, textStyle, isMultilineLayout, onDisabledMentionClick) } } From a2b106ed04548c007ea602649599678abf812aab Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 19 May 2026 16:01:11 +0200 Subject: [PATCH 4/7] fix detekt Signed-off-by: sowjanyakch --- .../com/nextcloud/talk/ui/chat/ChatMessageScaffold.kt | 6 +++--- .../java/com/nextcloud/talk/ui/chat/MentionChip.kt | 10 +++++++--- .../com/nextcloud/talk/ui/chat/MentionEnrichedText.kt | 9 ++++++++- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageScaffold.kt b/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageScaffold.kt index d397651994..cfdb7eb4ed 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageScaffold.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageScaffold.kt @@ -711,14 +711,14 @@ fun CommonMessageQuote(message: ChatMessageUi) { iconRes = R.drawable.baseline_location_pin_24, label = c.name ) - else -> QuoteTextContent(message, onClick = {onQuotedMessageClick(message.id)} ) + else -> QuoteTextContent(message, onClick = { onQuotedMessageClick(message.id) }) } } } @Composable -private fun QuoteTextContent(message: ChatMessageUi, onClick: () -> Unit) { - Column ( modifier = Modifier.combinedClickable(onClick = onClick)){ +private fun QuoteTextContent(message: ChatMessageUi, onClick: () -> Unit) { + Column(modifier = Modifier.combinedClickable(onClick = onClick)) { Text( message.actorDisplayName, fontSize = authorTextSize, diff --git a/app/src/main/java/com/nextcloud/talk/ui/chat/MentionChip.kt b/app/src/main/java/com/nextcloud/talk/ui/chat/MentionChip.kt index 70215ae7db..67c337d793 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/chat/MentionChip.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/chat/MentionChip.kt @@ -219,9 +219,14 @@ fun AnnotatedString.Builder.appendBoldToken(text: String) { addStyle(SpanStyle(fontWeight = FontWeight.Bold), start, length) } - +@Suppress("LongMethod") @Composable -fun MentionChip(mention: MentionChipModel, textStyle: TextStyle, isMultilineLayout: Boolean, onDisabledMentionClick: (() -> Unit)? = null) { +fun MentionChip( + mention: MentionChipModel, + textStyle: TextStyle, + isMultilineLayout: Boolean, + onDisabledMentionClick: (() -> Unit)? = null +) { val context = LocalContext.current val viewThemeUtils = LocalViewThemeUtils.current val density = LocalDensity.current @@ -251,7 +256,6 @@ fun MentionChip(mention: MentionChipModel, textStyle: TextStyle, isMultilineLayo mention.name } - val clickModifier = when { mention.isClickableUserMention -> Modifier.clickable { EventBus.getDefault().post(UserMentionClickEvent(mention.id)) diff --git a/app/src/main/java/com/nextcloud/talk/ui/chat/MentionEnrichedText.kt b/app/src/main/java/com/nextcloud/talk/ui/chat/MentionEnrichedText.kt index 57214e2a22..65bcc3cb29 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/chat/MentionEnrichedText.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/chat/MentionEnrichedText.kt @@ -127,7 +127,14 @@ private fun buildMentionRichText( } else { val inlineId = "mention-${message.id}-$mentionCounter" mentionCounter += 1 - appendMentionChip(inlineId, mention, inlineContent, textStyle, isMultilineLayout, onDisabledMentionClick) + appendMentionChip( + inlineId, + mention, + inlineContent, + textStyle, + isMultilineLayout, + onDisabledMentionClick + ) } } From 49cc9d75201e3bfffe349f3e4624d91bf8c4a782 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Wed, 20 May 2026 16:05:34 +0200 Subject: [PATCH 5/7] fix valid links - only links with protocol are clickable Signed-off-by: sowjanyakch --- .../talk/chat/ui/model/ChatMessageUi.kt | 17 +++-------------- .../com/nextcloud/talk/ui/chat/MarkdownText.kt | 2 +- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt b/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt index 87fca7624e..ea5f4ea6c8 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt @@ -243,13 +243,6 @@ fun resolveStatusIcon( else -> MessageStatusIcon.SENT } -private val validHttpsLinkRegex = Regex( - """(? Date: Tue, 26 May 2026 20:21:23 +0200 Subject: [PATCH 6/7] fix linkify order so URLs, phone numbers and emails all become links Linkify.addLinks with a bitmask can clear existing URLSpans before adding its own. Running the URL regex after the phone/email pass caused the URL span to be wiped. Phone/email now runs first so the URL regex span is never overwritten. AI-assistant: 1.9.1-251 (GPT-5.2-Codex) Signed-off-by: Marcel Hibbe --- app/src/main/java/com/nextcloud/talk/ui/chat/MarkdownText.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/ui/chat/MarkdownText.kt b/app/src/main/java/com/nextcloud/talk/ui/chat/MarkdownText.kt index 5135abffee..e181c644f2 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/chat/MarkdownText.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/chat/MarkdownText.kt @@ -158,8 +158,8 @@ fun MarkdownText( avatarGapPx = avatarGapPx ) - val hasLinks = Linkify.addLinks(ssb, validLinkRegex.toPattern(), null) || - Linkify.addLinks(ssb, MESSAGE_LINKIFY_MASK) + val hasOtherLinks = Linkify.addLinks(ssb, MESSAGE_LINKIFY_MASK) + val hasLinks = Linkify.addLinks(ssb, validLinkRegex.toPattern(), null) || hasOtherLinks resolveFileParams(ssb, message) applySearchHighlight(ssb, highlightSearchTerm, searchHighlightColorArgb) From ad59bde86833e197af8095fcd653115f53505065 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 27 May 2026 10:41:39 +0200 Subject: [PATCH 7/7] add enableLinks flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The enableLinks flag flows correctly through the call chain. Regular messages (via EnrichedText) keep the default enableLinks = true so URLs and markdown links are clickable; quoted messages pass enableLinks = false so neither render as tappable links — tapping anywhere on the quote navigates to the original message instead. AI-assistant: 1.9.1-251 (GPT-5.2-Codex) Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/ui/chat/ChatMessageScaffold.kt | 1 + .../nextcloud/talk/ui/chat/MentionEnrichedText.kt | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageScaffold.kt b/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageScaffold.kt index cfdb7eb4ed..7916036e99 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageScaffold.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageScaffold.kt @@ -741,6 +741,7 @@ private fun QuoteTextContent(message: ChatMessageUi, onClick: () -> Unit) { ), maxLines = 4, enableMentionClicks = false, + enableLinks = false, onDisabledMentionClick = onClick ) } diff --git a/app/src/main/java/com/nextcloud/talk/ui/chat/MentionEnrichedText.kt b/app/src/main/java/com/nextcloud/talk/ui/chat/MentionEnrichedText.kt index 65bcc3cb29..e925210103 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/chat/MentionEnrichedText.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/chat/MentionEnrichedText.kt @@ -41,6 +41,7 @@ fun MentionEnrichedText( maxLines: Int = Int.MAX_VALUE, highlightSearchTerm: String? = null, enableMentionClicks: Boolean = true, + enableLinks: Boolean = true, onDisabledMentionClick: (() -> Unit)? = null ) { var isMultilineLayout by remember(message.id, message.message) { @@ -53,7 +54,8 @@ fun MentionEnrichedText( isMultilineLayout, linkColor, codeBackground, - textStyle + textStyle, + enableLinks ) { buildMentionRichText( message = message, @@ -62,6 +64,7 @@ fun MentionEnrichedText( textStyle = textStyle, isMultilineLayout = isMultilineLayout, enableMentionClicks = enableMentionClicks, + enableLinks = enableLinks, onDisabledMentionClick = onDisabledMentionClick ) } @@ -97,6 +100,7 @@ private fun buildMentionRichText( textStyle: TextStyle, isMultilineLayout: Boolean, enableMentionClicks: Boolean, + enableLinks: Boolean, onDisabledMentionClick: (() -> Unit)? ): MentionRichText { val inlineContent = linkedMapOf() @@ -160,10 +164,12 @@ private fun buildMentionRichText( token.startsWith("[") -> { val textPart = token.substringAfter("[").substringBefore("]") val url = token.substringAfter("(").substringBefore(")") - appendLinkedToken(textPart, url, linkColor) + if (enableLinks) appendLinkedToken(textPart, url, linkColor) else append(textPart) } - token.startsWith("http") -> append(token) + token.startsWith("http") -> { + if (enableLinks) appendLinkedToken(token, token, linkColor) else append(token) + } } lastIndex = range.last + 1