diff options
Diffstat (limited to 'src/app.js')
| -rw-r--r-- | src/app.js | 91 |
1 files changed, 67 insertions, 24 deletions
@@ -3674,13 +3674,15 @@ function onboardScreen() { let saved = null; try { saved = localStorage.getItem(ONB_STEP_KEY); } catch {} // a saved step past wallet creation only makes sense if the wallet is here - if (saved && !['welcome', 'signin', 'seed'].includes(saved) && !activeAccount()) saved = 'welcome'; + if (saved && !['welcome', 'signin', 'seed', 'migrate'].includes(saved) && !activeAccount()) saved = 'welcome'; // 'legacy-went' survives the coinos.io round trip: the user left for the // migrate page, so coming back must offer "I'm done, continue" — the // in-memory flag died with the navigation and left them looping. ui.onb = saved === 'legacy-went' ? { step: 'legacy', wentToLegacy: true, fromLink: migrateLinkPending() } - : { step: saved || 'welcome', fromLink: saved === 'legacy' && migrateLinkPending() }; + // the /migrate link on a device with no wallet opens on the move's own + // page, which says a wallet comes first — not an unexplained welcome + : { step: saved || (!activeAccount() && migrateLinkPending() ? 'migrate' : 'welcome'), fromLink: saved === 'legacy' && migrateLinkPending() }; if (saved === 'signin') ui.nostrLoginOpen = true; // reopen the login card } const o = ui.onb; @@ -3719,9 +3721,17 @@ function onboardScreen() { if (o.step === 'spend') { const addr = featureHook('namesAddress'); if (o.enterAddr === undefined) o.enterAddr = addr || null; + const custom = !!addr && !/^npub1/.test(addr); + // A custom name this page didn't claim is an account that set itself up + // before: a wallet a sign-in restored, whose name the registrar handed + // back (owned by the login identity or the wallet key). There is nothing + // to claim — the form stood here anyway, suggesting a profile name, and + // retyping the name one already had "claimed" it again and painted the + // same page. Out of the wizard, into the wallet as it was. + if (custom && !o.claimedHere) { leaveWizard(); return null; } // Only a real (custom) name advances by itself — the background // auto-claim of the npub-shaped default doesn't count as setting up. - if (addr && addr !== o.enterAddr && !/^npub1/.test(addr)) { + if (custom && addr !== o.enterAddr) { // The avatar ask is for brand-new profiles. An identity that already // wears a face (a nostr login with a published picture) skips straight // past the punk picker — restyling a real profile helps nobody. @@ -3792,6 +3802,26 @@ function onboardScreen() { ui.onbError ? h('div', { class: 'notice err' }, ui.onbError) : null, ]); } + // Making your own keys: the words are the wallet, so show them at once, + // no Generate to tap. + const startCreate = () => { + ui.unlockTab = 'create'; + ui.unlockError = ''; + ui.createStep = 'gen'; + if (!ui.draftMnemonic) ui.draftRandom = ui.draftMnemonic = newMnemonic(); + o.step = 'seed'; + render(); + }; + // All the sign-in doors (Google, passkey, Nostr) — the feature supplies + // them; the single-button fallback keeps builds without it working. + const signinDoors = () => featureHook('frontDoorSignin') + || h('button', { class: 'btn-block', style: 'padding:14px', onClick: () => { + o.step = 'signin'; + ui.nostrLoginOpen = true; + render(); + } }, h('span', { style: 'display:flex;align-items:center;justify-content:center;gap:8px' }, + h('span', { style: 'display:flex;flex-shrink:0', html: NOSTR_MARK }), + t('nlSignIn'))); if (o.step === 'welcome') { return page([ h('div', { class: 'onb-hero' }, brandHeader(false)), @@ -3801,27 +3831,38 @@ function onboardScreen() { 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: () => { - ui.unlockTab = 'create'; - ui.unlockError = ''; - ui.createStep = 'gen'; - // the words are the wallet: show them at once, no Generate to tap - if (!ui.draftMnemonic) ui.draftRandom = ui.draftMnemonic = newMnemonic(); - o.step = 'seed'; - render(); - } }, t('onbStart')), + class: 'btn-primary btn-block', style: 'font-size:17px;padding:14px', onClick: startCreate, + }, t('onbStart')), h('div', { class: 'onb-or' }, h('span', {}, t('onbOrSignIn'))), - // All the sign-in doors right on the welcome screen (Google, passkey, - // Nostr) — the feature supplies them; the single-button fallback keeps - // builds without it working. - featureHook('frontDoorSignin') - || h('button', { class: 'btn-block', style: 'padding:14px', onClick: () => { - o.step = 'signin'; - ui.nostrLoginOpen = true; - render(); - } }, h('span', { style: 'display:flex;align-items:center;justify-content:center;gap:8px' }, - h('span', { style: 'display:flex;flex-shrink:0', html: NOSTR_MARK }), - t('nlSignIn'))), + signinDoors(), + // The move from the original coinos.io used to wait behind the + // Spending offer, where nobody looking for it found it. Its own door + // on the front page: 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')), + ui.onbError ? h('div', { class: 'notice err' }, ui.onbError) : null, + ]); + } + if (o.step === 'migrate') { + // The intent is kept across whichever way in they pick: once a wallet + // exists, the Spending offer gives way to the coinos.io move (above). + return page([ + h('div', { class: 'onb-hero' }, brandHeader(false)), + title(t('onbLegacyTitle')), + h('p', { class: 'muted', style: 'margin:0' }, t('onbMigrateIntro')), + h('button', { + id: 'onb-migrate-create', + 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('button', { class: 'linklike small', onClick: () => { endMigrateLink(); ui.onbError = ''; o.step = 'welcome'; render(); } }, t('back')), ui.onbError ? h('div', { class: 'notice err' }, ui.onbError) : null, ]); } @@ -3850,7 +3891,9 @@ function onboardScreen() { h('button', { class: 'btn-primary btn-block', style: 'padding:14px', onClick: done }, t('onbBackupDone')), // Later means later, not never: seedSeen stays false, so the wallet // keeps a quiet reminder until the phrase has actually been seen. - h('button', { class: 'btn-ghost btn-block', onClick: () => { + // Except on the way to a coinos.io move: the balance about to arrive + // must have its twelve words written down first, so Later isn't offered. + migrateLinkPending() ? null : h('button', { class: 'btn-ghost btn-block', onClick: () => { if (canOfferSpending()) { o.step = 'spend'; o.enterAddr = undefined; render(); return; } ui.onb = null; try { localStorage.removeItem(ONB_STEP_KEY); } catch {} |
