feat(jbm): use new mention style

Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
Infi 2024-12-13 21:32:42 +01:00
parent 498338d9de
commit d5c78b71d2
2 changed files with 59 additions and 10 deletions

View File

@ -78,10 +78,10 @@ import chat.revolt.components.generic.UserAvatar
import chat.revolt.components.generic.UserAvatarWidthPlaceholder import chat.revolt.components.generic.UserAvatarWidthPlaceholder
import chat.revolt.components.markdown.LocalMarkdownTreeConfig import chat.revolt.components.markdown.LocalMarkdownTreeConfig
import chat.revolt.components.markdown.RichMarkdown import chat.revolt.components.markdown.RichMarkdown
import chat.revolt.internals.text.Gigamoji
import chat.revolt.markdown.jbm.JBM import chat.revolt.markdown.jbm.JBM
import chat.revolt.markdown.jbm.JBMRenderer import chat.revolt.markdown.jbm.JBMRenderer
import chat.revolt.markdown.jbm.LocalJBMarkdownTreeState import chat.revolt.markdown.jbm.LocalJBMarkdownTreeState
import chat.revolt.internals.text.Gigamoji
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import chat.revolt.api.schemas.Message as MessageSchema import chat.revolt.api.schemas.Message as MessageSchema
@ -370,6 +370,7 @@ fun Message(
if (Experiments.useKotlinBasedMarkdownRenderer.isEnabled) { if (Experiments.useKotlinBasedMarkdownRenderer.isEnabled) {
CompositionLocalProvider( CompositionLocalProvider(
LocalJBMarkdownTreeState provides LocalJBMarkdownTreeState.current.copy( LocalJBMarkdownTreeState provides LocalJBMarkdownTreeState.current.copy(
currentServer = RevoltAPI.channelCache[message.channel]?.server,
fontSizeMultiplier = Gigamoji.useGigamojiForMessage( fontSizeMultiplier = Gigamoji.useGigamojiForMessage(
message.content message.content
) )

View File

@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth 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
@ -34,6 +35,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.structuralEqualityPolicy import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.draw.drawBehind
@ -74,6 +76,7 @@ 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.generic.RemoteImage
import chat.revolt.components.generic.UserAvatar
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
@ -98,13 +101,11 @@ enum class JBMAnnotations(val tag: String, val clickable: Boolean) {
ChannelMention("ChannelMention", true), ChannelMention("ChannelMention", true),
CustomEmote("CustomEmote", true), CustomEmote("CustomEmote", true),
Timestamp("Timestamp", false), Timestamp("Timestamp", false),
Checkbox("Checkbox", false) Checkbox("Checkbox", false),
UserAvatar("UserAvatar", true),
} }
object JBMRegularExpressions { 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])?>") val Timestamp = Regex("<t:([0-9]+?)(:[tTDfFR])?>")
} }
@ -130,6 +131,8 @@ data class JBMarkdownTreeState(
val LocalJBMarkdownTreeState = val LocalJBMarkdownTreeState =
compositionLocalOf(structuralEqualityPolicy()) { JBMarkdownTreeState() } compositionLocalOf(structuralEqualityPolicy()) { JBMarkdownTreeState() }
val avatarPadding = 2.dp
@Composable @Composable
@JBM @JBM
fun JBMRenderer(content: String, modifier: Modifier = Modifier) { fun JBMRenderer(content: String, modifier: Modifier = Modifier) {
@ -187,7 +190,6 @@ private fun annotateText(
append(annotateText(state, child)) append(annotateText(state, child))
} }
} else { } else {
// Now we're getting somewhere
pushStringAnnotation( pushStringAnnotation(
tag = JBMAnnotations.UserMention.tag, tag = JBMAnnotations.UserMention.tag,
annotation = userId annotation = userId
@ -198,13 +200,20 @@ private fun annotateText(
background = state.colors.clickableBackground background = state.colors.clickableBackground
) )
) )
val member = state.currentServer?.let { serverId -> val member = state.currentServer?.let { serverId ->
RevoltAPI.members.getMember(serverId, userId) RevoltAPI.members.getMember(serverId, userId)
} }
val mentionDisplay = member?.nickname?.let { nick -> "@$nick" } val mentionDisplay = member?.nickname
?: RevoltAPI.userCache[userId]?.username?.let { username -> "@$username" } ?: RevoltAPI.userCache[userId]?.username
?: "<@$userId>" ?: "<@$userId>"
append(" ")
appendInlineContent(JBMAnnotations.UserAvatar.tag, userId)
append(" ")
append(mentionDisplay) append(mentionDisplay)
append(" ")
pop() pop()
pop() pop()
} }
@ -219,7 +228,6 @@ private fun annotateText(
append(annotateText(state, child)) append(annotateText(state, child))
} }
} else { } else {
// Now we're getting somewhere
pushStringAnnotation( pushStringAnnotation(
tag = JBMAnnotations.ChannelMention.tag, tag = JBMAnnotations.ChannelMention.tag,
annotation = channelId annotation = channelId
@ -248,7 +256,6 @@ private fun annotateText(
append(annotateText(state, child)) append(annotateText(state, child))
} }
} else { } else {
// Now we're getting somewhere
pushStringAnnotation( pushStringAnnotation(
tag = JBMAnnotations.CustomEmote.tag, tag = JBMAnnotations.CustomEmote.tag,
annotation = emoteId annotation = emoteId
@ -673,6 +680,47 @@ private fun JBMText(node: ASTNode, modifier: Modifier) {
) )
} }
} }
},
JBMAnnotations.UserAvatar.tag to with(LocalDensity.current) {
val placeholderBaseWidth =
(LocalTextStyle.current.fontSize * 1.5).toPx() - (avatarPadding * 2).toPx()
val widthTolerancePx =
2 // Else we get a gap of about 1-2 pixels due to rounding errors
val placeholderBaseHeight = (LocalTextStyle.current.fontSize * 1.5).toPx()
val heightTolerancePx = 2 // Dito
InlineTextContent(
placeholder = Placeholder(
width = (placeholderBaseWidth - widthTolerancePx).toSp(),
height = (placeholderBaseHeight - heightTolerancePx).toSp(),
placeholderVerticalAlign = PlaceholderVerticalAlign.Center
),
) { id ->
val user = RevoltAPI.userCache[id]
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.background(LocalJBMarkdownTreeState.current.colors.clickableBackground)
.padding(vertical = avatarPadding)
) {
if (user == null) {
UserAvatar(
username = stringResource(R.string.unknown),
userId = id,
size = (LocalTextStyle.current.fontSize * 1.5).toDp() - (avatarPadding * 2),
modifier = Modifier.aspectRatio(1f, true)
)
} else {
UserAvatar(
username = user.username ?: "",
avatar = user.avatar,
userId = user.id ?: "",
size = (LocalTextStyle.current.fontSize * 1.5).toDp() - (avatarPadding * 2),
modifier = Modifier.aspectRatio(1f, true)
)
}
}
}
} }
) )
) )