From 5bdc606d1c5a1c3784dd76e7ef685a0e5554f32e Mon Sep 17 00:00:00 2001 From: rasputin Date: Thu, 4 Jun 2026 12:57:59 +0200 Subject: [PATCH] fix: get balance from websocket instead of separate api call --- .../bitcointxoko/gudariwallet/api/Models.kt | 1 + .../service/WalletNotificationService.kt | 4 +- .../ui/balance/BalanceViewModel.kt | 41 ++++++++++++------- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/api/Models.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/api/Models.kt index 6c90283..09451b0 100644 --- a/app/src/main/java/com/bitcointxoko/gudariwallet/api/Models.kt +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/api/Models.kt @@ -259,6 +259,7 @@ data class PaymentDetailResponse( // ── WebSocket payment message ───────────────────────────────────────────────── data class WsPaymentMessage( + @SerializedName("wallet_balance") val walletBalance: Long?, val payment: WsPayment? ) diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/service/WalletNotificationService.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/service/WalletNotificationService.kt index be4db18..067962b 100644 --- a/app/src/main/java/com/bitcointxoko/gudariwallet/service/WalletNotificationService.kt +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/service/WalletNotificationService.kt @@ -81,6 +81,7 @@ class WalletNotificationService : Service() { * Emitted to [paymentEvents] for in-process UI updates. */ data class PaymentEvent( + val walletBalance: Long?, val amountSats: Long, val memo: String?, val isOutgoing: Boolean, @@ -229,6 +230,7 @@ class WalletNotificationService : Service() { serviceScope.launch { _paymentEvents.emit( PaymentEvent( + walletBalance = msg.walletBalance, amountSats = amountSats, memo = payment.memo, isOutgoing = isOutgoing, @@ -245,7 +247,7 @@ class WalletNotificationService : Service() { checkingId = payment.checkingId, paymentHash = payment.paymentHash, amountMsat = payment.amount, - feeMsat = 0L, // WsPaymentMessage doesn't carry fee + feeMsat = payment.fee, memo = payment.memo, time = payment.time, status = payment.status, diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/balance/BalanceViewModel.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/balance/BalanceViewModel.kt index c39d026..103bdc4 100644 --- a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/balance/BalanceViewModel.kt +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/balance/BalanceViewModel.kt @@ -25,7 +25,7 @@ class BalanceViewModel( val balanceState: StateFlow = _balanceState // Emits after every successful balance refresh so other ViewModels - // (e.g. FiatViewModel) can react without polling. + // can react without polling. private val _balanceRefreshed = MutableSharedFlow(extraBufferCapacity = 1) // Emits incoming payment events so ReceiveViewModel can update its state. @@ -117,25 +117,38 @@ class BalanceViewModel( WalletNotificationService.paymentEvents.collect { event -> Log.d(TAG, "BALANCE [WS EVENT ] ${if (event.isOutgoing) "SENT" else "RECEIVED"} ${event.amountSats} sats") - val current = _balanceState.value - if (current is BalanceState.Success) { - val delta = if (event.isOutgoing) -event.amountSats else event.amountSats - val newSats = (current.sats + delta).coerceAtLeast(0L) - Log.d(TAG, "BALANCE [WS DELTA ] ${current.sats} ${if (delta >= 0) "+" else ""}$delta = $newSats sats (optimistic)") - _balanceState.value = current.copy(sats = newSats, isRefreshing = true) - persistBalance(newSats) + val newSats = if (event.walletBalance != null) { + // Authoritative balance from the server — no delta math needed + Log.d(TAG, "BALANCE [WS BALANCE] ${event.walletBalance} sats (from server)") + event.walletBalance } else { - Log.w(TAG, "BALANCE [WS EVENT ] Received event but balance state is ${current::class.simpleName} — skipping delta") + // Fallback: server didn't send balance, apply delta optimistically + val current = _balanceState.value + if (current is BalanceState.Success) { + val delta = if (event.isOutgoing) -event.amountSats else event.amountSats + val sats = (current.sats + delta).coerceAtLeast(0L) + Log.d(TAG, "BALANCE [WS DELTA ] ${current.sats} ${if (delta >= 0) "+" else ""}$delta = $sats sats (optimistic fallback)") + sats + } else null } - refreshBalance() + if (newSats != null) { + val now = System.currentTimeMillis() + _balanceState.value = BalanceState.Success( + sats = newSats, + isRefreshing = false, + lastUpdated = now + ) + persistBalance(newSats) + _balanceRefreshed.tryEmit(newSats) + onBalanceRefreshed?.invoke() + } + + // refreshBalance() call removed — balance is authoritative from WS if (!event.isOutgoing) { _incomingPayment.tryEmit( - IncomingPaymentEvent( - amountSats = event.amountSats, - memo = event.memo - ) + IncomingPaymentEvent(amountSats = event.amountSats, memo = event.memo) ) } }