refactor: move biometric prompt trigger to UI

This commit is contained in:
2026-06-06 17:12:21 +02:00
parent c7888f7bfd
commit 368d03d263
3 changed files with 44 additions and 46 deletions
@@ -10,7 +10,6 @@ import com.bitcointxoko.gudariwallet.data.WalletRepository
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import androidx.fragment.app.FragmentActivity
import com.bitcointxoko.gudariwallet.data.BalancePrefsStore import com.bitcointxoko.gudariwallet.data.BalancePrefsStore
import com.bitcointxoko.gudariwallet.data.LightningAddressStore import com.bitcointxoko.gudariwallet.data.LightningAddressStore
import com.bitcointxoko.gudariwallet.ui.balance.BalanceViewModel import com.bitcointxoko.gudariwallet.ui.balance.BalanceViewModel
@@ -103,12 +102,9 @@ class WalletViewModel(
fun payLnurlInvoice(state: SendState.LnurlInvoiceReady, strings: SendStrings) = fun payLnurlInvoice(state: SendState.LnurlInvoiceReady, strings: SendStrings) =
sendVm.payLnurlInvoice(state, strings) sendVm.payLnurlInvoice(state, strings)
fun requestPayment( fun onAuthFailed(message: String, strings: SendStrings) {
activity : FragmentActivity, sendVm.onAuthFailed(message, strings)
subtitle : String, }
strings : SendStrings,
pay : () -> Unit,
) = sendVm.requestPayment(activity, subtitle, strings, pay)
fun resetSendState() = sendVm.resetSendState() fun resetSendState() = sendVm.resetSendState()
val sendEvents: SharedFlow<SendEvent> = sendVm.events val sendEvents: SharedFlow<SendEvent> = sendVm.events
@@ -30,6 +30,7 @@ import com.bitcointxoko.gudariwallet.ui.SendState
import com.bitcointxoko.gudariwallet.ui.WalletViewModel import com.bitcointxoko.gudariwallet.ui.WalletViewModel
import com.bitcointxoko.gudariwallet.ui.components.CopyIconButton import com.bitcointxoko.gudariwallet.ui.components.CopyIconButton
import com.bitcointxoko.gudariwallet.ui.components.DetailRow import com.bitcointxoko.gudariwallet.ui.components.DetailRow
import com.bitcointxoko.gudariwallet.util.BiometricHelper
import com.bitcointxoko.gudariwallet.util.fiatLabel import com.bitcointxoko.gudariwallet.util.fiatLabel
import com.bitcointxoko.gudariwallet.util.payingToLabel import com.bitcointxoko.gudariwallet.util.payingToLabel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -52,13 +53,13 @@ internal fun Bolt11ConfirmCard(
title = strings.invoiceDetails, // ← "Invoice Details" title = strings.invoiceDetails, // ← "Invoice Details"
invoice = state, invoice = state,
fiatLabel = fiatLbl, fiatLabel = fiatLbl,
onPay = { onPay = rememberBiometricPay(
vm.requestPayment( activity = activity,
activity, subtitle = strings.sendBiometricPrompt(state.amountSats),
strings.sendBiometricPrompt(state.amountSats), strings = sendStrings,
strings = sendStrings, // ← "Send X sats" vm = vm,
) { vm.payBolt11(state.bolt11, sendStrings) } pay = { vm.payBolt11(state.bolt11, sendStrings) }
}, ),
onCancel = { vm.resetSendState() } onCancel = { vm.resetSendState() }
) )
} }
@@ -79,13 +80,13 @@ internal fun LnurlInvoiceConfirmCard(
title = strings.payingTo(label), // ← "Paying to X" title = strings.payingTo(label), // ← "Paying to X"
invoice = state, invoice = state,
fiatLabel = fiatLbl, fiatLabel = fiatLbl,
onPay = { onPay = rememberBiometricPay(
vm.requestPayment( activity = activity,
activity, subtitle = strings.sendBiometricPromptTo(state.amountSats, label),
strings.sendBiometricPromptTo(state.amountSats, label), strings = sendStrings,
strings = sendStrings, // ← "Send X sats to Y" vm = vm,
) { vm.payLnurlInvoice(state, sendStrings) } pay = { vm.payLnurlInvoice(state, sendStrings) }
}, ),
onCancel = { vm.resetSendState() } onCancel = { vm.resetSendState() }
) )
} }
@@ -270,3 +271,25 @@ private fun PayCancelButton(onCancel: () -> Unit) {
Text(strings.cancel) // ← "Cancel" 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()
}
}
}
@@ -71,6 +71,10 @@ class SendViewModel(
private val _sendState = MutableStateFlow<SendState>(SendState.Idle) private val _sendState = MutableStateFlow<SendState>(SendState.Idle)
val sendState: StateFlow<SendState> = _sendState val sendState: StateFlow<SendState> = _sendState
fun onAuthFailed(message: String, strings: SendStrings) {
_sendState.value = SendState.Error(strings.authFailed(message))
}
// ── Entry point ────────────────────────────────────────────────────────── // ── Entry point ──────────────────────────────────────────────────────────
fun scan(rawInput: String, strings: SendStrings) { 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 } fun resetSendState() { _sendState.value = SendState.Idle }
// ── Scan handlers ──────────────────────────────────────────────────────── // ── Scan handlers ────────────────────────────────────────────────────────