aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-15 21:56:18 -0700
committerAdam Soltys <[email protected]>2026-09-15 21:56:18 -0700
commitb58e3dc1eb2a578f1cae441d8bba9503bec94567 (patch)
tree0302793781a502739f8cabd1e534eac31445e981 /src
parentf23f4a36ad2856dea1493d884cb033be08bee9ad (diff)
downloadcoinosv3-b58e3dc1eb2a578f1cae441d8bba9503bec94567.tar.gz
coinosv3-b58e3dc1eb2a578f1cae441d8bba9503bec94567.zip
Gift detail links the coin behind the gift, and the claim once claimed.
An unclaimed on-chain gift has no transaction of its own yet — its funding transaction is presigned and only broadcast when someone claims. The coin it's locked against is on-chain from the start, so the detail page now links that outpoint's transaction on the block explorer, and the claim transaction once it exists. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
Diffstat (limited to 'src')
-rw-r--r--src/features/gifts.js19
-rw-r--r--src/i18n.js2
2 files changed, 19 insertions, 2 deletions
diff --git a/src/features/gifts.js b/src/features/gifts.js
index 111303e..20b192c 100644
--- a/src/features/gifts.js
+++ b/src/features/gifts.js
@@ -808,6 +808,15 @@ export function giftsFeature(ctx) {
const rec = g.claimed ? null : wallet.giftLink(g.id);
const line = (k, v) => h('div', { class: 'line' }, h('span', { class: 'k' }, k), h('span', { class: 'v' }, v));
const back = () => { ui.revokeId = null; ctx.goBack(() => { ui.giftDetail = null; }); };
+ const outs = g.outpoints || (wallet.loaded && (wallet.giftRecords()[g.id] || {}).outpoints) || [];
+ const coinTxid = outs.length ? String(outs[0]).split(':')[0] : null;
+ const txLink = (txid) => {
+ const url = wallet.api && wallet.api.explorerTx ? wallet.api.explorerTx(txid) : null;
+ const label = txid.slice(0, 8) + '…' + txid.slice(-6) + ' ↗';
+ return url
+ ? h('a', { href: url, target: '_blank', rel: 'noopener', onClick: (e) => { e.preventDefault(); openExternal(url); } }, label)
+ : label;
+ };
return h(
'div',
{ class: 'card col' },
@@ -819,10 +828,16 @@ export function giftsFeature(ctx) {
: null,
// only render the summary box when it has content (an empty gray box
// otherwise appears for reclaimed gifts with no recorded date)
- created || (!g.claimed && g.reserved)
+ // The coin the gift is locked against is on-chain from the start —
+ // the gift's own funding transaction is presigned and only broadcast
+ // when someone claims, so until then the coin IS what there is to
+ // look at. Once claimed, the claim transaction joins it.
+ created || (!g.claimed && g.reserved) || coinTxid || g.claimTxid
? h('div', { class: 'summary col', style: 'gap:0' },
created ? line(t('dateLabel'), new Date(created).toLocaleString()) : null,
- !g.claimed && g.reserved ? line(t('status'), t('lockedInGifts')) : null)
+ !g.claimed && g.reserved ? line(t('status'), t('lockedInGifts')) : null,
+ coinTxid ? line(t('giftCoinLabel'), txLink(coinTxid)) : null,
+ g.claimTxid ? line(t('giftClaimTxLabel'), txLink(g.claimTxid)) : null)
: null,
// Reclaim/Revoke up front — no confirm step. A reserved gift offers both;
// an already-reclaimed one only Revoke (its coin is spendable again, only
diff --git a/src/i18n.js b/src/i18n.js
index 09ccb30..d15cd9a 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -550,6 +550,8 @@ const STR = {
newWalletBody: 'Back up your recovery phrase to keep it safe.',
giftHistoryTitle: 'Gift link',
giftUnclaimedTag: 'unclaimed',
+ giftCoinLabel: 'Coin',
+ giftClaimTxLabel: 'Claim',
giftClaimedTag: 'claimed',
giftView: 'Gift link',
giftCodeLabel: 'Claim code (sent via DM)',