Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ 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)]*)?)"""
)

private fun ChatMessageUi.hasMentionChips(): Boolean =
messageParameters.any { (key, parameter) ->
message.contains("{$key}") && parameter["type"] in mentionChipTypes
Expand Down Expand Up @@ -674,9 +679,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
)
}
}
Expand Down Expand Up @@ -763,15 +772,16 @@ fun ThreadTitle(
}

@Composable
fun EnrichedText(message: ChatMessageUi, modifier: Modifier) {
fun EnrichedText(message: ChatMessageUi, modifier: Modifier, enableLinks: Boolean = true) {
MentionEnrichedText(
message = message,
modifier = modifier,
textStyle = TextStyle(
fontSize = regularTextSize,
color = colorScheme.onSurface,
lineHeight = regularTextSize * LINE_SPACING
)
),
enableLinks = enableLinks
)
}

Expand Down
44 changes: 33 additions & 11 deletions app/src/main/java/com/nextcloud/talk/ui/chat/MentionEnrichedText.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ import com.nextcloud.talk.ui.theme.LocalViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils
import org.greenrobot.eventbus.EventBus

private val messageTokenRegex =
Regex("""(\{[^{}]+\}|\*\*.*?\*\*|\*.*?\*|`.*?`|\[.*?]\(.*?\)|https?://\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")

Expand All @@ -68,6 +70,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
Expand All @@ -90,19 +93,25 @@ private data class MentionChipModel(
private data class MentionRichText(val annotated: AnnotatedString, val inlineContent: Map<String, InlineTextContent>)

@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()) {
Expand All @@ -125,12 +134,14 @@ fun MentionEnrichedText(message: ChatMessageUi, modifier: Modifier = Modifier, t
)
}

@Suppress("LongParameterList")
private fun buildMentionRichText(
message: ChatMessageUi,
linkColor: Color,
codeBackground: Color,
textStyle: TextStyle,
isMultilineLayout: Boolean
isMultilineLayout: Boolean,
enableLinks: Boolean
): MentionRichText {
val inlineContent = linkedMapOf<String, InlineTextContent>()
var mentionCounter = 0
Expand Down Expand Up @@ -177,10 +188,14 @@ 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("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
Expand All @@ -200,11 +215,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(
Expand Down
Loading