aboutsummaryrefslogtreecommitdiffstats
path: root/src/wallet.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-06-28 14:25:09 -0700
committerAdam Soltys <[email protected]>2026-06-28 14:25:09 -0700
commita267f7f37fd178ebb58871188067b571b18a67f1 (patch)
tree1b31c134c568fa04f0305f1e604d693310217d72 /src/wallet.js
parent06376a71d4b9a4f579095933d91fe0a369b5e0fe (diff)
downloadcoinosv3-a267f7f37fd178ebb58871188067b571b18a67f1.tar.gz
coinosv3-a267f7f37fd178ebb58871188067b571b18a67f1.zip
Gifts: re-view link/QR for unclaimed gifts + mark claimed ones in history
Persist a per-account gift record (shareable link/code + amount, plus a locked gift's claim code) when a gift is created, so: - Unclaimed gifts in History get a 'Gift link' button that re-opens the QR + link (and the claim code for locked gifts), to re-share without recreating the gift. - Once a gift's coin is spent and we didn't revoke it, it shows in History as 'claimed' instead of vanishing. Revoking marks the record so a reclaim isn't mislabelled as claimed. Verified on regtest: create → outstanding=1, link stored; after the recipient claims → outstanding=0, claimed=1 with the right amount. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'src/wallet.js')
-rw-r--r--src/wallet.js26
1 files changed, 26 insertions, 0 deletions
diff --git a/src/wallet.js b/src/wallet.js
index 6e3ea81..4a8ddc4 100644
--- a/src/wallet.js
+++ b/src/wallet.js
@@ -1549,6 +1549,32 @@ export class Wallet {
}));
}
+ // Persistent gift records (per account): the shareable link/code + amount, so a
+ // gift's link/QR can be re-viewed later and a claimed one identified in history.
+ _giftRecordsKey() { return this._cacheKey() + ':giftrec'; }
+ giftRecords() {
+ if (!this._giftRecords) { try { this._giftRecords = JSON.parse(localStorage.getItem(this._giftRecordsKey()) || '{}'); } catch { this._giftRecords = {}; } }
+ return this._giftRecords;
+ }
+ _saveGiftRecords() { try { localStorage.setItem(this._giftRecordsKey(), JSON.stringify(this.giftRecords())); } catch {} }
+ recordGift({ code, locked, amount, claimCode, outpoints }) {
+ if (!outpoints || !outpoints.length) return;
+ this.giftRecords()[outpoints[0]] = { id: outpoints[0], code, locked: !!locked, amount, claimCode: claimCode || null, outpoints, created: Date.now(), revoked: false };
+ this._saveGiftRecords();
+ }
+ giftLink(id) { return this.giftRecords()[id] || null; }
+ markGiftRevoked(id) { const r = this.giftRecords()[id]; if (r) { r.revoked = true; this._saveGiftRecords(); } }
+ // Gifts whose coin has been spent and weren't revoked by us → claimed. (Only
+ // once scanned, so an un-synced wallet doesn't mislabel everything as claimed.)
+ claimedGifts() {
+ if (!this.loaded) return [];
+ const live = new Set(this.utxos.map((u) => utxoId(u)));
+ return Object.values(this.giftRecords())
+ .filter((r) => !r.revoked && r.outpoints && !r.outpoints.some((o) => live.has(o)))
+ .map((r) => ({ id: r.id, amount: r.amount, locked: r.locked, created: r.created }))
+ .sort((a, b) => (b.created || 0) - (a.created || 0));
+ }
+
// Best-fit coin selection for a gift of `gift` sats (needs gift + a dust
// change): the smallest single confirmed coin that covers it — one input and
// the least value locked until claim — else the fewest large coins. Returns