diff options
| author | Adam Soltys <[email protected]> | 2026-09-10 22:20:38 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-10 22:20:38 -0700 |
| commit | 4260b7469bd487e574f97bdf99db31abf2caaa67 (patch) | |
| tree | dd86bab1f995da4cd8a6f51827a9d4370eb3273e /src | |
| parent | b19cf9672532a8f6c347dc8fe99d83492bd478fd (diff) | |
| download | coinosv3-4260b7469bd487e574f97bdf99db31abf2caaa67.tar.gz coinosv3-4260b7469bd487e574f97bdf99db31abf2caaa67.zip | |
Don't show "Get started" to someone who is signed in.
Boot decides whether a session restores, but plenty renders before it gets
there — the deferred feature chunks land and repaint, an emitter ticks — and
a render that early has no idea whether anyone is signed in. It painted the
welcome screen anyway, so a signed-in wallet flashed the sign-in page for the
~75ms before boot restored it. Waiting for the ark store widened the gap; the
race was already there.
The signed-out screen is now the one screen boot won't guess at: until the
session question is answered, the frame holds the boot shell (the brand row
it's already showing). Every other screen is a deliberate choice — a gift
claim, a locked vault, a chat — and still paints immediately. A release timer
makes sure a boot path that returns early can't hold it back for good.
tools/boot-signin-flash-test.js widens the race and checks every frame of
four reloads; before this it caught the flash on every one.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.js | 19 |
1 files changed, 18 insertions, 1 deletions
@@ -444,6 +444,16 @@ let _renderDeferred = false; // and every app render (feature init, emitter ticks) must stand down or the // onboarding screen paints over the chat moments after it mounts. let _publicChatMode = false; +// True until boot has decided whether a session is restoring. Anything that +// renders before then (a deferred feature chunk landing, an emitter tick) is +// rendering without knowing whether anyone is signed in — and the signed-out +// welcome screen is the one screen that must never be guessed at. The boot +// shell holds the frame for those few ms instead; see the dispatch below. +let _bootDeciding = true; +// Belt and braces: a boot path that returns early (or throws) must not leave +// the welcome screen held back forever. Nothing legitimate is still deciding +// four seconds in — the store wait alone caps at 800ms. +setTimeout(() => { if (_bootDeciding) { _bootDeciding = false; render(); } }, 4000); function render() { if (_publicChatMode) return; // A background render mid carousel-drag rebuilds the strip; the morph then @@ -529,7 +539,13 @@ function renderInner() { ? vaultScreen() : ui.screen === 'howItWorks' ? howItWorksScreen() - : shouldOnboard() ? onboardScreen() : unlockScreen()); + : _bootDeciding + // Not "nobody is signed in" — "we don't know yet". Painting + // Get started here is what flashed the sign-in page over a + // wallet that was about to restore. Every other screen is a + // deliberate choice and still paints; this one waits. + ? h('div', { class: 'col', style: 'gap:16px' }, brandHeader(false)) + : shouldOnboard() ? onboardScreen() : unlockScreen()); // Navigation animates; background repaints must not. The key is every // ui field that decides which page is on screen. const navKey = [ui.screen, ui.tab === 'settings', ui.chatOpen, ui.msgView, ui.msgPeer, ui.msgCommunity, @@ -5175,6 +5191,7 @@ loadLocale(getLang()).finally(async () => { // wrong balance. The hook caps its own wait. const storeReady = featureHook('arkStoreReady'); if (storeReady) await storeReady; + _bootDeciding = false; // from here on the screen is a decision, not a guess if (!restoreAccountsState()) render(); }); |
