aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-07-03 18:02:44 -0700
committerAdam Soltys <[email protected]>2026-07-03 18:02:44 -0700
commit565b34157a6895172459563c4f562b6d663a8677 (patch)
tree168d3783ec0d7569027940354e7748b4cacecb29
parentea2c4683e71da6c050430fd0ea122821902282e0 (diff)
downloadcoinosv3-565b34157a6895172459563c4f562b6d663a8677.tar.gz
coinosv3-565b34157a6895172459563c4f562b6d663a8677.zip
Ark: show Ark movements in the History tab
Receives, sends, and boards interleave with on-chain transactions in one time-sorted, paginated timeline, each tagged 'Ark'. Refreshes are internal churn and stay in the Settings activity list only. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
-rw-r--r--src/app.js41
-rw-r--r--src/i18n.js1
-rw-r--r--tools/ark/ui-test.js7
3 files changed, 43 insertions, 6 deletions
diff --git a/src/app.js b/src/app.js
index e4519e5..a87bb1f 100644
--- a/src/app.js
+++ b/src/app.js
@@ -3761,6 +3761,26 @@ function giftHistoryItem(g) {
);
}
+// An Ark movement (receive / send / board) as a history row. No txid to open —
+// these are off-chain; the Settings card shows the fuller activity detail.
+function arkHistoryItem(m) {
+ const incoming = m.type !== 'send';
+ const label = m.type === 'receive' ? t('received') : m.type === 'board' ? t('arkBoarded') : t('sent');
+ return h(
+ 'div',
+ { class: 'item' },
+ h('div', { class: `ico ${incoming ? 'in' : 'out'}` }, incoming ? '↓' : '↑'),
+ h('div', { class: 'grow' },
+ h('div', { class: 'row gap6' },
+ label,
+ h('span', { class: 'tag' }, 'Ark'),
+ m.status !== 'complete' ? h('span', { class: 'tag pending' }, m.status) : null),
+ h('div', { class: 'small faint' }, timeAgo(m.ts / 1000))),
+ h('div', { style: 'text-align:right' },
+ h('div', { class: incoming ? 'amount-pos' : 'amount-neg' }, (incoming ? '+' : '-') + fmtAmount(m.amountSat)))
+ );
+}
+
function txHistoryItem(tx) {
const incoming = tx.net >= 0;
const stuck = !tx.confirmed && wallet.isStuck(tx);
@@ -3837,26 +3857,35 @@ function historyTab() {
? [...wallet.outstandingGifts(), ...wallet.claimedGifts().map((c) => ({ ...c, claimed: true }))]
: [];
if (ui.giftsAll && gifts.length) return giftsAllView(gifts);
- if (!txs.length && !gifts.length)
+ // Ark receives/sends/boards interleave with on-chain txs by time. (Refreshes
+ // are internal churn — they stay in the Settings activity list only.)
+ const arkMoves = ark && ark.state
+ ? ark.movements().filter((m) => ['receive', 'send', 'board'].includes(m.type) && m.status === 'complete')
+ : [];
+ if (!txs.length && !gifts.length && !arkMoves.length)
return h('div', { class: 'card' }, h('p', { class: 'muted center', style: 'margin:0' }, t('noTxYet')));
// Show at most 3 gifts inline; the rest live behind "View all". Transactions
- // paginate 10 at a time.
+ // and Ark movements merge into one timeline, paginated 10 at a time.
const giftsHead = gifts.slice(0, 3);
- const txPages = Math.ceil(txs.length / PAGE_SIZE);
+ const entries = [
+ ...txs.map((tx) => ({ time: tx.confirmed ? (tx.blockTime || 0) * 1000 : Date.now(), render: () => txHistoryItem(tx) })),
+ ...arkMoves.map((m) => ({ time: m.ts, render: () => arkHistoryItem(m) })),
+ ].sort((a, b) => b.time - a.time);
+ const txPages = Math.ceil(entries.length / PAGE_SIZE);
const txPage = Math.min(ui.txPage, Math.max(0, txPages - 1));
- const txSlice = txs.slice(txPage * PAGE_SIZE, txPage * PAGE_SIZE + PAGE_SIZE);
+ const txSlice = entries.slice(txPage * PAGE_SIZE, txPage * PAGE_SIZE + PAGE_SIZE);
return h(
'div',
{ class: 'card' },
h('div', { class: 'list' },
...giftsHead.map(giftHistoryItem),
- ...txSlice.map(txHistoryItem)
+ ...txSlice.map((e) => e.render())
),
gifts.length > 3
? h('button', { class: 'btn-sm btn-block', style: 'margin-top:8px', onClick: () => { ui.giftsAll = true; ui.giftsPage = 0; ui.revokeId = null; render(); } }, t('viewAllGifts', { n: gifts.length }))
: null,
- pager(txPage, txs.length, (p) => { ui.txPage = p; render(); }),
+ pager(txPage, entries.length, (p) => { ui.txPage = p; render(); }),
wallet.historyLoading
? h(
'div',
diff --git a/src/i18n.js b/src/i18n.js
index 32af229..88e97a8 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -371,6 +371,7 @@ const STR = {
arkNoFee: 'none — instant off-chain send',
arkSendBtn: 'Send',
arkSentTitle: 'Sent!',
+ arkBoarded: 'Boarded to Ark',
lnReceiveIntro: 'Receive over Lightning — paid into this wallet on-chain, non-custodial.',
lnReceiveAwaiting: 'Share this invoice. When it’s paid, the sats land here automatically.',
lnReceiveWatching: 'Waiting for payment…',
diff --git a/tools/ark/ui-test.js b/tools/ark/ui-test.js
index 81dc3ae..006c7eb 100644
--- a/tools/ark/ui-test.js
+++ b/tools/ark/ui-test.js
@@ -129,6 +129,13 @@ try {
check('sent view', await waitText('Sent!', 15000));
check('alice +7000', aliceBalance() === before + 7000, String(aliceBalance() - before));
+ // history shows both ark movements
+ await clickText('.tabs button', 'History');
+ await sleep(400);
+ const hist = await bodyText();
+ check('history shows ark receive', /received\s*\n?\s*ark/i.test(hist));
+ check('history shows ark send', /sent\s*\n?\s*ark/i.test(hist));
+
// --- board from settings ---
console.log('\n[4] board');
await clickText('.tabs button', 'Receive');