feat: move lightning address and payment targets to wallet setup screen

- Add trailingContent lambda to UpdateZapAmountContent so callers can
  inject sections inside its scrollable Column
- Inject lightning address field and payment targets row into
  NIP47SetupScreen via trailingContent
- Add WalletViewModel to NIP47SetupScreen to load/save the lightning address
- Add settings icon button to WalletScreen TopAppBar when wallet is configured,
  navigating back to NIP47SetupScreen
- Restore WalletScreen to its original layout (no ln/payment sections)

https://claude.ai/code/session_017NPhwCnSzCuMn9sTX3ca37
This commit is contained in:
Claude
2026-04-08 18:23:25 +00:00
parent 05b352b61c
commit bbc4155c40
3 changed files with 148 additions and 107 deletions
@@ -46,6 +46,7 @@ import androidx.compose.animation.togetherWith
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
@@ -148,6 +149,7 @@ fun UpdateZapAmountContent(
onClose: () -> Unit,
nip47uri: String? = null,
accountViewModel: AccountViewModel,
trailingContent: @Composable ColumnScope.() -> Unit = {},
) {
val context = LocalContext.current
val clipboardManager = LocalClipboard.current
@@ -649,6 +651,8 @@ fun UpdateZapAmountContent(
}
}
trailingContent()
Spacer(modifier = Modifier.height(16.dp))
}
}
@@ -20,20 +20,43 @@
*/
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.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
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.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
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.wallet.WalletViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.placeholderText
@Composable
fun NIP47SetupScreen(
@@ -44,17 +67,22 @@ fun NIP47SetupScreen(
val postViewModel: UpdateZapAmountViewModel = viewModel()
postViewModel.init(accountViewModel)
val walletViewModel: WalletViewModel = viewModel()
walletViewModel.init(accountViewModel)
LaunchedEffect(accountViewModel, postViewModel) {
postViewModel.load()
walletViewModel.loadLnAddress()
}
NIP47SetupScreen(postViewModel, accountViewModel, nav, nip47)
NIP47SetupScreen(postViewModel, walletViewModel, accountViewModel, nav, nip47)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NIP47SetupScreen(
postViewModel: UpdateZapAmountViewModel,
walletViewModel: WalletViewModel,
accountViewModel: AccountViewModel,
nav: INav,
nip47: String?,
@@ -76,10 +104,89 @@ fun NIP47SetupScreen(
},
) {
Column(Modifier.padding(it)) {
UpdateZapAmountContent(postViewModel, onClose = {
postViewModel.cancel()
nav.popBack()
}, nip47, accountViewModel)
UpdateZapAmountContent(
postViewModel,
onClose = {
postViewModel.cancel()
nav.popBack()
},
nip47,
accountViewModel,
) {
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
LightningAddressSetupSection(walletViewModel)
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
PaymentTargetsSetupRow(nav)
}
}
}
}
@Composable
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),
) {
Text(
text = stringRes(R.string.lightning_address),
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.titleSmall,
)
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) {
Row(
modifier =
Modifier
.fillMaxWidth()
.padding(vertical = 8.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))
Text(
text = stringRes(R.string.payment_targets),
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.primary,
)
}
TextButton(onClick = { nav.nav(Route.EditPaymentTargets) }) {
Text(
text = stringRes(R.string.manage),
fontWeight = FontWeight.SemiBold,
)
}
}
}
@@ -30,28 +30,23 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.List
import androidx.compose.material.icons.filled.ArrowDownward
import androidx.compose.material.icons.filled.ArrowUpward
import androidx.compose.material.icons.outlined.AccountBalanceWallet
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
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.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -70,7 +65,6 @@ import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import java.text.NumberFormat
@OptIn(ExperimentalMaterial3Api::class)
@@ -84,10 +78,6 @@ fun WalletScreen(
val hasWallet by walletViewModel.hasWalletSetup.collectAsState()
LaunchedEffect(accountViewModel) {
walletViewModel.loadLnAddress()
}
Scaffold(
topBar = {
TopAppBar(
@@ -100,39 +90,43 @@ fun WalletScreen(
)
}
},
actions = {
if (hasWallet) {
IconButton(onClick = { nav.nav(Route.Nip47NWCSetup()) }) {
Icon(
imageVector = Icons.Outlined.Settings,
contentDescription = stringRes(R.string.settings),
)
}
}
},
)
},
) { padding ->
Column(
modifier =
Modifier
.padding(padding)
.fillMaxSize()
.verticalScroll(rememberScrollState()),
) {
if (!hasWallet) {
NoWalletSetup(nav = nav)
} else {
WalletHomeContent(
walletViewModel = walletViewModel,
nav = nav,
)
}
HorizontalDivider()
LightningAddressSection(walletViewModel = walletViewModel)
HorizontalDivider()
PaymentTargetsRow(nav = nav)
if (!hasWallet) {
NoWalletSetup(
modifier = Modifier.padding(padding),
nav = nav,
)
} else {
WalletHomeContent(
walletViewModel = walletViewModel,
modifier = Modifier.padding(padding),
nav = nav,
)
}
}
}
@Composable
private fun NoWalletSetup(nav: INav) {
private fun NoWalletSetup(
modifier: Modifier,
nav: INav,
) {
Column(
modifier =
Modifier
.fillMaxWidth()
modifier
.fillMaxSize()
.padding(32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
@@ -159,6 +153,7 @@ private fun NoWalletSetup(nav: INav) {
@Composable
private fun WalletHomeContent(
walletViewModel: WalletViewModel,
modifier: Modifier,
nav: INav,
) {
val balance by walletViewModel.balanceSats.collectAsState()
@@ -173,8 +168,8 @@ private fun WalletHomeContent(
Column(
modifier =
Modifier
.fillMaxWidth()
modifier
.fillMaxSize()
.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
@@ -222,7 +217,7 @@ private fun WalletHomeContent(
)
}
Spacer(modifier = Modifier.height(32.dp))
Spacer(modifier = Modifier.weight(1f))
// Action buttons
Row(
@@ -292,68 +287,3 @@ private fun WalletHomeContent(
Spacer(modifier = Modifier.height(24.dp))
}
}
@Composable
private fun LightningAddressSection(walletViewModel: WalletViewModel) {
val lnAddress by walletViewModel.lnAddress.collectAsState()
Column(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(
text = stringRes(R.string.lightning_address),
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.SemiBold,
)
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 PaymentTargetsRow(nav: INav) {
Row(
modifier =
Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
imageVector = Icons.Outlined.AccountBalanceWallet,
contentDescription = null,
modifier = Modifier.size(24.dp),
tint = MaterialTheme.colorScheme.onBackground,
)
Spacer(modifier = Modifier.width(16.dp))
Text(
text = stringRes(R.string.payment_targets),
style = MaterialTheme.typography.bodyLarge,
)
}
TextButton(onClick = { nav.nav(Route.EditPaymentTargets) }) {
Text(text = stringRes(R.string.manage))
}
}
}