diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletState.kt index 3569b71936..c975cb54e2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletState.kt @@ -324,12 +324,18 @@ class CashuWalletState( private suspend fun recomputeUnspent() { val all = tokenEvents.values.toList() // Decrypt anything we haven't seen before; reuse cached TokenContent - // for events we've already decrypted. + // for events we've already decrypted. Decryption failures are + // skipped — the proof set rebuilds the next time a re-key happens. all.forEach { evt -> - tokenContents.getOrPut(evt.id) { - runCatching { evt.tokenContent(signer) } - .onFailure { Log.w("CashuWallet") { "Failed to decrypt token ${evt.id.take(8)}: ${it.message}" } } - .getOrNull() ?: return@getOrPut return@forEach + if (!tokenContents.containsKey(evt.id)) { + val content = + runCatching { evt.tokenContent(signer) } + .onFailure { + Log.w("CashuWallet") { + "Failed to decrypt token ${evt.id.take(8)}: ${it.message}" + } + }.getOrNull() + if (content != null) tokenContents[evt.id] = content } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/CashuWalletScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/CashuWalletScreen.kt index 1cb204a811..4d7dd0ffb3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/CashuWalletScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/CashuWalletScreen.kt @@ -97,12 +97,23 @@ fun CashuWalletScreen( val mints by viewModel.mints.collectAsState() val balanceSats by viewModel.balanceSats.collectAsState() val history by viewModel.history.collectAsState() + val pendingQuotes by viewModel.pendingQuotes.collectAsState() var receiveOpen by remember { mutableStateOf(false) } var sendLnOpen by remember { mutableStateOf(false) } 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 + } + } + Scaffold( topBar = { TopAppBar(