Improves rendering performance of Chat Screens

This commit is contained in:
Vitor Pamplona
2023-06-21 11:57:49 -04:00
parent 6fe66986be
commit cce9d6cf68
7 changed files with 297 additions and 140 deletions

View File

@@ -275,9 +275,10 @@ private fun RenderUserAsClickableText(
CreateClickableTextWithEmoji( CreateClickableTextWithEmoji(
clickablePart = it, clickablePart = it,
suffix = addedCharts, suffix = addedCharts,
tags = userTags, maxLines = 1,
route = route, route = route,
nav = nav nav = nav,
tags = userTags
) )
} }
} }
@@ -286,24 +287,38 @@ private fun RenderUserAsClickableText(
fun CreateClickableText( fun CreateClickableText(
clickablePart: String, clickablePart: String,
suffix: String, suffix: String,
maxLines: Int = Int.MAX_VALUE,
overrideColor: Color? = null, overrideColor: Color? = null,
fontWeight: FontWeight = FontWeight.Normal, fontWeight: FontWeight = FontWeight.Normal,
route: String, route: String,
nav: (String) -> Unit nav: (String) -> Unit
) { ) {
ClickableText( val currentStyle = LocalTextStyle.current
text = buildAnnotatedString { val primaryColor = MaterialTheme.colors.primary
withStyle( val onBackgroundColor = MaterialTheme.colors.onBackground
LocalTextStyle.current.copy(color = overrideColor ?: MaterialTheme.colors.primary, fontWeight = fontWeight).toSpanStyle()
) { val clickablePartStyle = remember(primaryColor, overrideColor) {
currentStyle.copy(color = overrideColor ?: primaryColor, fontWeight = fontWeight).toSpanStyle()
}
val nonClickablePartStyle = remember(onBackgroundColor, overrideColor) {
currentStyle.copy(color = overrideColor ?: onBackgroundColor, fontWeight = fontWeight).toSpanStyle()
}
val text = remember(clickablePartStyle, nonClickablePartStyle, clickablePart, suffix) {
buildAnnotatedString {
withStyle(clickablePartStyle) {
append(clickablePart) append(clickablePart)
} }
withStyle( withStyle(nonClickablePartStyle) {
LocalTextStyle.current.copy(color = overrideColor ?: MaterialTheme.colors.onBackground, fontWeight = fontWeight).toSpanStyle()
) {
append(suffix) append(suffix)
} }
}, }
}
ClickableText(
text = text,
maxLines = maxLines,
onClick = { nav(route) } onClick = { nav(route) }
) )
} }
@@ -410,14 +425,17 @@ fun CreateTextWithEmoji(
modifier = modifier modifier = modifier
) )
} else { } else {
val style = LocalTextStyle.current.merge( val currentStyle = LocalTextStyle.current
TextStyle( val style = remember(currentStyle) {
color = textColor, currentStyle.merge(
textAlign = textAlign, TextStyle(
fontWeight = fontWeight, color = textColor,
fontSize = fontSize textAlign = textAlign,
) fontWeight = fontWeight,
).toSpanStyle() fontSize = fontSize
)
).toSpanStyle()
}
InLineIconRenderer(emojiList, style, maxLines, overflow, modifier) InLineIconRenderer(emojiList, style, maxLines, overflow, modifier)
} }
@@ -426,6 +444,7 @@ fun CreateTextWithEmoji(
@Composable @Composable
fun CreateClickableTextWithEmoji( fun CreateClickableTextWithEmoji(
clickablePart: String, clickablePart: String,
maxLines: Int = Int.MAX_VALUE,
tags: ImmutableListOfLists<String>?, tags: ImmutableListOfLists<String>?,
style: TextStyle, style: TextStyle,
onClick: (Int) -> Unit onClick: (Int) -> Unit
@@ -448,29 +467,37 @@ fun CreateClickableTextWithEmoji(
if (emojiList.isEmpty()) { if (emojiList.isEmpty()) {
ClickableText( ClickableText(
AnnotatedString(clickablePart), text = AnnotatedString(clickablePart),
style = style, style = style,
maxLines = maxLines,
onClick = onClick onClick = onClick
) )
} else { } else {
ClickableInLineIconRenderer(emojiList, style.toSpanStyle()) { ClickableInLineIconRenderer(emojiList, maxLines, style.toSpanStyle()) {
onClick(it) onClick(it)
} }
} }
} }
@Immutable
data class DoubleEmojiList(
val part1: ImmutableList<Renderable>,
val part2: ImmutableList<Renderable>
)
@Composable @Composable
fun CreateClickableTextWithEmoji( fun CreateClickableTextWithEmoji(
clickablePart: String, clickablePart: String,
suffix: String, suffix: String,
tags: ImmutableListOfLists<String>?, maxLines: Int = Int.MAX_VALUE,
overrideColor: Color? = null, overrideColor: Color? = null,
fontWeight: FontWeight = FontWeight.Normal, fontWeight: FontWeight = FontWeight.Normal,
route: String, route: String,
nav: (String) -> Unit nav: (String) -> Unit,
tags: ImmutableListOfLists<String>?
) { ) {
var emojiLists by remember(clickablePart) { var emojiLists by remember(clickablePart) {
mutableStateOf<Pair<ImmutableList<Renderable>, ImmutableList<Renderable>>?>(null) mutableStateOf<DoubleEmojiList?>(null)
} }
LaunchedEffect(key1 = clickablePart) { LaunchedEffect(key1 = clickablePart) {
@@ -483,20 +510,28 @@ fun CreateClickableTextWithEmoji(
val newEmojiList2 = assembleAnnotatedList(suffix, emojis) val newEmojiList2 = assembleAnnotatedList(suffix, emojis)
if (newEmojiList1.isNotEmpty() || newEmojiList2.isNotEmpty()) { if (newEmojiList1.isNotEmpty() || newEmojiList2.isNotEmpty()) {
emojiLists = Pair(newEmojiList1.toImmutableList(), newEmojiList2.toImmutableList()) emojiLists = DoubleEmojiList(newEmojiList1.toImmutableList(), newEmojiList2.toImmutableList())
} }
} }
} }
} }
if (emojiLists == null) { if (emojiLists == null) {
CreateClickableText(clickablePart, suffix, overrideColor, fontWeight, route, nav) CreateClickableText(clickablePart, suffix, maxLines, overrideColor, fontWeight, route, nav)
} else { } else {
ClickableInLineIconRenderer(emojiLists!!.first, LocalTextStyle.current.copy(color = overrideColor ?: MaterialTheme.colors.primary, fontWeight = fontWeight).toSpanStyle()) { ClickableInLineIconRenderer(
emojiLists!!.part1,
maxLines,
LocalTextStyle.current.copy(color = overrideColor ?: MaterialTheme.colors.primary, fontWeight = fontWeight).toSpanStyle()
) {
nav(route) nav(route)
} }
InLineIconRenderer(emojiLists!!.second, LocalTextStyle.current.copy(color = overrideColor ?: MaterialTheme.colors.onBackground, fontWeight = fontWeight).toSpanStyle()) InLineIconRenderer(
emojiLists!!.part2,
LocalTextStyle.current.copy(color = overrideColor ?: MaterialTheme.colors.onBackground, fontWeight = fontWeight).toSpanStyle(),
maxLines
)
} }
} }
@@ -521,7 +556,12 @@ class TextType(val text: String) : Renderable()
class ImageUrlType(val url: String) : Renderable() class ImageUrlType(val url: String) : Renderable()
@Composable @Composable
fun ClickableInLineIconRenderer(wordsInOrder: ImmutableList<Renderable>, style: SpanStyle, onClick: (Int) -> Unit) { fun ClickableInLineIconRenderer(
wordsInOrder: ImmutableList<Renderable>,
maxLines: Int = Int.MAX_VALUE,
style: SpanStyle,
onClick: (Int) -> Unit
) {
val inlineContent = wordsInOrder.mapIndexedNotNull { idx, value -> val inlineContent = wordsInOrder.mapIndexedNotNull { idx, value ->
if (value is ImageUrlType) { if (value is ImageUrlType) {
Pair( Pair(
@@ -574,6 +614,7 @@ fun ClickableInLineIconRenderer(wordsInOrder: ImmutableList<Renderable>, style:
text = annotatedText, text = annotatedText,
modifier = pressIndicator, modifier = pressIndicator,
inlineContent = inlineContent, inlineContent = inlineContent,
maxLines = maxLines,
onTextLayout = { onTextLayout = {
layoutResult.value = it layoutResult.value = it
} }

View File

@@ -1041,8 +1041,9 @@ private fun DisplayUserFromTag(
CreateClickableTextWithEmoji( CreateClickableTextWithEmoji(
clickablePart = displayName, clickablePart = displayName,
suffix = remember { "$addedChars " }, suffix = remember { "$addedChars " },
tags = userTags, maxLines = 1,
route = route, route = route,
nav = nav nav = nav,
tags = userTags
) )
} }

View File

@@ -52,6 +52,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
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.User
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent
import com.vitorpamplona.amethyst.service.model.PrivateDmEvent import com.vitorpamplona.amethyst.service.model.PrivateDmEvent
@@ -72,6 +73,7 @@ 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.Size16dp import com.vitorpamplona.amethyst.ui.theme.Size16dp
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.StdVertSpacer import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
@@ -261,11 +263,11 @@ fun NormalChatNote(
} }
} }
LaunchedEffect(key1 = routeForLastRead) { if (routeForLastRead != null) {
routeForLastRead?.let { LaunchedEffect(key1 = routeForLastRead) {
val createdAt = note.createdAt() val createdAt = note.createdAt()
if (createdAt != null) { if (createdAt != null) {
accountViewModel.account.markAsRead(it, createdAt) accountViewModel.account.markAsRead(routeForLastRead, createdAt)
} }
} }
} }
@@ -369,43 +371,72 @@ private fun RenderBubble(
} }
Column(modifier = bubbleModifier) { Column(modifier = bubbleModifier) {
if (drawAuthorInfo) { MessageBubbleLines(
DrawAuthorInfo( drawAuthorInfo,
baseNote, baseNote,
alignment, alignment,
nav nav,
) innerQuote,
} else { backgroundBubbleColor,
Spacer(modifier = StdVertSpacer) accountViewModel,
} onWantsToReply,
canPreview,
bubbleSize,
availableBubbleSize
)
}
}
RenderReplyRow( @Composable
note = baseNote, private fun MessageBubbleLines(
innerQuote = innerQuote, drawAuthorInfo: Boolean,
backgroundBubbleColor = backgroundBubbleColor, baseNote: Note,
alignment: Arrangement.Horizontal,
nav: (String) -> Unit,
innerQuote: Boolean,
backgroundBubbleColor: MutableState<Color>,
accountViewModel: AccountViewModel,
onWantsToReply: (Note) -> Unit,
canPreview: Boolean,
bubbleSize: MutableState<IntSize>,
availableBubbleSize: MutableState<IntSize>
) {
if (drawAuthorInfo) {
DrawAuthorInfo(
baseNote,
alignment,
nav
)
} else {
Spacer(modifier = StdVertSpacer)
}
RenderReplyRow(
note = baseNote,
innerQuote = innerQuote,
backgroundBubbleColor = backgroundBubbleColor,
accountViewModel = accountViewModel,
nav = nav,
onWantsToReply = onWantsToReply
)
NoteRow(
note = baseNote,
canPreview = canPreview,
backgroundBubbleColor = backgroundBubbleColor,
accountViewModel = accountViewModel,
nav = nav
)
ConstrainedStatusRow(
bubbleSize = bubbleSize,
availableBubbleSize = availableBubbleSize
) {
StatusRow(
baseNote = baseNote,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav,
onWantsToReply = onWantsToReply onWantsToReply = onWantsToReply
) )
NoteRow(
note = baseNote,
canPreview = canPreview,
backgroundBubbleColor = backgroundBubbleColor,
accountViewModel = accountViewModel,
nav = nav
)
ConstrainedStatusRow(
bubbleSize = bubbleSize,
availableBubbleSize = availableBubbleSize
) {
StatusRow(
baseNote = baseNote,
accountViewModel = accountViewModel,
onWantsToReply = onWantsToReply
)
}
} }
} }
@@ -418,25 +449,42 @@ private fun RenderReplyRow(
nav: (String) -> Unit, nav: (String) -> Unit,
onWantsToReply: (Note) -> Unit onWantsToReply: (Note) -> Unit
) { ) {
val replyTo by remember { val hasReply by remember {
derivedStateOf { derivedStateOf {
note.replyTo?.lastOrNull() innerQuote && note.replyTo?.lastOrNull() != null
} }
} }
if (!innerQuote && replyTo != null) {
Row(verticalAlignment = Alignment.CenterVertically) { if (hasReply) {
replyTo?.let { note -> RenderReply(note, backgroundBubbleColor, accountViewModel, nav, onWantsToReply)
ChatroomMessageCompose( }
note, }
null,
innerQuote = true, @Composable
parentBackgroundColor = backgroundBubbleColor, private fun RenderReply(
accountViewModel = accountViewModel, note: Note,
nav = nav, backgroundBubbleColor: MutableState<Color>,
onWantsToReply = onWantsToReply accountViewModel: AccountViewModel,
) nav: (String) -> Unit,
onWantsToReply: (Note) -> Unit
) {
Row(verticalAlignment = Alignment.CenterVertically) {
val replyTo by remember {
derivedStateOf {
note.replyTo?.lastOrNull()
} }
} }
replyTo?.let { note ->
ChatroomMessageCompose(
note,
null,
innerQuote = true,
parentBackgroundColor = backgroundBubbleColor,
accountViewModel = accountViewModel,
nav = nav,
onWantsToReply = onWantsToReply
)
}
} }
} }
@@ -501,9 +549,7 @@ private fun StatusRow(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
onWantsToReply: (Note) -> Unit onWantsToReply: (Note) -> Unit
) { ) {
val grayTint = MaterialTheme.colors.placeholderText Column {
Column() {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
ChatTimeAgo(baseNote) ChatTimeAgo(baseNote)
RelayBadges(baseNote) RelayBadges(baseNote)
@@ -511,16 +557,16 @@ private fun StatusRow(
} }
} }
Column() { Column {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
LikeReaction(baseNote, grayTint, accountViewModel) LikeReaction(baseNote, MaterialTheme.colors.placeholderText, accountViewModel)
Spacer(modifier = StdHorzSpacer) Spacer(modifier = StdHorzSpacer)
ZapReaction(baseNote, grayTint, accountViewModel) ZapReaction(baseNote, MaterialTheme.colors.placeholderText, accountViewModel)
Spacer(modifier = StdHorzSpacer) Spacer(modifier = StdHorzSpacer)
ReplyReaction( ReplyReaction(
baseNote, baseNote = baseNote,
grayTint, grayTint = MaterialTheme.colors.placeholderText,
accountViewModel, accountViewModel = accountViewModel,
showCounter = false, showCounter = false,
iconSize = Size16dp iconSize = Size16dp
) { ) {
@@ -644,51 +690,107 @@ private fun DrawAuthorInfo(
alignment: Arrangement.Horizontal, alignment: Arrangement.Horizontal,
nav: (String) -> Unit nav: (String) -> Unit
) { ) {
val userState by baseNote.author!!.live().metadata.observeAsState()
val pubkeyHex = remember { baseNote.author?.pubkeyHex } ?: return
val route = remember { "User/$pubkeyHex" }
val userDisplayName = remember(userState) { userState?.user?.toBestDisplayName() }
val userProfilePicture = remember(userState) { ResizeImage(userState?.user?.profilePicture(), 25.dp) }
val userTags = remember(userState) { userState?.user?.info?.latestMetadata?.tags?.toImmutableListOfLists() }
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = alignment, horizontalArrangement = alignment,
modifier = Modifier.padding(top = 5.dp) modifier = Modifier.padding(top = 5.dp)
) { ) {
RobohashAsyncImageProxy( DisplayAndWatchNoteAuthor(baseNote, nav)
robot = pubkeyHex, }
model = userProfilePicture, }
contentDescription = stringResource(id = R.string.profile_image),
modifier = remember {
Modifier
.width(25.dp)
.height(25.dp)
.clip(shape = CircleShape)
.clickable(onClick = {
nav(route)
})
}
)
userDisplayName?.let { @Composable
Spacer(modifier = StdHorzSpacer) private fun DisplayAndWatchNoteAuthor(
baseNote: Note,
nav: (String) -> Unit
) {
val author = remember {
baseNote.author
}
author?.let {
WatchAndDisplayUser(it, nav)
}
}
CreateClickableTextWithEmoji( @Composable
clickablePart = it, private fun WatchAndDisplayUser(
suffix = "", author: User,
tags = userTags, nav: (String) -> Unit
fontWeight = FontWeight.Bold, ) {
overrideColor = MaterialTheme.colors.onBackground, val pubkeyHex = remember { author.pubkeyHex }
route = route, val route = remember { "User/${author.pubkeyHex}" }
nav = nav
)
Spacer(modifier = StdHorzSpacer) val userState by author.live().metadata.observeAsState()
DrawPlayName(it)
val userDisplayName by remember(userState) {
derivedStateOf {
userState?.user?.toBestDisplayName()
} }
} }
val userProfilePicture by remember(userState) {
derivedStateOf {
ResizeImage(userState?.user?.profilePicture(), Size25dp)
}
}
val userTags by remember(userState) {
derivedStateOf {
userState?.user?.info?.latestMetadata?.tags?.toImmutableListOfLists()
}
}
UserIcon(pubkeyHex, userProfilePicture, nav, route)
userDisplayName?.let {
DisplayMessageUsername(it, userTags, route, nav)
}
}
@Composable
private fun UserIcon(
pubkeyHex: String,
userProfilePicture: ResizeImage,
nav: (String) -> Unit,
route: String
) {
RobohashAsyncImageProxy(
robot = pubkeyHex,
model = userProfilePicture,
contentDescription = stringResource(id = R.string.profile_image),
modifier = remember {
Modifier
.width(Size25dp)
.height(Size25dp)
.clip(shape = CircleShape)
.clickable(onClick = {
nav(route)
})
}
)
}
@Composable
private fun DisplayMessageUsername(
userDisplayName: String,
userTags: ImmutableListOfLists<String>?,
route: String,
nav: (String) -> Unit
) {
Spacer(modifier = StdHorzSpacer)
CreateClickableTextWithEmoji(
clickablePart = userDisplayName,
suffix = "",
maxLines = 1,
tags = userTags,
fontWeight = FontWeight.Bold,
overrideColor = MaterialTheme.colors.onBackground, // we do not want clickable names in purple here.
route = route,
nav = nav
)
Spacer(modifier = StdHorzSpacer)
DrawPlayName(userDisplayName)
} }
@Immutable @Immutable

View File

@@ -2062,9 +2062,10 @@ private fun LoadAndDisplayUser(
CreateClickableTextWithEmoji( CreateClickableTextWithEmoji(
clickablePart = userDisplayName, clickablePart = userDisplayName,
suffix = " ", suffix = " ",
tags = userTags, maxLines = 1,
route = route, route = route,
nav = nav nav = nav,
tags = userTags
) )
} }
} }

View File

@@ -65,14 +65,16 @@ private fun UserNameDisplay(
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
maxLines = 1 maxLines = 1
) )
CreateTextWithEmoji( if (bestDisplayName != bestUserName) {
text = remember { "@$bestUserName" }, CreateTextWithEmoji(
tags = tags, text = remember { "@$bestUserName" },
color = MaterialTheme.colors.placeholderText, tags = tags,
maxLines = 1, color = MaterialTheme.colors.placeholderText,
overflow = TextOverflow.Ellipsis, maxLines = 1,
modifier = modifier overflow = TextOverflow.Ellipsis,
) modifier = modifier
)
}
Spacer(StdHorzSpacer) Spacer(StdHorzSpacer)
DrawPlayName(bestDisplayName) DrawPlayName(bestDisplayName)
} else if (bestDisplayName != null) { } else if (bestDisplayName != null) {
@@ -88,7 +90,7 @@ private fun UserNameDisplay(
DrawPlayName(bestDisplayName) DrawPlayName(bestDisplayName)
} else if (bestUserName != null) { } else if (bestUserName != null) {
CreateTextWithEmoji( CreateTextWithEmoji(
text = remember { "@$bestUserName" }, text = bestUserName,
tags = tags, tags = tags,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
maxLines = 1, maxLines = 1,
@@ -99,7 +101,7 @@ private fun UserNameDisplay(
DrawPlayName(bestUserName) DrawPlayName(bestUserName)
} else { } else {
Text( Text(
npubDisplay, text = npubDisplay,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
@@ -113,8 +115,15 @@ fun DrawPlayName(name: String) {
val context = LocalContext.current val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
DrawPlayNameIcon {
speak(name, context, lifecycleOwner)
}
}
@Composable
fun DrawPlayNameIcon(onClick: () -> Unit) {
IconButton( IconButton(
onClick = { speak(name, context, lifecycleOwner) }, onClick = onClick,
modifier = StdButtonSizeModifier modifier = StdButtonSizeModifier
) { ) {
Icon( Icon(

View File

@@ -99,6 +99,7 @@ import com.vitorpamplona.amethyst.ui.note.ZapReaction
import com.vitorpamplona.amethyst.ui.screen.NostrChannelFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrChannelFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.RefreshingChatroomFeedView import com.vitorpamplona.amethyst.ui.screen.RefreshingChatroomFeedView
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
import com.vitorpamplona.amethyst.ui.theme.Size35dp import com.vitorpamplona.amethyst.ui.theme.Size35dp
import com.vitorpamplona.amethyst.ui.theme.SmallBorder import com.vitorpamplona.amethyst.ui.theme.SmallBorder
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
@@ -236,7 +237,7 @@ fun ChannelScreen(
) )
} }
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = DoubleVertSpacer)
replyTo.value?.let { replyTo.value?.let {
DisplayReplyingToNote(it, accountViewModel, nav) { DisplayReplyingToNote(it, accountViewModel, nav) {

View File

@@ -26,10 +26,12 @@ val StdButtonSizeModifier = Modifier.size(20.dp)
val StdHorzSpacer = Modifier.width(5.dp) val StdHorzSpacer = Modifier.width(5.dp)
val StdVertSpacer = Modifier.height(5.dp) val StdVertSpacer = Modifier.height(5.dp)
val DoubleHorzSpacer = Modifier.width(10.dp) val DoubleHorzSpacer = Modifier.width(10.dp)
val DoubleVertSpacer = Modifier.width(10.dp)
val Size35dp = 35.dp
val Size13dp = 13.dp val Size13dp = 13.dp
val Size16dp = 16.dp val Size16dp = 16.dp
val Size25dp = 25.dp
val Size35dp = 35.dp
val StdPadding = Modifier.padding(10.dp) val StdPadding = Modifier.padding(10.dp)