fix(iframe-drawer): resolve content being obscured at the top of video #1023 (#1067)

Co-authored-by: Hakadao <a578457889743@gmail.com>
This commit is contained in:
apades
2024-10-17 00:10:57 +08:00
committed by GitHub
parent 27bfbe93b8
commit 059280fbfd
4 changed files with 86 additions and 5 deletions

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import { onKeyStroke, useEventListener } from '@vueuse/core'
import { DRAWER_VIDEO_ENTER_PAGE_FULL, DRAWER_VIDEO_EXIT_PAGE_FULL } from '~/constants/globalEvents'
import { isHomePage } from '~/utils/main'
// TODO: support shortcuts like `Ctrl+Alt+T` to open in new tab, `Esc` to close
@@ -15,9 +16,13 @@ const emit = defineEmits<{
}>()
const show = ref(false)
const headerShow = ref(false)
const iframeRef = ref<HTMLIFrameElement | null>(null)
const currentUrl = ref<string>(props.url)
const delayCloseTimer = ref<NodeJS.Timeout | null>(null)
const inIframe = computed((): boolean => {
return window.self !== window.top
})
useEventListener(window, 'popstate', updateIframeUrl)
nextTick(() => {
@@ -27,6 +32,7 @@ nextTick(() => {
onMounted(async () => {
history.pushState(null, '', props.url)
show.value = true
headerShow.value = true
await nextTick()
iframeRef.value?.focus()
})
@@ -69,6 +75,7 @@ async function handleClose() {
}
await releaseIframeResources()
show.value = false
headerShow.value = false
delayCloseTimer.value = setTimeout(() => {
emit('close')
}, 300)
@@ -95,10 +102,13 @@ function handleOpenInNewTab() {
const isEscPressed = ref<boolean>(false)
const escPressedTimer = ref<NodeJS.Timeout | null>(null)
const disableEscPress = ref<boolean>(false)
nextTick(() => {
onKeyStroke('Escape', (e: KeyboardEvent) => {
e.preventDefault()
if (disableEscPress.value)
return
if (isEscPressed.value) {
handleClose()
}
@@ -114,6 +124,24 @@ nextTick(() => {
}, { target: iframeRef.value?.contentWindow })
})
watchEffect(() => {
if (inIframe.value)
return null
useEventListener(window, 'message', ({ data }) => {
switch (data) {
case DRAWER_VIDEO_ENTER_PAGE_FULL:
headerShow.value = false
disableEscPress.value = true
break
case DRAWER_VIDEO_EXIT_PAGE_FULL:
headerShow.value = true
disableEscPress.value = false
break
}
})
})
// const keys = useMagicKeys()
// const ctrlAltT = keys['Ctrl+Alt+T']
@@ -140,7 +168,7 @@ nextTick(() => {
<Transition name="fade">
<div
v-if="show"
v-if="headerShow"
pos="relative top-0" flex="~ items-center justify-end gap-2"
max-w="$bew-page-max-width" w-full h="$bew-top-bar-height"
m-auto px-4
@@ -195,15 +223,18 @@ nextTick(() => {
<Transition name="drawer">
<div
v-if="show"
pos="absolute top-$bew-top-bar-height left-0" of-hidden bg="$bew-bg"
:pos="`absolute ${headerShow ? 'top-$bew-top-bar-height' : 'top-0'} left-0`" of-hidden bg="$bew-bg"
rounded="t-$bew-radius" w-full h-full
>
<iframe
ref="iframeRef"
:src="currentUrl"
:style="{
bottom: headerShow ? `var(--bew-top-bar-height)` : '0',
}"
frameborder="0"
pointer-events-auto
pos="absolute bottom-$bew-top-bar-height left-0"
pos="absolute left-0"
w-full h-full
/>
</div>

View File

@@ -1,3 +1,5 @@
export const TOP_BAR_VISIBILITY_CHANGE = 'topBarVisibilityChange'
export const OVERLAY_SCROLL_BAR_SCROLL = 'overlayScrollBarScroll'
export const BEWLY_MOUNTED = 'bewlyMounted'
export const DRAWER_VIDEO_ENTER_PAGE_FULL = 'drawerVideoEnterPageFull'
export const DRAWER_VIDEO_EXIT_PAGE_FULL = 'drawerVideoExitPageFull'

View File

@@ -4,10 +4,10 @@ import type { Ref } from 'vue'
import type { BewlyAppProvider } from '~/composables/useAppProvider'
import { useDark } from '~/composables/useDark'
import { BEWLY_MOUNTED, OVERLAY_SCROLL_BAR_SCROLL } from '~/constants/globalEvents'
import { BEWLY_MOUNTED, DRAWER_VIDEO_ENTER_PAGE_FULL, DRAWER_VIDEO_EXIT_PAGE_FULL, OVERLAY_SCROLL_BAR_SCROLL } from '~/constants/globalEvents'
import { AppPage } from '~/enums/appEnums'
import { settings } from '~/logic'
import { isHomePage, scrollToTop } from '~/utils/main'
import { isHomePage, queryDomUntilFound, scrollToTop } from '~/utils/main'
import emitter from '~/utils/mitt'
import { setupNecessarySettingsWatchers } from './necessarySettingsWatchers'
@@ -127,6 +127,35 @@ function openIframeDrawer(url: string) {
showIframeDrawer.value = true
}
// In drawer video, watch btn className changed and post message to parent
watchEffect(async (onCleanUp) => {
if (!inIframe.value)
return null
const observer = new MutationObserver(([{ target: el }]) => {
if (!(el instanceof HTMLElement))
return null
if (el.classList.contains('bpx-state-entered')) {
parent.postMessage(DRAWER_VIDEO_ENTER_PAGE_FULL)
}
else {
parent.postMessage(DRAWER_VIDEO_EXIT_PAGE_FULL)
}
})
const abort = new AbortController()
queryDomUntilFound('.bpx-player-ctrl-btn.bpx-player-ctrl-web', 500, abort).then((openVideo2WebFullBtn) => {
if (!openVideo2WebFullBtn)
return
observer.observe(openVideo2WebFullBtn, { attributes: true })
})
onCleanUp(() => {
observer.disconnect()
abort.abort()
})
})
/**
* Checks if the current viewport has a scrollbar.
* @returns {boolean} Returns true if the viewport has a scrollbar, false otherwise.

View File

@@ -255,3 +255,22 @@ export function compareVersions(version1: string, version2: string): number {
return 0 // Versions are equal
}
export function queryDomUntilFound(selector: string, timeout = 500, abort?: AbortController): Promise<HTMLElement | null> {
return new Promise((resolve) => {
const interval = setInterval(() => {
const element = document.querySelector(selector)
if (element) {
clearInterval(interval)
resolve(element as HTMLElement)
}
}, timeout)
if (abort) {
abort.signal.addEventListener('abort', () => {
clearInterval(interval)
resolve(null)
})
}
})
}