feat(settings): touch screen optimization

This commit is contained in:
Hakadao
2024-09-09 00:59:58 +08:00
parent a96d2f0ef6
commit a34adefe0b
10 changed files with 149 additions and 105 deletions

View File

@@ -86,6 +86,9 @@ settings:
mandarin_cn: 官话 - 简体中文
mandarin_tw: 官話 - 正體中文
jyut: 廣東話
touch_screen_optimization: 触屏体验改进
touch_screen_optimization_desc: >
启用后,如顶栏,你可能需要点击项目以查看更多信息,而不是鼠标直接悬停查看。(旧版顶栏不支持此功能)
chk_box:
show: 显示
hidden: 隐藏

View File

@@ -86,6 +86,9 @@ settings:
mandarin_cn: 官话 - 简体中文
mandarin_tw: 官話 - 正體中文
jyut: 廣東話
touch_screen_optimization: 觸控螢幕體驗改進
touch_screen_optimization_desc: >
啟用後,如頂欄,你可能需要點選項目才能看到更多資訊,而不是滑鼠移過去就會顯示。(舊版頂欄不支援此功能)
chk_box:
show: 顯示
hidden: 隱藏

View File

@@ -86,6 +86,9 @@ settings:
mandarin_cn: Mandarin - Simplified Chinese
mandarin_tw: Mandarin - Traditional Chinese
jyut: Cantonese
touch_screen_optimization: Touch screen optimization
touch_screen_optimization_desc: >
After this setting is turned on, like the top bar, you must click on items to view more information instead of hovering over them. (This feature is not supported by the old top bar.)
chk_box:
show: Show
hidden: Hidden

View File

@@ -86,6 +86,9 @@ settings:
mandarin_cn: 官话 - 简体中文
mandarin_tw: 官話 - 正體中文
jyut: 廣東話
touch_screen_optimization: 觸控螢幕體驗改進
touch_screen_optimization_desc: >
呢個設定開咗之後,好似頂部欄噉,撳咗啲嘢先至可以睇到更多嘅資料,而唔係淨係靠游標懸停就睇到。(舊版頂部欄唔支援呢個功能)
chk_box:
show: 擺出嚟
hidden: 收埋

View File

@@ -58,6 +58,10 @@ watch(() => settings.value.language, (newValue) => {
/>
</SettingsItem>
<SettingsItem :title="$t('settings.touch_screen_optimization')" :desc="$t('settings.touch_screen_optimization_desc')">
<Radio v-model="settings.touchScreenOptimization" />
</SettingsItem>
<SettingsItem :title="$t('settings.enable_grid_layout_switcher')">
<Radio v-model="settings.enableGridLayoutSwitcher" />
</SettingsItem>

View File

@@ -126,7 +126,7 @@ function setCurrentTitle() {
flex justify-between items-center
>
<aside
class="group"
:class="{ group: !settings.touchScreenOptimization }"
shrink-0 p="x-4" pos="absolute left--84px" z-2
>
<ul
@@ -134,8 +134,8 @@ function setCurrentTitle() {
--un-shadow: var(--bew-shadow-4), var(--bew-shadow-edge-glow-2);
backdrop-filter: var(--bew-filter-glass-2);
"
flex="~ gap-2 col" rounded="30px hover:25px" p-2 shadow
bg="$bew-content-alt hover:$bew-elevated dark:$bew-elevated dark-hover:$bew-elevated"
flex="~ gap-2 col" rounded="30px group-hover:25px" p-2 shadow
bg="$bew-content-alt group-hover:$bew-elevated dark:$bew-elevated dark-group-hover:$bew-elevated"
scale="group-hover:105" duration-300 overflow-hidden antialiased transform-gpu
border="1 $bew-border-color"
>
@@ -143,7 +143,7 @@ function setCurrentTitle() {
<a
cursor-pointer w="40px group-hover:180px" h-40px
rounded-30px flex items-center overflow-x-hidden
duration-300 bg="hover:$bew-fill-2"
duration-300 bg="group-hover:$bew-fill-2"
:class="{ 'menu-item-activated': menuItem.value === activatedMenuItem }"
@click="changeMenuItem(menuItem.value)"
>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { useMouseInElement } from '@vueuse/core'
import { onClickOutside, useMouseInElement } from '@vueuse/core'
import type { Ref, UnwrapNestedRefs } from 'vue'
import { useApiClient } from '~/composables/api'
@@ -128,8 +128,8 @@ function closeAllTopBarPopup(exceptionKey?: keyof typeof popupVisible) {
const rightSideInactive = computed(() => {
let isInactive = false
Object.entries(popupVisible).forEach(([key, value]) => {
if (value && key !== 'userPanel') {
Object.entries(popupVisible).forEach(([_key, value]) => {
if (value) {
isInactive = true
}
})
@@ -137,104 +137,63 @@ const rightSideInactive = computed(() => {
})
// Channels
const channels = useDelayedHover({
beforeEnter: () => closeAllTopBarPopup('channels'),
enter: () => {
popupVisible.channels = true
},
leave: () => {
popupVisible.channels = false
},
})
const channels = setupTopBarItemHoverEvent('channels')
// Avatar
const avatar = useDelayedHover({
beforeEnter: () => closeAllTopBarPopup('userPanel'),
enter: () => {
popupVisible.userPanel = true
},
beforeLeave: () => {
popupVisible.userPanel = false
},
leave: () => {
popupVisible.userPanel = false
},
})
const avatar = setupTopBarItemHoverEvent('userPanel')
// Notifications
const notifications = useDelayedHover({
beforeEnter: () => closeAllTopBarPopup('notifications'),
enter: () => {
popupVisible.notifications = true
},
leave: () => {
popupVisible.notifications = false
},
})
const notifications = setupTopBarItemHoverEvent('notifications')
// Moments
const moments = useDelayedHover({
beforeEnter: () => closeAllTopBarPopup('moments'),
enter: () => {
popupVisible.moments = true
momentsPopRef.value && momentsPopRef.value.checkIfHasNewMomentsThenUpdateMoments()
},
leave: () => {
popupVisible.moments = false
},
})
const moments = setupTopBarItemHoverEvent('moments')
// Favorites
const favorites = useDelayedHover({
beforeEnter: () => closeAllTopBarPopup('favorites'),
enter: () => {
popupVisible.favorites = true
},
leave: () => {
popupVisible.favorites = false
},
})
const favorites = setupTopBarItemHoverEvent('favorites')
// History
const history = useDelayedHover({
beforeEnter: () => closeAllTopBarPopup('history'),
enter: () => {
popupVisible.history = true
},
leave: () => {
popupVisible.history = false
},
})
const history = setupTopBarItemHoverEvent('history')
// Watch Later
const watchLater = useDelayedHover({
beforeEnter: () => closeAllTopBarPopup('watchLater'),
enter: () => {
popupVisible.watchLater = true
},
leave: () => {
popupVisible.watchLater = false
},
})
const watchLater = setupTopBarItemHoverEvent('watchLater')
// Upload
const upload = useDelayedHover({
beforeEnter: () => closeAllTopBarPopup('upload'),
enter: () => {
popupVisible.upload = true
},
leave: () => {
popupVisible.upload = false
},
})
const upload = setupTopBarItemHoverEvent('upload')
// More
const more = useDelayedHover({
beforeEnter: () => closeAllTopBarPopup(),
enter: () => {
popupVisible.more = true
},
leave: () => popupVisible.more = false,
const more = setupTopBarItemHoverEvent('more')
const topBarItemElements = {
channels,
userPanel: avatar,
notifications,
moments,
favorites,
history,
watchLater,
upload,
}
function setupTopBarItemHoverEvent(key: keyof typeof popupVisible) {
return useDelayedHover({
beforeEnter: () => closeAllTopBarPopup(key),
enter: () => {
popupVisible[key] = true
},
leave: () => {
popupVisible[key] = false
},
})
}
const currentClickedTopBarItem = ref<keyof typeof popupVisible | null>(null)
function handleClickTopBarItem(event: MouseEvent, key: keyof typeof popupVisible) {
if (settings.value.touchScreenOptimization) {
event.preventDefault()
closeAllTopBarPopup(key)
popupVisible[key] = !popupVisible[key]
currentClickedTopBarItem.value = key
}
}
Object.entries(topBarItemElements).forEach(([key, val]) => {
onClickOutside(val, () => {
if (currentClickedTopBarItem.value === key)
popupVisible[key as keyof typeof popupVisible] = false
})
})
// #endregion
@@ -493,6 +452,7 @@ defineExpose({
bg="$bew-elevated hover:$bew-theme-color dark-hover:white"
shadow="[var(--bew-shadow-edge-glow-1),var(--bew-shadow-2)]"
w-46px h-46px transform-gpu
@click="event => handleClickTopBarItem(event, 'channels')"
>
<svg
@@ -580,6 +540,7 @@ defineExpose({
ref="moments"
class="right-side-item"
:class="{ active: popupVisible.moments }"
@click="event => handleClickTopBarItem(event, 'moments')"
>
<template v-if="newMomentsCount > 0">
<div
@@ -601,7 +562,12 @@ defineExpose({
</ALink>
<Transition name="slide-in">
<MomentsPop v-show="popupVisible.moments" ref="momentsPopRef" class="bew-popover" />
<MomentsPop
v-show="popupVisible.moments"
ref="momentsPopRef"
class="bew-popover"
@click.stop="() => {}"
/>
</Transition>
</div>
@@ -610,6 +576,7 @@ defineExpose({
ref="favorites"
class="right-side-item"
:class="{ active: popupVisible.favorites }"
@click="event => handleClickTopBarItem(event, 'favorites')"
>
<ALink
:href="`https://space.bilibili.com/${mid}/favlist`"
@@ -624,6 +591,7 @@ defineExpose({
v-if="popupVisible.favorites"
ref="favoritesPopRef"
class="bew-popover"
@click.stop="() => {}"
/>
</KeepAlive>
</Transition>
@@ -634,6 +602,7 @@ defineExpose({
ref="history"
class="right-side-item"
:class="{ active: popupVisible.history }"
@click="event => handleClickTopBarItem(event, 'history')"
>
<ALink
href="https://www.bilibili.com/account/history"
@@ -644,7 +613,11 @@ defineExpose({
</ALink>
<Transition name="slide-in">
<HistoryPop v-if="popupVisible.history" class="bew-popover" />
<HistoryPop
v-if="popupVisible.history"
class="bew-popover"
@click.stop="() => {}"
/>
</Transition>
</div>
@@ -653,6 +626,7 @@ defineExpose({
ref="watchLater"
class="right-side-item"
:class="{ active: popupVisible.watchLater }"
@click="event => handleClickTopBarItem(event, 'watchLater')"
>
<ALink
href="https://www.bilibili.com/watchlater/#/list"
@@ -665,6 +639,7 @@ defineExpose({
<WatchLaterPop
v-if="popupVisible.watchLater"
class="bew-popover"
@click.stop="() => {}"
/>
</Transition>
</div>
@@ -686,13 +661,18 @@ defineExpose({
ref="more"
class="right-side-item lg:!hidden flex"
:class="{ active: popupVisible.more }"
@click="event => handleClickTopBarItem(event, 'more')"
>
<a title="More">
<div i-mingcute:menu-line />
</a>
<Transition name="slide-in">
<MorePop v-show="popupVisible.more" class="bew-popover" />
<MorePop
v-show="popupVisible.more"
class="bew-popover"
@click.stop="() => {}"
/>
</Transition>
</div>
@@ -708,6 +688,7 @@ defineExpose({
ref="upload"
class="right-side-item"
:class="{ active: popupVisible.upload }"
@click="event => handleClickTopBarItem(event, 'upload')"
>
<a
href="https://member.bilibili.com/platform/upload/video/frame"
@@ -721,6 +702,7 @@ defineExpose({
<UploadPop
v-if="popupVisible.upload"
class="bew-popover"
@click.stop="() => {}"
/>
</Transition>
</div>
@@ -730,6 +712,7 @@ defineExpose({
ref="notifications"
class="right-side-item"
:class="{ active: popupVisible.notifications }"
@click="event => handleClickTopBarItem(event, 'notifications')"
>
<template v-if="unReadMessageCount > 0">
<div
@@ -755,6 +738,7 @@ defineExpose({
<NotificationsPop
v-if="popupVisible.notifications"
class="bew-popover"
@click.stop="() => {}"
/>
</Transition>
</div>
@@ -767,6 +751,7 @@ defineExpose({
ref="avatar"
:class="{ hover: popupVisible.userPanel }"
class="avatar right-side-item"
@click="event => handleClickTopBarItem(event, 'userPanel')"
>
<ALink
ref="avatarImg"
@@ -807,6 +792,7 @@ defineExpose({
:user-info="userInfo"
after:h="!0"
pos="!left-auto !right-0" transform="!translate-x-0"
@click.stop="() => {}"
/>
</Transition>
</div>

View File

@@ -3,10 +3,11 @@ import DOMPurify from 'dompurify'
import { useI18n } from 'vue-i18n'
import { useApiClient } from '~/composables/api'
import { settings } from '~/logic'
import { revokeAccessKey } from '~/utils/authProvider'
import { numFormatter } from '~/utils/dataFormatter'
import { LV0_ICON, LV1_ICON, LV2_ICON, LV3_ICON, LV4_ICON, LV5_ICON, LV6_ICON, LV6_LIGHTNING_ICON } from '~/utils/lvIcons'
import { getCSRF, getUserID } from '~/utils/main'
import { getCSRF, getUserID, isHomePage } from '~/utils/main'
import type { UserInfo, UserStat } from '../types'
import ALink from './ALink.vue'
@@ -111,6 +112,21 @@ function getLvIcon(level: number, isSigma: boolean = false): string {
}
return levelIcons[level] || ''
}
function handleClickChannel() {
if (settings.value.topBarLinkOpenMode === 'newTab') {
window.open(`https://space.bilibili.com/${mid.value}`, '_blank')
}
else if (settings.value.topBarLinkOpenMode === 'currentTabIfNotHomepage') {
if (isHomePage())
window.open(`https://space.bilibili.com/${mid.value}`, '_blank')
else
window.open(`https://space.bilibili.com/${mid.value}`, '_self')
}
else {
window.open(`https://space.bilibili.com/${mid.value}`, '_self')
}
}
</script>
<template>
@@ -121,9 +137,17 @@ function getLvIcon(level: number, isSigma: boolean = false): string {
shadow="[var(--bew-shadow-3),var(--bew-shadow-edge-glow-1)]"
>
<div
text="xl" font-medium
text="xl" font-medium flex="~ items-center gap-2"
>
{{ userInfo.uname ? userInfo.uname : '-' }}
<Button
v-if="settings.touchScreenOptimization"
type="secondary" strong @click="handleClickChannel"
>
{{ userInfo.uname ? userInfo.uname : '-' }}
</Button>
<span v-else>
{{ userInfo.uname ? userInfo.uname : '-' }}
</span>
</div>
<div
text="xs $bew-text-2"

View File

@@ -1,3 +1,6 @@
import { settings } from '~/logic'
// DISABLED WHEN IN TOUCHSCREEN OPTIMIZATION IS ENABLED IN SETTINGS
export function useDelayedHover({ enterDelay = 250, leaveDelay = 310, beforeEnter, enter, beforeLeave, leave }:
{ enterDelay?: number, leaveDelay?: number, beforeEnter?: Function, enter: Function, beforeLeave?: Function, leave: Function }) {
const el = ref<HTMLElement>()
@@ -40,8 +43,10 @@ export function useDelayedHover({ enterDelay = 250, leaveDelay = 310, beforeEnte
watch(el, (el, _, onCleanup) => {
if (el) {
el.addEventListener('mouseenter', handleMouseEnter)
el.addEventListener('mouseleave', handleMouseLeave)
if (!settings.value.touchScreenOptimization) {
el.addEventListener('mouseenter', handleMouseEnter)
el.addEventListener('mouseleave', handleMouseLeave)
}
}
onCleanup(() => {
@@ -52,5 +57,16 @@ export function useDelayedHover({ enterDelay = 250, leaveDelay = 310, beforeEnte
})
}, { flush: 'post' })
watch(() => settings.value.touchScreenOptimization, (newValue) => {
if (newValue) {
el.value?.removeEventListener('mouseenter', handleMouseEnter)
el.value?.removeEventListener('mouseleave', handleMouseLeave)
}
else {
el.value?.addEventListener('mouseenter', handleMouseEnter)
el.value?.addEventListener('mouseleave', handleMouseLeave)
}
}, { immediate: true })
return el
}

View File

@@ -10,6 +10,7 @@ export const accessKey = useStorageLocal('accessKey', '')
export interface Settings {
language: string
touchScreenOptimization: boolean
enableGridLayoutSwitcher: boolean
enableHorizontalScrolling: boolean
@@ -83,6 +84,7 @@ export interface Settings {
}
export const settings = useStorageLocal('settings', ref<Settings>({
language: '',
touchScreenOptimization: false,
enableGridLayoutSwitcher: true,
enableHorizontalScrolling: false,