aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/app.js15
-rw-r--r--src/i18n.js2
-rw-r--r--tools/onboard-backup-test.js35
3 files changed, 51 insertions, 1 deletions
diff --git a/src/app.js b/src/app.js
index 69c6275..2bc7a39 100644
--- a/src/app.js
+++ b/src/app.js
@@ -4160,6 +4160,19 @@ function onboardScreen() {
}, t('onbStart')),
h('div', { class: 'onb-or' }, h('span', {}, t('onbOrSignIn'))),
signinDoors(),
+ // Bringing a wallet from another device: its twelve words, typed in.
+ // This 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). Its own
+ // door, straight to the import pane.
+ 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')),
// 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
@@ -4234,7 +4247,7 @@ function onboardScreen() {
// means seeing (or bringing) your seed phrase, not a wallet minted
// behind your back. Opening one ends the wizard into the wallet + tour.
return page([
- title(t('onbSeedTitle')),
+ title(t(ui.unlockTab === 'import' ? 'onbRestoreTitle' : 'onbSeedTitle')),
unlockCard(),
// the entropy page brings its own Back — one is enough
ui.entropyPage ? null : h('button', { class: 'linklike small', onClick: () => { ui.unlockError = ''; o.step = 'welcome'; render(); } }, t('back')),
diff --git a/src/i18n.js b/src/i18n.js
index b944f96..ac6066a 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -1396,6 +1396,8 @@ const STR = {
backupReminder: 'Your recovery phrase is still unwritten.',
backupReminderGo: 'Write it down',
onbSeedTitle: 'Set up your wallet',
+ onbRestoreTitle: 'Restore your wallet',
+ onbRestoreDoor: 'Restore from twelve words',
onbSigninTitle: 'Sign in',
onbLegacyTitle: 'Move your coinos.io account',
onbLegacyBody: 'The original coinos. We can move your balance over and point your username here. Your old account keeps working, it just stops receiving payments.',
diff --git a/tools/onboard-backup-test.js b/tools/onboard-backup-test.js
index ddef8d7..3cb607c 100644
--- a/tools/onboard-backup-test.js
+++ b/tools/onboard-backup-test.js
@@ -93,6 +93,41 @@ try {
await page.close();
}
+ // Three: a wallet made elsewhere comes in by its twelve words, and that
+ // door has to be on the front page too — it used to be a line under the
+ // freshly minted words behind "Create a new wallet", found by chance.
+ console.log('\n[restoring from twelve words]');
+ {
+ const ctx = await browser.createBrowserContext();
+ const page = await ctx.newPage();
+ await page.setViewport({ width: 390, height: 844 });
+ await page.goto('http://localhost:5291/', { waitUntil: 'domcontentloaded' });
+ await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest'));
+ await page.reload({ waitUntil: 'domcontentloaded' });
+ await waitText(page, 'sign in with nostr');
+ const door = await page.evaluate(() => {
+ const b = document.getElementById('onb-restore');
+ const c = document.getElementById('onb-create');
+ return b && c ? { label: b.textContent.trim(), below: b.getBoundingClientRect().top > c.getBoundingClientRect().top, visible: !!b.offsetParent } : null;
+ });
+ check('the front page has a door for an existing wallet', !!door && door.visible && /twelve words|seed/i.test(door.label), door ? door.label : 'missing');
+ check('...below the way in, with the other alternatives', !!door && door.below);
+ await page.evaluate(() => document.getElementById('onb-restore')?.click());
+ await sleep(600);
+ const pane = await page.evaluate(() => ({
+ title: /restore your wallet/i.test(document.body.innerText),
+ field: !!document.querySelector('textarea[placeholder*="seed words" i]'),
+ minted: document.querySelectorAll('.words .w .t').length,
+ }));
+ check('it opens straight onto the phrase field, no minted words', pane.title && pane.field && pane.minted === 0,
+ `${pane.title ? 'titled' : 'untitled'}, ${pane.field ? 'field' : 'no field'}, ${pane.minted} minted words`);
+ const phrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
+ await page.type('textarea[placeholder*="seed words" i]', phrase);
+ await click(page, 'open wallet');
+ check('the typed phrase opens the wallet', await waitText(page, 'receive'));
+ await ctx.close();
+ }
+
console.log('\n[signing in, then writing it down]');
{
const { page, ctx, seed } = await signIn();