diff options
| author | Adam Soltys <[email protected]> | 2026-07-06 09:20:19 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-07-06 09:20:19 -0700 |
| commit | 43c73ab0150d1959b3181be6f3737a67588f2b1e (patch) | |
| tree | 45f3b4427fb57026f2fc256dfc52717fc4eba2a1 /src/features | |
| parent | dd748ba8f4cb2d45389e9996eefb59a5a0b1b9c9 (diff) | |
| download | coinosv3-43c73ab0150d1959b3181be6f3737a67588f2b1e.tar.gz coinosv3-43c73ab0150d1959b3181be6f3737a67588f2b1e.zip | |
No ark jitter on refresh: render from persisted state before connect
The balance line and history-row icons read the LIVE ark manager,
which is null until connectArk() finishes its async handshake — so on
refresh the ark balance briefly vanished and ark rows flashed the
Bitcoin icon before flipping to the ark mark. Read-only hooks
(balance, decorateTxRow, historyEntries, detail views) now fall back
to the persisted ark state from storage via arkStateNow(), so they
paint correct on first render; the manager connecting later just keeps
it fresh with identical data (no visible change). Verified: balance
present and board row shows the ark icon from the first 50ms sample
after reload.
Co-Authored-By: Claude Fable 5 <[email protected]>
Diffstat (limited to 'src/features')
| -rw-r--r-- | src/features/ark.js | 42 |
1 files changed, 31 insertions, 11 deletions
diff --git a/src/features/ark.js b/src/features/ark.js index 92b248a..29a55eb 100644 --- a/src/features/ark.js +++ b/src/features/ark.js @@ -198,9 +198,24 @@ export function arkFeature(ctx) { return arkConnectPromise; } + // The current ark state for READ-ONLY rendering: the live manager if it's + // connected, else the persisted state straight from storage. Lets the + // balance and history icons paint immediately on refresh instead of + // flickering (empty → populated) while the manager connects asynchronously. + function arkStateNow() { + if (ark && ark.state) return ark.state; + if (!arkAvailable()) return null; + return wallet.loadArkState(); + } + function arkBalance() { - if (!ark || !ark.state) return null; - return ark.balance(); + const s = arkStateNow(); + if (!s) return null; + const sum = (st) => (s.vtxos || []).filter((v) => v.state === st).reduce((n, v) => n + v.amountSat, 0); + const boardingSat = (s.actions || []) + .filter((a) => a.type === 'board' && a.fundingTxid && !['done', 'failed'].includes(a.step)) + .reduce((n, a) => n + (a.amountSat - a.feeSat), 0); + return { spendableSat: sum('spendable'), pendingSat: sum('pending'), boardingSat }; } // An ark address in the send form signals a send is coming: verify our @@ -1026,14 +1041,16 @@ export function arkFeature(ctx) { return true; }, historyEntries() { - if (!ark || !ark.state) return []; - return ark.movements() + const s = arkStateNow(); + if (!s) return []; + return (s.movements || []) .filter((m) => ['receive', 'send', 'board', 'offboard', 'exit'].includes(m.type) && m.status === 'complete') .map((m) => ({ time: m.ts, render: () => arkHistoryItem(m) })); }, historyDetail() { if (!ui.arkMoveDetail) return null; - const m = ark && ark.state ? ark.movements().find((x) => x.id === ui.arkMoveDetail) : null; + const s = arkStateNow(); + const m = s ? (s.movements || []).find((x) => x.id === ui.arkMoveDetail) : null; if (m) return arkMoveDetailView(m); ui.arkMoveDetail = null; return null; @@ -1049,19 +1066,22 @@ export function arkFeature(ctx) { return lines; }, decorateTxRow(tx) { - if (!ark || !ark.state) return null; + const s = arkStateNow(); + if (!s) return null; + const acts = s.actions || []; if (tx.net < 0) { - const a = ark.state.actions.find((x) => x.type === 'board' && x.fundingTxid === tx.txid); + const a = acts.find((x) => x.type === 'board' && x.fundingTxid === tx.txid); return a ? { icon: h('span', { html: ARK_MARK(16) }), label: h('span', {}, t('arkBoardHistory')) } : null; } - const o = ark.state.actions.find((x) => x.type === 'offboard' && x.txid === tx.txid); + const o = acts.find((x) => x.type === 'offboard' && x.txid === tx.txid); if (o) return { icon: h('span', { html: ARK_MARK(16) }), label: h('span', {}, t('arkOffboardHistory')) }; - const e = ark.state.actions.find((x) => x.type === 'exit' && x.claimTxid === tx.txid); + const e = acts.find((x) => x.type === 'exit' && x.claimTxid === tx.txid); return e ? { icon: h('span', { html: ARK_MARK(16) }), label: h('span', {}, t('arkExitHistory')) } : null; }, txDetailSection(tx) { - if (!ark || !ark.state) return null; - const a = ark.state.actions.find((x) => x.type === 'board' && x.fundingTxid === tx.txid); + const s = arkStateNow(); + if (!s) return null; + const a = (s.actions || []).find((x) => x.type === 'board' && x.fundingTxid === tx.txid); if (!a) return null; const done = a.step === 'done'; return h('div', { class: 'summary col', style: 'gap:0' }, |
