From 368d03d263d9eb8ef6c36c6cd1e18279fc854901 Mon Sep 17 00:00:00 2001 From: rasputin Date: Sat, 6 Jun 2026 17:12:21 +0200 Subject: [PATCH] refactor: move biometric prompt trigger to UI --- .../gudariwallet/ui/WalletViewModel.kt | 10 ++-- .../gudariwallet/ui/send/ConfirmCard.kt | 51 ++++++++++++++----- .../gudariwallet/ui/send/SendViewModel.kt | 29 ++--------- 3 files changed, 44 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/WalletViewModel.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/WalletViewModel.kt index 6cb8a1b..c2594dd 100644 --- a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/WalletViewModel.kt +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/WalletViewModel.kt @@ -10,7 +10,6 @@ import com.bitcointxoko.gudariwallet.data.WalletRepository import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch -import androidx.fragment.app.FragmentActivity import com.bitcointxoko.gudariwallet.data.BalancePrefsStore import com.bitcointxoko.gudariwallet.data.LightningAddressStore import com.bitcointxoko.gudariwallet.ui.balance.BalanceViewModel @@ -103,12 +102,9 @@ class WalletViewModel( fun payLnurlInvoice(state: SendState.LnurlInvoiceReady, strings: SendStrings) = sendVm.payLnurlInvoice(state, strings) - fun requestPayment( - activity : FragmentActivity, - subtitle : String, - strings : SendStrings, - pay : () -> Unit, - ) = sendVm.requestPayment(activity, subtitle, strings, pay) + fun onAuthFailed(message: String, strings: SendStrings) { + sendVm.onAuthFailed(message, strings) + } fun resetSendState() = sendVm.resetSendState() val sendEvents: SharedFlow = sendVm.events diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/send/ConfirmCard.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/send/ConfirmCard.kt index 2f7e892..a9b0572 100644 --- a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/send/ConfirmCard.kt +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/send/ConfirmCard.kt @@ -30,6 +30,7 @@ import com.bitcointxoko.gudariwallet.ui.SendState import com.bitcointxoko.gudariwallet.ui.WalletViewModel import com.bitcointxoko.gudariwallet.ui.components.CopyIconButton import com.bitcointxoko.gudariwallet.ui.components.DetailRow +import com.bitcointxoko.gudariwallet.util.BiometricHelper import com.bitcointxoko.gudariwallet.util.fiatLabel import com.bitcointxoko.gudariwallet.util.payingToLabel import kotlinx.coroutines.delay @@ -52,13 +53,13 @@ internal fun Bolt11ConfirmCard( title = strings.invoiceDetails, // ← "Invoice Details" invoice = state, fiatLabel = fiatLbl, - onPay = { - vm.requestPayment( - activity, - strings.sendBiometricPrompt(state.amountSats), - strings = sendStrings, // ← "Send X sats" - ) { vm.payBolt11(state.bolt11, sendStrings) } - }, + onPay = rememberBiometricPay( + activity = activity, + subtitle = strings.sendBiometricPrompt(state.amountSats), + strings = sendStrings, + vm = vm, + pay = { vm.payBolt11(state.bolt11, sendStrings) } + ), onCancel = { vm.resetSendState() } ) } @@ -79,13 +80,13 @@ internal fun LnurlInvoiceConfirmCard( title = strings.payingTo(label), // ← "Paying to X" invoice = state, fiatLabel = fiatLbl, - onPay = { - vm.requestPayment( - activity, - strings.sendBiometricPromptTo(state.amountSats, label), - strings = sendStrings, // ← "Send X sats to Y" - ) { vm.payLnurlInvoice(state, sendStrings) } - }, + onPay = rememberBiometricPay( + activity = activity, + subtitle = strings.sendBiometricPromptTo(state.amountSats, label), + strings = sendStrings, + vm = vm, + pay = { vm.payLnurlInvoice(state, sendStrings) } + ), onCancel = { vm.resetSendState() } ) } @@ -270,3 +271,25 @@ private fun PayCancelButton(onCancel: () -> Unit) { Text(strings.cancel) // ← "Cancel" } } + +@Composable +private fun rememberBiometricPay( + activity : FragmentActivity, + subtitle : String, + strings : SendStrings, + vm : WalletViewModel, + pay : () -> Unit, +): () -> Unit = remember(subtitle) { + { + if (BiometricHelper.canAuthenticate(activity)) { + BiometricHelper.prompt( + activity = activity, + subtitle = subtitle, + onSuccess = pay, + onError = { _, msg -> vm.onAuthFailed(msg.toString(), strings) } + ) + } else { + pay() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/send/SendViewModel.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/send/SendViewModel.kt index 6703503..ae9bd56 100644 --- a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/send/SendViewModel.kt +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/send/SendViewModel.kt @@ -71,6 +71,10 @@ class SendViewModel( private val _sendState = MutableStateFlow(SendState.Idle) val sendState: StateFlow = _sendState + fun onAuthFailed(message: String, strings: SendStrings) { + _sendState.value = SendState.Error(strings.authFailed(message)) + } + // ── Entry point ────────────────────────────────────────────────────────── fun scan(rawInput: String, strings: SendStrings) { @@ -321,31 +325,6 @@ class SendViewModel( } } - /** - * Biometric gate. Shows prompt if device supports it; calls [pay] directly if not. - */ - fun requestPayment( - activity : FragmentActivity, - subtitle : String, - strings : SendStrings, - pay : () -> Unit - ) { - if (!BiometricHelper.canAuthenticate(activity)) { - pay() - return - } - BiometricHelper.prompt( - activity = activity, - subtitle = subtitle, - onSuccess = pay, - onError = { _, msg -> - _sendState.value = SendState.Error( - strings.authFailed(msg.toString()) // ← "Authentication failed: $msg" - ) - } - ) - } - fun resetSendState() { _sendState.value = SendState.Idle } // ── Scan handlers ────────────────────────────────────────────────────────