Compare commits
2 Commits
0aa55710a9
...
2e11cecbeb
| Author | SHA1 | Date | |
|---|---|---|---|
| 2e11cecbeb | |||
| e57414de17 |
@@ -92,7 +92,8 @@ sealed class SendState {
|
||||
val amountSats : Long,
|
||||
val feeSats : Long? = null,
|
||||
val paymentHash: String,
|
||||
val pendingRecord: PaymentRecord? = null
|
||||
val pendingRecord: PaymentRecord? = null,
|
||||
val lnurl : String? = null
|
||||
) : SendState()
|
||||
data class Error(val message: String) : SendState()
|
||||
}
|
||||
|
||||
@@ -537,19 +537,30 @@ fun WalletScreen(
|
||||
composable(
|
||||
route = ROUTE_CONTACT_DETAIL,
|
||||
arguments = listOf(navArgument("contactId") { type = NavType.StringType }),
|
||||
enterTransition = { fadeIn(animationSpec = tween(150)) },
|
||||
enterTransition = { fadeIn(animationSpec = tween(150)) },
|
||||
popExitTransition = { fadeOut(animationSpec = tween(150)) }
|
||||
) { backStackEntry ->
|
||||
val contactId = backStackEntry.arguments?.getString("contactId") ?: ""
|
||||
val detailVm: ContactDetailViewModel = viewModel(
|
||||
key = contactId,
|
||||
key = contactId,
|
||||
factory = ContactDetailViewModel.Factory(contactId, vm.contactRepo)
|
||||
)
|
||||
ContactDetailScreen(
|
||||
viewModel = detailVm,
|
||||
onBack = { navController.popBackStack() }
|
||||
onBack = { navController.popBackStack() },
|
||||
onPayAddress = { address ->
|
||||
vm.prefillSend(address)
|
||||
navController.navigate(TabItem.Send.route) {
|
||||
popUpTo(navController.graph.findStartDestination().id) {
|
||||
saveState = true
|
||||
}
|
||||
launchSingleTop = true
|
||||
restoreState = false // don't restore stale SendScreen state
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ── Clipboard banner — floats over content, slides in from top ────
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.bitcointxoko.gudariwallet.ui.receive.ReceiveViewModel
|
||||
import com.bitcointxoko.gudariwallet.ui.send.SendEvent
|
||||
import com.bitcointxoko.gudariwallet.ui.send.SendStrings
|
||||
import com.bitcointxoko.gudariwallet.ui.send.SendViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
@@ -116,6 +117,16 @@ class WalletViewModel(
|
||||
|
||||
val sendEvents: SharedFlow<SendEvent> = sendVm.events
|
||||
|
||||
// ── Contact tap-to-pay prefill ────────────────────────────────────────────
|
||||
private val _pendingSend = MutableStateFlow<String?>(null)
|
||||
val pendingSend: StateFlow<String?> get() = _pendingSend
|
||||
|
||||
/** Called by ContactDetailScreen before navigating to the Send tab. */
|
||||
fun prefillSend(address: String) { _pendingSend.value = address }
|
||||
|
||||
/** Called by SendScreen once it has consumed the prefilled address. */
|
||||
fun consumePendingSend() { _pendingSend.value = null }
|
||||
|
||||
fun handleWithdrawScanned(raw: LnurlScanResponse, lnurl: String) {
|
||||
receiveVm.handleWithdrawResponse(raw, lnurl)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.bitcointxoko.gudariwallet.ui.common
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CheckCircle
|
||||
import androidx.compose.material.icons.filled.PersonAdd
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bitcointxoko.gudariwallet.LocalAppStrings
|
||||
import com.bitcointxoko.gudariwallet.data.db.PaymentAddressType
|
||||
import com.bitcointxoko.gudariwallet.ui.contacts.CreateContactSheet
|
||||
import com.bitcointxoko.gudariwallet.ui.receive.AmountDisplay
|
||||
|
||||
/**
|
||||
* Shared success screen used by both Send (PaymentSent) and Receive (PaymentReceived).
|
||||
*
|
||||
* @param title Headline string, e.g. strings.paymentSent / strings.receive.paymentReceived
|
||||
* @param amountSats Amount in sats.
|
||||
* @param fiatLabel Optional fiat conversion string (from [fiatLabel] util).
|
||||
* @param subLine Optional secondary line below amount: fee text (send) or memo (receive).
|
||||
* @param detailsEnabled Whether the Details button is clickable.
|
||||
* @param onDetails Navigate to payment detail.
|
||||
* @param secondaryLabel Label of the reset TextButton ("Send Another" / "Done").
|
||||
* @param onSecondary Reset screen state.
|
||||
* @param lnurl Pre-fill address for the save-contact sheet. Pass null to hide the button.
|
||||
* @param onSaveContact Forward to vm.saveContactFromSend — omit (null) to hide save-contact UI.
|
||||
*/
|
||||
@Composable
|
||||
fun PaymentSuccessContent(
|
||||
title : String,
|
||||
amountSats : Long,
|
||||
fiatLabel : String?,
|
||||
subLine : String? = null,
|
||||
detailsEnabled : Boolean = true,
|
||||
onDetails : () -> Unit,
|
||||
secondaryLabel : String,
|
||||
onSecondary : () -> Unit,
|
||||
lnurl : String? = null,
|
||||
onSaveContact : ((name: String, addressType: PaymentAddressType?, address: String?) -> Unit)? = null,
|
||||
) {
|
||||
var showSaveSheet by remember { mutableStateOf(false) }
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize().padding(24.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.CheckCircle,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(72.dp)
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
Text(text = title, style = MaterialTheme.typography.headlineSmall)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
AmountDisplay(amountSats = amountSats, fiatLabel = fiatLabel)
|
||||
if (!subLine.isNullOrBlank()) {
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = subLine,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
|
||||
// ── Save contact affordance (send-side only) ─────────────────────────
|
||||
if (lnurl != null && onSaveContact != null) {
|
||||
Spacer(Modifier.height(16.dp))
|
||||
OutlinedButton(
|
||||
onClick = { showSaveSheet = true },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.PersonAdd,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text("Save contact")
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(32.dp))
|
||||
Button(
|
||||
onClick = onDetails,
|
||||
enabled = detailsEnabled,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) { Text(LocalAppStrings.current.details) }
|
||||
Spacer(Modifier.height(32.dp))
|
||||
TextButton(
|
||||
onClick = onSecondary,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) { Text(secondaryLabel) }
|
||||
}
|
||||
|
||||
// ── Bottom sheet ─────────────────────────────────────────────────────────
|
||||
if (showSaveSheet && lnurl != null && onSaveContact != null) {
|
||||
// Detect whether the string is a Lightning Address (user@domain) or LNURL
|
||||
val isLnAddress = lnurl.contains('@') && !lnurl.startsWith("lnurl", ignoreCase = true)
|
||||
val initialType = if (isLnAddress)
|
||||
PaymentAddressType.LIGHTNING_ADDRESS
|
||||
else
|
||||
PaymentAddressType.LNURL
|
||||
|
||||
CreateContactSheet(
|
||||
initialAddress = lnurl,
|
||||
initialAddressType = initialType,
|
||||
onSave = { name, addressType, address ->
|
||||
onSaveContact(name, addressType, address)
|
||||
showSaveSheet = false
|
||||
},
|
||||
onDismiss = { showSaveSheet = false }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.bitcointxoko.gudariwallet.ui.contacts
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
@@ -37,7 +38,8 @@ import com.bitcointxoko.gudariwallet.ui.common.DetailSection
|
||||
@Composable
|
||||
fun ContactDetailScreen(
|
||||
viewModel: ContactDetailViewModel,
|
||||
onBack: () -> Unit
|
||||
onBack: () -> Unit,
|
||||
onPayAddress: (address: String) -> Unit = {}
|
||||
) {
|
||||
val strings = LocalAppStrings.current
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
@@ -127,6 +129,7 @@ fun ContactDetailScreen(
|
||||
isEditing = isEditing,
|
||||
onRemoveAddress = { addressId -> pendingAddressDelete = addressId },
|
||||
onSetDefault = { viewModel.setDefaultAddress(it) },
|
||||
onPayAddress = onPayAddress,
|
||||
onEditAlias = { viewModel.openEditAliasSheet() },
|
||||
onDeleteContact = { viewModel.requestDelete() },
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
@@ -209,6 +212,7 @@ private fun ContactDetailContent(
|
||||
isEditing: Boolean,
|
||||
onRemoveAddress: (addressId: String) -> Unit,
|
||||
onSetDefault: (PaymentAddressEntity) -> Unit,
|
||||
onPayAddress: (address: String) -> Unit,
|
||||
onEditAlias: () -> Unit,
|
||||
onDeleteContact: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
@@ -276,6 +280,7 @@ private fun ContactDetailContent(
|
||||
AddressRow(
|
||||
address = addr,
|
||||
isEditing = isEditing,
|
||||
onPay = { onPayAddress(addr.address) },
|
||||
onRemove = { onRemoveAddress(addr.id) },
|
||||
onSetDefault = { onSetDefault(addr) }
|
||||
)
|
||||
@@ -349,6 +354,7 @@ private fun ContactDetailContent(
|
||||
private fun AddressRow(
|
||||
address: PaymentAddressEntity,
|
||||
isEditing: Boolean,
|
||||
onPay: () -> Unit,
|
||||
onRemove: () -> Unit,
|
||||
onSetDefault: () -> Unit,
|
||||
) {
|
||||
@@ -365,6 +371,10 @@ private fun AddressRow(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(
|
||||
enabled = !isEditing,
|
||||
onClickLabel = "Pay with this address"
|
||||
) { onPay() }
|
||||
.padding(vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
||||
@@ -57,6 +57,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.bitcointxoko.gudariwallet.LocalAppStrings
|
||||
import com.bitcointxoko.gudariwallet.ui.ReceiveState
|
||||
import com.bitcointxoko.gudariwallet.ui.WalletViewModel
|
||||
import com.bitcointxoko.gudariwallet.ui.common.PaymentSuccessContent
|
||||
import com.bitcointxoko.gudariwallet.ui.common.QrDisplayCard
|
||||
import com.bitcointxoko.gudariwallet.ui.common.rememberBrightnessController
|
||||
import com.bitcointxoko.gudariwallet.ui.common.UnitWheelPicker
|
||||
@@ -156,16 +157,21 @@ fun ReceiveScreen(
|
||||
}
|
||||
|
||||
is ReceiveState.PaymentReceived -> {
|
||||
ReceiveSuccessContent(
|
||||
amountSats = state.amountSats,
|
||||
memo = state.memo,
|
||||
fiatRate = fiatRate,
|
||||
fiatCurrency = fiatCurrency,
|
||||
onDone = { vm.resetReceiveState() },
|
||||
onDetails = { onNavigateToPaymentDetail(state.checkingId) }
|
||||
PaymentSuccessContent(
|
||||
title = strings.receive.paymentReceived,
|
||||
amountSats = state.amountSats,
|
||||
fiatLabel = fiatLabel(state.amountSats, fiatRate, fiatCurrency),
|
||||
subLine = state.memo?.takeIf { it.isNotBlank() },
|
||||
onDetails = { onNavigateToPaymentDetail(state.checkingId) },
|
||||
secondaryLabel = strings.done,
|
||||
onSecondary = { vm.resetReceiveState() },
|
||||
lnurl = null, // sender identity not available
|
||||
onSaveContact = null,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
is ReceiveState.Error -> {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Column(
|
||||
@@ -722,51 +728,4 @@ private fun ReceiveInvoiceContent(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── ReceiveSuccessContent ─────────────────────────────────────────────────────
|
||||
|
||||
@Composable
|
||||
private fun ReceiveSuccessContent(
|
||||
amountSats : Long,
|
||||
memo : String?,
|
||||
fiatRate : Double?,
|
||||
fiatCurrency : String?,
|
||||
onDone : () -> Unit,
|
||||
onDetails : () -> Unit
|
||||
) {
|
||||
val strings = LocalAppStrings.current
|
||||
val fiatLabel = fiatLabel(amountSats, fiatRate, fiatCurrency)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize().padding(24.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.CheckCircle,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(72.dp)
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
Text(
|
||||
text = strings.receive.paymentReceived, // ← "Payment Received!"
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
AmountDisplay(amountSats = amountSats, fiatLabel = fiatLabel)
|
||||
if (!memo.isNullOrBlank()) {
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(memo, style = MaterialTheme.typography.bodyMedium)
|
||||
}
|
||||
Spacer(Modifier.height(32.dp))
|
||||
Button(onClick = onDetails, modifier = Modifier.fillMaxWidth()) {
|
||||
Text(strings.details) // ← "Details"
|
||||
}
|
||||
Spacer(Modifier.height(32.dp))
|
||||
TextButton(onClick = onDone, modifier = Modifier.fillMaxWidth()) {
|
||||
Text(strings.done) // ← "Done"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import com.bitcointxoko.gudariwallet.ui.BalanceState
|
||||
import com.bitcointxoko.gudariwallet.ui.NfcOfferState
|
||||
import com.bitcointxoko.gudariwallet.ui.SendState
|
||||
import com.bitcointxoko.gudariwallet.ui.WalletViewModel
|
||||
import com.bitcointxoko.gudariwallet.ui.common.PaymentSuccessContent
|
||||
import com.bitcointxoko.gudariwallet.ui.nfc.NfcViewModel
|
||||
import com.bitcointxoko.gudariwallet.ui.receive.AmountDisplay
|
||||
import com.bitcointxoko.gudariwallet.util.SendInputDetector
|
||||
@@ -59,6 +60,17 @@ fun SendScreen(
|
||||
}
|
||||
val fiatCurrency = (balanceState as? BalanceState.Success)?.fiatCurrency
|
||||
|
||||
// ── Consume address prefilled from Contacts tap-to-pay ───────────────────
|
||||
val pendingSend by vm.pendingSend.collectAsStateWithLifecycle()
|
||||
LaunchedEffect(pendingSend) {
|
||||
val address = pendingSend
|
||||
if (!address.isNullOrBlank() && sendState is SendState.Idle) {
|
||||
input = SendInputDetector.normalize(address)
|
||||
vm.scan(address, sendStrings)
|
||||
vm.consumePendingSend()
|
||||
}
|
||||
}
|
||||
|
||||
// ── Collect NFC offer at top level — never inside a conditional ───────────
|
||||
val nfcOffer by nfcVm.nfcOfferState.collectAsStateWithLifecycle()
|
||||
|
||||
@@ -275,56 +287,31 @@ fun SendScreen(
|
||||
} else null
|
||||
val feeText = when {
|
||||
state.feeSats == null || state.feeSats == 0L ->
|
||||
strings.noFees // ← "No fees"
|
||||
strings.noFees
|
||||
else -> {
|
||||
val ppmSuffix = if (ppm != null) " (${ppm} ppm)" else ""
|
||||
strings.routingFee(state.feeSats, ppmSuffix) // ← "Routing fee: X sats (Y ppm)"
|
||||
strings.routingFee(state.feeSats, ppmSuffix)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.CheckCircle,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(72.dp)
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
Text(
|
||||
text = strings.paymentSent, // ← "Payment sent!"
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
AmountDisplay(amountSats = state.amountSats, fiatLabel = fiatLabel)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = feeText,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Spacer(Modifier.height(32.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
val record = state.pendingRecord
|
||||
if (record != null) {
|
||||
onViewDetails(state.paymentHash, record)
|
||||
}
|
||||
},
|
||||
enabled = state.pendingRecord != null,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) { Text(strings.details) } // ← "Details"
|
||||
Spacer(Modifier.height(32.dp))
|
||||
TextButton(
|
||||
onClick = { vm.resetSendState(); input = "" },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) { Text(strings.sendAnother) } // ← "Send Another"
|
||||
}
|
||||
PaymentSuccessContent(
|
||||
title = strings.paymentSent,
|
||||
amountSats = state.amountSats,
|
||||
fiatLabel = fiatLabel,
|
||||
subLine = feeText,
|
||||
detailsEnabled = state.pendingRecord != null,
|
||||
onDetails = {
|
||||
val record = state.pendingRecord
|
||||
if (record != null) onViewDetails(state.paymentHash, record)
|
||||
},
|
||||
secondaryLabel = strings.sendAnother,
|
||||
onSecondary = { vm.resetSendState(); input = "" },
|
||||
lnurl = state.lnurl, // null for plain Bolt11
|
||||
onSaveContact = vm::saveContactFromSend
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
is SendState.Error -> {
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Text(
|
||||
|
||||
@@ -134,20 +134,21 @@ class SendViewModel(
|
||||
pubkey : String?,
|
||||
alias : String?,
|
||||
strings : SendStrings,
|
||||
lnurl : String? = null
|
||||
) {
|
||||
val detail = runCatching { repo.getPaymentDetail(paymentHash) }.getOrNull()
|
||||
|
||||
when {
|
||||
// ── Settled immediately
|
||||
detail?.paid == true -> {
|
||||
emitPaymentSent(paymentHash, amountSats, detail.details?.feeSat, bolt11, memo, pubkey, alias)
|
||||
emitPaymentSent(paymentHash, amountSats, detail.details?.feeSat, bolt11, memo, pubkey, alias, lnurl)
|
||||
}
|
||||
|
||||
// ── Hold invoice — poll
|
||||
detail?.details?.status == "pending" || detail?.details?.pending == true -> {
|
||||
when (val result = poller.poll(paymentHash)) {
|
||||
is PaymentPoller.PollResult.Settled ->
|
||||
emitPaymentSent(paymentHash, amountSats, result.feeSats, bolt11, memo, pubkey, alias)
|
||||
emitPaymentSent(paymentHash, amountSats, result.feeSats, bolt11, memo, pubkey, alias, lnurl)
|
||||
|
||||
is PaymentPoller.PollResult.Failed ->
|
||||
_sendState.value = SendState.Error(strings.paymentFailed)
|
||||
@@ -162,7 +163,7 @@ class SendViewModel(
|
||||
Timber.w("SEND [DETAIL NULL] polling as fallback")
|
||||
when (val result = poller.poll(paymentHash)) {
|
||||
is PaymentPoller.PollResult.Settled ->
|
||||
emitPaymentSent(paymentHash, amountSats, result.feeSats, bolt11, memo, pubkey, alias)
|
||||
emitPaymentSent(paymentHash, amountSats, result.feeSats, bolt11, memo, pubkey, alias, lnurl)
|
||||
else ->
|
||||
_sendState.value = SendState.Error(strings.paymentFailed)
|
||||
}
|
||||
@@ -183,12 +184,14 @@ class SendViewModel(
|
||||
memo : String?,
|
||||
pubkey : String?,
|
||||
alias : String?,
|
||||
lnurl : String? = null
|
||||
) {
|
||||
_sendState.value = SendState.PaymentSent(
|
||||
amountSats = amountSats,
|
||||
feeSats = feeSats,
|
||||
paymentHash = paymentHash,
|
||||
pendingRecord = PaymentRecord.fromPayment(paymentHash, amountSats, feeSats, bolt11, memo, pubkey, alias)
|
||||
pendingRecord = PaymentRecord.fromPayment(paymentHash, amountSats, feeSats, bolt11, memo, pubkey, alias),
|
||||
lnurl = lnurl
|
||||
)
|
||||
_events.tryEmit(SendEvent.PaymentCompleted)
|
||||
}
|
||||
@@ -224,6 +227,7 @@ class SendViewModel(
|
||||
pubkey = null,
|
||||
alias = null,
|
||||
strings = strings,
|
||||
lnurl = lnurl
|
||||
)
|
||||
} else {
|
||||
_sendState.value = SendState.Error(
|
||||
@@ -323,6 +327,7 @@ class SendViewModel(
|
||||
pubkey = state.payee,
|
||||
alias = state.nodeAlias,
|
||||
strings = strings,
|
||||
lnurl = state.lnurl
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user