aboutsummaryrefslogtreecommitdiffstats
path: root/src/app.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-06-24 14:01:39 -0700
committerAdam Soltys <[email protected]>2026-06-24 14:01:39 -0700
commit1a92d81b792f9e6d2ecb284aab979985a36d34c7 (patch)
treee5b358bcad0ba648363f373f9aa87e4c2f65045a /src/app.js
parentb78b9a1c7e4e42b7f673b0e3f92424a762bd0986 (diff)
downloadcoinosv3-1a92d81b792f9e6d2ecb284aab979985a36d34c7.tar.gz
coinosv3-1a92d81b792f9e6d2ecb284aab979985a36d34c7.zip
Keep reclaimed gifts revocable until the coin is spent
Passive reclaim previously dropped the coin from tracking, so there was no way to revoke afterward. Now reclaim moves the coin from "reserved" to a "reclaimed" set: it becomes spendable, but its still-live link stays listed (tagged "still claimable") with a Revoke option, until the coin is actually spent (by a payment or by revoking), at which point it drops off. revokeGift and outstandingGifts handle both sets; existing reserved gifts are unaffected. Strings updated across all 19 locales. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'src/app.js')
-rw-r--r--src/app.js18
1 files changed, 9 insertions, 9 deletions
diff --git a/src/app.js b/src/app.js
index 0ef0787..07fe347 100644
--- a/src/app.js
+++ b/src/app.js
@@ -722,7 +722,7 @@ function createGiftLink() {
render();
}
function giftCard() {
- const active = wallet.activeGifts();
+ const active = wallet.outstandingGifts();
return h(
'div',
{ class: 'card col' },
@@ -750,24 +750,24 @@ function giftCard() {
active.length
? h('div', { class: 'col gap6', style: 'margin-top:4px' },
h('span', { class: 'small muted' }, t('giftReserved', { n: active.length })),
- ...active.map((id) => {
- const u = wallet.utxos.find((x) => utxoId(x) === id);
- const label = u ? fmtAmount(u.value) + ' ' + unitLabel() : id.slice(0, 12) + '…';
- if (ui.revokeId === id) {
+ ...active.map((g) => {
+ const amt = g.value != null ? fmtAmount(g.value) + ' ' + unitLabel() : g.id.slice(0, 12) + '…';
+ const label = amt + (g.reserved ? '' : ' · ' + t('giftReclaimedTag'));
+ if (ui.revokeId === g.id) {
return h('div', { class: 'col gap6' },
- h('span', { class: 'small muted' }, t('giftReclaimPrompt')),
+ h('span', { class: 'small muted' }, g.reserved ? t('giftReclaimPrompt') : t('giftRevokeConfirm')),
ui.busy
? h('button', { class: 'btn-primary btn-block', disabled: true }, h('span', { class: 'spinner' }))
: h('div', { class: 'row gap6' },
- h('button', { class: 'btn-ghost grow', onClick: () => doReclaim(id) }, t('giftReclaim')),
- h('button', { class: 'btn-primary grow', onClick: () => doRevoke(id) }, t('giftRevoke'))
+ g.reserved ? h('button', { class: 'btn-ghost grow', onClick: () => doReclaim(g.id) }, t('giftReclaim')) : null,
+ h('button', { class: 'btn-primary grow', onClick: () => doRevoke(g.id) }, t('giftRevoke'))
),
ui.busy ? null : h('button', { class: 'btn-ghost btn-block', onClick: () => { ui.revokeId = null; render(); } }, t('back'))
);
}
return h('div', { class: 'row between' },
h('span', { class: 'small mono' }, label),
- h('button', { class: 'btn-sm', onClick: () => { ui.revokeId = id; render(); } }, t('giftReclaim'))
+ h('button', { class: 'btn-sm', onClick: () => { ui.revokeId = g.id; render(); } }, g.reserved ? t('giftReclaim') : t('giftRevoke'))
);
})
)