feat: add sending by nfc
This commit is contained in:
@@ -296,6 +296,7 @@ fun WalletScreen(
|
||||
composable(TabItem.Send.route) {
|
||||
SendScreen(
|
||||
vm = vm,
|
||||
nfcVm = nfcVm,
|
||||
onViewDetails = { checkingId ->
|
||||
historyVm.refresh()
|
||||
navController.navigate("payment_detail/$checkingId") {
|
||||
|
||||
@@ -7,6 +7,7 @@ import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ContentPaste
|
||||
import androidx.compose.material.icons.filled.Nfc
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -21,8 +22,10 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.bitcointxoko.gudariwallet.ui.BalanceState
|
||||
import com.bitcointxoko.gudariwallet.ui.NfcOfferState
|
||||
import com.bitcointxoko.gudariwallet.ui.SendState
|
||||
import com.bitcointxoko.gudariwallet.ui.WalletViewModel
|
||||
import com.bitcointxoko.gudariwallet.ui.nfc.NfcViewModel
|
||||
import com.bitcointxoko.gudariwallet.util.SendInputDetector
|
||||
import com.bitcointxoko.gudariwallet.util.feePpm
|
||||
import com.bitcointxoko.gudariwallet.util.formatFiat
|
||||
@@ -30,21 +33,38 @@ import com.bitcointxoko.gudariwallet.util.satsToFiat
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun SendScreen(vm: WalletViewModel, onViewDetails: (paymentHash: String) -> Unit = {}) {
|
||||
val sendState: SendState by vm.sendState.collectAsStateWithLifecycle()
|
||||
var input by remember { mutableStateOf("") }
|
||||
val context = LocalContext.current
|
||||
val activity = context as FragmentActivity
|
||||
val clipboard = LocalClipboard.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val balanceState by vm.balanceState.collectAsStateWithLifecycle()
|
||||
val fiatRate = (balanceState as? BalanceState.Success)?.let { s ->
|
||||
fun SendScreen(
|
||||
vm : WalletViewModel,
|
||||
nfcVm : NfcViewModel,
|
||||
onViewDetails: (paymentHash: String) -> Unit = {}
|
||||
) {
|
||||
val sendState : SendState by vm.sendState.collectAsStateWithLifecycle()
|
||||
var input by remember { mutableStateOf("") }
|
||||
val context = LocalContext.current
|
||||
val activity = context as FragmentActivity
|
||||
val clipboard = LocalClipboard.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val balanceState by vm.balanceState.collectAsStateWithLifecycle()
|
||||
val fiatRate = (balanceState as? BalanceState.Success)?.let { s ->
|
||||
s.fiatAmount?.let { if (s.sats > 0) s.sats.toDouble() / it else null }
|
||||
}
|
||||
val fiatCurrency = (balanceState as? BalanceState.Success)?.fiatCurrency
|
||||
val fiatCurrency = (balanceState as? BalanceState.Success)?.fiatCurrency
|
||||
|
||||
// ── Collect NFC offer at top level — never inside a conditional ───────────
|
||||
val nfcOffer by nfcVm.nfcOfferState.collectAsStateWithLifecycle()
|
||||
|
||||
DisposableEffect(Unit) { onDispose { vm.resetSendState() } }
|
||||
|
||||
// ── Auto-trigger scan when NFC offer arrives ──────────────────────────────
|
||||
LaunchedEffect(nfcOffer) {
|
||||
val offer = nfcOffer
|
||||
if (offer is NfcOfferState.Detected && sendState is SendState.Idle) {
|
||||
input = offer.raw
|
||||
vm.scan(offer.raw)
|
||||
nfcVm.dismissNfcOffer()
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -55,13 +75,31 @@ fun SendScreen(vm: WalletViewModel, onViewDetails: (paymentHash: String) -> Unit
|
||||
) {
|
||||
Spacer(Modifier.height(32.dp))
|
||||
|
||||
// ── Header row (matches ReceiveIdleContent) ──────────────────────────
|
||||
// ── Header row ────────────────────────────────────────────────────────
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text("Send", style = MaterialTheme.typography.headlineSmall)
|
||||
|
||||
// NFC indicator — only visible in Idle/Error, lights up when offer pending
|
||||
if (sendState is SendState.Idle || sendState is SendState.Error) {
|
||||
FilledTonalIconButton(
|
||||
onClick = { /* passive indicator */ },
|
||||
modifier = Modifier.size(40.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Nfc,
|
||||
contentDescription = "Tap NFC device to receive invoice",
|
||||
tint = if (nfcOffer is NfcOfferState.Detected)
|
||||
MaterialTheme.colorScheme.primary
|
||||
else
|
||||
MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sendState !is SendState.PaymentSent) {
|
||||
|
||||
Reference in New Issue
Block a user