From 144b82dbe3aea95f68d474deda53ad0e211cf001 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Apr 2026 18:32:08 +0000 Subject: [PATCH] refactor: inline lightning address and payment targets in wallet setup screen - Remove separate Save/Manage buttons; both are now saved via the screen's existing SavingTopBar save action - Lightning address: plain OutlinedTextField, saved on confirm - Payment targets: inline editable list (Column, not LazyColumn) with add/delete directly in the scrollable content area - Wire walletViewModel.saveLnAddress() and paymentTargetsViewModel.savePaymentTargets() into onPost - Wire paymentTargetsViewModel.refresh() into onCancel https://claude.ai/code/session_017NPhwCnSzCuMn9sTX3ca37 --- .../loggedIn/settings/NIP47SetupScreen.kt | 131 +++++++++++------- 1 file changed, 81 insertions(+), 50 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/NIP47SetupScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/NIP47SetupScreen.kt index b33273aac..cfb2b6870 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/NIP47SetupScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/NIP47SetupScreen.kt @@ -23,40 +23,42 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.AccountBalanceWallet +import androidx.compose.material.icons.rounded.Delete import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text -import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.ui.actions.paymentTargets.PaymentTargetAddField +import com.vitorpamplona.amethyst.ui.actions.paymentTargets.PaymentTargetsViewModel import com.vitorpamplona.amethyst.ui.navigation.navs.INav -import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.navigation.topbars.SavingTopBar import com.vitorpamplona.amethyst.ui.note.UpdateZapAmountContent import com.vitorpamplona.amethyst.ui.note.UpdateZapAmountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory import com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet.WalletViewModel import com.vitorpamplona.amethyst.ui.stringRes +import com.vitorpamplona.amethyst.ui.theme.SettingsCategorySpacingModifier +import com.vitorpamplona.amethyst.ui.theme.grayText import com.vitorpamplona.amethyst.ui.theme.placeholderText +import com.vitorpamplona.quartz.experimental.nipA3.PaymentTarget @Composable fun NIP47SetupScreen( @@ -70,12 +72,16 @@ fun NIP47SetupScreen( val walletViewModel: WalletViewModel = viewModel() walletViewModel.init(accountViewModel) + val paymentTargetsViewModel: PaymentTargetsViewModel = viewModel() + paymentTargetsViewModel.init(accountViewModel) + LaunchedEffect(accountViewModel, postViewModel) { postViewModel.load() walletViewModel.loadLnAddress() + paymentTargetsViewModel.load() } - NIP47SetupScreen(postViewModel, walletViewModel, accountViewModel, nav, nip47) + NIP47SetupScreen(postViewModel, walletViewModel, paymentTargetsViewModel, accountViewModel, nav, nip47) } @OptIn(ExperimentalMaterial3Api::class) @@ -83,6 +89,7 @@ fun NIP47SetupScreen( fun NIP47SetupScreen( postViewModel: UpdateZapAmountViewModel, walletViewModel: WalletViewModel, + paymentTargetsViewModel: PaymentTargetsViewModel, accountViewModel: AccountViewModel, nav: INav, nip47: String?, @@ -94,10 +101,13 @@ fun NIP47SetupScreen( isActive = postViewModel::hasChanged, onCancel = { postViewModel.cancel() + paymentTargetsViewModel.refresh() nav.popBack() }, onPost = { postViewModel.sendPost() + walletViewModel.saveLnAddress() + paymentTargetsViewModel.savePaymentTargets() nav.popBack() }, ) @@ -108,6 +118,7 @@ fun NIP47SetupScreen( postViewModel, onClose = { postViewModel.cancel() + paymentTargetsViewModel.refresh() nav.popBack() }, nip47, @@ -116,7 +127,7 @@ fun NIP47SetupScreen( HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp)) LightningAddressSetupSection(walletViewModel) HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp)) - PaymentTargetsSetupRow(nav) + PaymentTargetsInlineSection(paymentTargetsViewModel) } } } @@ -126,66 +137,86 @@ fun NIP47SetupScreen( private fun LightningAddressSetupSection(walletViewModel: WalletViewModel) { val lnAddress by walletViewModel.lnAddress.collectAsState() - Column( - modifier = Modifier.padding(horizontal = 0.dp, vertical = 4.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Text( text = stringRes(R.string.lightning_address), color = MaterialTheme.colorScheme.primary, style = MaterialTheme.typography.titleSmall, + modifier = SettingsCategorySpacingModifier, + ) + OutlinedTextField( + value = lnAddress, + onValueChange = { walletViewModel.updateLnAddress(it) }, + modifier = Modifier.fillMaxWidth(), + placeholder = { + Text( + text = "me@mylightningnode.com", + color = MaterialTheme.colorScheme.placeholderText, + ) + }, + singleLine = true, ) - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - OutlinedTextField( - value = lnAddress, - onValueChange = { walletViewModel.updateLnAddress(it) }, - modifier = Modifier.weight(1f), - placeholder = { - Text( - text = "me@mylightningnode.com", - color = MaterialTheme.colorScheme.placeholderText, - ) - }, - singleLine = true, - ) - TextButton(onClick = { walletViewModel.saveLnAddress() }) { - Text(text = stringRes(R.string.save)) - } - } } } @Composable -private fun PaymentTargetsSetupRow(nav: INav) { +private fun PaymentTargetsInlineSection(viewModel: PaymentTargetsViewModel) { + val targets by viewModel.paymentTargets.collectAsStateWithLifecycle() + + SettingsCategory( + R.string.payment_targets, + R.string.payment_targets_section_explainer, + SettingsCategorySpacingModifier, + ) + + if (targets.isEmpty()) { + Text( + text = stringRes(id = R.string.no_payment_targets_message), + modifier = Modifier.padding(vertical = 8.dp), + color = MaterialTheme.colorScheme.grayText, + style = MaterialTheme.typography.bodyMedium, + ) + } else { + Column { + targets.forEach { target -> + PaymentTargetInlineEntry(target = target, onDelete = { viewModel.removeTarget(target) }) + } + } + } + + PaymentTargetAddField { type, authority -> + viewModel.addTarget(type, authority) + } +} + +@Composable +private fun PaymentTargetInlineEntry( + target: PaymentTarget, + onDelete: () -> Unit, +) { Row( modifier = Modifier .fillMaxWidth() - .padding(vertical = 8.dp), + .padding(vertical = 6.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, ) { - Row(verticalAlignment = Alignment.CenterVertically) { - Icon( - imageVector = Icons.Outlined.AccountBalanceWallet, - contentDescription = null, - modifier = Modifier.size(20.dp), - tint = MaterialTheme.colorScheme.primary, - ) - Spacer(modifier = Modifier.width(8.dp)) + Column(modifier = Modifier.weight(1f)) { Text( - text = stringRes(R.string.payment_targets), - style = MaterialTheme.typography.titleSmall, - color = MaterialTheme.colorScheme.primary, + text = target.type.replaceFirstChar(Char::titlecase), + style = MaterialTheme.typography.bodyLarge, + ) + Text( + text = target.authority, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.grayText, ) } - TextButton(onClick = { nav.nav(Route.EditPaymentTargets) }) { - Text( - text = stringRes(R.string.manage), - fontWeight = FontWeight.SemiBold, + IconButton(onClick = onDelete) { + Icon( + imageVector = Icons.Rounded.Delete, + contentDescription = stringRes(id = R.string.delete_payment_target), ) } }