feat(wallet): real-time tx push — 0-conf shows in seconds, not on poll
Backend streams LND /v1/transactions/subscribe (fires on mempool arrival and each confirmation) and nudges the /ws/db revision per event; the Home wallet card subscribes and refetches (debounced 800ms). An incoming broadcast now appears while the 30s poll is still asleep. Reconnects forever with capped backoff when LND is down/locked/absent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
088b3e255a
commit
5e7e928650
@@ -300,6 +300,8 @@ import TransactionsModal from '@/components/TransactionsModal.vue'
|
||||
import WalletSettingsModal from '@/components/WalletSettingsModal.vue'
|
||||
import { useAppStore } from '../stores/app'
|
||||
import { useAppLauncherStore } from '@/stores/appLauncher'
|
||||
import { wsClient } from '@/api/websocket'
|
||||
import { useTxExplorer } from '@/composables/useTxExplorer'
|
||||
import { useLoginTransitionStore } from '../stores/loginTransition'
|
||||
import { useUIModeStore } from '@/stores/uiMode'
|
||||
import { useHomeStatusStore } from '@/stores/homeStatus'
|
||||
@@ -360,6 +362,8 @@ onBeforeUnmount(() => {
|
||||
if (typingInterval) clearInterval(typingInterval)
|
||||
if (systemStatsInterval) clearInterval(systemStatsInterval)
|
||||
if (walletRefreshInterval) clearInterval(walletRefreshInterval)
|
||||
if (unsubscribeWs) { unsubscribeWs(); unsubscribeWs = null }
|
||||
if (wsWalletDebounce) clearTimeout(wsWalletDebounce)
|
||||
})
|
||||
|
||||
watch(() => loginTransition.pendingWelcomeTyping, (pending) => { if (pending) showWelcomeBlock.value = true })
|
||||
@@ -524,6 +528,15 @@ onMounted(async () => {
|
||||
// pending on-chain receive (or a fresh instant payment) only shows up
|
||||
// after a manual wallet action or a remount.
|
||||
walletRefreshInterval = setInterval(loadWeb5Status, 30000)
|
||||
// Real-time wallet push (2026-07-22): the backend streams LND transaction
|
||||
// events and nudges /ws/db the moment a tx hits the mempool. Any push =
|
||||
// something changed → refetch the wallet (debounced; the RPC is cheap).
|
||||
// This is what makes an incoming 0-conf tx appear in seconds instead of
|
||||
// waiting out the 30s poll above.
|
||||
unsubscribeWs = wsClient.subscribe(() => {
|
||||
if (wsWalletDebounce) clearTimeout(wsWalletDebounce)
|
||||
wsWalletDebounce = setTimeout(() => { void loadWeb5Status() }, 800)
|
||||
})
|
||||
})
|
||||
|
||||
// Wallet modals
|
||||
@@ -535,8 +548,10 @@ const walletConnected = ref(false); const walletOnchain = ref(0); const walletLi
|
||||
const walletArk = ref(0)
|
||||
const walletTransactions = ref<WalletTransaction[]>([])
|
||||
|
||||
// Overlay the explorer above the current page — never navigate away.
|
||||
function openInMempool(txHash: string) { useAppLauncherStore().openSession('mempool', { path: `/tx/${txHash}` }) }
|
||||
// Overlay the local Mempool app when it's running; otherwise route through
|
||||
// the external-explorer consent flow (pruned nodes can't run Mempool).
|
||||
const txExplorer = useTxExplorer()
|
||||
function openInMempool(txHash: string) { txExplorer.openTx(txHash) }
|
||||
|
||||
// wallet.ecash-history's shape (see handle_wallet_ecash_history in
|
||||
// api/rpc/wallet.rs) — distinct from the LND-shaped WalletTransaction used
|
||||
@@ -600,6 +615,8 @@ const systemUptimeDisplay = computed(() => { if (homeStatus.stats.uptimeSecs ===
|
||||
|
||||
let systemStatsInterval: ReturnType<typeof setInterval> | null = null
|
||||
let walletRefreshInterval: ReturnType<typeof setInterval> | null = null
|
||||
let unsubscribeWs: (() => void) | null = null
|
||||
let wsWalletDebounce: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
async function loadSystemStats() {
|
||||
await homeStatus.refresh(packages.value)
|
||||
|
||||
Reference in New Issue
Block a user