aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-07-06 09:20:19 -0700
committerAdam Soltys <[email protected]>2026-07-06 09:20:19 -0700
commit43c73ab0150d1959b3181be6f3737a67588f2b1e (patch)
tree45f3b4427fb57026f2fc256dfc52717fc4eba2a1 /src
parentdd748ba8f4cb2d45389e9996eefb59a5a0b1b9c9 (diff)
downloadcoinosv3-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')
-rw-r--r--src/features/ark.js42
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' },