aboutsummaryrefslogtreecommitdiffstats
path: root/src/features
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-21 14:38:53 -0700
committerAdam Soltys <[email protected]>2026-09-21 14:38:53 -0700
commit5603cc9c0d75690678cdb182d5c3e41962c1180b (patch)
treebf1e07c505cdc80fec2c7a993f3ffb2c1ec5b174 /src/features
parentf9bd28e2a03aa0874ea0416dfac9293bec4c4456 (diff)
downloadcoinosv3-5603cc9c0d75690678cdb182d5c3e41962c1180b.tar.gz
coinosv3-5603cc9c0d75690678cdb182d5c3e41962c1180b.zip
Spending coins: "Unilateral exit", with an Exit now button behind a confirmation.
The confirmation shows the coin count, the amount, the estimated mining fees (paid from Savings as the exit unrolls) and what Savings holds for them, refuses when Savings can't carry the fees, and says the funds only land once the timelocks run out and that an exit can't be stopped. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
Diffstat (limited to 'src/features')
-rw-r--r--src/features/ark.js30
1 files changed, 29 insertions, 1 deletions
diff --git a/src/features/ark.js b/src/features/ark.js
index 8df45fe..96abcb1 100644
--- a/src/features/ark.js
+++ b/src/features/ark.js
@@ -2128,6 +2128,33 @@ export function arkFeature(ctx) {
const tick = (checked, onChange, title) => h('input', {
type: 'checkbox', checked, title, style: 'flex:0 0 auto;margin:0', onChange,
});
+ // "Exit now" asks once, with the numbers on the table, before anything
+ // is published: an exit cannot be called back.
+ if (ui.arkExitConfirm) {
+ const savings = wallet.spendable || 0;
+ const short = savings < exitFee;
+ const row = (k, v, cls = '') => h('div', { class: 'row between' }, h('span', { class: 'small muted' }, k), h('span', { class: 'small' + (cls ? ' ' + cls : '') }, v));
+ return h('div', { class: 'col', style: 'gap:16px' },
+ h('div', { class: 'card col', style: 'gap:10px' },
+ h('h3', { style: 'margin:0' }, t('arkExitConfirmTitle')),
+ h('p', { class: 'small muted', style: 'margin:0' }, t('arkExitConfirmBody')),
+ h('div', { class: 'col', style: 'gap:4px' },
+ row(t('arkExitConfirmCoins'), String(spend.length)),
+ row(t('arkExitConfirmAmount'), fmtSats(totalSat) + ' sats'),
+ row(t('arkExitConfirmFee'), fmtSats(exitFee) + ' sats'),
+ row(t('arkExitConfirmSavings'), fmtSats(savings) + ' sats', short ? 'err' : '')),
+ short ? h('div', { class: 'notice err small' }, t('arkExitFeeShort', { need: fmtSats(exitFee), have: fmtSats(savings) })) : null,
+ h('p', { class: 'small muted', style: 'margin:0' }, t('arkExitConfirmWait')),
+ h('button', { class: 'btn-primary btn-block', disabled: !!ui.arkBusy || short || !spend.length, onClick: () => {
+ ui.arkExitConfirm = null;
+ doArkExit();
+ ui.arkCoinsSel = null;
+ back();
+ ui.tab = 'history'; // the exit lives as a pending record there
+ render();
+ } }, ui.arkBusy === 'exit' ? h('span', { class: 'spinner sm' }) : t('arkExitConfirmGo'))),
+ h('button', { class: 'btn-ghost btn-block', onClick: () => { ui.arkExitConfirm = null; render(); } }, t('cancel')));
+ }
return h('div', { class: 'col', style: 'gap:16px' },
h('div', { class: 'card col', style: 'gap:8px' },
h('h3', { style: 'margin:0' }, t('arkCoinsTitle')),
@@ -2175,7 +2202,8 @@ export function arkFeature(ctx) {
h('div', { class: 'card col', style: 'gap:8px' },
h('h4', { style: 'margin:0' }, t('arkCoinsExitTitle')),
h('p', { class: 'small muted', style: 'margin:0' },
- t('arkCoinsExitDesc', { fee: fmtSats(exitFee), after: fmtSats(afterFee) }))),
+ t('arkCoinsExitDesc', { fee: fmtSats(exitFee), after: fmtSats(afterFee) })),
+ h('button', { class: 'btn-ghost btn-block', disabled: !!ui.arkBusy || !spend.length, onClick: () => { ui.arkExitConfirm = true; render(); } }, t('arkCoinsExitBtn'))),
h('div', { class: 'card col', style: 'gap:8px' },
h('h4', { style: 'margin:0' }, t('arkCoinsRenewTitle')),
// the round cadence comes from the server (it has been an hour and