mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 08:47:33 +00:00
fix(cashu): replace auto-popup with a tappable pending-quote banner
Bug: every entry to the wallet screen re-opened the Receive dialog if any pending kind:7374 mint quote existed. So a user who got an invoice and then navigated away — for any reason, even without dismissing — would be greeted by the same invoice dialog the next time they opened the wallet. Worse: even after they'd paid and the mint had issued proofs, the brief window before the kind:7374 NIP-09 delete propagated would cause the dialog to pop again. Fix: drop the LaunchedEffect(pendingQuotes) auto-resume; replace with a non-modal banner card just under the BalanceCard that shows "N pending invoices · Tap to resume". The user opts in by tapping it. The underlying CashuWalletState.pendingQuotes flow + the viewModel.resumeMintQuote() VM method are unchanged — only the trigger surface moves from "auto" to "user-initiated". playDebug + fdroidDebug compile clean; 24/24 jvm tests still pass.
This commit is contained in:
+80
-9
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -107,15 +108,11 @@ fun CashuWalletScreen(
|
||||
var sendTokenOpen by remember { mutableStateOf(false) }
|
||||
var redeemOpen by remember { mutableStateOf(false) }
|
||||
|
||||
// If the user has an unfinished kind:7374 quote, surface it the next time
|
||||
// they open the wallet so the in-flight invoice isn't lost. Auto-resumes
|
||||
// the most recent quote on first composition.
|
||||
LaunchedEffect(pendingQuotes) {
|
||||
if (!receiveOpen && pendingQuotes.isNotEmpty()) {
|
||||
viewModel.resumeMintQuote(pendingQuotes.first())
|
||||
receiveOpen = true
|
||||
}
|
||||
}
|
||||
// pendingQuotes drives a non-modal banner in the wallet body (see
|
||||
// PendingQuoteBanner below). Tapping the banner is what opens the
|
||||
// Receive dialog with the stored quote pre-loaded — we do NOT auto-pop
|
||||
// the dialog on every entry to the screen, which would re-surface
|
||||
// dismissed quotes every time the user navigates back.
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
@@ -147,11 +144,18 @@ fun CashuWalletScreen(
|
||||
balanceSats = balanceSats,
|
||||
mints = mints,
|
||||
history = history,
|
||||
pendingQuoteCount = pendingQuotes.size,
|
||||
onReceive = { receiveOpen = true },
|
||||
onSendLn = { sendLnOpen = true },
|
||||
onSendToken = { sendTokenOpen = true },
|
||||
onRedeem = { redeemOpen = true },
|
||||
onRecommendMint = { viewModel.recommendMint(it) },
|
||||
onResumePendingQuote = {
|
||||
pendingQuotes.firstOrNull()?.let {
|
||||
viewModel.resumeMintQuote(it)
|
||||
receiveOpen = true
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// NIP-60 wallets are portable across clients — show a "looking
|
||||
@@ -275,11 +279,13 @@ private fun CashuWalletContent(
|
||||
balanceSats: Long,
|
||||
mints: List<String>,
|
||||
history: List<CashuSpendingHistoryEvent>,
|
||||
pendingQuoteCount: Int,
|
||||
onReceive: () -> Unit,
|
||||
onSendLn: () -> Unit,
|
||||
onSendToken: () -> Unit,
|
||||
onRedeem: () -> Unit,
|
||||
onRecommendMint: (String) -> Unit,
|
||||
onResumePendingQuote: () -> Unit,
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier =
|
||||
@@ -293,6 +299,10 @@ private fun CashuWalletContent(
|
||||
BalanceCard(balanceSats)
|
||||
}
|
||||
|
||||
if (pendingQuoteCount > 0) {
|
||||
item { PendingQuoteBanner(count = pendingQuoteCount, onResume = onResumePendingQuote) }
|
||||
}
|
||||
|
||||
item {
|
||||
ActionRow(
|
||||
onReceive = onReceive,
|
||||
@@ -332,6 +342,67 @@ private fun CashuWalletContent(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Banner that surfaces unfinished mint quotes — tappable to resume the
|
||||
* receive flow with the stored invoice. Driven by
|
||||
* [com.vitorpamplona.amethyst.model.nip60Cashu.CashuWalletState.pendingQuotes].
|
||||
*
|
||||
* Replaces the earlier auto-popup behaviour which re-surfaced the Receive
|
||||
* dialog on every entry to the screen.
|
||||
*/
|
||||
@Composable
|
||||
private fun PendingQuoteBanner(
|
||||
count: Int,
|
||||
onResume: () -> Unit,
|
||||
) {
|
||||
Card(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onResume),
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.secondaryContainer),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 14.dp, vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.Bolt,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text =
|
||||
androidx.compose.ui.res
|
||||
.pluralStringResource(
|
||||
R.plurals.cashu_pending_quotes_title,
|
||||
count,
|
||||
count,
|
||||
),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
)
|
||||
Text(
|
||||
text = stringRes(R.string.cashu_pending_quotes_subtitle),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.8f),
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringRes(R.string.cashu_pending_quotes_resume),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BalanceCard(balanceSats: Long) {
|
||||
val formatted =
|
||||
|
||||
@@ -2857,6 +2857,13 @@
|
||||
<item quantity="other">%1$d mints</item>
|
||||
</plurals>
|
||||
|
||||
<plurals name="cashu_pending_quotes_title">
|
||||
<item quantity="one">%1$d pending invoice</item>
|
||||
<item quantity="other">%1$d pending invoices</item>
|
||||
</plurals>
|
||||
<string name="cashu_pending_quotes_subtitle">Tap to resume and check status.</string>
|
||||
<string name="cashu_pending_quotes_resume">Resume</string>
|
||||
|
||||
<string name="event_sync_less_than_until"><%1$s</string>
|
||||
<string name="event_sync_status_connecting">Connecting</string>
|
||||
<string name="event_sync_status_downloading">Downloading</string>
|
||||
|
||||
Reference in New Issue
Block a user