diff options
| author | Adam Soltys <[email protected]> | 2026-10-04 13:02:57 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-04 13:02:57 -0700 |
| commit | 53a4ad1602f7ce0398809bf15fd5196884e2c1a0 (patch) | |
| tree | 2385f4deea7deeda91b04508291feb8099a80fa7 | |
| parent | b0fdec8eef365805f7a3a57bd5ec5563472785f9 (diff) | |
| download | coinosv3-53a4ad1602f7ce0398809bf15fd5196884e2c1a0.tar.gz coinosv3-53a4ad1602f7ce0398809bf15fd5196884e2c1a0.zip | |
/migrate: a link that opens the coinos.io account move
Fresh devices reach it through the wizard (in place of the Spending
offer); an existing wallet gets the move page on its own once it can
mint a payment address, and leaves it straight back to the wallet.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
| -rw-r--r-- | src/app.js | 57 |
1 files changed, 53 insertions, 4 deletions
@@ -3515,6 +3515,20 @@ function vaultScreen() { // wallet brings it back. The current step is persisted so a refresh (or the // legacy-migration round trip) resumes exactly where it left off. const ONB_STEP_KEY = 'btc-wallet-onb-step'; +// Set by the /migrate link (read at boot, next to /pos): open the coinos.io +// migration page as soon as this device can offer it. +const MIGRATE_LINK_KEY = 'btc-wallet-migrate-link'; +function migrateLinkPending() { + try { + const at = Number(localStorage.getItem(MIGRATE_LINK_KEY)); + if (at && Date.now() - at < 86_400_000) return true; + localStorage.removeItem(MIGRATE_LINK_KEY); + } catch {} + return false; +} +function endMigrateLink() { + try { localStorage.removeItem(MIGRATE_LINK_KEY); } catch {} +} function onbInProgress() { try { return !!localStorage.getItem(ONB_STEP_KEY); } catch { return false; } } @@ -3592,8 +3606,8 @@ function onboardScreen() { // 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 } - : { step: saved || 'welcome' }; + ? { step: 'legacy', wentToLegacy: true, fromLink: migrateLinkPending() } + : { step: saved || 'welcome', fromLink: saved === 'legacy' && migrateLinkPending() }; if (saved === 'signin') ui.nostrLoginOpen = true; // reopen the login card } const o = ui.onb; @@ -3626,6 +3640,9 @@ function onboardScreen() { return null; // the caller falls through to the wallet itself } } + // Arriving by the /migrate link: the coinos.io move it asked for, in place + // of the Spending offer. Coming back from it is the ordinary wizard. + if (o.step === 'spend' && migrateLinkPending()) { endMigrateLink(); o.step = 'legacy'; } if (o.step === 'spend') { const addr = featureHook('namesAddress'); if (o.enterAddr === undefined) o.enterAddr = addr || null; @@ -3692,7 +3709,12 @@ function onboardScreen() { class: 'btn-primary btn-block', style: 'padding:14px', disabled: !!o.legacyBusy, onClick: goLegacy, }, o.legacyBusy ? h('span', { class: 'spinner sm' }) : t('onbLegacyGo')), - h('button', { class: 'btn-block', style: 'padding:14px', onClick: () => { o.step = 'spend'; render(); } }, + h('button', { class: 'btn-block', style: 'padding:14px', onClick: () => { + // an existing wallet sent here by the /migrate link has nothing + // left to set up: out of the wizard, back to the wallet + if (o.fromLink) { endMigrateLink(); leaveWizard(); } else o.step = 'spend'; + render(); + } }, o.wentToLegacy || claiming ? t('onbLegacyDone') : t('back')), ui.onbError ? h('div', { class: 'notice err' }, ui.onbError) : null, ]); @@ -3956,7 +3978,10 @@ function claimMigratedName() { // along instead of re-offering the page that was just completed. A // null enterAddr lets the spend step see the claimed name as change // and advance on its own. - if (ui.onb && ui.onb.step === 'legacy') { ui.onb.step = 'spend'; ui.onb.enterAddr = null; } + if (ui.onb && ui.onb.step === 'legacy') { + if (ui.onb.fromLink) { endMigrateLink(); leaveWizard(); } + else { ui.onb.step = 'spend'; ui.onb.enterAddr = null; } + } toast(t('migrateClaimed', { name })); render(); }) @@ -3972,7 +3997,17 @@ function claimMigratedName() { attempt(); } +function leaveWizard() { + ui.onb = null; + try { localStorage.removeItem(ONB_STEP_KEY); } catch {} +} + function walletScreen() { + // The /migrate link on a device that already has a wallet: the wizard's + // migrate page, on its own, once the wallet can mint a payment address. + if (!ui.onb && !onbInProgress() && activeAccount() && migrateLinkPending() && canOfferSpending()) { + ui.onb = { step: 'legacy', fromLink: true }; + } if (ui.onb || onbInProgress()) { const s = onboardScreen(); if (s) return s; // null = the wizard just handed over; fall through @@ -5941,6 +5976,20 @@ const BOOT_POS = (() => { } catch { return false; } })(); if (BOOT_POS) ui.posAtBoot = true; +// v3.coinos.io/migrate: a link to hand someone who hasn't found the +// coinos.io move in the UI. Stashed (not just flagged) because the page it +// leads to needs an open wallet and a payment address — a fresh device gets +// there through the whole wizard, a locked one through its unlock. A day +// later the link's intent is stale and is dropped unannounced. +if ((() => { + try { + if (!/^\/migrate\/?$/i.test(location.pathname)) return false; + history.replaceState(null, '', '/' + (location.search || '')); + return true; + } catch { return false; } +})()) { + try { localStorage.setItem(MIGRATE_LINK_KEY, String(Date.now())); } catch {} +} const INTENT_URI = (() => { try { const u = new URLSearchParams(location.search).get('u'); |
