From 1e43db6e50e5e1dc7ccb8fb53e7ce68cc580967d Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sun, 28 Jun 2026 09:42:25 -0700 Subject: Fix gift creation silently failing on large (many-input) gifts; one Back button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - A many-input gift produces a URL too long for a QR, and qrSvg threw — uncaught, inside render() — so the click did nothing and showed no error. Wrap the gift QR in try/catch: render the QR when it fits, otherwise fall back to a 'too large for a QR — use the link' note. The link itself still works. - The split-offer card has its own Back; hide the gift sub-view's outer Back while the split offer is showing, so there aren't two stacked Back buttons. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/app.js | 12 +++++++++--- src/i18n.js | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/app.js b/src/app.js index 803a82b..f069830 100644 --- a/src/app.js +++ b/src/app.js @@ -1549,8 +1549,13 @@ function giftCard() { })(), // A bearer gift uppercases the URL for a smaller alphanumeric QR (the // base32 code is case-insensitive). A locked gift's payload is - // case-sensitive base64url, so it uses a plain byte-mode QR. - h('div', { html: ui.giftLocked ? qrSvg(giftUrl()) : qrSvg(giftUrl().toUpperCase(), { ec: 'L', mode: 'Alphanumeric' }) }), + // case-sensitive base64url, so it uses a plain byte-mode QR. A large + // (many-input) gift can exceed QR capacity — then fall back to the link. + (() => { + let svg = null; + try { svg = ui.giftLocked ? qrSvg(giftUrl()) : qrSvg(giftUrl().toUpperCase(), { ec: 'L', mode: 'Alphanumeric' }); } catch {} + return svg ? h('div', { html: svg }) : h('div', { class: 'small faint', style: 'text-align:center;padding:8px' }, t('giftQrTooLong')); + })(), h('div', { class: 'addr-box break', style: 'width:100%;font-size:12px' }, giftUrl()), h('div', { class: 'row gap6 wrap' }, copyBtn(giftUrl(), t('copyLink')), @@ -2533,7 +2538,8 @@ function giftView() { 'div', { class: 'col', style: 'gap:12px' }, giftCard(), - h('button', { class: 'btn-ghost btn-block', onClick: () => { ui.giftCode = null; ui.giftError = ''; ui.giftMax = false; ui.giftSplitOffer = null; ui.revokeId = null; goBack(() => { ui.giftMode = false; }); } }, t('back')) + // The split-offer card has its own Back; don't show a second one under it. + ui.giftSplitOffer ? null : h('button', { class: 'btn-ghost btn-block', onClick: () => { ui.giftCode = null; ui.giftError = ''; ui.giftMax = false; ui.giftSplitOffer = null; ui.revokeId = null; goBack(() => { ui.giftMode = false; }); } }, t('back')) ); } diff --git a/src/i18n.js b/src/i18n.js index 31cc3c9..66f866d 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -240,6 +240,7 @@ const STR = { giftDmSending: 'Sending the claim code…', giftDmFailed: 'Couldn’t send the nostr DM. Share this code with them privately:', giftCopyCode: 'Copy claim code', + giftQrTooLong: 'Too large for a QR code — share the link below instead.', claimWithExtension: 'Claim with nostr extension', orEnterCode: 'or paste the claim code', extWrongAccount: 'Your extension is signed in as a different nostr account than this gift is for.', -- cgit v1.2.3