feat(iframe-page): improve loading experience and support for new watch later URL

- Add loading state and transition for iframe content
- Implement delayed loading animation to prevent unnecessary flashes
- Update supported URL patterns for watch later pages
- Enhance iframe page rendering with smooth transitions
- Add support for new Bilibili watch later list URL format
This commit is contained in:
Hakadao
2025-02-23 04:02:57 +08:00
parent 410e8648c4
commit 8d887a660d
5 changed files with 79 additions and 37 deletions

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import { useDark } from '~/composables/useDark'
import { delay } from '~/utils/main'
const props = defineProps<{
url: string
@@ -8,12 +9,32 @@ const { isDark } = useDark()
const headerShow = ref(false)
const iframeRef = ref<HTMLIFrameElement | null>(null)
const currentUrl = ref<string>(props.url)
const showIframe = ref<boolean>(false)
const showLoading = ref<boolean>(false)
watch(() => isDark.value, (newValue) => {
iframeRef.value?.contentDocument?.documentElement.classList.toggle('dark', newValue)
iframeRef.value?.contentDocument?.body?.classList.toggle('dark', newValue)
})
watch(() => props.url, () => {
showIframe.value = false
})
// Only show loading animation after 2 seconds to prevent annoying flash when content loads quickly
const showLoadingTimeout = ref()
watch(() => showIframe.value, async (newValue) => {
clearTimeout(showLoadingTimeout.value)
if (!newValue) {
showLoadingTimeout.value = setTimeout(() => {
showLoading.value = true
}, 2000)
}
else {
showLoading.value = false
}
}, { immediate: true })
onMounted(() => {
nextTick(() => {
iframeRef.value?.focus()
@@ -67,18 +88,25 @@ defineExpose({
<div
pos="relative top-0 left-0" of-hidden w-full h-full
>
<!-- Iframe -->
<iframe
ref="iframeRef"
:src="props.url"
:style="{
bottom: headerShow ? `var(--bew-top-bar-height)` : '0',
}"
frameborder="0"
pointer-events-auto
pos="absolute left-0"
w-inherit h-inherit
/>
<Transition name="fade">
<Loading v-if="showLoading" w-full h-full pos="absolute top-0 left-0" />
</Transition>
<Transition name="fade">
<!-- Iframe -->
<iframe
v-show="showIframe"
ref="iframeRef"
:src="props.url"
:style="{
bottom: headerShow ? `var(--bew-top-bar-height)` : '0',
}"
frameborder="0"
pointer-events-auto
pos="absolute left-0"
w-inherit h-inherit
@load="showIframe = true"
/>
</Transition>
</div>
</template>

View File

@@ -54,6 +54,7 @@ function isSupportedPages(): boolean {
|| /https?:\/\/(?:www\.)?bilibili\.com\/account\/history.*/.test(currentUrl)
// watcher later page
|| /https?:\/\/(?:www\.)?bilibili\.com\/watchlater\/#\/list.*/.test(currentUrl)
|| /https?:\/\/(?:www\.)?bilibili\.com\/watchlater\/list.*/.test(currentUrl)
// user space page
|| /https?:\/\/space\.bilibili\.com\.*/.test(currentUrl)
// notifications page
@@ -97,6 +98,7 @@ export function isSupportedIframePages(): boolean {
|| /https?:\/\/space\.bilibili\.com\/\d+\/favlist.*/.test(currentUrl)
|| /https?:\/\/www\.bilibili\.com\/history.*/.test(currentUrl)
|| /https?:\/\/www\.bilibili\.com\/watchlater\/#\/list.*/.test(currentUrl)
|| /https?:\/\/www\.bilibili\.com\/watchlater\/list.*/.test(currentUrl)
// moments page
// https://github.com/BewlyBewly/BewlyBewly/issues/1246
// https://github.com/BewlyBewly/BewlyBewly/issues/1256

View File

@@ -387,28 +387,33 @@ provide<BewlyAppProvider>('BEWLY_APP', {
height: showBewlyPage || iframePageURL ? '100dvh' : '0',
}"
>
<template v-if="showBewlyPage">
<OverlayScrollbarsComponent ref="scrollbarRef" element="div" h-inherit defer @os-scroll="handleOsScroll">
<main m-auto max-w="$bew-page-max-width">
<div
p="t-[calc(var(--bew-top-bar-height)+10px)]" m-auto
w="lg:[calc(100%-200px)] [calc(100%-150px)]"
>
<!-- control button group -->
<BackToTopOrRefreshButton
v-if="activatedPage !== AppPage.Search && !settings.moveBackToTopOrRefreshButtonToDock"
@refresh="handleThrottledPageRefresh"
@back-to-top="handleThrottledBackToTop"
/>
<Transition name="fade">
<template v-if="showBewlyPage">
<OverlayScrollbarsComponent ref="scrollbarRef" element="div" h-inherit defer @os-scroll="handleOsScroll">
<main m-auto max-w="$bew-page-max-width">
<div
p="t-[calc(var(--bew-top-bar-height)+10px)]" m-auto
w="lg:[calc(100%-200px)] [calc(100%-150px)]"
>
<!-- control button group -->
<BackToTopOrRefreshButton
v-if="activatedPage !== AppPage.Search && !settings.moveBackToTopOrRefreshButtonToDock"
@refresh="handleThrottledPageRefresh"
@back-to-top="handleThrottledBackToTop"
/>
<Transition name="page-fade">
<Component :is="pages[activatedPage]" />
</Transition>
</div>
</main>
</OverlayScrollbarsComponent>
</template>
<IframePage v-else-if="iframePageURL && !isInIframe()" ref="iframePageRef" :url="iframePageURL" />
<Transition name="page-fade">
<Component :is="pages[activatedPage]" />
</Transition>
</div>
</main>
</OverlayScrollbarsComponent>
</template>
</Transition>
<Transition v-if="!showBewlyPage && iframePageURL && !isInIframe()" name="fade">
<IframePage ref="iframePageRef" :url="iframePageURL" />
</Transition>
</div>
<IframeDrawer

View File

@@ -11,7 +11,7 @@ export interface DockItem {
page: AppPage
openInNewTab: boolean
useOriginalBiliPage: boolean
url: string
url: string | string[]
hasBewlyPage: boolean // Whether BewlyBewly has a page for this item
}
@@ -80,7 +80,7 @@ export const useMainStore = defineStore('main', () => {
page: AppPage.WatchLater,
openInNewTab: false,
useOriginalBiliPage: false,
url: `https://www.bilibili.com/watchlater/#/list`,
url: `https://www.bilibili.com/watchlater/list`,
hasBewlyPage: true,
},
{

View File

@@ -173,7 +173,7 @@ export function isVideoOrBangumiPage(url: string = location.href): boolean {
// anime playback & movie page
|| /https?:\/\/(?:www\.)?bilibili\.com\/bangumi\/play\/.*/.test(url)
// watch later playlist
|| /https?:\/\/(?:www\.)?bilibili\.com\/list\/watchlater.*/.test(url)
|| /https?:\/\/(?:www\.)?bilibili\.com\/list\/watchlater\?bvid.*/.test(url)
|| /https?:\/\/(?:www\.)?bilibili\.com\/watchlater\/list.*/.test(url)
// favorite playlist
|| /https?:\/\/(?:www\.)?bilibili\.com\/list\/ml.*/.test(url)) {
@@ -316,5 +316,12 @@ export function queryDomUntilFound(selector: string, timeout = 500, abort?: Abor
* @returns true if the current page is in an iframe
*/
export function isInIframe(): boolean {
return window.self !== window.top
try {
return window.self !== window.top
}
catch (e) {
// If we can't access window.top due to security restrictions,
// we're definitely in an iframe
return true
}
}