Improving the UI of the Custom Emoji page.
This commit is contained in:
+104
-85
@@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user