aboutsummaryrefslogtreecommitdiffstats
path: root/src/app.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-03 10:09:09 -0700
committerAdam Soltys <[email protected]>2026-08-03 10:09:09 -0700
commit4d09a091d0aa6a63860260637feddc1e11bed708 (patch)
treef2457ac5c29ff27ec17d1379e04188491d384260 /src/app.js
parent258f815bea0bd0e44091bda8b48b83ff15e39aa6 (diff)
downloadcoinosv3-4d09a091d0aa6a63860260637feddc1e11bed708.tar.gz
coinosv3-4d09a091d0aa6a63860260637feddc1e11bed708.zip
Onboarding: a step for migrating a legacy coinos.io account
Between "do you have a nostr account" and claiming a username, new users are asked whether they already have an account on the original coinos. Yes hands off to coinos.io/migrate with their fresh v3 payment address and a return URL — they're already signed in there, so no password or key ever travels. That page sweeps the balance over Lightning and renames the old account, releasing the username. Coming back, coinos.io returns ?migrated=<username>; v3 claims that name on the registrar (retrying for a minute, since the registrar's "is this a legacy user?" guard caches), and their lightning address now resolves to the v3 wallet. The legacy page ships in coinos-ui and is NOT deployed yet. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
Diffstat (limited to 'src/app.js')
-rw-r--r--src/app.js52
1 files changed, 51 insertions, 1 deletions
diff --git a/src/app.js b/src/app.js
index 9830800..4eeae3f 100644
--- a/src/app.js
+++ b/src/app.js
@@ -2035,7 +2035,7 @@ function onboardScreen() {
const o = ui.onb;
// steps that were waiting on something advance the moment it exists
if ((o.step === 'nostr' || o.step === 'signin') && ui.screen === 'wallet' && activeAccount()) {
- o.step = 'username';
+ o.step = 'legacy';
o.enterAddr = featureHook('namesAddress') || null;
}
if (o.step === 'username') {
@@ -2049,6 +2049,27 @@ function onboardScreen() {
const page = (kids) => h('div', { class: 'col onb', style: 'gap:20px' }, ...kids);
const title = (txt) => h('h2', { class: 'onb-title' }, txt);
+ if (o.step === 'legacy') {
+ const addr = featureHook('namesAddress');
+ const url = addr
+ ? `https://coinos.io/migrate?to=${encodeURIComponent(addr)}&back=${encodeURIComponent(location.origin + '/')}`
+ : null;
+ return page([
+ title(t('onbLegacyTitle')),
+ h('p', { class: 'muted', style: 'margin:0' }, t('onbLegacyBody')),
+ url
+ ? h('a', {
+ class: 'btn btn-primary btn-block', href: url,
+ style: 'text-align:center;display:block;text-decoration:none;padding:14px',
+ onClick: () => { o.wentToLegacy = true; },
+ }, t('onbLegacyGo'))
+ : h('div', { class: 'row gap6', style: 'align-items:center' },
+ h('span', { class: 'spinner sm' }), h('span', { class: 'small muted' }, t('onbNameWait'))),
+ h('button', { class: 'btn-block', style: 'padding:14px', onClick: () => { o.step = 'username'; render(); } },
+ o.wentToLegacy ? t('onbLegacyDone') : t('onbLegacyNo')),
+ h('button', { class: 'linklike small', onClick: () => { o.step = 'nostr'; render(); } }, t('back')),
+ ]);
+ }
if (o.step === 'welcome') {
return page([
h('div', { class: 'onb-hero' }, brandHeader(false)),
@@ -2153,6 +2174,34 @@ function onboardScreen() {
]);
}
+// Returning from the legacy migration: coinos.io sends ?migrated=<username>
+// once it has swept the balance and released the name. Claim it here — the
+// registrar's "is this a legacy user?" guard now passes.
+function claimMigratedName() {
+ let name = null;
+ try {
+ const u = new URL(location.href);
+ name = u.searchParams.get('migrated');
+ if (name) { u.searchParams.delete('migrated'); history.replaceState(null, '', u.pathname + u.search); }
+ } catch {}
+ if (!name) return;
+ ui.migrating = name;
+ render();
+ let tries = 0;
+ const attempt = () => {
+ featureHook('namesClaimName', name)
+ .then(() => { ui.migrating = null; toast(t('migrateClaimed', { name })); render(); })
+ .catch(() => {
+ // DNS/registrar caches the legacy lookup briefly; give it a minute
+ if (++tries < 12) return setTimeout(attempt, 5000);
+ ui.migrating = null;
+ toast(t('migrateClaimFailed', { name }));
+ render();
+ });
+ };
+ attempt();
+}
+
// ---- the coach-mark tour: three stops, measured against the live DOM ----
const TOUR = [
{ sel: '.header-avatar', key: 'tourAvatar' },
@@ -3450,6 +3499,7 @@ document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') onAppHidden();
else onAppVisible();
});
+window.addEventListener('load', () => setTimeout(() => { if (activeAccount()) claimMigratedName(); }, 1500));
loadLocale(getLang()).finally(() => {
applyBootAutoLogout(); // clear an overdue session before we read it for claim targets
if (featureHook('bootUrl')) return; // a feature consumed the URL (e.g. a gift claim)