Merge pull request 'fix(wallet): refetch balance and history on resume' (#164) from fix/wallet-resume-refetch into dev

Reviewed-on: #164
This commit is contained in:
padreug 2026-09-22 21:06:18 +00:00
commit 586698259a

View file

@ -437,6 +437,39 @@ export class WalletWebSocketService extends BaseService {
// Resume polling if we were using fallback // Resume polling if we were using fallback
this.schedulePolling() this.schedulePolling()
} }
// Reconnecting is not enough on its own — see below.
await this.reconcileMissedActivity()
}
/**
* Re-sync balance and history after the app was backgrounded.
*
* `onPause` closes the socket to save battery, and the LNbits websocket
* only pushes on live events — it replays nothing on reconnect. So any
* payment that settled while we were away produced a notification that
* reached no one: the balance and the transaction list both stayed at
* their pre-pause values until the user hit Refresh by hand.
*
* Reconnecting therefore has to be paired with an explicit refetch. Both
* halves are needed: the balance comes from PaymentService, the rows come
* from WalletService, and neither implies the other.
*
* `allSettled` so a failure in one does not leave the other un-refreshed.
*/
private async reconcileMissedActivity(): Promise<void> {
console.log('WalletWebSocketService: Reconciling activity missed while paused')
const results = await Promise.allSettled([
this.fetchAndUpdateBalance(),
this.walletService?.refresh?.()
])
results
.filter((r): r is PromiseRejectedResult => r.status === 'rejected')
.forEach(r =>
console.error('WalletWebSocketService: Resume reconciliation failed', r.reason)
)
} }
/** /**
@ -556,14 +589,27 @@ export class WalletWebSocketService extends BaseService {
*/ */
private async performPollingUpdate(): Promise<void> { private async performPollingUpdate(): Promise<void> {
try { try {
await this.fetchAndUpdateBalance()
} catch (error) {
console.error('WalletWebSocketService: Polling error', error)
} finally {
// Keep polling regardless of the outcome of this round.
this.schedulePolling()
}
}
/**
* Fetch the authoritative wallet balance from LNbits and push it into
* PaymentService. Shared by the polling fallback and by resume
* reconciliation.
*/
private async fetchAndUpdateBalance(): Promise<void> {
const wallet = this.paymentService?.getPreferredWallet?.() const wallet = this.paymentService?.getPreferredWallet?.()
if (!wallet?.inkey) { if (!wallet?.inkey) {
console.log('WalletWebSocketService: No wallet available for polling') console.log('WalletWebSocketService: No wallet available for balance fetch')
this.schedulePolling() // Continue polling
return return
} }
// Fetch balance from LNbits API
const baseUrl = import.meta.env.VITE_LNBITS_BASE_URL || 'http://localhost:5000' const baseUrl = import.meta.env.VITE_LNBITS_BASE_URL || 'http://localhost:5000'
const response = await fetch(`${baseUrl}/api/v1/wallet`, { const response = await fetch(`${baseUrl}/api/v1/wallet`, {
headers: { headers: {
@ -572,31 +618,23 @@ export class WalletWebSocketService extends BaseService {
}) })
if (!response.ok) { if (!response.ok) {
console.error('WalletWebSocketService: Polling failed', response.status, response.statusText) console.error(
this.schedulePolling() // Continue polling despite error 'WalletWebSocketService: Balance fetch failed',
response.status,
response.statusText
)
return return
} }
const data = await response.json() const data = await response.json()
if (data.balance !== undefined) { if (data.balance !== undefined) {
// LNbits API returns balance in millisats // LNbits API returns balance in millisats
const balanceMsat = parseInt(data.balance) const balanceMsat = parseInt(data.balance)
console.log('WalletWebSocketService: Polling update - balance:', balanceMsat, 'msat') console.log('WalletWebSocketService: Balance fetch -', balanceMsat, 'msat')
// Update balance via PaymentService
if (this.paymentService?.updateWalletBalance) { if (this.paymentService?.updateWalletBalance) {
this.paymentService.updateWalletBalance(balanceMsat, wallet.id) this.paymentService.updateWalletBalance(balanceMsat, wallet.id)
} }
} }
// Schedule next polling
this.schedulePolling()
} catch (error) {
console.error('WalletWebSocketService: Polling error', error)
this.schedulePolling() // Continue polling despite error
}
} }
/** /**