diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.js | 79 | ||||
| -rw-r--r-- | src/features/nostrlogin.js | 25 |
2 files changed, 62 insertions, 42 deletions
@@ -366,6 +366,14 @@ function morphKeyed(parent, cur, next) { } const root = document.getElementById('app'); +// The footer while setting up: the language, nothing else. +function setupFooter() { + const lang = languagePicker(); + lang.style.width = 'auto'; // as wide as the language's name, not the page + lang.style.flex = '0 0 auto'; + return h('div', { class: 'footer small muted center' }, + h('div', { class: 'row gap6', style: 'justify-content:center' }, lang)); +} function footer() { return h( 'div', @@ -891,10 +899,11 @@ function renderInner() { // not inside it — the page's entrance animation transforms it, and a // transform makes fixed descendants position within the page. const floaters = h('div', { class: 'page-floaters', style: 'display:contents' }, ...featureAll('pageFloaters')); - // The front door is two choices and nothing else: no credits, pickers or - // build stamp under them (the language follows the browser's). - const frontDoor = !activeAccount() && ui.screen === 'unlock' && ui.onb && ['welcome', 'doors', 'migrate'].includes(ui.onb.step); - morphChildren(root, [screen, floaters, ...(frontDoor ? [] : [footer()]), ...(bottomNav ? [bottomNav] : []), ...(side ? [side] : []), ...(ui.lightbox ? [imageViewer()] : []), ...(callOv ? [callOv] : [])]); + // The front door and every setup step keep only the language picker under + // them: no credits, theme toggle, currency or build stamp (Adam, + // 2026-11-06). Setup = the unlock screen, or a page the wizard drew (.onb). + const setup = ui.screen === 'unlock' || !!(screen && (screen.classList?.contains('onb') || screen.querySelector?.('.onb'))); + morphChildren(root, [screen, floaters, setup ? setupFooter() : footer(), ...(bottomNav ? [bottomNav] : []), ...(side ? [side] : []), ...(ui.lightbox ? [imageViewer()] : []), ...(callOv ? [callOv] : [])]); promoteLazySrc(root); // sources on the nodes that actually made it into the page try { document.documentElement.classList.toggle('no-scroll', !!(ui.lightbox || ui.mediaPager)); } catch {} if (fpath) { @@ -4194,16 +4203,18 @@ function onboardScreen() { return page([ h('div', { class: 'onb-hero' }, brandHeader(false)), title(t('onbWelcomeTitle')), - h('button', { - // a stable hook for tests, so this button survives its own copy - id: 'onb-create', - class: 'btn-primary btn-block', style: 'font-size:17px;padding:14px', onClick: startCreate, - }, t('onbStart')), - h('button', { - id: 'onb-signin', - class: 'btn-block', style: 'font-size:17px;padding:14px', - onClick: () => { ui.onbError = ''; o.step = 'doors'; render(); }, - }, t('onbSignInExisting')), + // spaced as the sign-in list is (10px) + h('div', { class: 'col', style: 'gap:10px' }, + h('button', { + // a stable hook for tests, so this button survives its own copy + id: 'onb-create', + class: 'btn-primary btn-block', style: 'font-size:17px;padding:14px', onClick: startCreate, + }, t('onbStart')), + h('button', { + id: 'onb-signin', + class: 'btn-block', style: 'font-size:17px;padding:14px', + onClick: () => { ui.onbError = ''; o.step = 'doors'; render(); }, + }, t('onbSignInExisting'))), ui.onbError ? h('div', { class: 'notice err' }, ui.onbError) : null, ]); } @@ -4215,24 +4226,26 @@ function onboardScreen() { // used to hide as a line under the freshly minted words behind // "Create a new wallet", where a person signing in on their second // phone found it only by chance (user feedback, 2026-11-02). - h('button', { id: 'onb-restore', class: 'btn-block', style: 'padding:14px', onClick: () => { - clearSeedDrafts(); - ui.unlockTab = 'import'; - ui.unlockError = ''; - ui.onbError = ''; - o.step = 'seed'; - render(); - } }, t('onbRestoreDoor')), - signinDoors(), - // The move from the original coinos.io: it stashes the intent the - // /migrate link stashes (the move needs a wallet and a payment address - // first) and explains that before any twelve words appear. - h('button', { id: 'onb-migrate', class: 'btn-block', style: 'padding:14px', onClick: () => { - try { localStorage.setItem(MIGRATE_LINK_KEY, String(Date.now())); } catch {} - ui.onbError = ''; - o.step = 'migrate'; - render(); - } }, t('onbMigrateDoor')), + // one evenly spaced list: seed phrase, passkey, Google, Nostr, coinos.io + h('div', { class: 'col', style: 'gap:10px' }, + h('button', { id: 'onb-restore', class: 'btn-block', style: 'padding:14px', onClick: () => { + clearSeedDrafts(); + ui.unlockTab = 'import'; + ui.unlockError = ''; + ui.onbError = ''; + o.step = 'seed'; + render(); + } }, t('onbRestoreDoor')), + signinDoors(), + // The move from the original coinos.io: it stashes the intent the + // /migrate link stashes (the move needs a wallet and a payment address + // first) and explains that before any twelve words appear. + h('button', { id: 'onb-migrate', class: 'btn-block', style: 'padding:14px', onClick: () => { + try { localStorage.setItem(MIGRATE_LINK_KEY, String(Date.now())); } catch {} + ui.onbError = ''; + o.step = 'migrate'; + render(); + } }, t('onbMigrateDoor'))), h('button', { class: 'linklike small', onClick: () => { ui.onbError = ''; o.step = 'welcome'; render(); } }, t('back')), ui.onbError ? h('div', { class: 'notice err' }, ui.onbError) : null, ]); @@ -4249,7 +4262,7 @@ function onboardScreen() { class: 'btn-primary btn-block', style: 'font-size:17px;padding:14px', onClick: startCreate, }, t('onbMigrateCreate')), h('div', { class: 'onb-or' }, h('span', {}, t('onbOrSignIn'))), - signinDoors(), + h('div', { class: 'col', style: 'gap:10px' }, signinDoors()), h('button', { class: 'linklike small', onClick: () => { endMigrateLink(); ui.onbError = ''; o.step = 'doors'; render(); } }, t('back')), ui.onbError ? h('div', { class: 'notice err' }, ui.onbError) : null, ]); diff --git a/src/features/nostrlogin.js b/src/features/nostrlogin.js index 83144cd..1c227c3 100644 --- a/src/features/nostrlogin.js +++ b/src/features/nostrlogin.js @@ -422,21 +422,24 @@ export function nostrLoginFeature(ctx) { h('button', { class: 'btn-sm', type: 'button', onClick: () => { if (amberDeliver(e.text)) e.text = ''; else { ui.nostrLoginError = t('nlAmberBadPaste'); render(); } } }, t('nlAmberPasteBtn'))), h('button', { class: 'btn-ghost btn-sm', type: 'button', onClick: () => amberCancel() }, t('cancel'))); } - function externalButtons(run, big = false) { + // passkeyFirst: the front door lists the passkey right under the seed + // phrase, before Google (Adam, 2026-11-06) + function externalButtons(run, big = false, passkeyFirst = false) { const hasPasskey = typeof window !== 'undefined' && !!window.PublicKeyCredential && !!navigator.credentials; const pad = big ? 'padding:14px' : ''; - return [ + const google = [ h('button', { class: 'btn-block', style: pad, disabled: ui.nostrLoginBusy, onClick: () => googleLogin(run) }, markedLabel(GOOGLE_MARK, t('nlGoogle'), ui.nostrLoginVia === 'google')), GOOGLE_CENTRAL !== GOOGLE_LEGACY ? h('button', { class: 'linklike small', style: 'align-self:center', disabled: ui.nostrLoginBusy, onClick: () => googleLogin(run, GOOGLE_LEGACY) }, t('nlGooglePrev')) : null, - hasPasskey - ? h('button', { class: 'btn-block', style: pad, disabled: ui.nostrLoginBusy, - onClick: () => passkeyLogin(run) }, markedLabel(PASSKEY_MARK, t('nlPasskey'), ui.nostrLoginVia === 'passkey')) - : null, ]; + const passkey = hasPasskey + ? h('button', { class: 'btn-block', style: pad, disabled: ui.nostrLoginBusy, + onClick: () => passkeyLogin(run) }, markedLabel(PASSKEY_MARK, t('nlPasskey'), ui.nostrLoginVia === 'passkey')) + : null; + return passkeyFirst ? [passkey, ...google] : [...google, passkey]; } // The ways in, as buttons. `run` receives a signer factory. The expanded @@ -852,9 +855,12 @@ export function nostrLoginFeature(ctx) { .catch(() => { _spendingHinted = false; }); return true; }, + // The front door's ways in, loose: the page spaces them evenly with the + // seed-phrase and coinos.io doors around them (a wrapper of their own + // put 8px between these and 20px between them and the rest). frontDoorSignin() { - return h('div', { class: 'col', style: 'gap:8px' }, - ...externalButtons(loginWith, true), + return [ + ...externalButtons(loginWith, true, true), h('button', { class: 'btn-block', style: 'padding:14px', onClick: () => { if (ui.onb) ui.onb.step = 'signin'; ui.nostrLoginOpen = true; @@ -863,7 +869,8 @@ export function nostrLoginFeature(ctx) { } }, h('span', { style: 'display:flex;align-items:center;justify-content:center;gap:8px' }, h('span', { style: 'display:flex;flex-shrink:0', html: OSTRICH }), t('nlSignInNostr'))), - ui.nostrLoginError ? h('div', { class: 'notice err' }, ui.nostrLoginError) : null); + ui.nostrLoginError ? h('div', { class: 'notice err' }, ui.nostrLoginError) : null, + ].filter(Boolean); }, nostrSettingsCards() { return [settingsCard()]; }, }; |
