feat(jbm): handle link and emote annotations
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
9dc4ecbdf4
commit
362ed5f399
|
|
@ -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())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue