From 45a4a9d5cec8eafc7e98ebd7ca2a9790c0ee94b4 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Mon, 6 Jul 2026 08:09:57 -0700 Subject: Icons in the receive selector and on silent-payment history rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The receive address-type picker was a native can't +// render SVG). opts: [{id,label,icon}] where icon is an emoji or an +// string. Closes on select or on a tap outside (transparent backdrop). +function iconGlyph(ic) { + if (!ic) return null; + return String(ic).trim().startsWith('<') + ? h('span', { class: 'sel-ico', html: ic }) + : h('span', { class: 'sel-ico' }, ic); +} +function iconSelect(opts, current, onSelect) { + const cur = opts.find((o) => o.id === current) || opts[0]; + return h('div', { class: 'icon-select', style: 'position:relative;width:100%' }, + h('button', { + type: 'button', class: 'icon-select-btn', + onClick: () => { ui.selectOpen = ui.selectOpen === 'receive' ? null : 'receive'; render(); }, + }, + iconGlyph(cur.icon), + h('span', { class: 'grow', style: 'text-align:left' }, cur.label), + h('span', { class: 'muted', style: 'font-size:12px' }, '▾')), + ui.selectOpen === 'receive' + ? h('div', {}, + h('div', { style: 'position:fixed;inset:0;z-index:20', onClick: () => { ui.selectOpen = null; render(); } }), + h('div', { class: 'icon-select-menu', style: 'position:absolute;left:0;right:0;top:calc(100% + 4px);z-index:21' }, + opts.map((o) => h('button', { + type: 'button', class: 'icon-select-item' + (o.id === current ? ' active' : ''), + onClick: () => { ui.selectOpen = null; onSelect(o.id); }, + }, iconGlyph(o.icon), h('span', { class: 'grow', style: 'text-align:left' }, o.label))))) + : null + ); +} + function receiveTab() { // Feature takeovers: boarding success, Ark receive celebration, etc. const takeover = featureHook('receiveTakeover'); @@ -2025,10 +2056,12 @@ function receiveTab() { const featModes = featureAll('receiveModes'); let mode = ui.receiveType || 'address'; if (mode !== 'address' && !featModes.some((m) => m.id === mode)) mode = 'address'; - const opts = [['address', t('receiveAddressTab')], ...featModes.map((m) => [m.id, m.label])]; + const opts = [ + { id: 'address', label: t('receiveAddressTab'), icon: '₿' }, + ...featModes.map((m) => ({ id: m.id, label: m.label, icon: m.icon })), + ]; const seg = opts.length > 1 - ? h('select', { style: 'width:100%', onChange: (e) => { ui.receiveType = e.target.value; render(); } }, - opts.map(([id, label]) => h('option', { value: id, selected: mode === id }, label))) + ? iconSelect(opts, mode, (id) => { ui.receiveType = id; render(); }) : null; const fm = featModes.find((m) => m.id === mode); if (fm) return fm.render(seg); diff --git a/src/features/ark.js b/src/features/ark.js index 6cafe35..5f64b21 100644 --- a/src/features/ark.js +++ b/src/features/ark.js @@ -979,7 +979,7 @@ export function arkFeature(ctx) { stop() { stopArk(); }, receiveModes() { if (!arkAvailable()) return []; - return [{ id: 'ark', label: t('receiveArkTab'), render: (seg) => arkReceivePane(seg) }]; + return [{ id: 'ark', label: t('receiveArkTab'), icon: ARK_MARK(18), render: (seg) => arkReceivePane(seg) }]; }, receiveTakeover() { const offboarded = arkOffboardedScreen(); diff --git a/src/features/sp.js b/src/features/sp.js index 5c55457..92f091f 100644 --- a/src/features/sp.js +++ b/src/features/sp.js @@ -42,7 +42,7 @@ export function spFeature(ctx) { const spAddr = wallet.silentPaymentsAvailable && wallet.silentPaymentsAvailable() ? wallet.silentPaymentAddress() : null; if (!spAddr) return []; return [{ - id: 'sp', label: t('receiveSpTab'), + id: 'sp', label: t('receiveSpTab'), icon: '🕶️', render: (seg) => h( 'div', { class: 'card col', style: 'align-items:center;gap:14px' }, @@ -54,6 +54,13 @@ export function spFeature(ctx) { }]; }, isSendDest(a) { return isSilentPaymentAddress(a); }, + // Silent-payment receipts merge into the main history as on-chain-looking + // rows (tx.sp) — give them a rail glyph + tag so they're recognizable. + decorateTxRow(tx) { + return tx && tx.sp + ? { icon: '🕶️', label: h('span', {}, t('received'), ' ', h('span', { class: 'tag' }, t('receiveSpTab'))) } + : null; + }, settingsCards() { return [spIndexerCard()]; }, }; } diff --git a/src/features/swaps.js b/src/features/swaps.js index b41b87a..2ff43ed 100644 --- a/src/features/swaps.js +++ b/src/features/swaps.js @@ -186,7 +186,7 @@ export function swapsFeature(ctx) { // a reverse swap needs our keys to claim on-chain if (wallet.watchOnly) return []; return [{ - id: 'ln', label: t('receiveLnTab'), + id: 'ln', label: t('receiveLnTab'), icon: '⚡', render: (seg) => h('div', { class: 'card col', style: 'align-items:center;gap:14px' }, seg, ...lnReceiveContent()), }]; }, diff --git a/src/style.css b/src/style.css index 545891c..4fe0394 100644 --- a/src/style.css +++ b/src/style.css @@ -289,3 +289,24 @@ hr.divider { margin: 16px 0; } .words { grid-template-columns: repeat(2, 1fr); } .balance .amt { font-size: 28px; } } + +/* Icon dropdown (receive mode) — a