mirror of
https://github.com/BewlyBewly/BewlyBewly.git
synced 2025-04-14 13:15:29 +00:00
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:
@@ -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
|
||||
|
||||
175
src/components/IframeDrawer.vue
Normal file
175
src/components/IframeDrawer.vue
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user