aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.js79
-rw-r--r--src/features/nostrlogin.js25
2 files changed, 62 insertions, 42 deletions
diff --git a/src/app.js b/src/app.js
index a7ab2fb..fff5b04 100644
--- a/src/app.js
+++ b/src/app.js
@@ -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()]; },
};