mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-09 08:04:45 +00:00
feat: move lightning address and payment targets to wallet screen
- Add lightning address field with save button to WalletScreen - Add payment targets navigation row to WalletScreen - Remove lightning address field from profile editor (NewUserMetadataScreen) - Remove payment targets entry from AllSettings - Extend WalletViewModel to load/save the lightning address via userMetadata https://claude.ai/code/session_017NPhwCnSzCuMn9sTX3ca37
This commit is contained in:
@@ -227,22 +227,6 @@ fun NewUserMetadataScreen(
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.lightning_address)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.lnAddress.value,
|
||||
onValueChange = { postViewModel.lnAddress.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "me@mylightningnode.com",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.nip_05) + " (NIP-05)") },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
||||
-8
@@ -57,8 +57,6 @@ class NewUserMetadataViewModel : ViewModel() {
|
||||
val website = mutableStateOf("")
|
||||
val pronouns = mutableStateOf("")
|
||||
val nip05 = mutableStateOf("")
|
||||
val lnAddress = mutableStateOf("")
|
||||
val lnURL = mutableStateOf("")
|
||||
|
||||
val twitter = mutableStateOf("")
|
||||
val github = mutableStateOf("")
|
||||
@@ -82,8 +80,6 @@ class NewUserMetadataViewModel : ViewModel() {
|
||||
website.value = it.info.website ?: ""
|
||||
pronouns.value = it.info.pronouns ?: ""
|
||||
nip05.value = it.info.nip05 ?: ""
|
||||
lnAddress.value = it.info.lud16 ?: ""
|
||||
lnURL.value = it.info.lud06 ?: ""
|
||||
}
|
||||
|
||||
twitter.value = ""
|
||||
@@ -121,8 +117,6 @@ class NewUserMetadataViewModel : ViewModel() {
|
||||
pronouns = pronouns.value,
|
||||
about = about.value,
|
||||
nip05 = nip05.value,
|
||||
lnAddress = lnAddress.value,
|
||||
lnURL = lnURL.value,
|
||||
)
|
||||
|
||||
val identities =
|
||||
@@ -146,8 +140,6 @@ class NewUserMetadataViewModel : ViewModel() {
|
||||
banner.value = ""
|
||||
website.value = ""
|
||||
nip05.value = ""
|
||||
lnAddress.value = ""
|
||||
lnURL.value = ""
|
||||
twitter.value = ""
|
||||
github.value = ""
|
||||
mastodon.value = ""
|
||||
|
||||
-8
@@ -29,7 +29,6 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.AccountBalanceWallet
|
||||
import androidx.compose.material.icons.outlined.Bolt
|
||||
import androidx.compose.material.icons.outlined.CloudUpload
|
||||
import androidx.compose.material.icons.outlined.DeleteForever
|
||||
@@ -122,13 +121,6 @@ fun AllSettingsScreen(
|
||||
onClick = { nav.nav(Route.EditMediaServers) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.payment_targets,
|
||||
icon = Icons.Outlined.AccountBalanceWallet,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.EditPaymentTargets) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.reactions,
|
||||
icon = Icons.Outlined.FavoriteBorder,
|
||||
|
||||
+103
-22
@@ -30,22 +30,28 @@ 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.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
|
||||
@@ -64,6 +70,7 @@ 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)
|
||||
@@ -73,10 +80,14 @@ fun WalletScreen(
|
||||
nav: INav,
|
||||
) {
|
||||
val walletViewModel: WalletViewModel = viewModel()
|
||||
walletViewModel.init(accountViewModel.account)
|
||||
walletViewModel.init(accountViewModel)
|
||||
|
||||
val hasWallet by walletViewModel.hasWalletSetup.collectAsState()
|
||||
|
||||
LaunchedEffect(accountViewModel) {
|
||||
walletViewModel.loadLnAddress()
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
@@ -92,30 +103,36 @@ fun WalletScreen(
|
||||
)
|
||||
},
|
||||
) { padding ->
|
||||
if (!hasWallet) {
|
||||
NoWalletSetup(
|
||||
modifier = Modifier.padding(padding),
|
||||
nav = nav,
|
||||
)
|
||||
} else {
|
||||
WalletHomeContent(
|
||||
walletViewModel = walletViewModel,
|
||||
modifier = Modifier.padding(padding),
|
||||
nav = nav,
|
||||
)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NoWalletSetup(
|
||||
modifier: Modifier,
|
||||
nav: INav,
|
||||
) {
|
||||
private fun NoWalletSetup(nav: INav) {
|
||||
Column(
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxSize()
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(32.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
@@ -142,7 +159,6 @@ private fun NoWalletSetup(
|
||||
@Composable
|
||||
private fun WalletHomeContent(
|
||||
walletViewModel: WalletViewModel,
|
||||
modifier: Modifier,
|
||||
nav: INav,
|
||||
) {
|
||||
val balance by walletViewModel.balanceSats.collectAsState()
|
||||
@@ -157,8 +173,8 @@ private fun WalletHomeContent(
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxSize()
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(24.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
@@ -206,7 +222,7 @@ private fun WalletHomeContent(
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
// Action buttons
|
||||
Row(
|
||||
@@ -276,3 +292,68 @@ 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+35
-3
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.GetBalanceMethod
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.GetBalanceSuccessResponse
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.GetInfoMethod
|
||||
@@ -86,10 +87,14 @@ private const val PAYMENT_FAILED = "Payment failed"
|
||||
|
||||
class WalletViewModel : ViewModel() {
|
||||
private var account: Account? = null
|
||||
private var accountViewModel: AccountViewModel? = null
|
||||
|
||||
private val _hasWalletSetup = MutableStateFlow(false)
|
||||
val hasWalletSetup = _hasWalletSetup.asStateFlow()
|
||||
|
||||
private val _lnAddress = MutableStateFlow("")
|
||||
val lnAddress = _lnAddress.asStateFlow()
|
||||
|
||||
private val _balanceSats = MutableStateFlow<Long?>(null)
|
||||
val balanceSats = _balanceSats.asStateFlow()
|
||||
|
||||
@@ -137,15 +142,42 @@ class WalletViewModel : ViewModel() {
|
||||
onTimeout()
|
||||
}
|
||||
|
||||
fun init(account: Account) {
|
||||
this.account = account
|
||||
_hasWalletSetup.value = account.nip47SignerState.hasWalletConnectSetup()
|
||||
fun init(accountViewModel: AccountViewModel) {
|
||||
this.accountViewModel = accountViewModel
|
||||
this.account = accountViewModel.account
|
||||
_hasWalletSetup.value = accountViewModel.account.nip47SignerState.hasWalletConnectSetup()
|
||||
}
|
||||
|
||||
fun refreshWalletSetup() {
|
||||
_hasWalletSetup.value = account?.nip47SignerState?.hasWalletConnectSetup() == true
|
||||
}
|
||||
|
||||
fun loadLnAddress() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val info =
|
||||
account
|
||||
?.userProfile()
|
||||
?.metadataOrNull()
|
||||
?.flow
|
||||
?.value
|
||||
?.info
|
||||
_lnAddress.value = info?.lud16 ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
fun updateLnAddress(address: String) {
|
||||
_lnAddress.value = address
|
||||
}
|
||||
|
||||
fun saveLnAddress() {
|
||||
val accountVm = accountViewModel ?: return
|
||||
val acc = account ?: return
|
||||
accountVm.launchSigner {
|
||||
val event = acc.userMetadata.sendNewUserMetadata(lnAddress = _lnAddress.value)
|
||||
acc.sendLiterallyEverywhere(event)
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchBalance() {
|
||||
val acc = account ?: return
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
|
||||
Reference in New Issue
Block a user