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..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 @@ -244,6 +244,7 @@ fun resolveStatusIcon( } fun getMessageTypeContent(user: User, message: ChatMessage): MessageTypeContent? = + if (message.isSystemMessage) { MessageTypeContent.SystemMessage } else if (message.isVoiceMessage) { 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 5846a6f2e8..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 @@ -711,23 +711,38 @@ 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, color = colorResource(R.color.no_emphasis_text) ) - EnrichedText( + + val isInspectionMode = LocalInspectionMode.current + val isSingleEmoji = !isInspectionMode && + message.messageParameters.isEmpty() && + TextMatchers.isMessageWithSingleEmoticonOnly(message.plainMessage) + val fontSize = if (isSingleEmoji) regularTextSize * SINGLE_EMOJI_SIZE_MULTIPLIER else regularTextSize + + MentionEnrichedText( message = message, modifier = Modifier.padding(end = 4.dp), - maxLines = 4 + textStyle = TextStyle( + fontSize = fontSize, + color = colorScheme.onSurface, + lineHeight = fontSize * LINE_SPACING + ), + maxLines = 4, + enableMentionClicks = false, + enableLinks = false, + onDisabledMentionClick = onClick ) } } 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 973efa2c03..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 @@ -60,10 +60,15 @@ private const val CHIP_START_PADDING_DP = 2f private const val CHIP_END_PADDING_DP = 5f private const val CHIP_VERTICAL_PADDING_DP = 2f private const val CHIP_CORNER_RADIUS_DP = 16f -private const val MESSAGE_LINKIFY_MASK = Linkify.WEB_URLS or - Linkify.PHONE_NUMBERS or + +private const val MESSAGE_LINKIFY_MASK = Linkify.PHONE_NUMBERS or Linkify.EMAIL_ADDRESSES +val validLinkRegex = Regex( + """(?>, 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,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) { +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 +256,18 @@ 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 +276,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 83e994503c..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 @@ -39,7 +39,10 @@ fun MentionEnrichedText( modifier: Modifier = Modifier, textStyle: TextStyle, maxLines: Int = Int.MAX_VALUE, - highlightSearchTerm: String? = null + highlightSearchTerm: String? = null, + enableMentionClicks: Boolean = true, + enableLinks: Boolean = true, + onDisabledMentionClick: (() -> Unit)? = null ) { var isMultilineLayout by remember(message.id, message.message) { mutableStateOf(message.message.contains("\n") || message.message.contains("\r")) @@ -51,14 +54,18 @@ fun MentionEnrichedText( isMultilineLayout, linkColor, codeBackground, - textStyle + textStyle, + enableLinks ) { buildMentionRichText( message = message, linkColor = linkColor, codeBackground = codeBackground, textStyle = textStyle, - isMultilineLayout = isMultilineLayout + isMultilineLayout = isMultilineLayout, + enableMentionClicks = enableMentionClicks, + enableLinks = enableLinks, + onDisabledMentionClick = onDisabledMentionClick ) } val highlightedText = remember(richText.annotated, highlightSearchTerm) { @@ -91,7 +98,10 @@ private fun buildMentionRichText( linkColor: Color, codeBackground: Color, textStyle: TextStyle, - isMultilineLayout: Boolean + isMultilineLayout: Boolean, + enableMentionClicks: Boolean, + enableLinks: Boolean, + onDisabledMentionClick: (() -> Unit)? ): MentionRichText { val inlineContent = linkedMapOf() var mentionCounter = 0 @@ -113,14 +123,22 @@ 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 + ) } } @@ -146,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") -> appendLinkedToken(token, token, linkColor) + token.startsWith("http") -> { + if (enableLinks) appendLinkedToken(token, token, linkColor) else append(token) + } } lastIndex = range.last + 1