feat: convert UpdateReactionTypeDialog to a route

Replace the dialog-based UpdateReactionTypeDialog with a
navigation route (Route.UpdateReactionType) that slides up
from the bottom. UpdateReactionTypeScreen replaces the Dialog
wrapper with a plain Scaffold. Call sites in AllSettingsScreen
and ReactionsRow now navigate to the route instead of managing
local dialog state.

https://claude.ai/code/session_01TSjuYdBADTRXwT4w8Yx1vU
This commit is contained in:
Claude
2026-03-03 15:42:51 +00:00
parent 3c32dbc701
commit fb066b3b6f
5 changed files with 87 additions and 118 deletions
@@ -55,6 +55,7 @@ import com.vitorpamplona.amethyst.ui.navigation.routes.getRouteWithArguments
import com.vitorpamplona.amethyst.ui.navigation.routes.isBaseRoute import com.vitorpamplona.amethyst.ui.navigation.routes.isBaseRoute
import com.vitorpamplona.amethyst.ui.navigation.routes.isSameRoute import com.vitorpamplona.amethyst.ui.navigation.routes.isSameRoute
import com.vitorpamplona.amethyst.ui.note.PayViaIntentScreen import com.vitorpamplona.amethyst.ui.note.PayViaIntentScreen
import com.vitorpamplona.amethyst.ui.note.UpdateReactionTypeScreen
import com.vitorpamplona.amethyst.ui.note.nip22Comments.ReplyCommentPostScreen import com.vitorpamplona.amethyst.ui.note.nip22Comments.ReplyCommentPostScreen
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountSwitcherAndLeftDrawerLayout import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountSwitcherAndLeftDrawerLayout
@@ -174,6 +175,7 @@ fun AppNavigation(
composableFromBottomArgs<Route.Nip47NWCSetup> { NIP47SetupScreen(accountViewModel, nav, it.nip47) } composableFromBottomArgs<Route.Nip47NWCSetup> { NIP47SetupScreen(accountViewModel, nav, it.nip47) }
composableFromEndArgs<Route.EditRelays> { AllRelayListScreen(accountViewModel, nav) } composableFromEndArgs<Route.EditRelays> { AllRelayListScreen(accountViewModel, nav) }
composableFromEndArgs<Route.EditMediaServers> { AllMediaServersScreen(accountViewModel, nav) } composableFromEndArgs<Route.EditMediaServers> { AllMediaServersScreen(accountViewModel, nav) }
composableFromBottom<Route.UpdateReactionType> { UpdateReactionTypeScreen(accountViewModel, nav) }
composableFromEndArgs<Route.ContentDiscovery> { DvmContentDiscoveryScreen(it.id, accountViewModel, nav) } composableFromEndArgs<Route.ContentDiscovery> { DvmContentDiscoveryScreen(it.id, accountViewModel, nav) }
composableFromEndArgs<Route.Profile> { ProfileScreen(it.id, accountViewModel, nav) } composableFromEndArgs<Route.Profile> { ProfileScreen(it.id, accountViewModel, nav) }
@@ -113,6 +113,8 @@ sealed class Route {
@Serializable object EditMediaServers : Route() @Serializable object EditMediaServers : Route()
@Serializable object UpdateReactionType : Route()
@Serializable data class Nip47NWCSetup( @Serializable data class Nip47NWCSetup(
val nip47: String? = null, val nip47: String? = null,
) : Route() ) : Route()
@@ -351,6 +353,7 @@ fun getRouteWithArguments(navController: NavHostController): Route? {
dest.hasRoute<Route.EventRedirect>() -> entry.toRoute<Route.EventRedirect>() dest.hasRoute<Route.EventRedirect>() -> entry.toRoute<Route.EventRedirect>()
dest.hasRoute<Route.EditRelays>() -> entry.toRoute<Route.EditRelays>() dest.hasRoute<Route.EditRelays>() -> entry.toRoute<Route.EditRelays>()
dest.hasRoute<Route.EditMediaServers>() -> entry.toRoute<Route.EditMediaServers>() dest.hasRoute<Route.EditMediaServers>() -> entry.toRoute<Route.EditMediaServers>()
dest.hasRoute<Route.UpdateReactionType>() -> entry.toRoute<Route.UpdateReactionType>()
dest.hasRoute<Route.Nip47NWCSetup>() -> entry.toRoute<Route.Nip47NWCSetup>() dest.hasRoute<Route.Nip47NWCSetup>() -> entry.toRoute<Route.Nip47NWCSetup>()
dest.hasRoute<Route.Room>() -> entry.toRoute<Route.Room>() dest.hasRoute<Route.Room>() -> entry.toRoute<Route.Room>()
dest.hasRoute<Route.NewShortNote>() -> entry.toRoute<Route.NewShortNote>() dest.hasRoute<Route.NewShortNote>() -> entry.toRoute<Route.NewShortNote>()
@@ -871,7 +871,6 @@ fun LikeReaction(
heartSizeModifier: Modifier = Size18Modifier, heartSizeModifier: Modifier = Size18Modifier,
iconFontSize: TextUnit = Font14SP, iconFontSize: TextUnit = Font14SP,
) { ) {
var wantsToChangeReactionSymbol by remember { mutableStateOf(false) }
var wantsToReact by remember { mutableStateOf(false) } var wantsToReact by remember { mutableStateOf(false) }
ClickableBox( ClickableBox(
@@ -883,7 +882,7 @@ fun LikeReaction(
onWantsToSignReaction = { accountViewModel.reactToOrDelete(baseNote) }, onWantsToSignReaction = { accountViewModel.reactToOrDelete(baseNote) },
) )
}, },
onLongClick = { wantsToChangeReactionSymbol = true }, onLongClick = { nav.nav(Route.UpdateReactionType) },
) { ) {
ObserveLikeIcon(baseNote, accountViewModel) { reactionType -> ObserveLikeIcon(baseNote, accountViewModel) { reactionType ->
CrossfadeIfEnabled(targetState = reactionType, contentAlignment = Center, label = "LikeIcon", accountViewModel = accountViewModel) { CrossfadeIfEnabled(targetState = reactionType, contentAlignment = Center, label = "LikeIcon", accountViewModel = accountViewModel) {
@@ -895,14 +894,6 @@ fun LikeReaction(
} }
} }
if (wantsToChangeReactionSymbol) {
UpdateReactionTypeDialog(
{ wantsToChangeReactionSymbol = false },
accountViewModel = accountViewModel,
nav,
)
}
if (wantsToReact) { if (wantsToReact) {
ReactionChoicePopup( ReactionChoicePopup(
baseNote, baseNote,
@@ -911,7 +902,7 @@ fun LikeReaction(
onDismiss = { wantsToReact = false }, onDismiss = { wantsToReact = false },
onChangeAmount = { onChangeAmount = {
wantsToReact = false wantsToReact = false
wantsToChangeReactionSymbol = true nav.nav(Route.UpdateReactionType)
}, },
) )
} }
@@ -69,8 +69,6 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
@@ -81,7 +79,6 @@ import com.vitorpamplona.amethyst.service.firstFullChar
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEventAndMapNotNull import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNoteEventAndMapNotNull
import com.vitorpamplona.amethyst.ui.components.AnimatedBorderTextCornerRadius import com.vitorpamplona.amethyst.ui.components.AnimatedBorderTextCornerRadius
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.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.navigation.topbars.SavingTopBar import com.vitorpamplona.amethyst.ui.navigation.topbars.SavingTopBar
@@ -153,8 +150,7 @@ class UpdateReactionTypeViewModel : ViewModel() {
} }
@Composable @Composable
fun UpdateReactionTypeDialog( fun UpdateReactionTypeScreen(
onClose: () -> Unit,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -165,119 +161,106 @@ fun UpdateReactionTypeDialog(
postViewModel.load() postViewModel.load()
} }
UpdateReactionTypeDialog(postViewModel, onClose, accountViewModel, nav) UpdateReactionTypeScreen(postViewModel, accountViewModel, nav)
} }
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) @OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
@Composable @Composable
fun UpdateReactionTypeDialog( fun UpdateReactionTypeScreen(
postViewModel: UpdateReactionTypeViewModel, postViewModel: UpdateReactionTypeViewModel,
onClose: () -> Unit,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
Dialog( Scaffold(
onDismissRequest = { onClose() }, topBar = {
properties = SavingTopBar(
DialogProperties( isActive = postViewModel::hasChanged,
usePlatformDefaultWidth = false, onCancel = {
dismissOnClickOutside = false, postViewModel.cancel()
decorFitsSystemWindows = false, nav.popBack()
), },
) { onPost = {
SetDialogToEdgeToEdge() postViewModel.sendPost()
nav.popBack()
Scaffold( },
topBar = { )
SavingTopBar( },
isActive = postViewModel::hasChanged, ) { pad ->
onCancel = { Surface(
postViewModel.cancel() modifier =
onClose() Modifier
}, .padding(pad)
onPost = { .consumeWindowInsets(pad)
postViewModel.sendPost() .imePadding(),
onClose() ) {
}, Column(
) modifier = Modifier.padding(10.dp),
},
) { pad ->
Surface(
modifier =
Modifier
.padding(pad)
.consumeWindowInsets(pad)
.imePadding(),
) { ) {
Column( Row(
modifier = Modifier.padding(10.dp), modifier = Modifier.fillMaxWidth(),
) { ) {
Row( Column(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.verticalScroll(rememberScrollState()),
) { ) {
Column( Row(modifier = Modifier.fillMaxWidth()) {
modifier = Modifier.verticalScroll(rememberScrollState()), Column(modifier = Modifier.animateContentSize()) {
) { FlowRow(
Row(modifier = Modifier.fillMaxWidth()) { modifier = Modifier.fillMaxWidth(),
Column(modifier = Modifier.animateContentSize()) { horizontalArrangement = Arrangement.Center,
FlowRow( ) {
modifier = Modifier.fillMaxWidth(), postViewModel.reactionSet.forEach { reactionType ->
horizontalArrangement = Arrangement.Center, RenderReactionOption(reactionType, postViewModel)
) {
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,
),
) { ) {
OutlinedTextField( Text(text = stringRes(R.string.add), color = Color.White)
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)
}
} }
} }
} }
}
Spacer(StdVertSpacer) Spacer(StdVertSpacer)
EmojiSelector( EmojiSelector(
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) { ) {
postViewModel.addChoice(it) postViewModel.addChoice(it)
}
} }
} }
} }
@@ -57,7 +57,6 @@ import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
import com.vitorpamplona.amethyst.ui.note.UpdateReactionTypeDialog
import com.vitorpamplona.amethyst.ui.note.UpdateZapAmountDialog import com.vitorpamplona.amethyst.ui.note.UpdateZapAmountDialog
import com.vitorpamplona.amethyst.ui.painterRes import com.vitorpamplona.amethyst.ui.painterRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -84,17 +83,8 @@ fun AllSettingsScreen(
) { ) {
val tint = MaterialTheme.colorScheme.onBackground val tint = MaterialTheme.colorScheme.onBackground
var showReactionDialog by remember { mutableStateOf(false) }
var wantsToChangeZapAmount by remember { mutableStateOf(false) } var wantsToChangeZapAmount by remember { mutableStateOf(false) }
if (showReactionDialog) {
UpdateReactionTypeDialog(
onClose = { showReactionDialog = false },
accountViewModel = accountViewModel,
nav = nav,
)
}
if (wantsToChangeZapAmount) { if (wantsToChangeZapAmount) {
UpdateZapAmountDialog( UpdateZapAmountDialog(
onClose = { wantsToChangeZapAmount = false }, onClose = { wantsToChangeZapAmount = false },
@@ -128,7 +118,7 @@ fun AllSettingsScreen(
title = R.string.reactions, title = R.string.reactions,
icon = Icons.Outlined.FavoriteBorder, icon = Icons.Outlined.FavoriteBorder,
tint = tint, tint = tint,
onClick = { showReactionDialog = true }, onClick = { nav.nav(Route.UpdateReactionType) },
) )
HorizontalDivider() HorizontalDivider()
SettingsNavigationRow( SettingsNavigationRow(