fix(intro): hold companion popup until dashboard reveal cinematic ends
Some checks failed
Demo images / Build & push demo images (push) Failing after 1m42s
Some checks failed
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:
parent
e9c3311eff
commit
fa91faa67c
@ -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, {
|
||||
|
||||
@ -15,6 +15,12 @@ export const useLoginTransitionStore = defineStore('loginTransition', () => {
|
||||
const pendingWelcomeTyping = ref(false)
|
||||
/** Trigger welcome typing on Home - set true after dashboard animation finishes */
|
||||
const startWelcomeTyping = ref(false)
|
||||
/**
|
||||
* True while the dashboard entrance cinematic (zoom/glitch reveal) is
|
||||
* playing. Overlays that would visually compete with it (e.g. the
|
||||
* companion intro popup) hold off until this flips back to false.
|
||||
*/
|
||||
const introCinematicPlaying = ref(false)
|
||||
|
||||
function setJustLoggedIn(value: boolean) {
|
||||
justLoggedIn.value = value
|
||||
@ -32,6 +38,10 @@ export const useLoginTransitionStore = defineStore('loginTransition', () => {
|
||||
startWelcomeTyping.value = value
|
||||
}
|
||||
|
||||
function setIntroCinematicPlaying(value: boolean) {
|
||||
introCinematicPlaying.value = value
|
||||
}
|
||||
|
||||
return {
|
||||
justLoggedIn,
|
||||
setJustLoggedIn,
|
||||
@ -41,5 +51,7 @@ export const useLoginTransitionStore = defineStore('loginTransition', () => {
|
||||
setPendingWelcomeTyping,
|
||||
startWelcomeTyping,
|
||||
setStartWelcomeTyping,
|
||||
introCinematicPlaying,
|
||||
setIntroCinematicPlaying,
|
||||
}
|
||||
})
|
||||
|
||||
@ -373,6 +373,7 @@ onMounted(() => {
|
||||
// un-forced call would gate itself silent.
|
||||
playDashboardLoadOomph(true)
|
||||
showZoomIn.value = true
|
||||
loginTransition.setIntroCinematicPlaying(true)
|
||||
loginTransition.setPendingWelcomeTyping(true)
|
||||
loginTransition.setJustCompletedOnboarding(false)
|
||||
loginTransition.setJustLoggedIn(false)
|
||||
@ -383,7 +384,10 @@ onMounted(() => {
|
||||
scheduledTimeout(triggerRevealGlitch, 500)
|
||||
scheduledTimeout(triggerRevealGlitch, 1200)
|
||||
scheduledTimeout(triggerRevealGlitch, 2000)
|
||||
scheduledTimeout(() => { showZoomIn.value = false }, 8000)
|
||||
scheduledTimeout(() => {
|
||||
showZoomIn.value = false
|
||||
loginTransition.setIntroCinematicPlaying(false)
|
||||
}, 8000)
|
||||
scheduledTimeout(() => {
|
||||
loginTransition.setStartWelcomeTyping(true)
|
||||
loginTransition.setPendingWelcomeTyping(false)
|
||||
@ -405,6 +409,9 @@ onMounted(() => {
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.body.classList.remove('dashboard-active')
|
||||
// Timers are cleared below; make sure overlays waiting on the cinematic
|
||||
// aren't left blocked forever if we unmount mid-reveal.
|
||||
loginTransition.setIntroCinematicPlaying(false)
|
||||
window.removeEventListener('keydown', handleKioskShortcuts)
|
||||
for (const id of pendingTimers) clearTimeout(id)
|
||||
pendingTimers.length = 0
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user