diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletOps.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletOps.kt index 69451f36e0..886e6ee1ad 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletOps.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletOps.kt @@ -616,6 +616,7 @@ class CashuWalletOps( zappedEvent: EventHintBundle, message: String, available: List, + onProgress: ((Float) -> Unit)? = null, ): NutzapSent { if (amountSats <= 0) throw IllegalArgumentException("Amount must be positive") seedWarmer() @@ -628,6 +629,10 @@ class CashuWalletOps( recipientP2pkPubkeyHex = recipientP2pkPubkeyHex, targetSplit = amountSats, ) + // Mint round-trip done — biggest chunk of the wall clock. Surface + // visible motion here so the user knows the click registered even + // before the recipient sees the kind:9321. + onProgress?.invoke(0.55f) // Build the kind:9321 first so we have its id to reference from history. val proofJsons = swap.send.map { nutzapProofJson.encodeToString(NutzapProofJson.serializer(), it.toNutzapJson()) } @@ -642,6 +647,8 @@ class CashuWalletOps( ) val nutzapEvent = signer.sign(nutzapTemplate) publish(nutzapEvent) + // kind:9321 is out — recipient's auto-redeem can fire from here. + onProgress?.invoke(0.80f) // Roll over change locally if any. val keepEvent = @@ -663,6 +670,7 @@ class CashuWalletOps( publish(it) } } + onProgress?.invoke(0.95f) val historyTemplate = CashuSpendingHistoryEvent.build( @@ -677,6 +685,7 @@ class CashuWalletOps( ) val historyEvent = signer.sign(historyTemplate) publish(historyEvent) + onProgress?.invoke(1.0f) return NutzapSent( nutzapEvent = nutzapEvent, 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 c2c2921bed..4e6f296435 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 @@ -1021,6 +1021,7 @@ class CashuWalletState( recipientPubKey: HexKey, zappedEvent: EventHintBundle, message: String = "", + onProgress: ((Float) -> Unit)? = null, ): NutzapSent { check(started) { "CashuWalletState.start() not called" } val target = @@ -1033,6 +1034,10 @@ class CashuWalletState( // partial-failure persists until the user clicks send. Heal // first so the selection below works on a known-fresh view. scrubLocallyStaleProofs(target.mintUrl) + // First on-network step done. The caller already showed an + // instant 0.05 when the chip was tapped; lift to 0.20 here so + // the bar visibly moves even before the (slow) swap call. + onProgress?.invoke(0.20f) val available = _tokenEntries.value.filter { it.content.mint == target.mintUrl } if (available.isEmpty()) { @@ -1047,6 +1052,7 @@ class CashuWalletState( zappedEvent = zappedEvent, message = message, available = available, + onProgress = onProgress, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt index 54cc96c0be..65abc41c87 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt @@ -2012,11 +2012,16 @@ fun ZapAmountChoicePopup( nutzapAmountChoices = if (nutzapEnabled) zapAmountChoices else persistentListOf(), onNutzap = { amountInSats -> onZapStarts() + // Instant click feedback. Without this initial nudge + // the bar stays flat for ~1s until scrubLocallyStale- + // Proofs returns; the user thinks the tap was lost. + onProgress(0.05f) accountViewModel.sendNutzap( baseNote = baseNote, amountSats = amountInSats, message = "", onError = onError, + onProgress = onProgress, ) visibilityState.targetState = false }, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt index cb40bb677e..1c254fc462 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt @@ -923,6 +923,7 @@ class AccountViewModel( amountSats: Long, message: String, onError: (String, String, User?) -> Unit, + onProgress: (Float) -> Unit = {}, ) = launchSigner { val recipient = baseNote.author?.pubkeyHex if (recipient == null) { @@ -948,6 +949,7 @@ class AccountViewModel( recipientPubKey = recipient, zappedEvent = zappedEvent, message = message, + onProgress = onProgress, ) // No success toast — the kind:9321 round-trips through the // cache, attaches to the target Note via addNutzap, and the