diff options
| author | Adam Soltys <[email protected]> | 2026-08-03 09:22:55 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-08-03 09:22:55 -0700 |
| commit | c7ba5d5f0c8e80da1133fc6f53cc37bfedc281f2 (patch) | |
| tree | d2eea5b1a931226cc384c581677fa3fbcdf2eb1d /src | |
| parent | b5b38eef005dab031d75804d96c8de2c1694764a (diff) | |
| download | coinosv3-c7ba5d5f0c8e80da1133fc6f53cc37bfedc281f2.tar.gz coinosv3-c7ba5d5f0c8e80da1133fc6f53cc37bfedc281f2.zip | |
Pay screens: bigger recipient chip, no loading-screen flashes
Resolving a recipient (ark advert → BIP-353 → lightning) no longer
takes over the screen twice on the way: both steps keep the send form
in place and report progress as one inline line, so picking someone
goes straight to something actionable. The recipient chip on the zap
card gets a large variant — a 30px avatar and full-size name, since
it's who the money is going to.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.js | 7 | ||||
| -rw-r--r-- | src/features/ark.js | 5 | ||||
| -rw-r--r-- | src/features/messages.js | 8 | ||||
| -rw-r--r-- | src/features/zaps.js | 15 | ||||
| -rw-r--r-- | src/style.css | 3 |
5 files changed, 29 insertions, 9 deletions
@@ -2812,6 +2812,13 @@ function recipientRow(s, r, i) { !single && h('button', { type: 'button', class: 'btn-sm', title: t('remove'), onClick: () => { s.recipients.splice(i, 1); render(); } }, '✕') ), check, + i === 0 ? (() => { + const note = featureHook('resolvingNote'); + return note + ? h('div', { class: 'row gap6', style: 'align-items:center;padding:2px 0' }, + h('span', { class: 'spinner sm' }), h('span', { class: 'small muted' }, note)) + : null; + })() : null, i === 0 && sendSearch.rows && sendSearch.rows.length && searchable(r.address) ? h('div', { class: 'list send-suggest' }, resultRows(h, sendSearch.rows, pickRecipient)) : null, diff --git a/src/features/ark.js b/src/features/ark.js index 8a485e8..7fda8ed 100644 --- a/src/features/ark.js +++ b/src/features/ark.js @@ -967,6 +967,8 @@ export function arkFeature(ctx) { } function arkZapView() { + // the npub lookup is a background step — no takeover card for it + if (ui.arkZap && ui.arkZap.status === 'lookup') return null; if (ui.arkZapped) { return h('div', { class: 'card col', @@ -982,7 +984,7 @@ export function arkFeature(ctx) { if (!z) return null; return h('div', { class: 'card col', style: 'gap:12px' }, h('h3', {}, '⚡ ' + t('arkZapTitle')), - ctx.hook('profileChip', z.pk) || h('div', { class: 'small muted', style: 'word-break:break-all' }, z.npub), + ctx.hook('profileChip', z.pk, 'lg') || h('div', { class: 'small muted', style: 'word-break:break-all' }, z.npub), z.status === 'lookup' ? h('div', { class: 'row gap6', style: 'align-items:center' }, h('span', { class: 'spinner sm' }), h('span', { class: 'small muted' }, t('arkZapLookup'))) : null, z.status === 'noark' ? h('div', { class: 'notice err' }, t('arkZapNoArk')) : null, // No Ark address, but they may still take a Lightning zap — hand off to @@ -1634,6 +1636,7 @@ export function arkFeature(ctx) { // swaps feature): the ASP pays invoices natively over ark. canLnPay() { return arkAvailable() && !wallet.watchOnly && !!(ark && ark.info); }, lnSpendableSat() { const b = arkBalance(); return b ? b.spendableSat : 0; }, + resolvingNote() { return ui.arkZap && ui.arkZap.status === 'lookup' ? t('arkZapLookup') : null; }, startLnPay(invoice, meta) { return startArkLnPay(invoice, meta); }, // ---- headless seam for the NWC wallet service ---- arkPayInvoice(invoice, opts) { return payInvoiceHeadless(invoice, opts); }, diff --git a/src/features/messages.js b/src/features/messages.js index 41a961d..27beff9 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -1387,11 +1387,13 @@ export function messagesFeature(ctx) { profiles.set(id.pubkey, { name: merged.name || null, picture: merged.picture || null, t: Date.now() }); return true; }, - profileChip(pk) { + profileChip(pk, size) { + const big = size === 'lg'; return h('span', { - class: 'zap-chip', + class: 'zap-chip' + (big ? ' lg' : ''), onClick: (e) => { e.stopPropagation(); openProfile(pk); }, - }, avatar(pk, 'chat-avatar mini', false), h('span', { class: 'small' }, displayName(pk))); + }, avatar(pk, 'chat-avatar ' + (big ? 'chip-lg' : 'mini'), false), + h('span', { class: big ? '' : 'small' }, displayName(pk))); }, init() { if (urlInvite && !pendingLink) { diff --git a/src/features/zaps.js b/src/features/zaps.js index 2b93a30..3298eaf 100644 --- a/src/features/zaps.js +++ b/src/features/zaps.js @@ -90,15 +90,19 @@ export function zapsFeature(ctx) { const z = ui.zap; if (!z) return null; const back = () => { ui.zap = null; ui.sendError = ''; ui.send = blankSend(); render(); }; - if (z.status === 'resolving' || z.status === 'invoicing') { + // Resolution is a background step, not a screen: the send form stays put + // and shows one inline line (resolvingNote below) until we can render + // something the user can act on. + if (z.status === 'resolving') return null; + if (z.status === 'invoicing') { return h('div', { class: 'card col', style: 'align-items:center;gap:14px;padding:32px 14px' }, h('span', { class: 'spinner' }), - h('p', { class: 'muted', style: 'margin:0' }, z.status === 'invoicing' ? t('lnZapRequesting') : t('lnZapResolving'))); + h('p', { class: 'muted', style: 'margin:0' }, t('lnZapRequesting'))); } if (z.status === 'error') { return h('div', { class: 'card col', style: 'gap:12px' }, h('h3', {}, '⚡ ' + t('lnZapTitle')), - (z.target && z.target.pk && hook('profileChip', z.target.pk)) || h('div', { class: 'small muted', style: 'word-break:break-all' }, z.name || ''), + (z.target && z.target.pk && hook('profileChip', z.target.pk, 'lg')) || h('div', { class: 'small muted', style: 'word-break:break-all' }, z.name || ''), h('div', { class: 'notice err' }, z.error || t('lnZapFailed')), h('button', { class: 'btn-ghost btn-block', onClick: back }, t('back'))); } @@ -115,13 +119,13 @@ export function zapsFeature(ctx) { if (broke) { return h('div', { class: 'card col', style: 'gap:12px' }, h('h3', {}, '⚡ ' + t('lnZapTitle')), - (z.target && z.target.pk && hook('profileChip', z.target.pk)) || h('div', { class: 'small muted', style: 'word-break:break-all' }, z.name || z.address || ''), + (z.target && z.target.pk && hook('profileChip', z.target.pk, 'lg')) || h('div', { class: 'small muted', style: 'word-break:break-all' }, z.name || z.address || ''), h('div', { class: 'notice info' }, t('zapNoBalance')), h('button', { class: 'btn-ghost btn-block', onClick: back }, t('back'))); } return h('div', { class: 'card col', style: 'gap:12px' }, h('h3', {}, '⚡ ' + t('lnZapTitle')), - (z.target && z.target.pk && hook('profileChip', z.target.pk)) || h('div', { class: 'small muted', style: 'word-break:break-all' }, z.name || z.address || ''), + (z.target && z.target.pk && hook('profileChip', z.target.pk, 'lg')) || h('div', { class: 'small muted', style: 'word-break:break-all' }, z.name || z.address || ''), h('div', { class: 'col gap6' }, h('div', { class: 'input-group' }, h('input', { type: 'number', min: '0', inputmode: 'decimal', placeholder: t('lnPayAmount'), value: z.amount, @@ -182,6 +186,7 @@ export function zapsFeature(ctx) { // True when an npub can be zapped over Lightning from this build/wallet — // ark's "no Ark address" screen checks this before offering the fallback. canLnZap() { return canPay() && !!wallet.nostrProfile; }, + resolvingNote() { return ui.zap && ui.zap.status === 'resolving' ? t('lnZapResolving') : null; }, // Ark's "no Ark address published" screen offers a Lightning fallback, // which lands here with the recipient's pubkey. lnZapNpub(pk, npub) { diff --git a/src/style.css b/src/style.css index f151bf5..c12efd3 100644 --- a/src/style.css +++ b/src/style.css @@ -528,3 +528,6 @@ body::after { border: 0; border-radius: 7px; background: var(--accent-soft); color: var(--muted); cursor: pointer; } .amt-max:hover { background: var(--surface2); color: var(--ink); } +/* recipient chip: large variant for pay/zap screens */ +.zap-chip.lg { gap: 8px; padding: 4px 12px 4px 4px; font-weight: 600; } +.chat-avatar.chip-lg { width: 30px; height: 30px; font-size: 11px; flex-shrink: 0; } |
