From d25841aa1622be55fed77537bce13635fe7d7a93 Mon Sep 17 00:00:00 2001 From: Mahdi Date: Wed, 19 Aug 2026 18:07:45 +0330 Subject: [PATCH] Fix mobile viewer chrome and keep pages visible during autoplay. Co-authored-by: Cursor --- client/src/components/CatalogViewer.vue | 109 +++++++++++++---- .../src/components/effects/MagazineEffect.vue | 19 ++- client/src/pages/ViewPage.vue | 8 +- client/src/styles.css | 112 ++++++++++++++++++ deploy/DEPLOY.md | 49 ++------ docs/PROJECT.md | 62 +++++++--- 6 files changed, 281 insertions(+), 78 deletions(-) diff --git a/client/src/components/CatalogViewer.vue b/client/src/components/CatalogViewer.vue index 5bcf717..d2d8ea9 100644 --- a/client/src/components/CatalogViewer.vue +++ b/client/src/components/CatalogViewer.vue @@ -15,6 +15,10 @@ const props = defineProps<{ flipMs?: number; }>(); +const emit = defineEmits<{ + immersive: [value: boolean]; +}>(); + const flipMs = computed(() => props.flipMs ?? DEFAULT_FLIP_MS); const autoIntervalMs = computed(() => flipMs.value + AUTO_PAUSE_AFTER_FLIP_MS); @@ -24,6 +28,7 @@ const fullscreen = ref(false); const fallbackSlide = ref(false); const playing = ref(true); const waiting = ref(true); +const hideChrome = ref(false); const stage = ref(null); const magazine = ref<{ next: () => void; prev: () => void } | null>(null); @@ -35,6 +40,12 @@ const visualEffect = computed(() => { return props.effect; }); +const immersive = computed( + () => singlePage.value && (hideChrome.value || (playing.value && !waiting.value)), +); + +watch(immersive, (value) => emit("immersive", value), { immediate: true }); + let delayTimer = 0; let loopTimer = 0; @@ -96,6 +107,11 @@ function togglePlay() { } } +function onFab() { + hideChrome.value = false; + if (playing.value) togglePlay(); +} + function onKey(ev: KeyboardEvent) { if (ev.key === "ArrowLeft") onUserNav(next); if (ev.key === "ArrowRight") onUserNav(prev); @@ -103,21 +119,49 @@ function onKey(ev: KeyboardEvent) { ev.preventDefault(); togglePlay(); } - if (ev.key === "Escape") zoom.value = false; -} - -async function toggleFullscreen() { - if (!stage.value) return; - if (!document.fullscreenElement) { - await stage.value.requestFullscreen(); - fullscreen.value = true; - } else { - await document.exitFullscreen(); - fullscreen.value = false; + if (ev.key === "Escape") { + zoom.value = false; + hideChrome.value = false; } } +function fullscreenElement() { + const doc = document as Document & { webkitFullscreenElement?: Element | null }; + return document.fullscreenElement ?? doc.webkitFullscreenElement ?? null; +} + +async function toggleFullscreen() { + if (singlePage.value) { + hideChrome.value = !hideChrome.value; + return; + } + const el = stage.value as + | (HTMLElement & { webkitRequestFullscreen?: () => Promise | void }) + | null; + if (!el) return; + try { + if (!fullscreenElement()) { + const req = el.requestFullscreen ?? el.webkitRequestFullscreen; + if (!req) return; + await req.call(el); + } else { + const exit = + document.exitFullscreen ?? + (document as Document & { webkitExitFullscreen?: () => Promise | void }) + .webkitExitFullscreen; + if (exit) await exit.call(document); + } + } catch { + /* mobile / iframe / permission — ignore */ + } +} + +function onFullscreenChange() { + fullscreen.value = Boolean(fullscreenElement()); +} + function onResize() { + if (fullscreenElement()) return; windowInner.value = window.innerWidth; } @@ -136,12 +180,16 @@ function armAutoplay() { onMounted(() => { window.addEventListener("keydown", onKey); window.addEventListener("resize", onResize); + document.addEventListener("fullscreenchange", onFullscreenChange); + document.addEventListener("webkitfullscreenchange", onFullscreenChange); armAutoplay(); }); onUnmounted(() => { window.removeEventListener("keydown", onKey); window.removeEventListener("resize", onResize); + document.removeEventListener("fullscreenchange", onFullscreenChange); + document.removeEventListener("webkitfullscreenchange", onFullscreenChange); window.clearTimeout(delayTimer); stopLoop(); }); @@ -152,25 +200,42 @@ watch( index.value = 0; fallbackSlide.value = false; playing.value = true; + hideChrome.value = false; armAutoplay(); }, );