mirror of
https://github.com/BewlyBewly/BewlyBewly.git
synced 2025-04-14 13:15:29 +00:00
377 lines
11 KiB
Vue
377 lines
11 KiB
Vue
<script setup lang="ts">
|
|
import { useDark, useToggle } from '@vueuse/core'
|
|
import 'uno.css'
|
|
import { useI18n } from 'vue-i18n'
|
|
import browser from 'webextension-polyfill'
|
|
import Home from './Home/Home.vue'
|
|
import Search from './Search/Search.vue'
|
|
import Anime from './Anime/Anime.vue'
|
|
import History from './History/History.vue'
|
|
import WatchLater from './WatchLater/WatchLater.vue'
|
|
import Favorites from './Favorites/Favorites.vue'
|
|
import Video from './Video/Video.vue'
|
|
import { accessKey, activatedPage, settings } from '~/logic'
|
|
import '~/styles/index.ts'
|
|
import { AppPage, LanguageType } from '~/enums/appEnums'
|
|
import { getUserID, hexToRGBA } from '~/utils/main'
|
|
|
|
const { locale } = useI18n()
|
|
const [showSettings, toggle] = useToggle(false)
|
|
const isDark = useDark({
|
|
onChanged: (isDark: boolean) => {
|
|
if (isDark) {
|
|
document.querySelector('html')?.classList.add('dark')
|
|
document.querySelector('#bewly')?.classList.add('dark')
|
|
}
|
|
else {
|
|
document.querySelector('html')?.classList.remove('dark')
|
|
document.querySelector('#bewly')?.classList.remove('dark')
|
|
}
|
|
},
|
|
})
|
|
const toggleDark = useToggle(isDark)
|
|
const pages = { Home, Search, Anime, History, WatchLater, Favorites, Video }
|
|
const isVideoPage = ref<boolean>(false)
|
|
const mainApp = ref<HTMLElement>()
|
|
|
|
const tooltipPlacement = computed(() => {
|
|
if (settings.value.dockPosition === 'left')
|
|
return 'right'
|
|
else if (settings.value.dockPosition === 'right')
|
|
return 'left'
|
|
else if (settings.value.dockPosition === 'bottom')
|
|
return 'top'
|
|
return 'right'
|
|
})
|
|
|
|
watch(
|
|
() => activatedPage.value,
|
|
(newValue, oldValue) => {
|
|
setTimeout(() => {
|
|
window.scrollTo(0, 0)
|
|
}, 500)
|
|
},
|
|
)
|
|
|
|
watch(
|
|
() => settings.value.themeColor,
|
|
(newValue) => {
|
|
const bewlyElement = document.querySelector('#bewly') as HTMLElement
|
|
if (bewlyElement) {
|
|
bewlyElement.style.setProperty('--bew-theme-color', newValue)
|
|
for (let i = 0; i < 9; i++)
|
|
bewlyElement.style.setProperty(`--bew-theme-color-${i + 1}0`, hexToRGBA(newValue, i * 0.1 + 0.1))
|
|
}
|
|
},
|
|
)
|
|
|
|
// Watch for changes in the 'isDark' variable and add the 'dark' class to the 'mainApp' element
|
|
// to prevent some Unocss dark-specific styles from failing to take effect
|
|
watch(() => isDark.value, () => {
|
|
setAppAppearance()
|
|
})
|
|
|
|
watch(() => settings.value.language, () => {
|
|
setAppLanguage()
|
|
})
|
|
|
|
watch(() => accessKey.value, () => {
|
|
// Clear accessKey if not logged in
|
|
if (!getUserID())
|
|
accessKey.value = ''
|
|
})
|
|
|
|
watch(() => settings.value.wallpaper, (newValue) => {
|
|
document.documentElement.style.backgroundImage = `url(${newValue})`
|
|
document.documentElement.style.backgroundSize = 'cover'
|
|
document.documentElement.style.backgroundAttachment = 'fixed'
|
|
document.documentElement.style.backgroundPosition = 'center'
|
|
})
|
|
|
|
watch(() => settings.value.wallpaperMaskOpacity, (newValue) => {
|
|
const bewlyElement = document.querySelector('#bewly') as HTMLElement
|
|
|
|
bewlyElement.style
|
|
.setProperty('--bew-bg-mask-opacity', `${newValue}%`)
|
|
})
|
|
|
|
onMounted(() => {
|
|
nextTick(() => {
|
|
setTimeout(() => {
|
|
if (mainApp.value)
|
|
mainApp.value.style.opacity = '1'
|
|
}, 800)
|
|
})
|
|
|
|
if (/https?:\/\/(www.)?bilibili.com\/video\/.*/.test(location.href))
|
|
isVideoPage.value = true
|
|
setAppAppearance()
|
|
setAppLanguage()
|
|
})
|
|
|
|
function changeActivatePage(pageName: AppPage) {
|
|
activatedPage.value = pageName
|
|
}
|
|
|
|
async function setAppLanguage() {
|
|
// if there is first-time load extension, set the default language by browser display language
|
|
if (!settings.value.language) {
|
|
if (browser.i18n.getUILanguage() === 'zh-CN') {
|
|
settings.value.language = LanguageType.Mandarin_CN
|
|
}
|
|
else if (browser.i18n.getUILanguage() === 'zh-TW') {
|
|
// Since getUILanguage() cannot get the zh-HK language code
|
|
// use getAcceptLanguages() to get the language code
|
|
const languages: string[] = await browser.i18n.getAcceptLanguages()
|
|
if (languages.includes('zh-HK'))
|
|
settings.value.language = LanguageType.Cantonese
|
|
else settings.value.language = LanguageType.Mandarin_TW
|
|
}
|
|
else {
|
|
settings.value.language = LanguageType.English
|
|
}
|
|
}
|
|
|
|
locale.value = settings.value.language
|
|
}
|
|
|
|
/**
|
|
* Watch for changes in the 'isDark' variable and add the 'dark' class to the 'mainApp' element
|
|
* to prevent some Unocss dark-specific styles from failing to take effect
|
|
*/
|
|
function setAppAppearance() {
|
|
if (isDark.value)
|
|
mainApp.value?.classList.add('dark')
|
|
|
|
else
|
|
mainApp.value?.classList.remove('dark')
|
|
|
|
// Override Bilibili Evolved background color
|
|
document.body.style.setProperty('background-color', 'unset', 'important')
|
|
document.documentElement.style.setProperty('background-color', 'var(--bew-bg)', 'important')
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<!-- background mask -->
|
|
<div
|
|
v-if="settings.enableWallpaperMasking"
|
|
pos="absolute top-0 left-0" w-full h-full pointer-events-none will-change-contents bg="$bew-bg-mask" z--1
|
|
:style="{ backdropFilter: `blur(${settings.wallpaperBlurIntensity}px)` }"
|
|
/>
|
|
<div ref="mainApp" text="$bew-text-1" transition="opacity duration-300" h-100vh overflow-y-scroll>
|
|
<div m-auto max-w="$bew-page-max-width" :style="{ opacity: showSettings ? 0.6 : 1 }">
|
|
<Transition name="topbar">
|
|
<Topbar
|
|
v-show="settings.isShowTopbar"
|
|
:show-search-bar="activatedPage !== AppPage.Search"
|
|
class="absolute top-0 left-0 z-50"
|
|
/>
|
|
</Transition>
|
|
|
|
<div flex="~" max-w="$bew-page-max-width">
|
|
<aside
|
|
class="dock-wrap"
|
|
:class="{
|
|
left: settings.dockPosition === 'left',
|
|
right: settings.dockPosition === 'right',
|
|
bottom: settings.dockPosition === 'bottom',
|
|
}"
|
|
pos="absolute top-0" flex="~ col" h-100vh justify-center z-999
|
|
>
|
|
<div
|
|
class="dock-content"
|
|
p-2 m-2 bg="$bew-content-1" flex="~ col gap-2 shrink-0"
|
|
rounded="$bew-radius"
|
|
shadow="$bew-shadow-2"
|
|
style="backdrop-filter: var(--bew-filter-glass)"
|
|
>
|
|
<Tooltip :content="$t('dock.search')" :placement="tooltipPlacement">
|
|
<button
|
|
class="dock-item"
|
|
:class="{ active: activatedPage === AppPage.Search && !isVideoPage }"
|
|
@click="changeActivatePage(AppPage.Search)"
|
|
>
|
|
<tabler:search />
|
|
</button>
|
|
</Tooltip>
|
|
|
|
<Tooltip :content="$t('dock.home')" :placement="tooltipPlacement">
|
|
<button
|
|
class="dock-item"
|
|
:class="{ active: activatedPage === AppPage.Home && !isVideoPage }"
|
|
@click="changeActivatePage(AppPage.Home)"
|
|
>
|
|
<tabler:home />
|
|
</button>
|
|
</Tooltip>
|
|
|
|
<Tooltip :content="$t('dock.anime')" :placement="tooltipPlacement">
|
|
<button
|
|
class="dock-item"
|
|
:class="{ active: activatedPage === AppPage.Anime && !isVideoPage }"
|
|
@click="changeActivatePage(AppPage.Anime)"
|
|
>
|
|
<tabler:device-tv />
|
|
</button>
|
|
</Tooltip>
|
|
|
|
<Tooltip :content="$t('dock.history')" :placement="tooltipPlacement">
|
|
<button
|
|
class="dock-item"
|
|
:class="{ active: activatedPage === AppPage.History && !isVideoPage }"
|
|
@click="changeActivatePage(AppPage.History)"
|
|
>
|
|
<tabler:clock />
|
|
</button>
|
|
</Tooltip>
|
|
|
|
<Tooltip :content="$t('dock.watch_later')" :placement="tooltipPlacement">
|
|
<button
|
|
class="dock-item"
|
|
:class="{ active: activatedPage === AppPage.WatchLater && !isVideoPage }"
|
|
@click="changeActivatePage(AppPage.WatchLater)"
|
|
>
|
|
<iconoir:playlist-play />
|
|
</button>
|
|
</Tooltip>
|
|
|
|
<!-- <div
|
|
class="dock-item"
|
|
:class="{ active: activatedPage === AppPage.Favorites }"
|
|
@click="changeActivatePage(AppPage.Favorites)"
|
|
>
|
|
<tabler:star />
|
|
</div> -->
|
|
|
|
<template v-if="isVideoPage">
|
|
<!-- dividing line -->
|
|
<div class="divider" />
|
|
|
|
<!-- video page -->
|
|
<button class="dock-item video active">
|
|
<tabler:player-play />
|
|
</button>
|
|
</template>
|
|
|
|
<!-- dividing line -->
|
|
<div class="divider" />
|
|
|
|
<Tooltip :content="isDark ? $t('dock.dark_mode') : $t('dock.light_mode')" :placement="tooltipPlacement">
|
|
<button class="dock-item" @click="toggleDark()">
|
|
<tabler:moon-stars v-if="isDark" />
|
|
<tabler:sun v-else />
|
|
</button>
|
|
</Tooltip>
|
|
|
|
<Tooltip :content="$t('dock.settings')" :placement="tooltipPlacement">
|
|
<button class="dock-item" @click="toggle()">
|
|
<tabler:settings />
|
|
</button>
|
|
</Tooltip>
|
|
</div>
|
|
</aside>
|
|
|
|
<main
|
|
p="t-80px" m-auto
|
|
:w="isVideoPage ? '[calc(100%-160px)]' : 'lg:85% md:[calc(90%-60px)] [calc(100%-120px)]'"
|
|
relative
|
|
>
|
|
<Transition name="fade">
|
|
<Component :is="pages[activatedPage]" absolute w-full />
|
|
<!-- <Video v-else /> -->
|
|
</Transition>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
<!-- settings dialog -->
|
|
<Settings v-if="showSettings" @close="showSettings = false" />
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.topbar-enter-active,
|
|
.topbar-leave-active {
|
|
transition: all 0.5s ease;
|
|
}
|
|
|
|
.topbar-enter-from,
|
|
.topbar-leave-to {
|
|
--at-apply: opacity-0 transform -translate-y-full;
|
|
}
|
|
|
|
.dock-wrap {
|
|
&.left {
|
|
--at-apply: left-0;
|
|
}
|
|
|
|
&.right {
|
|
--at-apply: right-0;
|
|
}
|
|
|
|
&.bottom {
|
|
--at-apply: top-unset bottom-0 items-center w-full h-[fit-content];
|
|
|
|
.dock-content {
|
|
--at-apply: flex-row;
|
|
}
|
|
}
|
|
|
|
.divider {
|
|
--at-apply: my-2 w-full h-2px bg-$bew-fill-3;
|
|
}
|
|
|
|
&.bottom .divider {
|
|
--at-apply: w-2px h-auto my-0 mx-2;
|
|
}
|
|
|
|
.dock-item {
|
|
--shadow: 0 0 30px 4px var(--bew-theme-color-50);
|
|
--shadow-dark: 0 0 30px 4px rgba(255, 255, 255, 0.6);
|
|
--shadow-active: 0 0 20px var(--bew-theme-color-50);
|
|
--shadow-dark-active: 0 0 20px rgba(255, 255, 255, 0.6);
|
|
|
|
--at-apply: transform active:scale-90
|
|
md:w-45px w-35px
|
|
md:p-3 p-2
|
|
md:text-2xl text-xl
|
|
aspect-square relative
|
|
text-$bew-text-1 leading-0 duration-300
|
|
rounded-$bew-radius
|
|
bg-$bew-fill-2 cursor-pointer
|
|
hover:bg-$bew-theme-color hover:text-white hover:shadow-$shadow
|
|
active:shadow-$shadow-active dark-active:shadow-$shadow-dark-active
|
|
dark-hover:bg-white dark-hover:text-black dark-hover:shadow-$shadow-dark;
|
|
|
|
&.active {
|
|
--at-apply: bg-$bew-theme-color dark-bg-white
|
|
text-white dark-text-black
|
|
shadow-$shadow dark:shadow-$shadow-dark
|
|
active:shadow-$shadow-active dark-active:shadow-$shadow-dark-active;
|
|
}
|
|
|
|
&.active.video {
|
|
--shadow: 0 0 30px 4px var(--bew-warning-color-50);
|
|
--shadow-dark: var(--shadow);
|
|
--shadow-active: 0 0 20px var(--bew-warning-color-50);
|
|
--shadow-dark-active: var(--shadow-active);
|
|
|
|
--at-apply: bg-$bew-warning-color text-black;
|
|
}
|
|
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<style lang="scss">
|
|
.fade-enter-active,
|
|
.fade-leave-active {
|
|
transition: all 0.5s ease;
|
|
}
|
|
|
|
.fade-enter-from,
|
|
.fade-leave-to {
|
|
--at-apply: opacity-0;
|
|
}
|
|
</style>
|