Skip to content
Merged
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 @@ -244,6 +244,7 @@ fun resolveStatusIcon(
}

fun getMessageTypeContent(user: User, message: ChatMessage): MessageTypeContent? =

if (message.isSystemMessage) {
MessageTypeContent.SystemMessage
} else if (message.isVoiceMessage) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}
}
Expand Down
14 changes: 11 additions & 3 deletions app/src/main/java/com/nextcloud/talk/ui/chat/MarkdownText.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
"""(?<!\w)https?://(?:www\.)?[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+(?:/[^\s)]*)?""",
RegexOption.IGNORE_CASE
)

@Suppress("LongMethod", "LongParameterList")
@Composable
fun MarkdownText(
Expand Down Expand Up @@ -152,7 +157,10 @@ fun MarkdownText(
avatarSizePx = avatarSizePx,
avatarGapPx = avatarGapPx
)
val hasLinks = 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)
textView.text = ssb
Expand Down
42 changes: 31 additions & 11 deletions app/src/main/java/com/nextcloud/talk/ui/chat/MentionChip.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -88,7 +88,8 @@ fun parseMentionChipModel(
messageParameters: Map<String, Map<String, String>>,
activeUserId: String?,
activeUserBaseUrl: String?,
roomToken: String?
roomToken: String?,
enableMentionClicks: Boolean = true
): MentionChipModel? =
messageParameters[key]
?.takeIf { it["type"] in mentionParameterTypes }
Expand Down Expand Up @@ -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
)
}
Expand Down Expand Up @@ -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)
Expand All @@ -192,7 +194,8 @@ fun buildMentionInlineContent(
MentionChip(
mention = mention,
textStyle = textStyle,
isMultilineLayout = isMultilineLayout
isMultilineLayout = isMultilineLayout,
onDisabledMentionClick = onDisabledMentionClick
)
}
}
Expand All @@ -203,9 +206,10 @@ fun AnnotatedString.Builder.appendMentionChip(
mention: MentionChipModel,
inlineContent: MutableMap<String, InlineTextContent>,
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}")
}

Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand All @@ -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) {
Expand Down Expand Up @@ -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<String, InlineTextContent>()
var mentionCounter = 0
Expand All @@ -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
)
}
}

Expand All @@ -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
Expand Down
Loading