Merge pull request #2160 from surakin/msc4027

Implement MSC4027 (Custom images in reactions)
This commit is contained in:
Benoit Marty 2024-01-04 16:23:30 +01:00 committed by GitHub
commit 676ec90c22
3 changed files with 61 additions and 22 deletions

1
changelog.d/2159.feature Normal file
View file

@ -0,0 +1 @@
Added support for MSC4027 (render custom images in reactions)

View file

@ -24,7 +24,9 @@ import androidx.compose.foundation.border
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
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.layout.width
@ -41,6 +43,8 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage
import io.element.android.compound.theme.ElementTheme
import io.element.android.features.messages.impl.R import io.element.android.features.messages.impl.R
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
import io.element.android.features.messages.impl.timeline.model.AggregatedReactionProvider import io.element.android.features.messages.impl.timeline.model.AggregatedReactionProvider
@ -52,7 +56,8 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.ui.media.MediaRequestData
@Composable @Composable
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@ -114,8 +119,9 @@ sealed interface MessagesReactionsButtonContent {
val isHighlighted get() = this is Reaction && reaction.isHighlighted val isHighlighted get() = this is Reaction && reaction.isHighlighted
} }
private val reactionEmojiLineHeight = 20.sp internal val REACTION_EMOJI_LINE_HEIGHT = 20.sp
private val addEmojiSize = 16.dp internal const val REACTION_IMAGE_ASPECT_RATIO = 1.0f
private val ADD_EMOJI_SIZE = 16.dp
@Composable @Composable
private fun TextContent( private fun TextContent(
@ -123,7 +129,7 @@ private fun TextContent(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) = Text( ) = Text(
modifier = modifier modifier = modifier
.height(reactionEmojiLineHeight.toDp()), .height(REACTION_EMOJI_LINE_HEIGHT.toDp()),
text = text, text = text,
style = ElementTheme.typography.fontBodyMdRegular, style = ElementTheme.typography.fontBodyMdRegular,
color = ElementTheme.materialColors.primary color = ElementTheme.materialColors.primary
@ -138,7 +144,7 @@ private fun IconContent(
contentDescription = stringResource(id = R.string.screen_room_timeline_add_reaction), contentDescription = stringResource(id = R.string.screen_room_timeline_add_reaction),
tint = ElementTheme.materialColors.secondary, tint = ElementTheme.materialColors.secondary,
modifier = modifier modifier = modifier
.size(addEmojiSize) .size(ADD_EMOJI_SIZE)
) )
@ -150,13 +156,25 @@ private fun ReactionContent(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = modifier, modifier = modifier,
) { ) {
Text( // Check if this is a custom reaction (MSC4027)
text = reaction.displayKey, if (reaction.key.startsWith("mxc://")) {
style = ElementTheme.typography.fontBodyMdRegular.copy( AsyncImage(
fontSize = 15.sp, modifier = modifier
lineHeight = reactionEmojiLineHeight, .heightIn(min = REACTION_EMOJI_LINE_HEIGHT.toDp(), max = REACTION_EMOJI_LINE_HEIGHT.toDp())
), .aspectRatio(REACTION_IMAGE_ASPECT_RATIO, false),
) model = MediaRequestData(MediaSource(reaction.key), MediaRequestData.Kind.Content),
contentDescription = null
)
}
else {
Text(
text = reaction.displayKey,
style = ElementTheme.typography.fontBodyMdRegular.copy(
fontSize = 15.sp,
lineHeight = REACTION_EMOJI_LINE_HEIGHT,
),
)
}
if (reaction.count > 1) { if (reaction.count > 1) {
Spacer(modifier = Modifier.width(4.dp)) Spacer(modifier = Modifier.width(4.dp))
Text( Text(

View file

@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.heightIn
@ -57,20 +58,27 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage
import io.element.android.compound.theme.ElementTheme
import io.element.android.features.messages.impl.timeline.components.REACTION_IMAGE_ASPECT_RATIO
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.text.toDp
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.media.MediaRequestData
import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.compound.theme.ElementTheme
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
internal val REACTION_SUMMARY_LINE_HEIGHT = 25.sp
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun ReactionSummaryView( fun ReactionSummaryView(
@ -192,13 +200,25 @@ private fun AggregatedReactionButton(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier, modifier = Modifier,
) { ) {
Text( // Check if this is a custom reaction (MSC4027)
text = reaction.displayKey, if (reaction.key.startsWith("mxc://")) {
style = ElementTheme.typography.fontBodyMdRegular.copy( AsyncImage(
fontSize = 20.sp, modifier = Modifier
lineHeight = 25.sp .heightIn(min = REACTION_SUMMARY_LINE_HEIGHT.toDp(), max = REACTION_SUMMARY_LINE_HEIGHT.toDp())
), .aspectRatio(REACTION_IMAGE_ASPECT_RATIO, false),
) model = MediaRequestData(MediaSource(reaction.key), MediaRequestData.Kind.Content),
contentDescription = null
)
}
else {
Text(
text = reaction.displayKey,
style = ElementTheme.typography.fontBodyMdRegular.copy(
fontSize = 20.sp,
lineHeight = REACTION_SUMMARY_LINE_HEIGHT
),
)
}
if (reaction.count > 1) { if (reaction.count > 1) {
Spacer(modifier = Modifier.width(4.dp)) Spacer(modifier = Modifier.width(4.dp))
Text( Text(
@ -206,7 +226,7 @@ private fun AggregatedReactionButton(
color = textColor, color = textColor,
style = ElementTheme.typography.fontBodyMdRegular.copy( style = ElementTheme.typography.fontBodyMdRegular.copy(
fontSize = 20.sp, fontSize = 20.sp,
lineHeight = 25.sp lineHeight = REACTION_SUMMARY_LINE_HEIGHT
) )
) )
} }