diff options
| author | Adam Soltys <[email protected]> | 2026-06-23 23:17:19 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-06-23 23:17:19 -0700 |
| commit | 06e48290ca5e6da7866715cc26c4e3fdf3e03b91 (patch) | |
| tree | 998ba58087dd40d839fc919635b3b2e328cfc24a /src/app.js | |
| parent | d5e55c9c577feb6f8000bad3c37045a2cd7c9b4c (diff) | |
| download | coinosv3-06e48290ca5e6da7866715cc26c4e3fdf3e03b91.tar.gz coinosv3-06e48290ca5e6da7866715cc26c4e3fdf3e03b91.zip | |
Show full recipient address below the input for verification
The address input scrolls/truncates on mobile, hiding the ends. Render
the full address as wrapping text beneath it with the first/last 6
chars emphasized, so paste/type/scan can be verified without scrolling.
Updated imperatively on input so the field keeps focus.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'src/app.js')
| -rw-r--r-- | src/app.js | 31 |
1 files changed, 29 insertions, 2 deletions
@@ -964,18 +964,42 @@ async function broadcastScanned() { render(); } +// Full address as wrapping nodes, first/last 6 chars emphasized — readable +// without horizontally scrolling the input. Returns DOM nodes for in-place +// updates (the address input doesn't re-render on every keystroke). +function addrVerifyNodes(a) { + const n = 6; + if (!a) return []; + if (a.length <= n * 2) return [document.createTextNode(a)]; + return [ + h('span', { class: 'hl' }, a.slice(0, n)), + document.createTextNode(a.slice(n, -n)), + h('span', { class: 'hl' }, a.slice(-n)), + ]; +} + // One recipient: address + amount. Max is only offered for a single recipient. function recipientRow(s, r, i) { const single = s.recipients.length === 1; const maxOn = single && s.max; - return h( + + // Updated imperatively on input (and on render) so paste, typing, and scan + // all reflect immediately without disrupting the input's focus/cursor. + const check = h('div', { class: 'addr-check' }); + const syncCheck = () => { + const a = r.address.trim(); + check.replaceChildren(...addrVerifyNodes(a)); + check.style.display = a ? '' : 'none'; + }; + + const row = h( 'div', { class: 'col gap6' }, h('div', { class: 'input-group' }, h('input', { type: 'text', class: 'mono-input grow', placeholder: 'bc1q…', autocapitalize: 'none', autocomplete: 'off', spellcheck: 'false', value: r.address, - onInput: (e) => (r.address = e.target.value), + onInput: (e) => { r.address = e.target.value; syncCheck(); }, }), i === 0 && canScan() && h('button', { type: 'button', class: 'btn-sm', title: t('scanQr'), onClick: scanIntoSend, @@ -983,6 +1007,7 @@ function recipientRow(s, r, i) { }), !single && h('button', { type: 'button', class: 'btn-sm', title: t('remove'), onClick: () => { s.recipients.splice(i, 1); render(); } }, '✕') ), + check, h('div', { class: 'input-group' }, h('input', { type: 'number', step: unit === 'sats' ? '1' : '0.00000001', min: '0', @@ -1008,6 +1033,8 @@ function recipientRow(s, r, i) { single && h('button', { type: 'button', class: s.max ? 'btn-primary' : '', onClick: () => { s.max = !s.max; render(); } }, t('max')) ) ); + syncCheck(); + return row; } function sendForm() { |
