aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-07-06 09:29:03 -0700
committerAdam Soltys <[email protected]>2026-07-06 09:29:03 -0700
commitda883adfab30f0fb6d992b73514d8ea850d860df (patch)
treeeef449357b65e04bcc06ca3d5b025302d643f1ff
parent43c73ab0150d1959b3181be6f3737a67588f2b1e (diff)
downloadcoinosv3-da883adfab30f0fb6d992b73514d8ea850d860df.tar.gz
coinosv3-da883adfab30f0fb6d992b73514d8ea850d860df.zip
Gift claim into an existing wallet is one step
Choosing a wallet used to just load it and show a second screen with a Claim button. Now tapping a wallet claims immediately (the pre-claim check auto-fires doClaim), and the claim fee — network fee estimate, or 'instant, no fee' for ark gifts — shows on the choose screen so it's still visible before you pick. Fresh-wallet claims keep the welcome+backup flow. Co-Authored-By: Claude Fable 5 <[email protected]>
-rw-r--r--src/features/gifts.js31
1 files changed, 26 insertions, 5 deletions
diff --git a/src/features/gifts.js b/src/features/gifts.js
index ff2ab5a..27fea33 100644
--- a/src/features/gifts.js
+++ b/src/features/gifts.js
@@ -70,6 +70,7 @@ export function giftsFeature(ctx) {
function claimGift(code) {
ui.claimLocked = null;
ui.claimArkAmount = null;
+ ui.claimAuto = false;
// An ark gift can only land in a wallet on ITS network — other wallets
// aren't valid claim targets. (On-chain gift codes don't carry a network.)
const all = claimTargets();
@@ -89,9 +90,20 @@ export function giftsFeature(ctx) {
// the seed-backup step.
function claimIntoAccount(acc, code) {
ui.claimChoose = null;
+ ui.claimAuto = true; // one-step: claim as soon as the gift verifies unspent
activateAccount(acc, { gift: code, existingClaim: true, fresh: true });
}
+ // The claim fee for a gift, and a display note ('' when free / ark).
+ function claimFeeNote(code) {
+ if (arkGiftOf(code)) return t('claimArkFree');
+ const pv = previewGift(code);
+ if (!pv) return '';
+ const rate = Math.max(1, Math.round((wallet.feeRates && wallet.feeRates.halfHourFee) || 5));
+ const fee = Math.ceil((11 + 68 * pv.inputs + 31 * 2) * rate);
+ return t('claimFeeNote', { n: fmtAmount(fee) + ' ' + unitLabel() });
+ }
+
// Pick where to receive a gift: an existing wallet, or a brand-new one.
function claimChooseView() {
const code = ui.claimChoose.code;
@@ -105,6 +117,7 @@ export function giftsFeature(ctx) {
h('div', { class: 'check-badge', style: 'background:var(--accent)' }, '🎁'),
h('h2', { style: 'margin:0' }, t('giftWelcome')),
pv ? h('div', { class: 'amt', style: 'font-size:30px' }, h('span', { class: 'amount-pos' }, fmtAmount(pv.room)), ' ', unitTag('unit')) : null,
+ h('div', { class: 'small faint' }, claimFeeNote(code)),
h('p', { class: 'muted', style: 'margin:0' }, t('claimIntoPrompt'))
),
h('div', { class: 'card col gap6' },
@@ -264,6 +277,15 @@ export function giftsFeature(ctx) {
// Has this gift already been claimed? Its funding coin being spent — even by an
// unconfirmed claim — means the gift is gone; show the "already claimed" screen
// rather than let the user attempt a doomed double-spend.
+ // End of the pre-claim check: if this is a one-step claim into a chosen
+ // wallet, fire the claim now (unless the gift is taken / not visible /
+ // errored); otherwise just render the welcome + Claim button.
+ function finishGiftCheck() {
+ ui.claimChecking = false;
+ if (ui.claimAuto && !ui.claimTaken && !ui.claimError && !ui.claimNotVisible) doClaim();
+ else render();
+ }
+
async function checkGiftClaimed(code) {
if (arkGiftOf(code)) {
try {
@@ -275,12 +297,11 @@ export function giftsFeature(ctx) {
} catch {
// transient — leave claimable; the sweep itself is the final guard
}
- ui.claimChecking = false;
- render();
+ finishGiftCheck();
return;
}
const ops = giftOutpoints(code);
- if (!ops.length) { ui.claimChecking = false; render(); return; }
+ if (!ops.length) { finishGiftCheck(); return; }
try {
const res = await wallet.api.outspend(ops[0].txid, ops[0].vout);
if (res && res.spent) ui.claimTaken = { txid: res.txid || null };
@@ -295,8 +316,7 @@ export function giftsFeature(ctx) {
// Couldn't check (offline/transient) — leave it claimable; the broadcast in
// doClaim is the final guard (a double-spend is rejected by the network).
}
- ui.claimChecking = false;
- render();
+ finishGiftCheck();
}
// Broadcast the presigned gift to this fresh wallet's first receive address.
@@ -318,6 +338,7 @@ export function giftsFeature(ctx) {
}
async function doClaim() {
+ ui.claimAuto = false; // one-shot; a manual retry shouldn't re-auto-fire
if (wallet.offline) { ui.claimError = t('scanOffline'); render(); return; }
if (ui.claimChecking || ui.claimTaken) return; // not verified / already taken
ui.busy = true;