aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.js42
-rw-r--r--src/style.css3
2 files changed, 28 insertions, 17 deletions
diff --git a/src/app.js b/src/app.js
index ec7ef53..69425be 100644
--- a/src/app.js
+++ b/src/app.js
@@ -4944,6 +4944,12 @@ function sendTab() {
);
}
if (ui.sendResult) return sendResultView();
+ // an NFC/intent payment waiting for its feature: the tap flow's paying shell
+ if (ui.intentWait) {
+ return h('div', { class: 'card col autopay-card', style: 'align-items:center;text-align:center;gap:14px;padding:48px 20px' },
+ h('div', { class: 'check-badge autopay-badge' }, '\u26a1'),
+ h('div', { class: 'small muted autopay-foot' }, ''));
+ }
if (ui.broadcastTx) { sendFocused = true; return broadcastConfirmView(); }
const featView = featureHook('sendView');
if (featView) { sendFocused = true; return featView; }
@@ -6236,6 +6242,7 @@ document.addEventListener('visibilitychange', () => {
// boot can put that well after load. handleScanned routes it exactly like a
// scanned QR: bolt11s and lnurls through the feature chain, bitcoin: through
// BIP-21 (nostr profile intents already rerouted onto the /npub deep link).
+const LN_INTENT = /^(lightning:|lnurl|lnbc|lntb|lntbs|lnbcrt|lno1|noffer1)|^[^\s:@/]+@[^\s@/]+\.[a-z]{2,}$/i;
if (INTENT_URI) {
const started = Date.now();
const tick = setInterval(() => {
@@ -6243,25 +6250,26 @@ if (INTENT_URI) {
if (!activeAccount() || !wallet.loaded || ui.screen !== 'wallet') return;
clearInterval(tick);
ui.tab = 'send';
+ // A lightning:lnurl… (or ln-address, bolt11, offer) intent — an NFC tap
+ // at a till — often lands BEFORE the feature that pays it is ready (ark
+ // connects lazily, so zaps' matchSendText declines). Hold it on the same
+ // paying shell the tap flow shows, never the raw URI in the recipient
+ // field, and hand it over the moment a feature claims it; only after a
+ // long wait does it fall back to the form like a scan.
+ if (featureMatchSend(INTENT_URI)) return render();
+ if (!LN_INTENT.test(INTENT_URI)) { render(); return handleScanned(INTENT_URI); }
+ ui.intentWait = true;
render();
- handleScanned(INTENT_URI);
- // A lightning:lnurl… (or ln-address) intent often lands BEFORE the
- // feature that pays it is ready — ark connects lazily, so zaps'
- // matchSendText declines and the raw URI falls through into the
- // recipient field. Keep re-offering the text to the feature chain for a
- // while; when a feature finally claims it (the zap amount screen takes
- // over), sweep the leftover raw text out of the recipient.
- let tries = 0;
+ const waitStart = Date.now();
const retry = setInterval(() => {
- const s = ui.send;
- const pending = s && s.recipients.length === 1 && s.recipients[0].address === INTENT_URI;
- if (!pending || ++tries > 20) return clearInterval(retry);
- if (featureMatchSend(INTENT_URI)) {
- clearInterval(retry);
- s.recipients[0].address = '';
- render();
- }
- }, 1000);
+ const claimed = ui.intentWait && featureMatchSend(INTENT_URI);
+ if (!claimed && ui.intentWait && Date.now() - waitStart < 20_000) return;
+ clearInterval(retry);
+ const wasWaiting = ui.intentWait;
+ ui.intentWait = false;
+ if (!claimed && wasWaiting) handleScanned(INTENT_URI);
+ render();
+ }, 150);
}, 500);
}
diff --git a/src/style.css b/src/style.css
index 28990fa..db6e00e 100644
--- a/src/style.css
+++ b/src/style.css
@@ -277,6 +277,9 @@ textarea.send-dest {
/* the hint in the page's own face: narrower than the address font, so it
fits the one line beside Paste and Scan on a phone */
textarea.send-dest::placeholder { font-family: var(--sans); }
+/* Paste and Scan stay one line tall at the top as a long address grows the
+ field (a flex row stretches them to its full height otherwise) */
+.input-group:has(> textarea.send-dest) > button { align-self: flex-start; height: calc(1.4rem + 24px); }
/* unit toggle */
.seg { display: inline-flex; border: 1px solid var(--line-strong); border-radius: 10px; overflow: hidden; }