From 045382203e0d3280cd0f498bf6c5187f4b058e28 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 28 Apr 2026 11:31:36 +0200 Subject: [PATCH 1/4] make links clickable Signed-off-by: sowjanyakch --- .../java/com/nextcloud/talk/ui/chat/MentionEnrichedText.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 b9e11debe8c..7d31ded9b11 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 @@ -59,7 +59,7 @@ import com.nextcloud.talk.utils.ApiUtils import org.greenrobot.eventbus.EventBus private val messageTokenRegex = - Regex("""(\{[^{}]+\}|\*\*.*?\*\*|\*.*?\*|`.*?`|\[.*?]\(.*?\)|https?://\S+)""") + Regex("""(\{[^{}]+\}|\*\*.*?\*\*|\*.*?\*|`.*?`|\[.*?]\(.*?\)|https?://\S+|www\.\S+)""") private val mentionParameterTypes = setOf("user", "guest", "call", "user-group", "email", "circle") @@ -68,6 +68,7 @@ private val mentionIconSize = 20.dp private const val MIN_CHIP_LABEL_LENGTH = 4 private const val MAX_CHIP_LABEL_LENGTH = 22 +private const val WWW_PREFIX = "www." private const val CHIP_BASE_WIDTH_EM = 2.45f private const val CHIP_CHAR_WIDTH_EM = 0.56f private const val CHIP_HEIGHT_EM = 1.75f @@ -181,6 +182,7 @@ private fun buildMentionRichText( } token.startsWith("http") -> appendLinkedToken(token, token, linkColor) + token.startsWith(WWW_PREFIX) -> appendLinkedToken(token, "https://$token", linkColor) } lastIndex = range.last + 1 From 335df4e56fd14d04c848d1ad27e9be6486353352 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 28 Apr 2026 13:34:22 +0200 Subject: [PATCH 2/4] links in parent messages are not clickable Signed-off-by: sowjanyakch --- .../talk/ui/chat/ChatMessageScaffold.kt | 13 +++++-- .../talk/ui/chat/MentionEnrichedText.kt | 34 +++++++++++++------ 2 files changed, 34 insertions(+), 13 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 239ece847d9..968b8c0e6b2 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 @@ -139,6 +139,8 @@ private fun shouldShowTimeNextToContent( private val mentionChipTypes = setOf("user", "guest", "call", "user-group", "email", "circle") +private val parentMessageLinkRegex = Regex("""(\[[^\]]+]\([^)]*\)|(?:https?://|www\.)\S+)""") + private fun ChatMessageUi.hasMentionChips(): Boolean = messageParameters.any { (key, parameter) -> message.contains("{$key}") && parameter["type"] in mentionChipTypes @@ -674,9 +676,13 @@ fun CommonMessageQuote(message: ChatMessageUi) { fontSize = authorTextSize, color = colorResource(R.color.no_emphasis_text) ) + + val hasParentLink = parentMessageLinkRegex.containsMatchIn(message.message) + EnrichedText( message, - Modifier.padding(start = 10.dp) + Modifier.padding(start = 10.dp), + !hasParentLink ) } } @@ -763,7 +769,7 @@ fun ThreadTitle( } @Composable -fun EnrichedText(message: ChatMessageUi, modifier: Modifier) { +fun EnrichedText(message: ChatMessageUi, modifier: Modifier, enableLinks: Boolean = true) { MentionEnrichedText( message = message, modifier = modifier, @@ -771,7 +777,8 @@ fun EnrichedText(message: ChatMessageUi, modifier: Modifier) { fontSize = regularTextSize, color = colorScheme.onSurface, lineHeight = regularTextSize * LINE_SPACING - ) + ), + enableLinks = enableLinks ) } 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 7d31ded9b11..91e284477c0 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 @@ -91,19 +91,25 @@ private data class MentionChipModel( private data class MentionRichText(val annotated: AnnotatedString, val inlineContent: Map) @Composable -fun MentionEnrichedText(message: ChatMessageUi, modifier: Modifier = Modifier, textStyle: TextStyle) { +fun MentionEnrichedText( + message: ChatMessageUi, + modifier: Modifier = Modifier, + textStyle: TextStyle, + enableLinks: Boolean = true +) { var isMultilineLayout by remember(message.id, message.message) { mutableStateOf(message.message.contains("\n") || message.message.contains("\r")) } val linkColor = MaterialTheme.colorScheme.primary val codeBackground = MaterialTheme.colorScheme.surfaceVariant - val richText = remember(message, isMultilineLayout, linkColor, codeBackground, textStyle) { + val richText = remember(message, isMultilineLayout, linkColor, codeBackground, textStyle, enableLinks) { buildMentionRichText( message = message, linkColor = linkColor, codeBackground = codeBackground, textStyle = textStyle, - isMultilineLayout = isMultilineLayout + isMultilineLayout = isMultilineLayout, + enableLinks = enableLinks ) } val resolvedTextStyle = if (richText.inlineContent.isEmpty()) { @@ -131,7 +137,8 @@ private fun buildMentionRichText( linkColor: Color, codeBackground: Color, textStyle: TextStyle, - isMultilineLayout: Boolean + isMultilineLayout: Boolean, + enableLinks: Boolean ): MentionRichText { val inlineContent = linkedMapOf() var mentionCounter = 0 @@ -178,11 +185,11 @@ private fun buildMentionRichText( token.startsWith("[") -> { val textPart = token.substringAfter("[").substringBefore("]") val url = token.substringAfter("(").substringBefore(")") - appendLinkedToken(textPart, url, linkColor) + appendLinkedToken(textPart, url, linkColor, enableLinks) } - token.startsWith("http") -> appendLinkedToken(token, token, linkColor) - token.startsWith(WWW_PREFIX) -> appendLinkedToken(token, "https://$token", linkColor) + token.startsWith("http") -> appendLinkedToken(token, token, linkColor, enableLinks) + token.startsWith(WWW_PREFIX) -> appendLinkedToken(token, "https://$token", linkColor, enableLinks) } lastIndex = range.last + 1 @@ -202,11 +209,18 @@ private fun AnnotatedString.Builder.appendStyledToken(text: String, style: SpanS addStyle(style, start, length) } -private fun AnnotatedString.Builder.appendLinkedToken(text: String, url: String, linkColor: Color) { +private fun AnnotatedString.Builder.appendLinkedToken( + text: String, + url: String, + linkColor: Color, + enableLinks: Boolean +) { val start = length append(text) - addStyle(SpanStyle(color = linkColor, textDecoration = TextDecoration.Underline), start, length) - addLink(LinkAnnotation.Url(url), start, length) + if (enableLinks) { + addStyle(SpanStyle(color = linkColor, textDecoration = TextDecoration.Underline), start, length) + addLink(LinkAnnotation.Url(url), start, length) + } } private fun AnnotatedString.Builder.appendFallbackParameter( From 486eb04ab6453326e916c57463bf504bf1d28940 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 28 Apr 2026 14:13:38 +0200 Subject: [PATCH 3/4] support valid links Signed-off-by: sowjanyakch --- .../com/nextcloud/talk/ui/chat/ChatMessageScaffold.kt | 4 +++- .../com/nextcloud/talk/ui/chat/MentionEnrichedText.kt | 11 +++++++++-- 2 files changed, 12 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 968b8c0e6b2..fc6cbd68e69 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 @@ -139,7 +139,9 @@ private fun shouldShowTimeNextToContent( private val mentionChipTypes = setOf("user", "guest", "call", "user-group", "email", "circle") -private val parentMessageLinkRegex = Regex("""(\[[^\]]+]\([^)]*\)|(?:https?://|www\.)\S+)""") +private val parentMessageLinkRegex = Regex( + """(\[[^\]]+]\([^)]*\)|(?:https?://|www\.)[^\s)]+|(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(?:/[^\s)]*)?)""" +) private fun ChatMessageUi.hasMentionChips(): Boolean = messageParameters.any { (key, parameter) -> 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 91e284477c0..587ebfead9a 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 @@ -58,8 +58,9 @@ import com.nextcloud.talk.ui.theme.LocalViewThemeUtils import com.nextcloud.talk.utils.ApiUtils import org.greenrobot.eventbus.EventBus -private val messageTokenRegex = - Regex("""(\{[^{}]+\}|\*\*.*?\*\*|\*.*?\*|`.*?`|\[.*?]\(.*?\)|https?://\S+|www\.\S+)""") +val messageTokenRegex = Regex( + """(\{[^{}]+\}|\*\*.*?\*\*|\*.*?\*|`.*?`|\[.*?]\(.*?\)|https?://[^\s)]+|www\.[^\s)]+|(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(?:/[^\s)]*)?)""" +) private val mentionParameterTypes = setOf("user", "guest", "call", "user-group", "email", "circle") @@ -132,6 +133,9 @@ fun MentionEnrichedText( ) } + + +@Suppress("LongParameterList") private fun buildMentionRichText( message: ChatMessageUi, linkColor: Color, @@ -190,6 +194,9 @@ private fun buildMentionRichText( token.startsWith("http") -> appendLinkedToken(token, token, linkColor, enableLinks) token.startsWith(WWW_PREFIX) -> appendLinkedToken(token, "https://$token", linkColor, enableLinks) + token.matches( + Regex("""(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}.*""") + ) -> appendLinkedToken(token, "https://$token", linkColor, enableLinks) } lastIndex = range.last + 1 From 7a41e7752d429ade9b1850bb2f14766b0a17be03 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 28 Apr 2026 14:52:35 +0200 Subject: [PATCH 4/4] regex split into multilines Signed-off-by: sowjanyakch --- .../java/com/nextcloud/talk/ui/chat/ChatMessageScaffold.kt | 3 ++- .../java/com/nextcloud/talk/ui/chat/MentionEnrichedText.kt | 5 ++--- 2 files changed, 4 insertions(+), 4 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 fc6cbd68e69..9a0d268731c 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 @@ -140,7 +140,8 @@ private fun shouldShowTimeNextToContent( private val mentionChipTypes = setOf("user", "guest", "call", "user-group", "email", "circle") private val parentMessageLinkRegex = Regex( - """(\[[^\]]+]\([^)]*\)|(?:https?://|www\.)[^\s)]+|(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(?:/[^\s)]*)?)""" + """(\[[^\]]+]\([^)]*\)|(?:https?://|www\.)[^\s)]+|""" + + """(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(?:/[^\s)]*)?)""" ) private fun ChatMessageUi.hasMentionChips(): Boolean = 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 587ebfead9a..44f2f367424 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 @@ -59,7 +59,8 @@ import com.nextcloud.talk.utils.ApiUtils import org.greenrobot.eventbus.EventBus val messageTokenRegex = Regex( - """(\{[^{}]+\}|\*\*.*?\*\*|\*.*?\*|`.*?`|\[.*?]\(.*?\)|https?://[^\s)]+|www\.[^\s)]+|(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(?:/[^\s)]*)?)""" + """(\{[^{}]+\}|\*\*.*?\*\*|\*.*?\*|`.*?`|\[.*?]\(.*?\)|https?://[^\s)]+|""" + + """www\.[^\s)]+|(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(?:/[^\s)]*)?)""" ) private val mentionParameterTypes = setOf("user", "guest", "call", "user-group", "email", "circle") @@ -133,8 +134,6 @@ fun MentionEnrichedText( ) } - - @Suppress("LongParameterList") private fun buildMentionRichText( message: ChatMessageUi,