diff options
Diffstat (limited to 'src/app.js')
| -rw-r--r-- | src/app.js | 39 |
1 files changed, 31 insertions, 8 deletions
@@ -1117,9 +1117,16 @@ function toast(msg, ms = 1600) { document.body.append(t); } t.textContent = msg; - requestAnimationFrame(() => t.classList.add('show')); clearTimeout(toastTimer); - toastTimer = setTimeout(() => t.classList.remove('show'), ms); + // The hide timer starts when the toast shows: a hidden tab pauses + // requestAnimationFrame but not setTimeout, so a toast raised in the + // background (a zap failing after you switched apps) used to be hidden + // first and shown on return, then stay up for good. + requestAnimationFrame(() => { + t.classList.add('show'); + clearTimeout(toastTimer); + toastTimer = setTimeout(() => t.classList.remove('show'), ms); + }); } async function copy(text) { @@ -3939,7 +3946,11 @@ async function publicMediaUpload(file) { if (error instanceof MediaUploadError && error.code === 'unauthorized') throw new Error(t('mediaUploadUnauthorized')); if (error instanceof MediaUploadError && error.code === 'too-big') throw new Error(t('mediaUploadTooBig')); if (error instanceof MediaUploadError && error.code === 'unsupported') throw new Error(t('mediaUploadUnsupported')); - throw new Error(t('msgUploadFailed')); + // what went wrong, said: "check your connection" alone hid a signer + // that never answered and hosts that refused the file + const why = String((error instanceof MediaUploadError ? error.details : '') || error?.message || '').slice(0, 200); + console.warn('media upload failed', error, why); + throw new Error(why && why !== 'failed' ? `${t('msgUploadFailed')} (${why})` : t('msgUploadFailed')); } finally { ui.mediaProgress = null; } @@ -4210,16 +4221,18 @@ function onboardScreen() { } if (o.step === 'spend') { return page([ - title(t('onbSpendTitle')), - h('p', { class: 'muted', style: 'margin:0' }, t('onbSpendBody')), + title(t(o.fromGift ? 'onbGiftNameTitle' : 'onbSpendTitle')), + h('p', { class: 'muted', style: 'margin:0' }, t(o.fromGift ? 'onbGiftNameBody' : 'onbSpendBody')), featureHook('namesClaimForm') || h('div', { class: 'row gap6', style: 'align-items:center' }, h('span', { class: 'spinner sm' }), h('span', { class: 'small muted' }, t('onbNameWait'))), // Optional means optional: one tap to the wallet, no questions — on - // Savings, the account that exists without any setup. + // Savings, the account that exists without any setup (or, after a + // gift, the account the gift arrived in). h('button', { class: 'btn-ghost btn-block', onClick: () => { + const home = o.home || 'savings'; ui.onb = null; try { localStorage.removeItem(ONB_STEP_KEY); } catch {} - ui.account = 'savings'; - try { localStorage.setItem(ACCOUNT_KEY, 'savings'); } catch {} + ui.account = home; + try { localStorage.setItem(ACCOUNT_KEY, home); } catch {} ui.tab = 'receive'; render(); } }, t('onbNotNow')), @@ -4439,6 +4452,15 @@ function claimMigratedName() { attempt(); } +// After a gift is claimed into a fresh wallet (and its seed written down): +// the wizard's name → look → done steps, the rest of a first run. `home` is +// the account a skip lands on — the one the gift arrived in. +function offerProfileSetup({ home = 'savings' } = {}) { + if (!canOfferSpending()) return false; + ui.onb = { step: 'spend', enterAddr: undefined, fromGift: true, home }; + return true; +} + function leaveWizard() { ui.onb = null; try { localStorage.removeItem(ONB_STEP_KEY); } catch {} @@ -6480,6 +6502,7 @@ const ctx = { || (wallet.nostrPubkey && wallet.nostrPubkey()) || (activeAccount() || {}).nostrPk || null, getAccount: () => accountSel(), setAccount: (a, dir) => setAccountSel(a, dir), + offerProfileSetup, pickConfirm, // Open (or create) a wallet from a mnemonic — used by nostr login. openMnemonic: async (mnemonic, passphrase, opts) => enterWallet(mnemonic, passphrase, opts), // Profile pictures retain the metadata-scrubbing image uploader. Public |
