feat: add video card open mode config && open video in drawer (#984)

* feat: video card open mode

* update

* update

* fix: non-homepage show the bewly homepage

* fix(video-card): click the more icon will trigger opening drawer effect

* feat(video-drawer): improve ui and functionally

* feat(i18n): add i18n support for video card link opening behavior setting

* chore: reorder settings
This commit is contained in:
Hakadao
2024-09-08 13:36:42 +08:00
committed by GitHub
parent 35bd528524
commit bb3c6b5e2c
13 changed files with 313 additions and 43 deletions

View File

@@ -1,11 +1,13 @@
<script setup lang="ts">
import { useBewlyApp } from '~/composables/useAppProvider'
import { useDark } from '~/composables/useDark'
import { settings } from '~/logic'
import { numFormatter } from '~/utils/dataFormatter'
import { removeHttpFromUrl } from '~/utils/main'
import BangumiCardSkeleton from './BangumiCardSkeleton.vue'
defineProps<{
const props = defineProps<{
skeleton?: boolean
bangumi: Bangumi
horizontal?: boolean
@@ -31,6 +33,15 @@ interface Bangumi {
}
const { isDark } = useDark()
const { openIframeDrawer } = useBewlyApp()
function handleClick(event: MouseEvent) {
if (settings.value.videoCardLinkOpenMode === 'drawer' && props.bangumi.url) {
event.preventDefault()
openIframeDrawer(props.bangumi.url)
}
}
</script>
<template>
@@ -46,6 +57,7 @@ const { isDark } = useDark()
content-visibility-auto intrinsic-size-400px
transition="all ease-in-out 300"
rounded="$bew-radius" h-fit
@click="handleClick"
>
<!-- Cover -->
<div

View File

@@ -0,0 +1,175 @@
<script setup lang="ts">
import { onKeyStroke } from '@vueuse/core'
import { isHomePage } from '~/utils/main'
// TODO: support shortcuts like `Ctrl+Alt+T` to open in new tab, `Esc` to close
const props = defineProps<{
url: string
title?: string
}>()
const emit = defineEmits<{
(e: 'close'): void
}>()
const show = ref(false)
const iframeRef = ref<HTMLIFrameElement | null>(null)
const currentUrl = ref<string>(props.url)
const delayCloseTimer = ref<NodeJS.Timeout | null>(null)
onMounted(async () => {
history.pushState({}, '', props.url)
show.value = true
await nextTick()
iframeRef.value?.contentWindow?.addEventListener('pushstate', updateCurrentUrl)
window.addEventListener('popstate', updateIframeUrl)
})
onUnmounted(() => {
history.pushState({}, '', 'https://www.bilibili.com')
iframeRef.value?.contentWindow?.removeEventListener('pushstate', updateCurrentUrl)
window.removeEventListener('popstate', updateIframeUrl)
})
function updateCurrentUrl() {
if (iframeRef.value?.contentWindow) {
try {
currentUrl.value = iframeRef.value.contentWindow.location.href
history.pushState({}, '', currentUrl.value)
}
catch (error) {
console.error('Unable to access iframe URL:', error)
}
}
}
function updateIframeUrl() {
if (isHomePage()) {
handleClose()
return
}
if (iframeRef.value?.contentWindow) {
iframeRef.value.contentWindow.location.replace(location.href)
// iframeRef.value.contentWindow.location.reload()
}
}
function handleClose() {
if (delayCloseTimer.value) {
clearTimeout(delayCloseTimer.value)
}
show.value = false
delayCloseTimer.value = setTimeout(() => {
emit('close')
}, 300)
}
function handleOpenInNewTab() {
window.open(props.url, '_blank')
}
onKeyStroke('Escape', (e: KeyboardEvent) => {
e.preventDefault()
handleClose()
})
// const keys = useMagicKeys()
// const ctrlAltT = keys['Ctrl+Alt+T']
// watch(() => ctrlAltT, (value) => {
// if (value) {
// console.log('ctrlAltT', value)
// handleOpenInNewTab()
// }
// })
</script>
<template>
<div
pos="absolute top-0 left-0" of-hidden w-full h-full
z-999999
>
<!-- Mask -->
<Transition name="fade">
<div
v-if="show"
pos="absolute bottom-0 left-0" w-full h-full bg="black opacity-60"
@click="handleClose"
/>
</Transition>
<Transition name="fade">
<div
v-if="show"
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
pointer-events-none
>
<Button
style="
--b-button-color: var(--bew-elevated-solid);
--b-button-color-hover: var(--bew-elevated-solid-hover);
"
pointer-events-auto
@click="handleOpenInNewTab"
>
<template #left>
<i i-mingcute:external-link-line />
</template>
{{ $t('iframe_drawer.open_in_new_tab') }}
<!-- <div flex="~">
<kbd>Ctrl</kbd><kbd>Alt</kbd><kbd>T</kbd>
</div> -->
</Button>
<Button
style="
--b-button-color: var(--bew-elevated-solid);
--b-button-color-hover: var(--bew-elevated-solid-hover);
"
pointer-events-auto
@click="handleClose"
>
<template #left>
<i i-mingcute:close-line />
</template>
{{ $t('iframe_drawer.close') }}
<!-- <kbd>Esc</kbd> -->
</Button>
</div>
</Transition>
<!-- Iframe -->
<Transition name="drawer">
<div
v-if="show"
pos="absolute top-$bew-top-bar-height left-0" of-hidden bg="$bew-bg"
rounded="t-$bew-radius" w-full h-full
>
<iframe
ref="iframeRef"
:src="url"
frameborder="0"
pointer-events-auto
pos="absolute bottom-$bew-top-bar-height left-0"
w-full h-full
/>
</div>
</Transition>
</div>
</template>
<style lang="scss" scoped>
.drawer-enter-active,
.drawer-leave-active {
transition: transform 0.3s;
}
.drawer-enter-from,
.drawer-leave-to {
transform: translateY(100%);
}
</style>

View File

@@ -11,20 +11,33 @@ const { t, locale } = useI18n()
const langOptions = computed(() => {
return [
{
value: 'en',
label: t('settings.select_language_opt.english'),
value: 'en',
},
{
value: 'cmn-CN',
label: t('settings.select_language_opt.mandarin_cn'),
value: 'cmn-CN',
},
{
value: 'cmn-TW',
label: t('settings.select_language_opt.mandarin_tw'),
value: 'cmn-TW',
},
{
value: 'jyut',
label: t('settings.select_language_opt.jyut'),
value: 'jyut',
},
]
})
const videoCardOpenModeOptions = computed(() => {
return [
{
label: t('settings.video_card_link_opening_behavior_opt.drawer'),
value: 'drawer',
},
{
label: t('settings.video_card_link_opening_behavior_opt.new_tab'),
value: 'new_tab',
},
]
})
@@ -52,10 +65,6 @@ watch(() => settings.value.language, (newValue) => {
<SettingsItem :title="$t('settings.enable_horizontal_scrolling')" :desc="$t('settings.enable_horizontal_scrolling_desc')">
<Radio v-model="settings.enableHorizontalScrolling" />
</SettingsItem>
<!-- <SettingsItem title="Open link in current tab">
<Radio v-model="settings.openLinkInCurrentTab" />
</SettingsItem> -->
</SettingsItemGroup>
<SettingsItemGroup :title="$t('settings.group_performance')">
@@ -77,6 +86,13 @@ watch(() => settings.value.language, (newValue) => {
</SettingsItemGroup>
<SettingsItemGroup :title="$t('settings.group_video_card')">
<SettingsItem :title="$t('settings.video_card_link_opening_behavior')">
<Select
v-model="settings.videoCardLinkOpenMode"
:options="videoCardOpenModeOptions"
w="full"
/>
</SettingsItem>
<SettingsItem :title="$t('settings.enable_video_preview')">
<Radio v-model="settings.enableVideoPreview" />
</SettingsItem>

View File

@@ -3,6 +3,7 @@ import { Icon } from '@iconify/vue'
import Button from '~/components/Button.vue'
import { useApiClient } from '~/composables/api'
import { useBewlyApp } from '~/composables/useAppProvider'
import { settings } from '~/logic'
import type { VideoPreviewResult } from '~/models/video/videoPreview'
import { calcCurrentTime, calcTimeSince, numFormatter } from '~/utils/dataFormatter'
@@ -64,9 +65,7 @@ const emit = defineEmits<{
}>()
const api = useApiClient()
// Used to click and control herf attribute
const isClick = ref<boolean>(false)
const { openIframeDrawer } = useBewlyApp()
function getCurrentVideoUrl(video: Video) {
const baseUrl = `https://www.bilibili.com/video/${video.bvid ?? `av${video.aid}`}`
@@ -75,7 +74,7 @@ function getCurrentVideoUrl(video: Video) {
}
const videoUrl = computed(() => {
if (props.removed || !isClick.value || !props.video)
if (props.removed || !props.video)
return undefined
if (props.video.url)
@@ -189,14 +188,11 @@ function handelMouseLeave() {
clearTimeout(mouseLeaveTimeOut.value)
}
function switchClickState(flag: boolean) {
if (flag) {
isClick.value = flag
}
else {
setTimeout(() => {
isClick.value = flag
})
function handleClick(event: MouseEvent) {
if (settings.value.videoCardLinkOpenMode === 'drawer' && videoUrl.value) {
event.preventDefault()
openIframeDrawer(videoUrl.value)
}
}
@@ -235,9 +231,7 @@ function handleUndo() {
:href="videoUrl" target="_blank" rel="noopener noreferrer"
@mouseenter="handleMouseEnter"
@mouseleave="handelMouseLeave"
@mousedown="switchClickState(true)"
@mouseup="switchClickState(false)"
@dragend="switchClickState(false)"
@click="handleClick"
>
<!-- Cover -->
<div
@@ -424,7 +418,7 @@ function handleUndo() {
:class="{ 'more-active': moreBtnActive }"
shrink-0 w-30px h-30px m="t--3px r--8px" translate-x--8px
grid place-items-center cursor-pointer rounded="50%" duration-300
@click.prevent="handleMoreBtnClick"
@click.stop.prevent="handleMoreBtnClick"
>
<div i-mingcute:more-2-line text="lg" />
</div>