From 80875a2ed47f63e61601811052dee719b74beed1 Mon Sep 17 00:00:00 2001 From: archipelago Date: Thu, 23 Jul 2026 16:15:43 -0400 Subject: [PATCH] =?UTF-8?q?feat(wallet):=20invoice-first=20lightning=20sen?= =?UTF-8?q?d=20=E2=80=94=20amount=20locks=20from=20the=20pasted=20invoice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A pasted fixed-amount invoice auto-fills and locks the amount ('set by invoice' — nothing to type, just review and send); zero-amount invoices get an explicit 'enter how many sats' hint. Plus a clipboard Paste button on the destination field (hidden where clipboard read can't work). Co-Authored-By: Claude Fable 5 --- neode-ui/src/components/SendBitcoinModal.vue | 55 ++++++++++++++++++-- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/neode-ui/src/components/SendBitcoinModal.vue b/neode-ui/src/components/SendBitcoinModal.vue index 55a4364e..f3abe5fa 100644 --- a/neode-ui/src/components/SendBitcoinModal.vue +++ b/neode-ui/src/components/SendBitcoinModal.vue @@ -67,6 +67,7 @@
+ set by invoice
- +
+ + +
@@ -177,6 +193,35 @@ function toggleSendAll() { // Leaving the on-chain tab disarms the sweep so it can never apply elsewhere watch(sendMethod, (m) => { if (m !== 'onchain') sendAll.value = false }) +// Invoice-first lightning UX: a pasted invoice that fixes its amount locks +// the amount field (auto-filled, "set by invoice"); zero-amount invoices +// leave it editable. Clearing/leaving lightning unlocks again. +const pastedInvoiceAmount = computed(() => { + if (effectiveMethod.value !== 'lightning') return null + const d = dest.value.trim() + if (!d) return null + return parseBolt11AmountSats(d.toLowerCase().startsWith('lightning:') ? d.slice(10) : d) +}) +watch(pastedInvoiceAmount, (fixed, prev) => { + if (fixed !== null) amount.value = fixed + // Swapping a fixed-amount invoice for a zero-amount one: don't silently + // keep the previous invoice's sats — make the user type the new amount. + else if (prev !== null) amount.value = 0 +}) + +// Clipboard read needs a secure context (or the companion bridge); hide the +// button where it can't work — the textarea still accepts a manual paste. +const canReadClipboard = typeof navigator !== 'undefined' && !!navigator.clipboard?.readText + +async function pasteFromClipboard() { + try { + const text = (await navigator.clipboard.readText()).trim() + if (text) dest.value = text + } catch { + // Permission denied — user can long-press/Ctrl+V into the field instead. + } +} + const effectiveMethod = computed(() => { if (sendMethod.value !== 'auto') return sendMethod.value const amt = amount.value || 0