Improving the UI of the Custom Emoji page.

This commit is contained in:
Vitor Pamplona
2025-03-05 16:56:34 -05:00
parent 987d8fa5dc
commit 8722f37fa5
3 changed files with 123 additions and 97 deletions
@@ -30,6 +30,7 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
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.consumeWindowInsets
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.imePadding import androidx.compose.foundation.layout.imePadding
@@ -44,10 +45,13 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -77,6 +81,7 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.service.firstFullChar import com.vitorpamplona.amethyst.service.firstFullChar
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.routeFor import com.vitorpamplona.amethyst.ui.navigation.routeFor
import com.vitorpamplona.amethyst.ui.note.types.RenderEmojiPack import com.vitorpamplona.amethyst.ui.note.types.RenderEmojiPack
@@ -85,9 +90,9 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses
import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
import com.vitorpamplona.quartz.nip30CustomEmoji.selection.EmojiPackSelectionEvent import com.vitorpamplona.quartz.nip30CustomEmoji.selection.EmojiPackSelectionEvent
@@ -157,7 +162,7 @@ fun UpdateReactionTypeDialog(
UpdateReactionTypeDialog(postViewModel, onClose, accountViewModel, nav) UpdateReactionTypeDialog(postViewModel, onClose, accountViewModel, nav)
} }
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
@Composable @Composable
fun UpdateReactionTypeDialog( fun UpdateReactionTypeDialog(
postViewModel: UpdateReactionTypeViewModel, postViewModel: UpdateReactionTypeViewModel,
@@ -174,98 +179,112 @@ fun UpdateReactionTypeDialog(
decorFitsSystemWindows = false, decorFitsSystemWindows = false,
), ),
) { ) {
Surface( SetDialogToEdgeToEdge()
modifier = Modifier.fillMaxWidth(),
) { Scaffold(
Column( topBar = {
modifier = Modifier.padding(10.dp).imePadding(), TopAppBar(
actions = {
SaveButton(
onPost = {
postViewModel.sendPost()
onClose()
},
isActive = postViewModel.hasChanged(),
)
Spacer(modifier = StdHorzSpacer)
},
title = {},
navigationIcon = {
Row {
Spacer(modifier = StdHorzSpacer)
CloseButton(
onPress = {
postViewModel.cancel()
onClose()
},
)
}
},
)
},
) { pad ->
Surface(
modifier =
Modifier
.padding(pad)
.consumeWindowInsets(pad)
.imePadding(),
) { ) {
Row( Column(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.padding(10.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) { ) {
CloseButton( Row(
onPress = { modifier = Modifier.fillMaxWidth(),
postViewModel.cancel()
onClose()
},
)
SaveButton(
onPost = {
postViewModel.sendPost()
onClose()
},
isActive = postViewModel.hasChanged(),
)
}
Spacer(modifier = Modifier.height(10.dp))
Row(
modifier = Modifier.fillMaxWidth(),
) {
Column(
modifier = Modifier.verticalScroll(rememberScrollState()),
) { ) {
Row(modifier = Modifier.fillMaxWidth()) { Column(
Column(modifier = Modifier.animateContentSize()) { modifier = Modifier.verticalScroll(rememberScrollState()),
FlowRow( ) {
modifier = Modifier.fillMaxWidth(), Row(modifier = Modifier.fillMaxWidth()) {
horizontalArrangement = Arrangement.Center, Column(modifier = Modifier.animateContentSize()) {
) { FlowRow(
postViewModel.reactionSet.forEach { reactionType -> modifier = Modifier.fillMaxWidth(),
RenderReactionOption(reactionType, postViewModel) horizontalArrangement = Arrangement.Center,
) {
postViewModel.reactionSet.forEach { reactionType ->
RenderReactionOption(reactionType, postViewModel)
}
} }
} }
} }
}
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
Row( Row(
modifier = Modifier.fillMaxWidth().padding(vertical = 5.dp), modifier = Modifier.fillMaxWidth().padding(vertical = 5.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) {
OutlinedTextField(
label = { Text(text = stringRes(R.string.new_reaction_symbol)) },
value = postViewModel.nextChoice,
onValueChange = { postViewModel.nextChoice = it },
keyboardOptions =
KeyboardOptions.Default.copy(
capitalization = KeyboardCapitalization.None,
keyboardType = KeyboardType.Text,
),
placeholder = {
Text(
text = "\uD83D\uDCAF, \uD83C\uDF89, \uD83D\uDC4E",
color = MaterialTheme.colorScheme.placeholderText,
)
},
singleLine = true,
modifier = Modifier.padding(end = 10.dp).weight(1f),
)
Button(
onClick = { postViewModel.addChoice() },
shape = ButtonBorder,
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
),
) { ) {
Text(text = stringRes(R.string.add), color = Color.White) OutlinedTextField(
label = { Text(text = stringRes(R.string.new_reaction_symbol)) },
value = postViewModel.nextChoice,
onValueChange = { postViewModel.nextChoice = it },
keyboardOptions =
KeyboardOptions.Default.copy(
capitalization = KeyboardCapitalization.None,
keyboardType = KeyboardType.Text,
),
placeholder = {
Text(
text = "\uD83D\uDCAF, \uD83C\uDF89, \uD83D\uDC4E",
color = MaterialTheme.colorScheme.placeholderText,
)
},
singleLine = true,
modifier = Modifier.padding(end = 10.dp).weight(1f),
)
Button(
onClick = { postViewModel.addChoice() },
shape = ButtonBorder,
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
),
) {
Text(text = stringRes(R.string.add), color = Color.White)
}
} }
} }
} }
}
EmojiSelector( Spacer(StdVertSpacer)
accountViewModel = accountViewModel,
nav = nav, EmojiSelector(
) { accountViewModel = accountViewModel,
postViewModel.addChoice(it) nav = nav,
) {
postViewModel.addChoice(it)
}
} }
} }
} }
@@ -348,11 +367,11 @@ private fun EmojiSelector(
usersEmojiList usersEmojiList
.live() .live()
.metadata .metadata
.map { (it.note.event as? EmojiPackSelectionEvent)?.taggedAddresses()?.toImmutableList() } .map { (it.note.event as? EmojiPackSelectionEvent)?.emojiPackIds()?.toImmutableList() }
.distinctUntilChanged() .distinctUntilChanged()
.observeAsState( .observeAsState(
(usersEmojiList.event as? EmojiPackSelectionEvent) (usersEmojiList.event as? EmojiPackSelectionEvent)
?.taggedAddresses() ?.emojiPackIds()
?.toImmutableList(), ?.toImmutableList(),
) )
@@ -363,7 +382,7 @@ private fun EmojiSelector(
@Composable @Composable
fun EmojiCollectionGallery( fun EmojiCollectionGallery(
emojiCollections: ImmutableList<Address>, emojiCollections: ImmutableList<String>,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
onClick: ((EmojiUrlTag) -> Unit)? = null, onClick: ((EmojiUrlTag) -> Unit)? = null,
@@ -21,14 +21,15 @@
package com.vitorpamplona.amethyst.ui.note.types package com.vitorpamplona.amethyst.ui.note.types
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
@@ -40,6 +41,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
@@ -112,7 +114,7 @@ public fun RenderEmojiPack(
allEmojis.take(60) allEmojis.take(60)
} }
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(top = 10.dp)) {
Text( Text(
text = remember(noteEvent) { "#${noteEvent.dTag()}" }, text = remember(noteEvent) { "#${noteEvent.dTag()}" },
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
@@ -131,16 +133,19 @@ public fun RenderEmojiPack(
} }
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.TopCenter) { Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.TopCenter) {
FlowRow(modifier = Modifier.padding(top = 5.dp)) { FlowRow(
modifier = Modifier.padding(top = 5.dp),
verticalArrangement = spacedBy(1.dp),
horizontalArrangement = spacedBy(1.dp),
) {
emojisToShow.forEach { emoji -> emojisToShow.forEach { emoji ->
if (onClick != null) { if (onClick != null) {
IconButton(onClick = { onClick(emoji) }, modifier = Size35Modifier) { AsyncImage(
AsyncImage( model = emoji.url,
model = emoji.url, contentDescription = emoji.code,
contentDescription = emoji.code, modifier = Size35Modifier.clickable { onClick(emoji) },
modifier = Size35Modifier, contentScale = ContentScale.Crop,
) )
}
} else { } else {
Box( Box(
modifier = Size35Modifier, modifier = Size35Modifier,
@@ -150,6 +155,7 @@ public fun RenderEmojiPack(
model = emoji.url, model = emoji.url,
contentDescription = emoji.code, contentDescription = emoji.code,
modifier = Size35Modifier, modifier = Size35Modifier,
contentScale = ContentScale.Crop,
) )
} }
} }
@@ -30,7 +30,6 @@ import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.signers.eventUpdate import com.vitorpamplona.quartz.nip01Core.signers.eventUpdate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedATags
import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent
import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
@@ -47,7 +46,9 @@ class EmojiPackSelectionEvent(
AddressHintProvider { AddressHintProvider {
override fun addressHints() = tags.mapNotNull(ATag::parseAsHint) override fun addressHints() = tags.mapNotNull(ATag::parseAsHint)
fun emojiPacks() = taggedATags() fun emojiPacks() = tags.mapNotNull(ATag::parseAddress)
fun emojiPackIds() = tags.mapNotNull(ATag::parseAddressId)
companion object { companion object {
const val KIND = 10030 const val KIND = 10030