fix: spinner when loading details from successful send
This commit is contained in:
@@ -91,7 +91,8 @@ sealed class SendState {
|
|||||||
data class PaymentSent(
|
data class PaymentSent(
|
||||||
val amountSats : Long,
|
val amountSats : Long,
|
||||||
val feeSats : Long? = null,
|
val feeSats : Long? = null,
|
||||||
val paymentHash: String
|
val paymentHash: String,
|
||||||
|
val pendingRecord: PaymentRecord? = null
|
||||||
) : SendState()
|
) : SendState()
|
||||||
data class Error(val message: String) : SendState()
|
data class Error(val message: String) : SendState()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -297,8 +297,8 @@ fun WalletScreen(
|
|||||||
SendScreen(
|
SendScreen(
|
||||||
vm = vm,
|
vm = vm,
|
||||||
nfcVm = nfcVm,
|
nfcVm = nfcVm,
|
||||||
onViewDetails = { checkingId ->
|
onViewDetails = { checkingId, record ->
|
||||||
historyVm.refresh()
|
historyVm.primePayment(record)
|
||||||
navController.navigate("payment_detail/$checkingId") {
|
navController.navigate("payment_detail/$checkingId") {
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ class HistoryViewModel(
|
|||||||
scope = viewModelScope,
|
scope = viewModelScope,
|
||||||
onNewPayments = { payments -> enricher.enrich(payments) }
|
onNewPayments = { payments -> enricher.enrich(payments) }
|
||||||
)
|
)
|
||||||
|
// ── Optimistic insert (called after a successful send) ─────────────────────
|
||||||
|
fun primePayment(record: PaymentRecord) {
|
||||||
|
syncManager.primePayment(record)
|
||||||
|
}
|
||||||
private val enricher: PaymentEnricher = PaymentEnricher(
|
private val enricher: PaymentEnricher = PaymentEnricher(
|
||||||
repo = repo,
|
repo = repo,
|
||||||
aliasRepo = aliasRepo,
|
aliasRepo = aliasRepo,
|
||||||
|
|||||||
@@ -192,4 +192,20 @@ class PaymentSyncManager(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Optimistic insert ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
fun primePayment(record: PaymentRecord) {
|
||||||
|
val current = mutableState.value
|
||||||
|
if (current is HistoryState.Success) {
|
||||||
|
val already = current.payments.any { it.checkingId == record.checkingId }
|
||||||
|
if (!already) {
|
||||||
|
mutableState.value = current.copy(
|
||||||
|
payments = listOf(record) + current.payments
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If state is Loading/Error (e.g. first launch), the next syncNewPayments()
|
||||||
|
// will fetch the real record from the API — no action needed here.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import com.bitcointxoko.gudariwallet.api.PaymentRecord
|
||||||
import com.bitcointxoko.gudariwallet.ui.BalanceState
|
import com.bitcointxoko.gudariwallet.ui.BalanceState
|
||||||
import com.bitcointxoko.gudariwallet.ui.NfcOfferState
|
import com.bitcointxoko.gudariwallet.ui.NfcOfferState
|
||||||
import com.bitcointxoko.gudariwallet.ui.SendState
|
import com.bitcointxoko.gudariwallet.ui.SendState
|
||||||
@@ -36,7 +37,7 @@ import kotlinx.coroutines.launch
|
|||||||
fun SendScreen(
|
fun SendScreen(
|
||||||
vm : WalletViewModel,
|
vm : WalletViewModel,
|
||||||
nfcVm : NfcViewModel,
|
nfcVm : NfcViewModel,
|
||||||
onViewDetails: (paymentHash: String) -> Unit = {}
|
onViewDetails: (paymentHash: String, record: PaymentRecord) -> Unit = { _, _ -> }
|
||||||
) {
|
) {
|
||||||
val sendState : SendState by vm.sendState.collectAsStateWithLifecycle()
|
val sendState : SendState by vm.sendState.collectAsStateWithLifecycle()
|
||||||
var input by remember { mutableStateOf("") }
|
var input by remember { mutableStateOf("") }
|
||||||
@@ -244,7 +245,13 @@ fun SendScreen(
|
|||||||
)
|
)
|
||||||
Spacer(Modifier.height(32.dp))
|
Spacer(Modifier.height(32.dp))
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
onClick = { onViewDetails(state.paymentHash) },
|
onClick = {
|
||||||
|
val record = state.pendingRecord
|
||||||
|
if (record != null) {
|
||||||
|
onViewDetails(state.paymentHash, record)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enabled = state.pendingRecord != null,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) { Text("Details") }
|
) { Text("Details") }
|
||||||
Spacer(Modifier.height(8.dp))
|
Spacer(Modifier.height(8.dp))
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import androidx.fragment.app.FragmentActivity
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.bitcointxoko.gudariwallet.api.LnurlScanResponse
|
import com.bitcointxoko.gudariwallet.api.LnurlScanResponse
|
||||||
|
import com.bitcointxoko.gudariwallet.api.PaymentRecord
|
||||||
import com.bitcointxoko.gudariwallet.api.RouteHintHop
|
import com.bitcointxoko.gudariwallet.api.RouteHintHop
|
||||||
import com.bitcointxoko.gudariwallet.data.LnurlCacheRepository
|
import com.bitcointxoko.gudariwallet.data.LnurlCacheRepository
|
||||||
import com.bitcointxoko.gudariwallet.data.NodeAliasRepository
|
import com.bitcointxoko.gudariwallet.data.NodeAliasRepository
|
||||||
@@ -65,6 +66,7 @@ class SendViewModel(
|
|||||||
|
|
||||||
fun payBolt11(bolt11: String) {
|
fun payBolt11(bolt11: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
val decoded = _sendState.value as? SendState.Bolt11Decoded
|
||||||
val amountSats = (_sendState.value as? SendState.Bolt11Decoded)?.amountSats ?: 0L
|
val amountSats = (_sendState.value as? SendState.Bolt11Decoded)?.amountSats ?: 0L
|
||||||
_sendState.value = SendState.Paying
|
_sendState.value = SendState.Paying
|
||||||
runCatching { repo.payBolt11(bolt11) }
|
runCatching { repo.payBolt11(bolt11) }
|
||||||
@@ -73,9 +75,18 @@ class SendViewModel(
|
|||||||
repo.getPaymentDetail(response.paymentHash).details?.feeSat
|
repo.getPaymentDetail(response.paymentHash).details?.feeSat
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
_sendState.value = SendState.PaymentSent(
|
_sendState.value = SendState.PaymentSent(
|
||||||
amountSats = amountSats,
|
amountSats = amountSats,
|
||||||
feeSats = feeSats,
|
feeSats = feeSats,
|
||||||
paymentHash = response.paymentHash
|
paymentHash = response.paymentHash,
|
||||||
|
pendingRecord = buildPendingRecord(
|
||||||
|
paymentHash = response.paymentHash,
|
||||||
|
amountSats = amountSats,
|
||||||
|
feeSats = feeSats,
|
||||||
|
bolt11 = decoded?.bolt11,
|
||||||
|
memo = decoded?.memo,
|
||||||
|
pubkey = decoded?.payee,
|
||||||
|
alias = decoded?.nodeAlias
|
||||||
|
)
|
||||||
)
|
)
|
||||||
balanceVm.refreshBalance()
|
balanceVm.refreshBalance()
|
||||||
}
|
}
|
||||||
@@ -230,9 +241,18 @@ class SendViewModel(
|
|||||||
repo.getPaymentDetail(paymentHash).details?.feeSat
|
repo.getPaymentDetail(paymentHash).details?.feeSat
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
_sendState.value = SendState.PaymentSent(
|
_sendState.value = SendState.PaymentSent(
|
||||||
amountSats = state.amountSats,
|
amountSats = state.amountSats,
|
||||||
feeSats = feeSats,
|
feeSats = feeSats,
|
||||||
paymentHash = paymentHash
|
paymentHash = paymentHash,
|
||||||
|
pendingRecord = buildPendingRecord(
|
||||||
|
paymentHash = paymentHash,
|
||||||
|
amountSats = state.amountSats,
|
||||||
|
feeSats = feeSats,
|
||||||
|
bolt11 = state.bolt11,
|
||||||
|
memo = state.memo,
|
||||||
|
pubkey = state.payee,
|
||||||
|
alias = state.nodeAlias
|
||||||
|
)
|
||||||
)
|
)
|
||||||
balanceVm.refreshBalance()
|
balanceVm.refreshBalance()
|
||||||
return@launch
|
return@launch
|
||||||
@@ -454,14 +474,31 @@ class SendViewModel(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun recordPaymentSuccess(paymentHash: String, amountMsat: Long) {
|
private suspend fun recordPaymentSuccess(
|
||||||
|
paymentHash: String,
|
||||||
|
amountMsat: Long,
|
||||||
|
bolt11 : String? = null,
|
||||||
|
memo : String? = null,
|
||||||
|
pubkey : String? = null,
|
||||||
|
alias : String? = null
|
||||||
|
) {
|
||||||
val feeSats = runCatching {
|
val feeSats = runCatching {
|
||||||
repo.getPaymentDetail(paymentHash).details?.feeSat
|
repo.getPaymentDetail(paymentHash).details?.feeSat
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
|
val amountSats = amountMsat / WalletConstants.MSAT_PER_SAT
|
||||||
_sendState.value = SendState.PaymentSent(
|
_sendState.value = SendState.PaymentSent(
|
||||||
amountSats = amountMsat / WalletConstants.MSAT_PER_SAT,
|
amountSats = amountSats,
|
||||||
feeSats = feeSats,
|
feeSats = feeSats,
|
||||||
paymentHash = paymentHash
|
paymentHash = paymentHash,
|
||||||
|
pendingRecord = buildPendingRecord(
|
||||||
|
paymentHash = paymentHash,
|
||||||
|
amountSats = amountSats,
|
||||||
|
feeSats = feeSats,
|
||||||
|
bolt11 = bolt11,
|
||||||
|
memo = memo,
|
||||||
|
pubkey = pubkey,
|
||||||
|
alias = alias
|
||||||
|
)
|
||||||
)
|
)
|
||||||
balanceVm.refreshBalance()
|
balanceVm.refreshBalance()
|
||||||
}
|
}
|
||||||
@@ -551,4 +588,28 @@ class SendViewModel(
|
|||||||
key to value
|
key to value
|
||||||
}.toMap()
|
}.toMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun buildPendingRecord(
|
||||||
|
paymentHash: String,
|
||||||
|
amountSats : Long,
|
||||||
|
feeSats : Long?,
|
||||||
|
bolt11 : String?,
|
||||||
|
memo : String?,
|
||||||
|
pubkey : String?,
|
||||||
|
alias : String?
|
||||||
|
): PaymentRecord = PaymentRecord(
|
||||||
|
paymentHash = paymentHash,
|
||||||
|
checkingId = paymentHash,
|
||||||
|
amountMsat = -(amountSats * 1_000L), // negative = outgoing
|
||||||
|
feeMsat = (feeSats ?: 0L) * 1_000L,
|
||||||
|
memo = memo,
|
||||||
|
time = (System.currentTimeMillis() / 1_000L).toString(),
|
||||||
|
status = "complete",
|
||||||
|
bolt11 = bolt11,
|
||||||
|
pending = false,
|
||||||
|
preimage = null,
|
||||||
|
extra = null,
|
||||||
|
pubkey = pubkey,
|
||||||
|
alias = alias
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user