feat(jbm): handle link and emote annotations

Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
Infi 2024-10-21 16:41:21 +02:00
parent 9dc4ecbdf4
commit 362ed5f399
1 changed files with 90 additions and 5 deletions

View File

@ -17,6 +17,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.InlineTextContent import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.appendInlineContent import androidx.compose.foundation.text.appendInlineContent
@ -43,6 +44,7 @@ import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
@ -63,10 +65,14 @@ import androidx.compose.ui.unit.sp
import androidx.core.net.toUri import androidx.core.net.toUri
import chat.revolt.R import chat.revolt.R
import chat.revolt.activities.InviteActivity import chat.revolt.activities.InviteActivity
import chat.revolt.api.REVOLT_FILES
import chat.revolt.api.RevoltAPI
import chat.revolt.api.routes.custom.fetchEmoji
import chat.revolt.api.schemas.isInviteUri import chat.revolt.api.schemas.isInviteUri
import chat.revolt.api.settings.LoadedSettings import chat.revolt.api.settings.LoadedSettings
import chat.revolt.callbacks.Action import chat.revolt.callbacks.Action
import chat.revolt.callbacks.ActionChannel import chat.revolt.callbacks.ActionChannel
import chat.revolt.components.generic.RemoteImage
import chat.revolt.components.markdown.Annotations import chat.revolt.components.markdown.Annotations
import chat.revolt.components.utils.detectTapGesturesConditionalConsume import chat.revolt.components.utils.detectTapGesturesConditionalConsume
import chat.revolt.ui.theme.FragmentMono import chat.revolt.ui.theme.FragmentMono
@ -90,7 +96,15 @@ enum class JBMAnnotations(val tag: String, val clickable: Boolean) {
UserMention("UserMention", true), UserMention("UserMention", true),
ChannelMention("ChannelMention", true), ChannelMention("ChannelMention", true),
CustomEmote("CustomEmote", true), CustomEmote("CustomEmote", true),
Timestamp("Timestamp", false) Timestamp("Timestamp", false),
Checkbox("Checkbox", false)
}
object JBMRegularExpressions {
val Mention = Regex("<@([0-9A-Z]{26})>")
val Channel = Regex("<#([0-9A-Z]{26})>")
val CustomEmote = Regex(":([0-9A-Z]{26}):")
val Timestamp = Regex("<t:([0-9]+?)(:[tTDfFR])?>")
} }
data class JBMColors( data class JBMColors(
@ -244,9 +258,9 @@ private fun annotateText(
GFMTokenTypes.CHECK_BOX -> { GFMTokenTypes.CHECK_BOX -> {
if (node.getTextInNode(sourceText).trim() == "[ ]") { if (node.getTextInNode(sourceText).trim() == "[ ]") {
appendInlineContent("checkbox", "") appendInlineContent(JBMAnnotations.Checkbox.tag, "")
} else { } else {
appendInlineContent("checkbox", "") appendInlineContent(JBMAnnotations.Checkbox.tag, "")
} }
append(" ") append(" ")
} }
@ -450,6 +464,47 @@ private fun JBMText(node: ASTNode, modifier: Modifier) {
return@handler true return@handler true
} }
annotatedText.getStringAnnotations(
tag = Annotations.UserMention.tag,
start = offset,
end = offset
).firstOrNull()?.let { annotation ->
scope.launch {
ActionChannel.send(
Action.OpenUserSheet(
annotation.item,
mdState.currentServer
)
)
}
return@handler true
}
annotatedText.getStringAnnotations(
tag = Annotations.ChannelMention.tag,
start = offset,
end = offset
).firstOrNull()?.let { annotation ->
scope.launch {
ActionChannel.send(Action.SwitchChannel(annotation.item))
}
return@handler true
}
annotatedText.getStringAnnotations(
tag = Annotations.CustomEmote.tag,
start = offset,
end = offset
).firstOrNull()?.let { annotation ->
scope.launch {
ActionChannel.send(Action.EmoteInfo(annotation.item))
}
return@handler true
}
} }
return@handler false return@handler false
@ -483,7 +538,7 @@ private fun JBMText(node: ASTNode, modifier: Modifier) {
) )
}, },
inlineContent = mapOf( inlineContent = mapOf(
"checkbox" to InlineTextContent( JBMAnnotations.Checkbox.tag to InlineTextContent(
placeholder = Placeholder( placeholder = Placeholder(
width = LocalTextStyle.current.fontSize * 1.5, width = LocalTextStyle.current.fontSize * 1.5,
height = LocalTextStyle.current.fontSize * 1.5, height = LocalTextStyle.current.fontSize * 1.5,
@ -518,7 +573,37 @@ private fun JBMText(node: ASTNode, modifier: Modifier) {
} }
} }
} }
) ),
JBMAnnotations.CustomEmote.tag to InlineTextContent(
placeholder = Placeholder(
width = LocalTextStyle.current.fontSize * 1.5,
height = LocalTextStyle.current.fontSize * 1.5,
placeholderVerticalAlign = PlaceholderVerticalAlign.Center
),
) { id ->
val emote = RevoltAPI.emojiCache[id]
if (emote == null) {
scope.launch {
try {
RevoltAPI.emojiCache[id] = fetchEmoji(id)
} catch (e: Exception) {
// no-op
}
}
return@InlineTextContent
} else {
with(LocalDensity.current) {
RemoteImage(
url = "$REVOLT_FILES/emojis/${id}/emoji.gif",
description = emote.name,
contentScale = ContentScale.Fit,
modifier = Modifier
.width((LocalTextStyle.current.fontSize * 1.5).toDp())
.height((LocalTextStyle.current.fontSize * 1.5).toDp())
)
}
}
}
) )
) )
} }