mirror of
https://github.com/BewlyBewly/BewlyBewly.git
synced 2025-04-14 13:15:29 +00:00
feat(VideoCard): improve not interested feature
This commit is contained in:
@@ -24,6 +24,8 @@ common:
|
||||
refresh: 刷新
|
||||
reset: 重置
|
||||
no_more_content: 没有更多内容了
|
||||
confirm: 确定
|
||||
cancel: 取消
|
||||
settings:
|
||||
title: 设置
|
||||
|
||||
@@ -248,7 +250,8 @@ dock:
|
||||
light_mode: 亮色模式
|
||||
settings: 设置
|
||||
home:
|
||||
not_interested_in: 对哪方面不感兴趣?
|
||||
not_interested: 不感兴趣
|
||||
tell_us_why: 为什么这么做...
|
||||
video_removed: 视频已移除
|
||||
for_you: 个性推荐
|
||||
following: 正在关注
|
||||
|
||||
@@ -24,6 +24,8 @@ common:
|
||||
refresh: 重新整理
|
||||
reset: 重置
|
||||
no_more_content: 沒有更多內容了
|
||||
confirm: 確定
|
||||
cancel: 取消
|
||||
settings:
|
||||
title: 設定
|
||||
|
||||
@@ -251,7 +253,8 @@ dock:
|
||||
light_mode: 淺色模式
|
||||
settings: 設定
|
||||
home:
|
||||
not_interested_in: 對哪方面不感興趣?
|
||||
not_interested: 不感興趣
|
||||
tell_us_why: 為什麼這樣做...
|
||||
video_removed: 影片已移除
|
||||
for_you: 爲你推薦
|
||||
following: 正在跟隨
|
||||
|
||||
@@ -24,6 +24,8 @@ common:
|
||||
refresh: Refresh
|
||||
reset: Reset
|
||||
no_more_content: No more content now, owari da
|
||||
confirm: Confirm
|
||||
cancel: Cancel
|
||||
settings:
|
||||
title: Settings
|
||||
|
||||
@@ -249,7 +251,8 @@ dock:
|
||||
light_mode: Light Mode
|
||||
settings: Settings
|
||||
home:
|
||||
not_interested_in: Not interested in...
|
||||
not_interested: Not interested
|
||||
tell_us_why: Choose a reason why
|
||||
video_removed: Video removed
|
||||
for_you: For you
|
||||
following: Following
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
common:
|
||||
view_all: 睇晒佢哋
|
||||
loading: 撈緊...
|
||||
undo: 整返
|
||||
undo: 整返返去
|
||||
view: '{count}次'
|
||||
viewWithoutNum: 次
|
||||
danmaku: '{count} 彈幕'
|
||||
@@ -24,6 +24,8 @@ common:
|
||||
refresh: 重新整理
|
||||
reset: 重置
|
||||
no_more_content: 唔使睇喇,冇嘢喇
|
||||
confirm: 確定
|
||||
cancel: 取消
|
||||
settings:
|
||||
title: 設定
|
||||
|
||||
@@ -251,8 +253,9 @@ dock:
|
||||
light_mode: 淺色模式
|
||||
settings: 設定
|
||||
home:
|
||||
not_interested_in: 對邊方面冇癮?
|
||||
video_removed: 經已鏟咗條片
|
||||
not_interested: 唔鍾意
|
||||
tell_us_why: 點解噉做...
|
||||
video_removed: 已經鏟咗條片
|
||||
for_you: 估你心水
|
||||
following: Follow 緊
|
||||
trending: 時下至 Hit
|
||||
|
||||
@@ -1,58 +1,57 @@
|
||||
import browser from 'webextension-polyfill'
|
||||
|
||||
function handleMessage(message: any) {
|
||||
// #region APP端api,遺棄
|
||||
/** Recommend Videos */
|
||||
// if (message.contentScriptQuery === 'getRecommendVideos') {
|
||||
// // https://github.com/indefined/UserScripts/blob/master/bilibiliHome/bilibiliHome.API.md#%E8%8E%B7%E5%8F%96%E9%A6%96%E9%A1%B5%E5%86%85%E5%AE%B9
|
||||
// const url = `${APP_URL}/x/feed/index?build=1&idx=${message.idx}&appkey=27eb53fc9058f8c3&access_key=${message.accessKey}`
|
||||
// return fetch(url)
|
||||
// .then(response => response.json())
|
||||
// .then(data => data)
|
||||
// .catch(error => console.error(error))
|
||||
// }
|
||||
// /** Submit a video that is not of interest */
|
||||
// else if (message.contentScriptQuery === 'submitDislike') {
|
||||
// // https://github.com/indefined/UserScripts/blob/master/bilibiliHome/bilibiliHome.API.md#%E6%8F%90%E4%BA%A4%E4%B8%8D%E5%96%9C%E6%AC%A2
|
||||
// let url = `https://app.bilibili.com/x/feed/dislike?access_key=${message.accessKey}
|
||||
// &goto=${message.goto}
|
||||
// &id=${message.id}
|
||||
// &mid=${message.mid}
|
||||
// &reason_id=${message.reasonID}
|
||||
// &rid=${message.rid}
|
||||
// &tag_id=${message.tagID}
|
||||
// &build=5000000`
|
||||
/** Submit a video that is not of interest */
|
||||
if (message.contentScriptQuery === 'dislikeVideo') {
|
||||
// https://github.com/indefined/UserScripts/blob/master/bilibiliHome/bilibiliHome.API.md#%E6%8F%90%E4%BA%A4%E4%B8%8D%E5%96%9C%E6%AC%A2
|
||||
const url = `https://app.bilibili.com/x/feed/dislike?access_key=${message.access_key}`
|
||||
+ `&goto=${message.goto}`
|
||||
+ `&id=${message.id}`
|
||||
+ `&idx=${message.idx}`
|
||||
// + `&mid=${message.mid}`
|
||||
+ `&reason_id=${message.reason_id}`
|
||||
// + `&rid=${message.rid}`
|
||||
// + `&tag_id=${message.tagId}`
|
||||
+ `&device=${message.device}`
|
||||
+ `&mobi_app=${message.mobi_app}`
|
||||
+ `&build=${message.build}`
|
||||
+ `&sign=${message.sign}`
|
||||
+ `&appkey=${message.appkey}`
|
||||
|
||||
// // remove url empty spaces
|
||||
// url = url.replace(/\s+/g, '')
|
||||
// // remove url empty spaces
|
||||
// url = url.replace(/\s+/g, '')
|
||||
|
||||
// return fetch(url)
|
||||
// .then(response => response.json())
|
||||
// .then(data => data)
|
||||
// .catch(error => console.error(error))
|
||||
// }
|
||||
return fetch(url)
|
||||
.then(response => response.json())
|
||||
.then(data => data)
|
||||
.catch(error => console.error(error))
|
||||
}
|
||||
|
||||
// /** Undo a video that is not of interest */
|
||||
// else if (message.contentScriptQuery === 'undoDislike') {
|
||||
// // https://github.com/indefined/UserScripts/blob/master/bilibiliHome/bilibiliHome.API.md#%E6%92%A4%E9%94%80%E4%B8%8D%E5%96%9C%E6%AC%A2
|
||||
// let url = `https://app.bilibili.com/x/feed/dislike/cancel?access_key=${message.accessKey}
|
||||
// &goto=${message.goto}
|
||||
// &id=${message.id}
|
||||
// &mid=${message.mid}
|
||||
// &reason_id=${message.reasonID}
|
||||
// &rid=${message.rid}
|
||||
// &tag_id=${message.tagID}
|
||||
// &build=5000000`
|
||||
/** Undo a video that is not of interest */
|
||||
else if (message.contentScriptQuery === 'undoDislikeVideo') {
|
||||
// https://github.com/indefined/UserScripts/blob/master/bilibiliHome/bilibiliHome.API.md#%E6%92%A4%E9%94%80%E4%B8%8D%E5%96%9C%E6%AC%A2
|
||||
const url = `https://app.bilibili.com/x/feed/dislike/cancel?access_key=${message.access_key}`
|
||||
+ `&goto=${message.goto}`
|
||||
+ `&id=${message.id}`
|
||||
+ `&idx=${message.idx}`
|
||||
// + `&mid=${message.mid}`
|
||||
+ `&reason_id=${message.reason_id}`
|
||||
// + `&rid=${message.rid}`
|
||||
// + `&tag_id=${message.tagId}`
|
||||
+ `&device=${message.device}`
|
||||
+ `&mobi_app=${message.mobi_app}`
|
||||
+ `&build=${message.build}`
|
||||
+ `&sign=${message.sign}`
|
||||
+ `&appkey=${message.appkey}`
|
||||
|
||||
// // remove url empty spaces
|
||||
// url = url.replace(/\s+/g, '')
|
||||
// // remove url empty spaces
|
||||
// url = url.replace(/\s+/g, '')
|
||||
|
||||
// return fetch(url)
|
||||
// .then(response => response.json())
|
||||
// .then(data => data)
|
||||
// .catch(error => console.error(error))
|
||||
// }
|
||||
// #endregion
|
||||
return fetch(url)
|
||||
.then(response => response.json())
|
||||
.then(data => data)
|
||||
.catch(error => console.error(error))
|
||||
}
|
||||
|
||||
if (message.contentScriptQuery === 'getRecommendVideos') {
|
||||
const url = `https://api.bilibili.com/x/web-interface/index/top/feed/rcmd?fresh_idx=${message.refreshIdx}&feed_version=V2&fresh_type=4&ps=30&plat=1`
|
||||
|
||||
@@ -113,7 +113,7 @@ function handleClick(evt: MouseEvent) {
|
||||
}
|
||||
|
||||
&--custom-color {
|
||||
--at-apply: hover:opacity-80;
|
||||
--at-apply: hover:opacity-70;
|
||||
}
|
||||
|
||||
&--strong {
|
||||
|
||||
172
src/components/Dialog.vue
Normal file
172
src/components/Dialog.vue
Normal file
@@ -0,0 +1,172 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { onKeyStroke } from '@vueuse/core'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
title: string
|
||||
desc?: string
|
||||
center?: boolean
|
||||
appendToBewlyBody?: boolean
|
||||
width?: string | number
|
||||
height?: string | number
|
||||
centerFooter?: boolean
|
||||
loading?: boolean
|
||||
preventCloseWhenLoading?: boolean
|
||||
}>(), {
|
||||
preventCloseWhenLoading: true,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close', 'confirm'])
|
||||
|
||||
onKeyStroke('Enter', (e: KeyboardEvent) => {
|
||||
e.preventDefault()
|
||||
if (!props.loading)
|
||||
handleConfirm()
|
||||
})
|
||||
onKeyStroke('Escape', (e: KeyboardEvent) => {
|
||||
e.preventDefault()
|
||||
if (props.loading && props.preventCloseWhenLoading)
|
||||
return
|
||||
handleClose()
|
||||
})
|
||||
|
||||
const showShortcut = ref<boolean>(false)
|
||||
const { mainAppRef } = useBewlyApp()
|
||||
const showDialog = ref<boolean>(false)
|
||||
|
||||
const dialogWidth = computed(() => {
|
||||
return typeof props.width === 'number' ? `${props.width}px` : props.width || '400px'
|
||||
})
|
||||
const dialogHeight = computed(() => {
|
||||
return typeof props.height === 'number' ? `${props.height}px` : props.height || 'auto'
|
||||
})
|
||||
|
||||
onKeyStroke('Alt', (e: KeyboardEvent) => {
|
||||
e.preventDefault()
|
||||
showShortcut.value = true
|
||||
}, { eventName: 'keydown' })
|
||||
onKeyStroke('Alt', (e: KeyboardEvent) => {
|
||||
e.preventDefault()
|
||||
showShortcut.value = false
|
||||
}, { eventName: 'keyup' })
|
||||
|
||||
onMounted(() => {
|
||||
showDialog.value = true
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
handleClose()
|
||||
})
|
||||
|
||||
function handleClose() {
|
||||
if (props.loading && props.preventCloseWhenLoading)
|
||||
return
|
||||
|
||||
showDialog.value = false
|
||||
nextTick(() => {
|
||||
emit('close')
|
||||
})
|
||||
}
|
||||
|
||||
function handleConfirm() {
|
||||
emit('confirm')
|
||||
if (!props.loading)
|
||||
handleClose()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport :to="mainAppRef" :disabled="!appendToBewlyBody">
|
||||
<Transition name="modal">
|
||||
<div
|
||||
v-if="showDialog"
|
||||
class="dialog"
|
||||
pos="fixed top-0 left-0" w-full h-full z-100
|
||||
>
|
||||
<div
|
||||
bg="black opacity-40 dark:opacity-40"
|
||||
pos="absolute top-0 left-0" w-full h-full z-0
|
||||
@click="handleClose"
|
||||
/>
|
||||
<div
|
||||
style="
|
||||
--un-shadow: var(--bew-shadow-3) var(--bew-shadow-edge-glow-2);
|
||||
backdrop-filter: var(--bew-filter-glass-2);
|
||||
"
|
||||
:style="{ width: dialogWidth, height: dialogHeight }"
|
||||
pos="absolute top-1/2 left-1/2" bg="$bew-content-2 dark:$bew-elevated-1" rounded="$bew-radius"
|
||||
transform="translate--1/2" z-2 shadow overflow="x-hidden y-overlay"
|
||||
antialiased
|
||||
>
|
||||
<!-- loading masking -->
|
||||
<Transition name="fade">
|
||||
<div
|
||||
v-if="loading"
|
||||
pos="absolute top-0 left-0" w-full h-full bg="white dark:black opacity-60 dark:opacity-60" flex="~ justify-center items-center"
|
||||
z-2
|
||||
>
|
||||
<Icon icon="svg-spinners:ring-resize" text="4xl" />
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<header
|
||||
style="
|
||||
text-shadow: 0 0 15px var(--bew-elevated-solid-1), 0 0 20px var(--bew-elevated-solid-1)
|
||||
"
|
||||
pos="sticky top-0 left-0" w-full h-80px px-8 flex
|
||||
items-center justify-between
|
||||
rounded="t-$bew-radius" z-1
|
||||
>
|
||||
<div
|
||||
:style="{ textAlign: center ? 'center' : 'left' }"
|
||||
w-full
|
||||
>
|
||||
<p text-xl fw-bold>
|
||||
{{ title }}
|
||||
</p>
|
||||
<p text="sm $bew-text-2">
|
||||
<slot name="desc">
|
||||
{{ desc }}
|
||||
</slot>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style="backdrop-filter: var(--bew-filter-glass-1)"
|
||||
text-2xl leading-0 bg="$bew-fill-1 hover:$bew-theme-color-30" w="32px" h="32px"
|
||||
ml-8 p="1" rounded-8 cursor="pointer"
|
||||
hover:ring="2 $bew-theme-color" hover:text="$bew-theme-color" duration-300
|
||||
@click="handleClose"
|
||||
>
|
||||
<ic-baseline-clear />
|
||||
</div>
|
||||
</header>
|
||||
<main p="x-8 t-0 b-4" relative>
|
||||
<!-- <div h-80px mt--8 /> -->
|
||||
<slot />
|
||||
</main>
|
||||
<footer
|
||||
:style="{ justifyContent: centerFooter ? 'center' : 'flex-end' }"
|
||||
flex="~ gap-2" p="8 t-0"
|
||||
>
|
||||
<Button type="secondary" size="large" @click="handleClose">
|
||||
<div>
|
||||
{{ $t('common.cancel') }}
|
||||
<span v-show="showShortcut" text="xs $bew-text-2">(Esc)</span>
|
||||
</div>
|
||||
</Button>
|
||||
<Button type="primary" size="large" @click="handleConfirm">
|
||||
<div>
|
||||
{{ $t('common.confirm') }}
|
||||
<span v-show="showShortcut" text="xs">(Enter)</span>
|
||||
</div>
|
||||
</Button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { getCSRF, removeHttpFromUrl } from '~/utils/main'
|
||||
import { calcCurrentTime, calcTimeSince, numFormatter } from '~/utils/dataFormatter'
|
||||
import type { VideoPreviewResult } from '~/models/video/videoPreview'
|
||||
@@ -37,8 +38,6 @@ interface Props {
|
||||
moreBtn?: boolean
|
||||
moreBtnActive?: boolean
|
||||
removed?: boolean
|
||||
showDislikeOptions?: boolean
|
||||
dislikeReasons?: { id: number, name: string }[]
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -47,6 +46,8 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'moreClick', event: MouseEvent): MouseEvent
|
||||
(e: 'undo'): void
|
||||
(e: 'tellUsWhy'): void
|
||||
}>()
|
||||
|
||||
const videoUrl = computed(() => {
|
||||
@@ -76,7 +77,6 @@ const wValue = computed((): string => {
|
||||
return 'w-full'
|
||||
})
|
||||
|
||||
const isDislike = ref<boolean>(false)
|
||||
const isInWatchLater = ref<boolean>(false)
|
||||
const isHover = ref<boolean>(false)
|
||||
const contentVisibility = ref<'auto' | 'visible'>('auto')
|
||||
@@ -151,6 +151,10 @@ function handelMouseLeave() {
|
||||
function handleMoreBtnClick(event: MouseEvent) {
|
||||
emit('moreClick', event)
|
||||
}
|
||||
|
||||
function handleUndo() {
|
||||
emit('undo')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -161,21 +165,41 @@ function handleMoreBtnClick(event: MouseEvent) {
|
||||
<div hidden w="xl:280px lg:250px md:200px 200px" />
|
||||
<div hidden w="full" />
|
||||
|
||||
<template v-if="showDislikeOptions">
|
||||
<div flex="~ gap-2 wrap">
|
||||
<button
|
||||
v-for="reason in dislikeReasons" :key="reason.id"
|
||||
p="x-4 y-2" bg="$bew-fill-1 hover:$bew-fill-3" rounded="$bew-radius"
|
||||
cursor-pointer
|
||||
<template v-if="removed">
|
||||
<div
|
||||
:style="{ contentVisibility }"
|
||||
pos="absolute top-0 left-0" w-full aspect-video
|
||||
rounded="$bew-radius"
|
||||
>
|
||||
<img
|
||||
:src="`${removeHttpFromUrl(cover)}@672w_378h_1c`" alt=""
|
||||
w-full h-full object-cover pos="absolute top-0 left-0" aspect-video
|
||||
z--1
|
||||
>
|
||||
{{ reason.name }}
|
||||
</button>
|
||||
|
||||
<div
|
||||
style="backdrop-filter: var(--bew-filter-glass-1);"
|
||||
pos="absolute top-0 left-0" w-full h-full flex="~ col gap-2 items-center justify-center"
|
||||
bg="$bew-fill-4"
|
||||
>
|
||||
<p mb-2 color-white text-lg>
|
||||
{{ $t('home.video_removed') }}
|
||||
</p>
|
||||
<Button
|
||||
color="rgba(255,255,255,.35)" text-color="white" size="small"
|
||||
@click="handleUndo"
|
||||
>
|
||||
<template #left>
|
||||
<Icon icon="mingcute:back-line" text-lg />
|
||||
</template>
|
||||
{{ $t('common.undo') }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div
|
||||
v-else
|
||||
class="video-card group"
|
||||
:class="isDislike ? 'is-dislike' : ''"
|
||||
w="full" pos="absolute top-0 left-0"
|
||||
rounded="$bew-radius" duration-300 ease-in-out
|
||||
bg="hover:$bew-fill-2 active:$bew-fill-3" hover:ring="8 $bew-fill-2" active:ring="8 $bew-fill-3"
|
||||
@@ -425,7 +449,7 @@ function handleMoreBtnClick(event: MouseEvent) {
|
||||
<!-- skeleton -->
|
||||
<template v-if="!horizontal">
|
||||
<div
|
||||
block mb-10 pointer-events-none select-none invisible
|
||||
block mb-6 pointer-events-none select-none invisible
|
||||
>
|
||||
<!-- Cover -->
|
||||
<div w-full shrink-0 aspect-video h-fit rounded="$bew-radius" />
|
||||
@@ -457,7 +481,7 @@ function handleMoreBtnClick(event: MouseEvent) {
|
||||
<template v-else>
|
||||
<div
|
||||
flex="~ gap-6"
|
||||
mb-10 pointer-events-none select-none invisible
|
||||
mb-6 pointer-events-none select-none invisible
|
||||
>
|
||||
<!-- Cover -->
|
||||
<div
|
||||
@@ -489,11 +513,11 @@ function handleMoreBtnClick(event: MouseEvent) {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.video-card.is-dislike {
|
||||
> *:not(#dislike-control) {
|
||||
--at-apply: invisible pointer-events-none duration-0 transition-none;
|
||||
}
|
||||
}
|
||||
// .video-card.is-dislike {
|
||||
// > *:not(#dislike-control) {
|
||||
// --at-apply: invisible pointer-events-none duration-0 transition-none;
|
||||
// }
|
||||
// }
|
||||
|
||||
.more-active {
|
||||
--at-apply: opacity-100 bg-$bew-fill-3;
|
||||
|
||||
@@ -15,7 +15,7 @@ const wValue = computed((): string => {
|
||||
<template>
|
||||
<div
|
||||
v-if="!horizontal"
|
||||
mb-8 pointer-events-none select-none
|
||||
mb-6 pointer-events-none select-none
|
||||
>
|
||||
<div aspect-video bg="$bew-fill-4" rounded="$bew-radius" />
|
||||
<div flex mt-4>
|
||||
@@ -45,7 +45,7 @@ const wValue = computed((): string => {
|
||||
<div
|
||||
v-else
|
||||
flex="~ gap-6"
|
||||
mb-10 pointer-events-none select-none
|
||||
mb-6 pointer-events-none select-none
|
||||
>
|
||||
<!-- By directly using predefined unocss width properties, it is possible to dynamically set the width attribute -->
|
||||
<div hidden w="xl:280px lg:250px md:200px 200px" />
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import type { Ref } from 'vue'
|
||||
import { onClickOutside } from '@vueuse/core'
|
||||
import { onKeyStroke } from '@vueuse/core'
|
||||
import { Type as ThreePointV2Type } from '~/models/video/appForYou'
|
||||
import type { AppForYouResult, Item as AppVideoItem, ThreePointV2 } from '~/models/video/appForYou'
|
||||
import type { Item as VideoItem, forYouResult } from '~/models/video/forYou'
|
||||
import type { GridLayout } from '~/logic'
|
||||
import { accessKey, settings } from '~/logic'
|
||||
import { LanguageType } from '~/enums/appEnums'
|
||||
import { TVAppKey } from '~/utils/authProvider'
|
||||
import { TVAppKey, getTvSign } from '~/utils/authProvider'
|
||||
|
||||
const props = defineProps<{
|
||||
gridLayout: GridLayout
|
||||
@@ -37,10 +37,39 @@ const { handleReachBottom, handlePageRefresh, scrollbarRef } = useBewlyApp()
|
||||
const showVideoOptions = ref<boolean>(false)
|
||||
const videoOptions = ref<ThreePointV2[] | undefined>([])
|
||||
const videoOptionsPosition = reactive<{ top: string, left: string }>({ top: '0', left: '0' })
|
||||
// const activatedVideoId = ref<number>(0)
|
||||
const activatedVideoIdx = ref<number>(0)
|
||||
const activatedVideo = ref<AppVideoItem | null>()
|
||||
const videoCardRef = ref(null)
|
||||
const dislikedVideoIds = ref<number[]>([])
|
||||
const showDislikeDialog = ref<boolean>(false)
|
||||
const selectedDislikeReason = ref<number>(1)
|
||||
const loadingDislikeDialog = ref<boolean>(false)
|
||||
|
||||
onKeyStroke((e: KeyboardEvent) => {
|
||||
if (showDislikeDialog.value) {
|
||||
const dislikeReasons = activatedVideo.value?.three_point_v2?.find(option => option.type === ThreePointV2Type.Dislike)?.reasons || []
|
||||
|
||||
if (e.key >= '0' && e.key <= '9') {
|
||||
e.preventDefault()
|
||||
dislikeReasons.forEach((reason) => {
|
||||
if (dislikeReasons[Number(e.key) - 1] && reason.id === dislikeReasons[Number(e.key) - 1].id)
|
||||
selectedDislikeReason.value = reason.id
|
||||
})
|
||||
}
|
||||
else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault()
|
||||
const currentIndex = dislikeReasons.findIndex(reason => selectedDislikeReason.value === reason.id)
|
||||
if (currentIndex > 0)
|
||||
selectedDislikeReason.value = dislikeReasons[currentIndex - 1].id
|
||||
}
|
||||
else if (e.key === 'ArrowDown') {
|
||||
e.preventDefault()
|
||||
const currentIndex = dislikeReasons.findIndex(reason => selectedDislikeReason.value === reason.id)
|
||||
if (currentIndex < dislikeReasons.length - 1)
|
||||
selectedDislikeReason.value = dislikeReasons[currentIndex + 1].id
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => settings.value.recommendationMode, () => {
|
||||
initData()
|
||||
@@ -177,12 +206,13 @@ async function getAppRecommendVideos() {
|
||||
}
|
||||
|
||||
function handleMoreClick(e: MouseEvent, data: AppVideoItem) {
|
||||
if (activatedVideo.value && activatedVideo.value?.idx === data.idx) {
|
||||
if (activatedVideo.value && activatedVideoIdx.value === data.idx) {
|
||||
closeVideoOptions()
|
||||
return
|
||||
}
|
||||
|
||||
showVideoOptions.value = true
|
||||
activatedVideoIdx.value = data.idx
|
||||
activatedVideo.value = data
|
||||
const osInstance = scrollbarRef.value?.osInstance()
|
||||
const scrollTop = osInstance.elements().viewport.scrollTop || 0
|
||||
@@ -192,25 +222,80 @@ function handleMoreClick(e: MouseEvent, data: AppVideoItem) {
|
||||
}
|
||||
|
||||
function handleMoreCommand(command: ThreePointV2Type) {
|
||||
if (activatedVideo.value)
|
||||
dislikedVideoIds.value.push(activatedVideo.value.idx)
|
||||
closeVideoOptions()
|
||||
|
||||
switch (command) {
|
||||
case ThreePointV2Type.Feedback:
|
||||
break
|
||||
case ThreePointV2Type.Dislike:
|
||||
dislikedVideoIds.value.push(activatedVideo.value!.idx)
|
||||
closeVideoOptions()
|
||||
openDislikeDialog()
|
||||
break
|
||||
}
|
||||
|
||||
// if (command === 'close')
|
||||
// closeVideoOptions()
|
||||
}
|
||||
|
||||
function closeVideoOptions() {
|
||||
showVideoOptions.value = false
|
||||
activatedVideo.value = null
|
||||
activatedVideoIdx.value = 0
|
||||
}
|
||||
|
||||
function openDislikeDialog() {
|
||||
showDislikeDialog.value = true
|
||||
}
|
||||
|
||||
function closeDislikeDialog() {
|
||||
showDislikeDialog.value = false
|
||||
selectedDislikeReason.value = 1
|
||||
}
|
||||
|
||||
function handleDislike() {
|
||||
loadingDislikeDialog.value = true
|
||||
const params = {
|
||||
access_key: accessKey.value,
|
||||
goto: activatedVideo.value?.goto,
|
||||
id: activatedVideo.value?.param,
|
||||
// https://github.com/magicdawn/bilibili-app-recommend/blob/cb51f75f415f48235ce048537f2013122c16b56b/src/components/VideoCard/card.service.ts#L115
|
||||
idx: (Date.now() / 1000).toFixed(0),
|
||||
reason_id: selectedDislikeReason.value,
|
||||
build: 74800100,
|
||||
device: 'pad',
|
||||
mobi_app: 'iphone',
|
||||
appkey: TVAppKey.appkey,
|
||||
}
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
contentScriptQuery: 'dislikeVideo',
|
||||
...params,
|
||||
sign: getTvSign(params),
|
||||
}).then((res) => {
|
||||
if (res.code === 0)
|
||||
dislikedVideoIds.value.push(activatedVideo.value!.idx)
|
||||
}).finally(() => {
|
||||
loadingDislikeDialog.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function handleUndoDislike(video: AppVideoItem) {
|
||||
const params = {
|
||||
access_key: accessKey.value,
|
||||
goto: video.goto,
|
||||
id: video.param,
|
||||
// https://github.com/magicdawn/bilibili-app-recommend/blob/cb51f75f415f48235ce048537f2013122c16b56b/src/components/VideoCard/card.service.ts#L115
|
||||
idx: (Date.now() / 1000).toFixed(0),
|
||||
reason_id: 1, // 1 means dislike, e.g. {"id": 1, "name": "不感兴趣","toast": "将减少相似内容推荐"}
|
||||
build: 74800100,
|
||||
device: 'pad',
|
||||
mobi_app: 'iphone',
|
||||
appkey: TVAppKey.appkey,
|
||||
}
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
contentScriptQuery: 'undoDislikeVideo',
|
||||
...params,
|
||||
sign: getTvSign(params),
|
||||
}).then((res) => {
|
||||
if (res.code === 0)
|
||||
dislikedVideoIds.value = dislikedVideoIds.value.filter(currentIdx => currentIdx !== video.idx)
|
||||
})
|
||||
}
|
||||
|
||||
function jumpToLoginPage() {
|
||||
@@ -227,7 +312,7 @@ defineExpose({ initData })
|
||||
<div hidden grid="~ cols-1 xl:cols-2 gap-4" />
|
||||
<div hidden grid="~ cols-1 gap-4" />
|
||||
|
||||
<!-- dislike popup -->
|
||||
<!-- more popup -->
|
||||
<div v-show="showVideoOptions">
|
||||
<div
|
||||
pos="fixed top-0 left-0" w-full h-full z-1
|
||||
@@ -243,17 +328,52 @@ defineExpose({ initData })
|
||||
<ul flex="~ col gap-1">
|
||||
<template v-for="option in videoOptions" :key="option.type">
|
||||
<li
|
||||
v-if="option.type !== ThreePointV2Type.WatchLater"
|
||||
v-if="option.type !== ThreePointV2Type.WatchLater && option.type !== ThreePointV2Type.Feedback"
|
||||
bg="hover:$bew-fill-2" p="x-4 y-2" rounded="$bew-radius-half" cursor-pointer
|
||||
@click="handleMoreCommand(option.type)"
|
||||
>
|
||||
{{ option.title }}
|
||||
<span v-if="option.type === ThreePointV2Type.Dislike">{{ $t('home.not_interested') }}</span>
|
||||
<span v-else>{{ option.title }}</span>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- dislike dialog -->
|
||||
<Dialog
|
||||
v-if="showDislikeDialog"
|
||||
:title="$t('home.tell_us_why')"
|
||||
width="400px"
|
||||
append-to-bewly-body
|
||||
center-footer
|
||||
:loading="loadingDislikeDialog"
|
||||
@close="closeDislikeDialog"
|
||||
@confirm="handleDislike"
|
||||
>
|
||||
<ul flex="~ col gap-2">
|
||||
<li
|
||||
v-for="(reason, index) in activatedVideo?.three_point_v2?.find(option => option.type === ThreePointV2Type.Dislike)?.reasons"
|
||||
:key="reason.id"
|
||||
:class="{ 'activated-dislike-reason': selectedDislikeReason === reason.id }"
|
||||
p="x-6 y-4" rounded="$bew-radius" cursor-pointer bg="$bew-fill-1 hover:$bew-fill-2"
|
||||
flex="~ gap-2 items-center justify-between"
|
||||
@click="selectedDislikeReason = reason.id"
|
||||
>
|
||||
<div flex="~ gap-2">
|
||||
<div
|
||||
bg="$bew-theme-color" color-white w-20px h-20px rounded-10
|
||||
flex="~ justify-center items-center"
|
||||
>
|
||||
{{ index + 1 }}
|
||||
</div>
|
||||
{{ reason.name }}
|
||||
</div>
|
||||
<line-md:confirm v-if="selectedDislikeReason === reason.id" />
|
||||
</li>
|
||||
</ul>
|
||||
</Dialog>
|
||||
|
||||
<Empty v-if="needToLoginFirst" mt-6 :description="$t('common.please_log_in_first')">
|
||||
<Button type="primary" @click="jumpToLoginPage()">
|
||||
{{ $t('common.login') }}
|
||||
@@ -308,10 +428,11 @@ defineExpose({ initData })
|
||||
show-preview
|
||||
:horizontal="gridLayout !== 'adaptive'"
|
||||
more-btn
|
||||
:more-btn-active="video.idx === activatedVideo?.idx"
|
||||
:show-dislike-options="dislikedVideoIds.includes(video.idx)"
|
||||
:more-btn-active="video.idx === activatedVideoIdx"
|
||||
:removed="dislikedVideoIds.includes(video.idx)"
|
||||
:dislike-reasons="video.three_point_v2?.find(option => option.type === ThreePointV2Type.Dislike)?.reasons || []"
|
||||
@more-click="(e) => handleMoreClick(e, video)"
|
||||
@undo="handleUndoDislike(video)"
|
||||
/>
|
||||
<!-- :more-options="video.three_point_v2" -->
|
||||
</template>
|
||||
@@ -335,4 +456,7 @@ defineExpose({ initData })
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.activated-dislike-reason {
|
||||
--at-apply: bg-$bew-theme-color-20 color-$bew-theme-color;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -29,4 +29,13 @@
|
||||
}
|
||||
.list-leave-to {
|
||||
--at-apply: hidden
|
||||
}
|
||||
|
||||
.modal-enter-active,
|
||||
.modal-leave-active {
|
||||
--at-apply: transition-all duration-500;
|
||||
}
|
||||
.modal-enter-from,
|
||||
.modal-leave-to {
|
||||
--at-apply: opacity-0 blur-14px scale-110;
|
||||
}
|
||||
@@ -75,7 +75,7 @@ export const TVAppKey = {
|
||||
}
|
||||
|
||||
// https://github.com/magicdawn/bilibili-app-recommend/blob/e91722cc076fe65b98116fb0248236851ae6e1dc/src/utility/access-key/tv-qrcode/api.ts#L8
|
||||
function tvSignSearchParams(params: Record<string, any>) {
|
||||
export function tvSignSearchParams(params: Record<string, any>) {
|
||||
const sign = appSign(params, TVAppKey.appkey, TVAppKey.appsec)
|
||||
return new URLSearchParams({
|
||||
...params,
|
||||
@@ -83,6 +83,10 @@ function tvSignSearchParams(params: Record<string, any>) {
|
||||
})
|
||||
}
|
||||
|
||||
export function getTvSign(params: Record<string, any>) {
|
||||
return appSign(params, TVAppKey.appkey, TVAppKey.appsec)
|
||||
}
|
||||
|
||||
export function pollTVLoginQRCode(authCode: string): Promise<any> {
|
||||
const url = 'https://passport.bilibili.com/x/passport-tv-login/qrcode/poll'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user