fix(intro): hold companion popup until dashboard reveal cinematic ends
Demo images / Build & push demo images (push) Failing after 1m42s

The remote-companion popup fired on a blind 5s timer while the
first-visit dashboard entrance cinematic runs for 8s, so on a fresh
install it appeared mid-reveal and broke the intro. The cinematic now
publishes an introCinematicPlaying flag via the loginTransition store,
and the popup waits for it to clear (plus a 2s grace) before showing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-07-16 06:57:42 -04:00
co-authored by Claude Fable 5
parent e9c3311eff
commit fa91faa67c
3 changed files with 46 additions and 3 deletions
@@ -78,6 +78,7 @@
import { ref, onMounted, watch } from 'vue'
import * as QRCode from 'qrcode'
import { IS_DEMO } from '@/composables/useDemoIntro'
import { useLoginTransitionStore } from '@/stores/loginTransition'
const STORAGE_KEY = 'neode_companion_intro_seen'
// Absolute URL so the QR works when scanned by a phone (a relative path has no
@@ -93,17 +94,40 @@ const visible = ref(false)
const qrDataUrl = ref('')
const companionDownloadUrl = import.meta.env.VITE_COMPANION_APK_URL || DEFAULT_DOWNLOAD_URL
const loginTransition = useLoginTransitionStore()
// Base delay before the popup may appear, and extra breathing room after the
// dashboard entrance cinematic ends so the popup never cuts into the reveal.
const BASE_DELAY_MS = 5000
const POST_INTRO_GRACE_MS = 2000
onMounted(() => {
try {
if (localStorage.getItem(STORAGE_KEY) !== '1') {
// Delay slightly so it doesn't compete with login animation
setTimeout(() => { visible.value = true }, 5000)
setTimeout(maybeShow, BASE_DELAY_MS)
}
} catch {
// localStorage unavailable
}
})
function maybeShow() {
if (loginTransition.introCinematicPlaying) {
// First-visit reveal is still running — hold until it finishes.
const stop = watch(
() => loginTransition.introCinematicPlaying,
(playing) => {
if (!playing) {
stop()
setTimeout(() => { visible.value = true }, POST_INTRO_GRACE_MS)
}
},
)
return
}
visible.value = true
}
watch(visible, async (isVisible) => {
if (!isVisible) return
qrDataUrl.value = await QRCode.toDataURL(companionDownloadUrl, {