From 11749a428c6837986e415c9c91823fb6b7b8e249 Mon Sep 17 00:00:00 2001 From: Gronod Date: Sat, 16 May 2026 17:16:31 +0100 Subject: [PATCH] fix: splash screen hangs after login, never dismisses Root cause: showSplash() sets display:flex + opacity:1 synchronously, then dismissSplash() immediately adds the fade-out class (opacity:0). The browser batches these in the same paint frame so the CSS transition from opacity:1 -> 0 never starts, and transitionend never fires, leaving the Promise unresolved and the splash stuck. Two-part fix: 1. handleLogin: await two requestAnimationFrames between showSplash() and dismissSplash() so the browser paints opacity:1 first, ensuring the CSS opacity transition actually runs. 2. dismissSplash: add a 500ms fallback setTimeout that hides the splash and resolves the Promise even if transitionend is never fired (acts as a safety net for any future edge cases). --- public/app.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/public/app.js b/public/app.js index fb5e607..75a38ec 100644 --- a/public/app.js +++ b/public/app.js @@ -99,7 +99,15 @@ function dismissSplash(startTime) { setTimeout(() => { const splash = document.getElementById('splash-screen'); splash.classList.add('fade-out'); + // Fallback: resolve after transition duration + buffer in case + // transitionend never fires (e.g. display was toggled in same frame) + const TRANSITION_MS = 400; + const fallback = setTimeout(() => { + splash.style.display = 'none'; + resolve(); + }, TRANSITION_MS + 100); splash.addEventListener('transitionend', () => { + clearTimeout(fallback); splash.style.display = 'none'; resolve(); }, { once: true }); @@ -152,9 +160,13 @@ async function handleLogin(e) { if (data.success) { currentUser = data.user; isAdmin = !!data.user.isAdmin; - // Fade out login, then show splash while loading data + // Fade out login, then show splash while loading data. + // requestAnimationFrame ensures the browser paints the splash at + // opacity:1 before dismissSplash adds fade-out, so the CSS + // transition fires and transitionend is guaranteed. await fadeOutLogin(); showSplash(); + await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r))); showDashboard(); const splashStart = Date.now(); await fetchUserDownloads(true);