Merge branch 'main' into claude/convert-dialog-to-route-9lrlG

This commit is contained in:
Vitor Pamplona
2026-03-03 10:51:00 -05:00
committed by GitHub
6 changed files with 100 additions and 143 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.isSameRoute
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.screen.AccountStateViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountSwitcherAndLeftDrawerLayout
@@ -105,6 +106,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.relay.RelayFeedScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.AllRelayListScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.RelayInformationScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.search.SearchScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.keyBackup.AccountBackupScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.AllSettingsScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.NIP47SetupScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.UpdateZapAmountScreen
@@ -166,6 +168,7 @@ fun AppNavigation(
composable<Route.Search> { SearchScreen(accountViewModel, nav) }
composableFromEnd<Route.AllSettings> { AllSettingsScreen(accountViewModel, nav) }
composableFromBottom<Route.AccountBackup> { AccountBackupScreen(accountViewModel, nav) }
composableFromEnd<Route.SecurityFilters> { SecurityFiltersScreen(accountViewModel, nav) }
composableFromEnd<Route.PrivacyOptions> { PrivacyOptionsScreen(Amethyst.instance.torPrefs.value, nav) }
composableFromEnd<Route.Bookmarks> { BookmarkListScreen(accountViewModel, nav) }
@@ -176,6 +179,7 @@ fun AppNavigation(
composableFromBottomArgs<Route.UpdateZapAmount> { UpdateZapAmountScreen(accountViewModel, nav, it.nip47) }
composableFromEndArgs<Route.EditRelays> { AllRelayListScreen(accountViewModel, nav) }
composableFromEndArgs<Route.EditMediaServers> { AllMediaServersScreen(accountViewModel, nav) }
composableFromBottom<Route.UpdateReactionType> { UpdateReactionTypeScreen(accountViewModel, nav) }
composableFromEndArgs<Route.ContentDiscovery> { DvmContentDiscoveryScreen(it.id, accountViewModel, nav) }
composableFromEndArgs<Route.Profile> { ProfileScreen(it.id, accountViewModel, nav) }
@@ -81,6 +81,8 @@ sealed class Route {
@Serializable object AllSettings : Route()
@Serializable object AccountBackup : Route()
@Serializable object Settings : Route()
@Serializable object UserSettings : Route()
@@ -113,6 +115,8 @@ sealed class Route {
@Serializable object EditMediaServers : Route()
@Serializable object UpdateReactionType : Route()
@Serializable data class Nip47NWCSetup(
val nip47: String? = null,
) : Route()
@@ -336,6 +340,7 @@ fun getRouteWithArguments(navController: NavHostController): Route? {
dest.hasRoute<Route.ContentDiscovery>() -> entry.toRoute<Route.ContentDiscovery>()
dest.hasRoute<Route.Drafts>() -> entry.toRoute<Route.Drafts>()
dest.hasRoute<Route.AllSettings>() -> entry.toRoute<Route.AllSettings>()
dest.hasRoute<Route.AccountBackup>() -> entry.toRoute<Route.AccountBackup>()
dest.hasRoute<Route.Settings>() -> entry.toRoute<Route.Settings>()
dest.hasRoute<Route.EditProfile>() -> entry.toRoute<Route.EditProfile>()
dest.hasRoute<Route.Profile>() -> entry.toRoute<Route.Profile>()
@@ -355,6 +360,7 @@ fun getRouteWithArguments(navController: NavHostController): Route? {
dest.hasRoute<Route.EventRedirect>() -> entry.toRoute<Route.EventRedirect>()
dest.hasRoute<Route.EditRelays>() -> entry.toRoute<Route.EditRelays>()
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.UpdateZapAmount>() -> entry.toRoute<Route.UpdateZapAmount>()
dest.hasRoute<Route.Room>() -> entry.toRoute<Route.Room>()
@@ -871,7 +871,6 @@ fun LikeReaction(
heartSizeModifier: Modifier = Size18Modifier,
iconFontSize: TextUnit = Font14SP,
) {
var wantsToChangeReactionSymbol by remember { mutableStateOf(false) }
var wantsToReact by remember { mutableStateOf(false) }
ClickableBox(
@@ -883,7 +882,7 @@ fun LikeReaction(
onWantsToSignReaction = { accountViewModel.reactToOrDelete(baseNote) },
)
},
onLongClick = { wantsToChangeReactionSymbol = true },
onLongClick = { nav.nav(Route.UpdateReactionType) },
) {
ObserveLikeIcon(baseNote, accountViewModel) { reactionType ->
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) {
ReactionChoicePopup(
baseNote,
@@ -911,7 +902,7 @@ fun LikeReaction(
onDismiss = { wantsToReact = false },
onChangeAmount = {
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.style.TextAlign
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.compose.viewModel
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.ui.components.AnimatedBorderTextCornerRadius
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.routes.routeFor
import com.vitorpamplona.amethyst.ui.navigation.topbars.SavingTopBar
@@ -153,8 +150,7 @@ class UpdateReactionTypeViewModel : ViewModel() {
}
@Composable
fun UpdateReactionTypeDialog(
onClose: () -> Unit,
fun UpdateReactionTypeScreen(
accountViewModel: AccountViewModel,
nav: INav,
) {
@@ -165,119 +161,106 @@ fun UpdateReactionTypeDialog(
postViewModel.load()
}
UpdateReactionTypeDialog(postViewModel, onClose, accountViewModel, nav)
UpdateReactionTypeScreen(postViewModel, accountViewModel, nav)
}
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
@Composable
fun UpdateReactionTypeDialog(
fun UpdateReactionTypeScreen(
postViewModel: UpdateReactionTypeViewModel,
onClose: () -> Unit,
accountViewModel: AccountViewModel,
nav: INav,
) {
Dialog(
onDismissRequest = { onClose() },
properties =
DialogProperties(
usePlatformDefaultWidth = false,
dismissOnClickOutside = false,
decorFitsSystemWindows = false,
),
) {
SetDialogToEdgeToEdge()
Scaffold(
topBar = {
SavingTopBar(
isActive = postViewModel::hasChanged,
onCancel = {
postViewModel.cancel()
onClose()
},
onPost = {
postViewModel.sendPost()
onClose()
},
)
},
) { pad ->
Surface(
modifier =
Modifier
.padding(pad)
.consumeWindowInsets(pad)
.imePadding(),
Scaffold(
topBar = {
SavingTopBar(
isActive = postViewModel::hasChanged,
onCancel = {
postViewModel.cancel()
nav.popBack()
},
onPost = {
postViewModel.sendPost()
nav.popBack()
},
)
},
) { pad ->
Surface(
modifier =
Modifier
.padding(pad)
.consumeWindowInsets(pad)
.imePadding(),
) {
Column(
modifier = Modifier.padding(10.dp),
) {
Column(
modifier = Modifier.padding(10.dp),
Row(
modifier = Modifier.fillMaxWidth(),
) {
Row(
modifier = Modifier.fillMaxWidth(),
Column(
modifier = Modifier.verticalScroll(rememberScrollState()),
) {
Column(
modifier = Modifier.verticalScroll(rememberScrollState()),
) {
Row(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.animateContentSize()) {
FlowRow(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) {
postViewModel.reactionSet.forEach { reactionType ->
RenderReactionOption(reactionType, postViewModel)
}
Row(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.animateContentSize()) {
FlowRow(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) {
postViewModel.reactionSet.forEach { reactionType ->
RenderReactionOption(reactionType, postViewModel)
}
}
}
}
Spacer(modifier = Modifier.height(10.dp))
Spacer(modifier = Modifier.height(10.dp))
Row(
modifier = Modifier.fillMaxWidth().padding(vertical = 5.dp),
verticalAlignment = Alignment.CenterVertically,
Row(
modifier = Modifier.fillMaxWidth().padding(vertical = 5.dp),
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(
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)
}
Text(text = stringRes(R.string.add), color = Color.White)
}
}
}
}
Spacer(StdVertSpacer)
Spacer(StdVertSpacer)
EmojiSelector(
accountViewModel = accountViewModel,
nav = nav,
) {
postViewModel.addChoice(it)
}
EmojiSelector(
accountViewModel = accountViewModel,
nav = nav,
) {
postViewModel.addChoice(it)
}
}
}
@@ -80,8 +80,6 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.fragment.app.FragmentActivity
import com.halilibo.richtext.commonmark.CommonMarkdownParseOptions
import com.halilibo.richtext.commonmark.CommonmarkAstNodeParser
@@ -91,6 +89,7 @@ import com.halilibo.richtext.ui.material3.RichText
import com.halilibo.richtext.ui.resolveDefaults
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
import com.vitorpamplona.amethyst.ui.note.authenticate
import com.vitorpamplona.amethyst.ui.painterRes
@@ -112,23 +111,18 @@ import kotlinx.coroutines.launch
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun AccountBackupDialog(
fun AccountBackupScreen(
accountViewModel: AccountViewModel,
onClose: () -> Unit,
nav: INav,
) {
Dialog(
onDismissRequest = onClose,
properties = DialogProperties(usePlatformDefaultWidth = false),
) {
DialogContents(accountViewModel, onClose)
}
AccountBackupScreenContent(accountViewModel, nav::popBack)
}
@Preview(device = "spec:width=2160px,height=2340px,dpi=440")
@Composable
fun DialogContentsPreview() {
fun AccountBackupScreenPreview() {
ThemeComparisonRow {
DialogContents(
AccountBackupScreenContent(
mockAccountViewModel(),
) {}
}
@@ -136,7 +130,7 @@ fun DialogContentsPreview() {
@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun DialogContents(
private fun AccountBackupScreenContent(
accountViewModel: AccountViewModel,
onClose: () -> Unit,
) {
@@ -57,10 +57,8 @@ import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
import com.vitorpamplona.amethyst.ui.note.UpdateReactionTypeDialog
import com.vitorpamplona.amethyst.ui.painterRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.keyBackup.AccountBackupDialog
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
@@ -83,16 +81,6 @@ fun AllSettingsScreen(
) {
val tint = MaterialTheme.colorScheme.onBackground
var showReactionDialog by remember { mutableStateOf(false) }
if (showReactionDialog) {
UpdateReactionTypeDialog(
onClose = { showReactionDialog = false },
accountViewModel = accountViewModel,
nav = nav,
)
}
Scaffold(
topBar = {
TopBarWithBackButton(stringRes(id = R.string.settings), nav::popBack)
@@ -119,7 +107,7 @@ fun AllSettingsScreen(
title = R.string.reactions,
icon = Icons.Outlined.FavoriteBorder,
tint = tint,
onClick = { showReactionDialog = true },
onClick = { nav.nav(Route.UpdateReactionType) },
)
HorizontalDivider()
SettingsNavigationRow(
@@ -143,22 +131,13 @@ fun AllSettingsScreen(
onClick = { nav.nav(Route.UserSettings) },
)
accountViewModel.account.settings.keyPair.privKey?.let {
var backupDialogOpen by remember { mutableStateOf(false) }
HorizontalDivider()
SettingsNavigationRow(
title = R.string.backup_keys,
icon = Icons.Outlined.Key,
tint = tint,
onClick = {
nav.closeDrawer()
backupDialogOpen = true
},
onClick = { nav.nav(Route.AccountBackup) },
)
if (backupDialogOpen) {
AccountBackupDialog(accountViewModel, onClose = { backupDialogOpen = false })
}
}
HorizontalDivider(thickness = 4.dp)
SettingsSectionHeader(R.string.app_settings)