aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-07-03 21:11:30 -0700
committerAdam Soltys <[email protected]>2026-07-03 21:11:30 -0700
commit97ccbbca7a2e914e69e163ca05d1d0427d31596a (patch)
tree598171a5ad7fc3aa90820fd04965058a76f2f4a9
parentc8d384ebfe760682fe9ba3bcdcc31276df352929 (diff)
downloadcoinosv3-97ccbbca7a2e914e69e163ca05d1d0427d31596a.tar.gz
coinosv3-97ccbbca7a2e914e69e163ca05d1d0427d31596a.zip
Ark: boarding lives on the Receive tab's Ark view
Selecting Ark on Receive now shows the address plus an inline "move on-chain funds into Ark" form (with available balance / minimum hints). The Settings card slims down to server choice, coin count, in-progress operations, and refresh/consolidate; the balance already shows on the balance card. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
-rw-r--r--src/app.js33
-rw-r--r--src/i18n.js3
-rw-r--r--tools/ark/ui-test.js13
3 files changed, 32 insertions, 17 deletions
diff --git a/src/app.js b/src/app.js
index 9bddb8a..75c29b4 100644
--- a/src/app.js
+++ b/src/app.js
@@ -1889,7 +1889,6 @@ function arkCard() {
: h('button', { class: 'btn-ghost btn-block', onClick: () => connectArk().catch(() => {}) }, t('arkConnectBtn')));
}
- const bal = ark.balance();
const spendables = ark.vtxos().filter((v) => v.state === 'spendable');
const pendingActions = ark.pendingActions();
const row = (k, v) => h('div', { class: 'row between' }, h('span', { class: 'small muted' }, k), h('span', { class: 'small' }, v));
@@ -1898,22 +1897,10 @@ function arkCard() {
'div',
{ class: 'card col', style: 'gap:10px' },
...head,
- row(t('arkBalance'), fmtAmount(bal.spendableSat) + ' ' + unitLabel() + (bal.pendingSat ? ` (+${fmtAmount(bal.pendingSat)} ${t('pending').toLowerCase()})` : '')),
spendables.length ? row(t('arkVtxos'), String(spendables.length)) : null,
pendingActions.length
? h('div', { class: 'small muted' }, t('arkPendingActions', { n: pendingActions.length }) + ' — ' + pendingActions.map((a) => `${a.type}:${a.step}`).join(', '))
: null,
-
- // Board: move on-chain sats into the Ark (one on-chain tx, then confirms).
- h('div', { class: 'input-group' },
- h('input', {
- type: 'number', inputmode: 'numeric', min: '0',
- placeholder: t('arkBoardPlaceholder', { n: (ark.info.minBoardAmountSat || 0).toLocaleString() }),
- value: ui.arkBoardAmt || '',
- onInput: (e) => { ui.arkBoardAmt = e.target.value; },
- }),
- h('button', { class: 'btn-sm', disabled: !!ui.arkBusy, onClick: doArkBoard }, ui.arkBusy === 'board' ? h('span', { class: 'spinner sm' }) : t('arkBoardBtn'))),
-
spendables.length >= 1
? h('button', { class: 'btn-ghost btn-block', disabled: !!ui.arkBusy, onClick: doArkRefresh },
ui.arkBusy === 'refresh' ? h('span', { class: 'spinner sm' }) : t('arkRefreshBtn', { n: spendables.length }))
@@ -2884,6 +2871,8 @@ function receiveTab() {
);
}
const arkAddr = ark.address();
+ const minBoard = ark.info.minBoardAmountSat || 0;
+ const canBoard = wallet.spendable >= minBoard;
return h(
'div',
{ class: 'card col', style: 'align-items:center;gap:14px' },
@@ -2891,7 +2880,23 @@ function receiveTab() {
h('p', { class: 'small muted', style: 'margin:0;text-align:center' }, t('arkReceiveIntro')),
h('div', { html: qrSvg(arkAddr) }),
h('div', { class: 'addr-box break', style: 'width:100%;font-size:11px' }, arkAddr),
- copyBtn(arkAddr, t('copyAddress'))
+ copyBtn(arkAddr, t('copyAddress')),
+ // Board: fund your Ark balance from this wallet's on-chain coins.
+ h('div', { class: 'col', style: 'width:100%;gap:8px;border-top:1px solid var(--border,rgba(128,128,128,.2));padding-top:14px' },
+ h('div', { class: 'small muted', style: 'text-align:center' }, t('arkBoardIntro')),
+ h('div', { class: 'input-group' },
+ h('input', {
+ type: 'number', inputmode: 'numeric', min: '0',
+ placeholder: t('arkBoardPlaceholder', { n: minBoard.toLocaleString() }),
+ value: ui.arkBoardAmt || '',
+ onInput: (e) => { ui.arkBoardAmt = e.target.value; },
+ }),
+ h('button', { class: 'btn-sm', disabled: !!ui.arkBusy || !canBoard, onClick: doArkBoard },
+ ui.arkBusy === 'board' ? h('span', { class: 'spinner sm' }) : t('arkBoardBtn'))),
+ canBoard
+ ? h('div', { class: 'small faint', style: 'text-align:center' }, t('arkBoardAvailable', { n: fmtAmount(wallet.spendable) + ' ' + unitLabel() }))
+ : h('div', { class: 'small faint', style: 'text-align:center' }, t('arkBoardNoFunds', { n: minBoard.toLocaleString() })),
+ ui.arkError ? h('div', { class: 'notice err' }, ui.arkError) : null)
);
}
const addr = mode === 'sp' ? spAddr : fresh.address;
diff --git a/src/i18n.js b/src/i18n.js
index c9417f3..43a32d9 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -364,6 +364,9 @@ const STR = {
arkBoardPlaceholder: 'Board amount (min {n} sats)',
arkBoardBtn: 'Board',
arkBoardStarted: 'Boarding — spendable in the Ark after 1 confirmation.',
+ arkBoardIntro: 'Or move this wallet\u2019s on-chain funds into Ark:',
+ arkBoardAvailable: '{n} available on-chain',
+ arkBoardNoFunds: 'Needs at least {n} sats of on-chain balance.',
arkRefreshBtn: 'Refresh / consolidate {n} coin(s)',
arkRefreshStarted: 'Refresh submitted — completes with the next round.',
arkSendTitle: 'Send over Ark',
diff --git a/tools/ark/ui-test.js b/tools/ark/ui-test.js
index 1d36b06..48f2f83 100644
--- a/tools/ark/ui-test.js
+++ b/tools/ark/ui-test.js
@@ -146,12 +146,17 @@ try {
sh(`${BCLI} sendtoaddress ${onchainAddr} 0.001`);
mine(1);
check('onchain funds arrive', await waitText('100,000', 30000));
-
- await clickText('.tabs button', 'Settings');
- await sleep(300);
+ // the on-chain receive celebration takes over the tab — tap through it
+ if ((await bodyText()).toLowerCase().includes('payment received')) {
+ await clickText('.card', 'Payment received');
+ await sleep(400);
+ }
+ await page.select('select', 'ark');
+ await sleep(400);
const preBoard = await labeledNumber('ark balance');
const boardInput = await page.evaluateHandle(() =>
[...document.querySelectorAll('input')].find((i) => (i.placeholder || '').toLowerCase().startsWith('board amount')));
+ check('board form on receive/ark', !!(await boardInput.asElement()));
await boardInput.asElement().type('30000');
await clickText('button', 'Board');
check('board started', await waitText('Boarding', 20000));
@@ -161,6 +166,8 @@ try {
// --- refresh / consolidate ---
console.log('\n[5] refresh');
+ await clickText('.tabs button', 'Settings');
+ await sleep(300);
await clickText('button', 'Refresh / consolidate');
check('refresh submitted', await waitText('Refresh submitted', 10000));
const done = await (async () => {