aboutsummaryrefslogtreecommitdiffstats
path: root/src/app.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-10 22:20:38 -0700
committerAdam Soltys <[email protected]>2026-09-10 22:20:38 -0700
commit4260b7469bd487e574f97bdf99db31abf2caaa67 (patch)
treedd86bab1f995da4cd8a6f51827a9d4370eb3273e /src/app.js
parentb19cf9672532a8f6c347dc8fe99d83492bd478fd (diff)
downloadcoinosv3-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/app.js')
-rw-r--r--src/app.js19
1 files changed, 18 insertions, 1 deletions
diff --git a/src/app.js b/src/app.js
index e5c3a06..f36c9bc 100644
--- a/src/app.js
+++ b/src/app.js
@@ -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();
});