Trying to improve rendering speed of chat messages.

This commit is contained in:
Vitor Pamplona
2023-06-25 19:18:32 -04:00
parent fe31855860
commit 7438973a20
2 changed files with 129 additions and 112 deletions
@@ -1,5 +1,6 @@
package com.vitorpamplona.amethyst.ui.note package com.vitorpamplona.amethyst.ui.note
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
@@ -27,7 +28,6 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ChevronRight import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.ripple.rememberRipple import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
@@ -50,9 +50,9 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.IntSize
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 androidx.lifecycle.map
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.RelayInformation import com.vitorpamplona.amethyst.model.RelayInformation
@@ -75,21 +75,19 @@ import com.vitorpamplona.amethyst.ui.theme.ChatBubbleMaxSizeModifier
import com.vitorpamplona.amethyst.ui.theme.ChatBubbleShapeMe import com.vitorpamplona.amethyst.ui.theme.ChatBubbleShapeMe
import com.vitorpamplona.amethyst.ui.theme.ChatBubbleShapeThem import com.vitorpamplona.amethyst.ui.theme.ChatBubbleShapeThem
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.ReactionRowHeightChat
import com.vitorpamplona.amethyst.ui.theme.RelayIconFilter import com.vitorpamplona.amethyst.ui.theme.RelayIconFilter
import com.vitorpamplona.amethyst.ui.theme.Size13dp import com.vitorpamplona.amethyst.ui.theme.Size13dp
import com.vitorpamplona.amethyst.ui.theme.Size15Modifier import com.vitorpamplona.amethyst.ui.theme.Size15Modifier
import com.vitorpamplona.amethyst.ui.theme.Size15dp import com.vitorpamplona.amethyst.ui.theme.Size15dp
import com.vitorpamplona.amethyst.ui.theme.Size16dp
import com.vitorpamplona.amethyst.ui.theme.Size25dp import com.vitorpamplona.amethyst.ui.theme.Size25dp
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.StdStartPadding
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.theme.subtleBorder import com.vitorpamplona.amethyst.ui.theme.subtleBorder
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.persistentSetOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableSet import kotlinx.collections.immutable.toImmutableSet
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@@ -103,10 +101,12 @@ fun ChatroomMessageCompose(
nav: (String) -> Unit, nav: (String) -> Unit,
onWantsToReply: (Note) -> Unit onWantsToReply: (Note) -> Unit
) { ) {
val noteState by baseNote.live().metadata.observeAsState() val isBlank by baseNote.live().metadata.map {
val noteEvent = remember(noteState) { noteState?.note?.event } it.note.event == null
}.observeAsState(baseNote.event == null)
if (noteEvent == null) { Crossfade(targetState = isBlank) {
if (it) {
LongPressToQuickAction(baseNote = baseNote, accountViewModel = accountViewModel) { showPopup -> LongPressToQuickAction(baseNote = baseNote, accountViewModel = accountViewModel) { showPopup ->
BlankNote( BlankNote(
remember { remember {
@@ -128,6 +128,7 @@ fun ChatroomMessageCompose(
onWantsToReply onWantsToReply
) )
} }
}
} }
@Composable @Composable
@@ -197,7 +198,8 @@ fun LoadedChatMessageCompose(
} }
} }
if (showHiddenNote) { Crossfade(targetState = showHiddenNote) {
if (it) {
HiddenNote( HiddenNote(
state.relevantReports, state.relevantReports,
accountViewModel, accountViewModel,
@@ -224,6 +226,7 @@ fun LoadedChatMessageCompose(
onWantsToReply onWantsToReply
) )
} }
}
} }
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@@ -299,7 +302,7 @@ fun NormalChatNote(
modifier = modif, modifier = modif,
horizontalArrangement = alignment horizontalArrangement = alignment
) { ) {
val availableBubbleSize = remember { mutableStateOf(IntSize.Zero) } val availableBubbleSize = remember { mutableStateOf(0) }
var popupExpanded by remember { mutableStateOf(false) } var popupExpanded by remember { mutableStateOf(false) }
val modif2 = remember { val modif2 = remember {
@@ -321,7 +324,9 @@ fun NormalChatNote(
Row( Row(
horizontalArrangement = alignment, horizontalArrangement = alignment,
modifier = modif2.onSizeChanged { modifier = modif2.onSizeChanged {
availableBubbleSize.value = it if (availableBubbleSize.value != it.width) {
availableBubbleSize.value = it.width
}
} }
) { ) {
Surface( Surface(
@@ -363,17 +368,19 @@ private fun RenderBubble(
backgroundBubbleColor: MutableState<Color>, backgroundBubbleColor: MutableState<Color>,
onWantsToReply: (Note) -> Unit, onWantsToReply: (Note) -> Unit,
canPreview: Boolean, canPreview: Boolean,
availableBubbleSize: MutableState<IntSize>, availableBubbleSize: MutableState<Int>,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit nav: (String) -> Unit
) { ) {
val bubbleSize = remember { mutableStateOf(IntSize.Zero) } val bubbleSize = remember { mutableStateOf(0) }
val bubbleModifier = remember { val bubbleModifier = remember {
Modifier Modifier
.padding(start = 10.dp, end = 5.dp, bottom = 5.dp) .padding(start = 10.dp, end = 5.dp, bottom = 5.dp)
.onSizeChanged { .onSizeChanged {
bubbleSize.value = it if (bubbleSize.value != it.width) {
bubbleSize.value = it.width
}
} }
} }
@@ -405,8 +412,8 @@ private fun MessageBubbleLines(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
onWantsToReply: (Note) -> Unit, onWantsToReply: (Note) -> Unit,
canPreview: Boolean, canPreview: Boolean,
bubbleSize: MutableState<IntSize>, bubbleSize: MutableState<Int>,
availableBubbleSize: MutableState<IntSize> availableBubbleSize: MutableState<Int>
) { ) {
if (drawAuthorInfo) { if (drawAuthorInfo) {
DrawAuthorInfo( DrawAuthorInfo(
@@ -459,7 +466,7 @@ private fun RenderReplyRow(
) { ) {
val hasReply by remember { val hasReply by remember {
derivedStateOf { derivedStateOf {
innerQuote && note.replyTo?.lastOrNull() != null !innerQuote && note.replyTo?.lastOrNull() != null
} }
} }
@@ -529,23 +536,22 @@ private fun NoteRow(
@Composable @Composable
private fun ConstrainedStatusRow( private fun ConstrainedStatusRow(
bubbleSize: MutableState<IntSize>, bubbleSize: MutableState<Int>,
availableBubbleSize: MutableState<IntSize>, availableBubbleSize: MutableState<Int>,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier modifier = with(LocalDensity.current) {
Modifier
.height(26.dp)
.padding(top = 5.dp) .padding(top = 5.dp)
.then( .widthIn(
with(LocalDensity.current) { bubbleSize.value.toDp(),
Modifier.widthIn( availableBubbleSize.value.toDp()
bubbleSize.value.width.toDp(),
availableBubbleSize.value.width.toDp()
) )
} }
)
) { ) {
content() content()
} }
@@ -558,29 +564,30 @@ private fun StatusRow(
nav: (String) -> Unit, nav: (String) -> Unit,
onWantsToReply: (Note) -> Unit onWantsToReply: (Note) -> Unit
) { ) {
Column { Column(modifier = ReactionRowHeightChat) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically, modifier = ReactionRowHeightChat) {
ChatTimeAgo(baseNote) ChatTimeAgo(baseNote)
RelayBadges(baseNote, accountViewModel, nav = nav) RelayBadges(baseNote, accountViewModel, nav = nav)
Spacer(modifier = DoubleHorzSpacer) Spacer(modifier = DoubleHorzSpacer)
} }
} }
Column { Column(modifier = ReactionRowHeightChat) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically, modifier = ReactionRowHeightChat) {
LikeReaction(baseNote, MaterialTheme.colors.placeholderText, accountViewModel) LikeReaction(baseNote, MaterialTheme.colors.placeholderText, accountViewModel)
Spacer(modifier = StdHorzSpacer) Spacer(modifier = StdHorzSpacer)
ZapReaction(baseNote, MaterialTheme.colors.placeholderText, accountViewModel) ZapReaction(baseNote, MaterialTheme.colors.placeholderText, accountViewModel)
Spacer(modifier = StdHorzSpacer) Spacer(modifier = DoubleHorzSpacer)
ReplyReaction( ReplyReaction(
baseNote = baseNote, baseNote = baseNote,
grayTint = MaterialTheme.colors.placeholderText, grayTint = MaterialTheme.colors.placeholderText,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
showCounter = false, showCounter = false,
iconSize = Size16dp iconSize = Size15dp
) { ) {
onWantsToReply(baseNote) onWantsToReply(baseNote)
} }
Spacer(modifier = StdHorzSpacer)
} }
} }
} }
@@ -800,44 +807,54 @@ private fun DisplayMessageUsername(
DrawPlayName(userDisplayName) DrawPlayName(userDisplayName)
} }
@Immutable @Composable
data class RelayBadgesState( private fun RelayBadges(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val shouldDisplayExpandButton: Boolean, val expanded = remember { mutableStateOf(false) }
val noteRelays: ImmutableList<String>,
val noteRelaysSimple: ImmutableList<String> RenderRelayList(baseNote, expanded, accountViewModel, nav)
)
RenderExpandButton(baseNote, expanded) {
ChatRelayExpandButton { expanded.value = true }
}
}
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class)
@Composable @Composable
private fun RelayBadges(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) { fun RenderRelayList(baseNote: Note, expanded: MutableState<Boolean>, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val noteRelaysState by baseNote.live().relays.observeAsState() val noteRelays by baseNote.live().relays.map {
it.note.relays
}.observeAsState(baseNote.relays)
val state: RelayBadgesState by remember(noteRelaysState) { FlowRow(StdStartPadding) {
val newShouldDisplayExpandButton = (noteRelaysState?.note?.relays?.size ?: 0) > 3 val relaysToDisplay = remember(noteRelays, expanded.value) {
val noteRelays = noteRelaysState?.note?.relays?.toImmutableList() ?: persistentListOf() if (expanded.value) noteRelays else noteRelays.take(3)
val noteRelaysSimple = noteRelaysState?.note?.relays?.take(3)?.toImmutableList() ?: persistentListOf()
mutableStateOf(RelayBadgesState(newShouldDisplayExpandButton, noteRelays, noteRelaysSimple))
} }
var expanded by remember { mutableStateOf(false) }
val relaysToDisplay by remember(noteRelaysState) {
derivedStateOf {
if (expanded) state.noteRelays else state.noteRelaysSimple
}
}
FlowRow(Modifier.padding(start = 10.dp)) {
relaysToDisplay.forEach { relaysToDisplay.forEach {
RenderRelay(it, accountViewModel, nav) RenderRelay(it, accountViewModel, nav)
} }
} }
}
if (state.shouldDisplayExpandButton && !expanded) { @Composable
fun RenderExpandButton(
baseNote: Note,
expanded: MutableState<Boolean>,
content: @Composable () -> Unit
) {
val showExpandButton by baseNote.live().relays.map {
it.note.relays.size > 3
}.observeAsState(baseNote.relays.size > 3)
if (showExpandButton && !expanded.value) {
content()
}
}
@Composable
fun ChatRelayExpandButton(onClick: () -> Unit) {
IconButton( IconButton(
modifier = Size15Modifier, modifier = Size15Modifier,
onClick = { expanded = true } onClick = onClick
) { ) {
Icon( Icon(
imageVector = Icons.Default.ChevronRight, imageVector = Icons.Default.ChevronRight,
@@ -846,7 +863,6 @@ private fun RelayBadges(baseNote: Note, accountViewModel: AccountViewModel, nav:
tint = MaterialTheme.colors.placeholderText tint = MaterialTheme.colors.placeholderText
) )
} }
}
} }
@Composable @Composable
@@ -75,6 +75,7 @@ val WidthAuthorPictureModifier = Modifier.width(55.dp)
val DiviserThickness = 0.25.dp val DiviserThickness = 0.25.dp
val ReactionRowHeight = Modifier.height(24.dp).padding(start = 10.dp) val ReactionRowHeight = Modifier.height(24.dp).padding(start = 10.dp)
val ReactionRowHeightChat = Modifier.height(25.dp)
val Height4dpModifier = Modifier.height(4.dp) val Height4dpModifier = Modifier.height(4.dp)