mirror of
https://github.com/BewlyBewly/BewlyBewly.git
synced 2025-04-14 13:15:29 +00:00
Merge pull request #241 from hakadao/refactor-lazy-components
Refactor lazy components
This commit is contained in:
@@ -14,6 +14,7 @@ module.exports = antfu({
|
||||
},
|
||||
},
|
||||
],
|
||||
'no-alert': 'off',
|
||||
},
|
||||
eslint: {
|
||||
ignorePatterns: [
|
||||
|
||||
32
package.json
32
package.json
@@ -3,7 +3,7 @@
|
||||
"displayName": "BewlyBewly",
|
||||
"version": "0.14.3",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@8.14.1",
|
||||
"packageManager": "pnpm@8.15.0",
|
||||
"description": "Just make a few small changes to your Bilibili homepage.",
|
||||
"homepage": "https://github.com/hakadao/BewlyBewly",
|
||||
"scripts": {
|
||||
@@ -37,36 +37,36 @@
|
||||
"dplayer": "^1.27.1",
|
||||
"md5": "^2.3.0",
|
||||
"mitt": "^3.0.1",
|
||||
"overlayscrollbars": "^2.4.5",
|
||||
"overlayscrollbars-vue": "^0.5.6",
|
||||
"overlayscrollbars": "^2.4.7",
|
||||
"overlayscrollbars-vue": "^0.5.7",
|
||||
"pinia": "^2.1.7",
|
||||
"qrcode.vue": "^3.4.1",
|
||||
"vue-i18n": "^9.7.0",
|
||||
"vue-i18n": "^9.9.0",
|
||||
"vue-toastification": "2.0.0-rc.5",
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^2.6.1",
|
||||
"@ffflorian/jszip-cli": "^3.5.1",
|
||||
"@iconify/json": "^2.2.143",
|
||||
"@antfu/eslint-config": "^2.6.3",
|
||||
"@ffflorian/jszip-cli": "^3.6.2",
|
||||
"@iconify/json": "^2.2.176",
|
||||
"@iconify/vue": "^4.1.1",
|
||||
"@intlify/unplugin-vue-i18n": "^0.8.2",
|
||||
"@rollup/plugin-replace": "^5.0.5",
|
||||
"@types/dplayer": "^1.25.5",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/node": "^18.18.10",
|
||||
"@types/node": "^18.19.10",
|
||||
"@types/webextension-polyfill": "^0.9.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"@unocss/reset": "^0.54.3",
|
||||
"@vitejs/plugin-vue": "^3.2.0",
|
||||
"@vue/compiler-sfc": "^3.3.8",
|
||||
"@vue/test-utils": "^2.4.2",
|
||||
"@vueuse/core": "^10.6.1",
|
||||
"@vue/compiler-sfc": "^3.4.15",
|
||||
"@vue/test-utils": "^2.4.4",
|
||||
"@vueuse/core": "^10.7.2",
|
||||
"chokidar": "^3.5.3",
|
||||
"cross-env": "^7.0.3",
|
||||
"crx": "^5.0.1",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint": "^8.56.0",
|
||||
"esno": "^4.0.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"jsdom": "^20.0.3",
|
||||
@@ -74,9 +74,9 @@
|
||||
"lint-staged": "^15.2.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"rimraf": "^3.0.2",
|
||||
"sass": "^1.69.5",
|
||||
"sass": "^1.70.0",
|
||||
"simple-git-hooks": "^2.9.0",
|
||||
"terser": "^5.24.0",
|
||||
"terser": "^5.27.0",
|
||||
"tsup": "^6.7.0",
|
||||
"typescript": "^4.9.5",
|
||||
"unocss": "^0.54.3",
|
||||
@@ -85,9 +85,9 @@
|
||||
"unplugin-vue-components": "^0.22.12",
|
||||
"vite": "^3.2.8",
|
||||
"vitest": "^0.24.5",
|
||||
"vue": "^3.3.8",
|
||||
"vue": "^3.4.15",
|
||||
"vue-demi": "^0.13.11",
|
||||
"web-ext": "^7.8.0",
|
||||
"web-ext": "^7.11.0",
|
||||
"webext-bridge": "^5.0.5",
|
||||
"webextension-polyfill": "^0.10.0"
|
||||
},
|
||||
|
||||
523
pnpm-lock.yaml
generated
523
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,7 @@ common:
|
||||
disable: 禁用
|
||||
refresh: 刷新
|
||||
reset: 重置
|
||||
no_more_content: 没有更多内容了
|
||||
settings:
|
||||
title: 设置
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ common:
|
||||
disable: 停用
|
||||
refresh: 重新整理
|
||||
reset: 重置
|
||||
no_more_content: 沒有更多內容了
|
||||
settings:
|
||||
title: 設定
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ common:
|
||||
disable: Disable
|
||||
refresh: Refresh
|
||||
reset: Reset
|
||||
no_more_content: No more content now, owari da
|
||||
settings:
|
||||
title: Settings
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ common:
|
||||
disable: 閂埋
|
||||
refresh: 重新整理
|
||||
reset: 重置
|
||||
no_more_content: 唔使睇喇,冇嘢喇
|
||||
settings:
|
||||
title: 設定
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Icon } from '@iconify/vue'
|
||||
import type { Ref } from 'vue'
|
||||
import type { CurrentDockItem, HoveringDockItem } from './types'
|
||||
import type { AppPage } from '~/enums/appEnums'
|
||||
import { settings } from '~/logic'
|
||||
@@ -11,8 +10,7 @@ import { useMainStore } from '~/stores/mainStore'
|
||||
defineProps<{ activatedPage: AppPage }>()
|
||||
|
||||
const emit = defineEmits(['change-page', 'settings-visibility-change'])
|
||||
|
||||
const mainAppRef = inject('mainAppRef') as Ref<HTMLDivElement>
|
||||
const { mainAppRef } = useBewlyApp()
|
||||
|
||||
const mainStore = useMainStore()
|
||||
const { t } = useI18n()
|
||||
|
||||
6
src/components/OverlayScrollbarsComponent.ts
Normal file
6
src/components/OverlayScrollbarsComponent.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import 'overlayscrollbars/overlayscrollbars.css'
|
||||
|
||||
export default defineAsyncComponent(async () => {
|
||||
const { OverlayScrollbarsComponent } = await import('overlayscrollbars-vue')
|
||||
return OverlayScrollbarsComponent
|
||||
})
|
||||
@@ -1,11 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { Ref } from 'vue'
|
||||
import type { HoveringDockItem } from './types'
|
||||
import { settings } from '~/logic'
|
||||
|
||||
const emit = defineEmits(['settings-visibility-change'])
|
||||
|
||||
const mainAppRef = inject('mainAppRef') as Ref<HTMLDivElement>
|
||||
const { mainAppRef } = useBewlyApp()
|
||||
|
||||
const hoveringDockItem = reactive<HoveringDockItem>({
|
||||
themeMode: false,
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Icon } from '@iconify/vue'
|
||||
import General from './components/General.vue'
|
||||
import Appearance from './components/Appearance.vue'
|
||||
import SearchPage from './components/SearchPage.vue'
|
||||
import Home from './components/Home.vue'
|
||||
import Compatibility from './components/Compatibility.vue'
|
||||
import About from './components/About.vue'
|
||||
|
||||
import type { MenuItem } from './types'
|
||||
import { MenuType } from './types'
|
||||
import { settings } from '~/logic'
|
||||
@@ -15,7 +10,14 @@ const emit = defineEmits(['close'])
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const settingsMenu = { General, Appearance, SearchPage, Home, Compatibility, About }
|
||||
const settingsMenu = {
|
||||
[MenuType.General]: defineAsyncComponent(() => import('./components/General.vue')),
|
||||
[MenuType.Appearance]: defineAsyncComponent(() => import('./components/Appearance.vue')),
|
||||
[MenuType.SearchPage]: defineAsyncComponent(() => import('./components/SearchPage.vue')),
|
||||
[MenuType.Home]: defineAsyncComponent(() => import('./components/Home.vue')),
|
||||
[MenuType.Compatibility]: defineAsyncComponent(() => import('./components/Compatibility.vue')),
|
||||
[MenuType.About]: defineAsyncComponent(() => import('./components/About.vue')),
|
||||
}
|
||||
const activatedMenuItem = ref<MenuType>(MenuType.General)
|
||||
const title = ref<string>(t('settings.title'))
|
||||
const preventCloseSettings = ref<boolean>(false)
|
||||
|
||||
@@ -10,7 +10,6 @@ import HistoryPop from './components/HistoryPop.vue'
|
||||
import { getUserID, isHomePage } from '~/utils/main'
|
||||
import { settings } from '~/logic'
|
||||
import emitter from '~/utils/mitt'
|
||||
import type { AppPage } from '~/enums/appEnums'
|
||||
|
||||
// import { useTopBarStore } from '~/stores/topBarStore'
|
||||
|
||||
@@ -33,8 +32,7 @@ interface Props {
|
||||
// return topBarStore.topBarItems
|
||||
// })
|
||||
|
||||
const activatedPage = inject('activatedPage') as Ref<AppPage>
|
||||
const scrollbarRef = inject('scrollbarRef') as Ref
|
||||
const { activatedPage, scrollbarRef } = useBewlyApp()
|
||||
|
||||
const mid = getUserID() || ''
|
||||
const userInfo = reactive<UserInfo | NonNullable<unknown>>({}) as UnwrapNestedRefs<UserInfo>
|
||||
|
||||
20
src/composables/useAppProvider.ts
Normal file
20
src/composables/useAppProvider.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { Ref } from 'vue'
|
||||
import type { AppPage } from '~/enums/appEnums'
|
||||
|
||||
export interface BewlyAppProvider {
|
||||
activatedPage: Ref<AppPage>
|
||||
scrollbarRef: Ref<any>
|
||||
mainAppRef: Ref<HTMLElement>
|
||||
handleReachBottom: Ref<(() => void) | undefined>
|
||||
handlePageRefresh: Ref<(() => void) | undefined>
|
||||
handleBackToTop: (targetScrollTop: number) => void
|
||||
}
|
||||
|
||||
export function useBewlyApp(): BewlyAppProvider {
|
||||
const provider = inject<BewlyAppProvider>('BEWLY_APP')
|
||||
|
||||
if (import.meta.env.DEV && !provider)
|
||||
throw new Error('AppProvider is not injected')
|
||||
|
||||
return provider!
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
import AnimeTimeTable from './components/AnimeTimeTable.vue'
|
||||
import { getUserID, openLinkToNewTab } from '~/utils/main'
|
||||
import { numFormatter } from '~/utils/dataFormatter'
|
||||
import emitter from '~/utils/mitt'
|
||||
import type { List as WatchListItem, WatchListResult } from '~/models/anime/watchList'
|
||||
import type { List as PopularAnimeItem, PopularAnimeResult } from '~/models/anime/popular'
|
||||
import type { ItemSubItem as RecommendationItem, RecommendationResult } from '~/models/anime/recommendation'
|
||||
@@ -15,22 +14,47 @@ const isLoadingAnimeWatchList = ref<boolean>()
|
||||
const isLoadingPopularAnime = ref<boolean>()
|
||||
const isLoadingRecommendAnime = ref<boolean>()
|
||||
const activatedSeasonId = ref<number>()
|
||||
const noMoreContent = ref<boolean>()
|
||||
const animeTimeTableRef = ref()
|
||||
const { handleReachBottom, handlePageRefresh } = useBewlyApp()
|
||||
|
||||
const isLoading = computed(() => {
|
||||
return isLoadingAnimeWatchList.value || isLoadingPopularAnime.value || isLoadingRecommendAnime.value
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getAnimeWatchList()
|
||||
getPopularAnimeList()
|
||||
getRecommendAnimeList()
|
||||
|
||||
emitter.off('reachBottom')
|
||||
emitter.on('reachBottom', () => {
|
||||
if (!isLoadingRecommendAnime.value)
|
||||
getRecommendAnimeList()
|
||||
})
|
||||
initPageAction()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off('reachBottom')
|
||||
})
|
||||
function initPageAction() {
|
||||
handleReachBottom.value = () => {
|
||||
if (isLoadingRecommendAnime.value)
|
||||
return
|
||||
if (noMoreContent.value)
|
||||
return
|
||||
|
||||
getRecommendAnimeList()
|
||||
}
|
||||
handlePageRefresh.value = () => {
|
||||
if (isLoading.value)
|
||||
return
|
||||
|
||||
animeWatchList.length = 0
|
||||
recommendAnimeList.length = 0
|
||||
popularAnimeList.length = 0
|
||||
cursor.value = 0
|
||||
noMoreContent.value = false
|
||||
|
||||
getAnimeWatchList()
|
||||
getPopularAnimeList()
|
||||
getRecommendAnimeList()
|
||||
animeTimeTableRef.value?.refreshAnimeTimeTable()
|
||||
}
|
||||
}
|
||||
|
||||
function getAnimeWatchList() {
|
||||
isLoadingAnimeWatchList.value = true
|
||||
@@ -70,10 +94,13 @@ function getRecommendAnimeList() {
|
||||
if (code === 0 && has_next) {
|
||||
if (recommendAnimeList.length === 0)
|
||||
Object.assign(recommendAnimeList, items[0].sub_items as RecommendationItem[])
|
||||
else recommendAnimeList.push(...items[0].sub_items)
|
||||
else
|
||||
recommendAnimeList.push(...items[0].sub_items)
|
||||
|
||||
cursor.value = coursor
|
||||
}
|
||||
if (code === 0 && !has_next)
|
||||
noMoreContent.value = true
|
||||
})
|
||||
.finally(() => {
|
||||
isLoadingRecommendAnime.value = false
|
||||
@@ -213,7 +240,7 @@ function getPopularAnimeList() {
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<AnimeTimeTable w="[calc(100%+1.5rem)]" />
|
||||
<AnimeTimeTable ref="animeTimeTableRef" w="[calc(100%+1.5rem)]" />
|
||||
</section>
|
||||
|
||||
<!-- Recommended for you -->
|
||||
@@ -246,6 +273,10 @@ function getPopularAnimeList() {
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- no more content -->
|
||||
<Empty v-if="noMoreContent" class="pb-4" :description="$t('common.no_more_content')" />
|
||||
|
||||
<!-- loading -->
|
||||
<loading v-if="isLoadingRecommendAnime && recommendAnimeList.length !== 0" m="-t-4" />
|
||||
</div>
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
import type { Ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import browser from 'webextension-polyfill'
|
||||
import type { AnimeTimeTableItem } from '../types'
|
||||
import { removeHttpFromUrl } from '~/utils/main'
|
||||
import type { Result as TimetableItem, TimetableResult } from '~/models/apiModels/anime/timetable'
|
||||
import type { Result as TimetableItem, TimetableResult } from '~/models/anime/timeTable'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const animeTimeTable = reactive<AnimeTimeTableItem[]>([])
|
||||
const animeTimeTable = reactive<TimetableItem[]>([])
|
||||
const animeTimeTableWrap = ref<HTMLElement>() as Ref<HTMLElement>
|
||||
|
||||
const daysOfTheWeekList = computed(() => {
|
||||
@@ -27,6 +26,11 @@ onMounted(() => {
|
||||
getAnimeTimeTable()
|
||||
})
|
||||
|
||||
function refreshAnimeTimeTable() {
|
||||
animeTimeTable.length = 0
|
||||
getAnimeTimeTable()
|
||||
}
|
||||
|
||||
function getAnimeTimeTable() {
|
||||
browser.runtime
|
||||
.sendMessage({
|
||||
@@ -38,6 +42,8 @@ function getAnimeTimeTable() {
|
||||
Object.assign(animeTimeTable, result as TimetableItem[])
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ refreshAnimeTimeTable })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,28 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
import { useToggle } from '@vueuse/core'
|
||||
import { useThrottleFn, useToggle } from '@vueuse/core'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import browser from 'webextension-polyfill'
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
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 { accessKey, settings } from '~/logic'
|
||||
import { AppPage, LanguageType } from '~/enums/appEnums'
|
||||
import { getUserID, hexToRGBA, isHomePage, smoothScrollToTop } from '~/utils/main'
|
||||
import emitter from '~/utils/mitt'
|
||||
import type { BewlyAppProvider } from '~/composables/useAppProvider'
|
||||
|
||||
const activatedPage = ref<AppPage>(settings.value.dockItemVisibilityList.find(e => e.visible === true)?.page ?? AppPage.Home)
|
||||
const { locale } = useI18n()
|
||||
const [showSettings, toggleSettings] = useToggle(false)
|
||||
const pages = { Home, Search, Anime, History, WatchLater, Favorites }
|
||||
const pages = {
|
||||
[AppPage.Home]: defineAsyncComponent(() => import('./Home/Home.vue')),
|
||||
[AppPage.Search]: defineAsyncComponent(() => import('./Search/Search.vue')),
|
||||
[AppPage.Anime]: defineAsyncComponent(() => import('./Anime/Anime.vue')),
|
||||
[AppPage.History]: defineAsyncComponent(() => import('./History/History.vue')),
|
||||
[AppPage.WatchLater]: defineAsyncComponent(() => import('./WatchLater/WatchLater.vue')),
|
||||
[AppPage.Favorites]: defineAsyncComponent(() => import('./Favorites/Favorites.vue')),
|
||||
}
|
||||
const mainAppRef = ref<HTMLElement>() as Ref<HTMLElement>
|
||||
const scrollbarRef = ref()
|
||||
const showTopBarMask = ref<boolean>(false)
|
||||
const dynamicComponentKey = ref<string>(`dynamicComponent${Number(new Date())}`)
|
||||
const handlePageRefresh = ref<() => void>()
|
||||
const handleReachBottom = ref<() => void>()
|
||||
const handleThrottledPageRefresh = useThrottleFn(() => handlePageRefresh.value?.(), 500)
|
||||
const handleThrottledReachBottom = useThrottleFn(() => handleReachBottom.value?.(), 500)
|
||||
const topBarRef = ref()
|
||||
|
||||
const isVideoPage = computed(() => {
|
||||
@@ -127,7 +131,7 @@ function changeActivatePage(pageName: AppPage) {
|
||||
if (activatedPage.value === pageName) {
|
||||
if (activatedPage.value !== AppPage.Search) {
|
||||
if (scrollTop === 0)
|
||||
handleRefresh()
|
||||
handleThrottledPageRefresh()
|
||||
else
|
||||
handleBackToTop()
|
||||
}
|
||||
@@ -202,12 +206,6 @@ function setAppThemeColor() {
|
||||
document.documentElement.style.setProperty(`--bew-theme-color-${i + 1}0`, hexToRGBA(settings.value.themeColor, i * 0.1 + 0.1))
|
||||
}
|
||||
|
||||
function handleRefresh() {
|
||||
emitter.emit('pageRefresh')
|
||||
if (activatedPage.value === AppPage.Anime)
|
||||
dynamicComponentKey.value = `dynamicComponent${Number(new Date())}`
|
||||
}
|
||||
|
||||
function handleBackToTop(targetScrollTop = 0 as number) {
|
||||
const osInstance = scrollbarRef.value?.osInstance()
|
||||
|
||||
@@ -233,15 +231,15 @@ function handleOsScroll() {
|
||||
showTopBarMask.value = true
|
||||
|
||||
if (clientHeight + scrollTop >= scrollHeight - 20)
|
||||
emitter.emit('reachBottom')
|
||||
handleThrottledReachBottom()
|
||||
|
||||
if (isHomePage())
|
||||
topBarRef.value?.handleScroll()
|
||||
}
|
||||
|
||||
// // fix #166 https://github.com/hakadao/BewlyBewly/issues/166
|
||||
// fix #166 https://github.com/hakadao/BewlyBewly/issues/166
|
||||
// function openVideoPageIfBvidExists() {
|
||||
// // Assume the URL is https://www.bilibili.com/?bvid=BV1be41127ft&spm_id_from=333.788.seo.out
|
||||
// Assume the URL is https://www.bilibili.com/?bvid=BV1be41127ft&spm_id_from=333.788.seo.out
|
||||
|
||||
// // Get the current URL's query string
|
||||
// const queryString = window.location.search
|
||||
@@ -253,11 +251,14 @@ function handleOsScroll() {
|
||||
// window.open(`https://www.bilibili.com/video/${bvid}`, '_self')
|
||||
// }
|
||||
|
||||
provide('handleBackToTop', handleBackToTop)
|
||||
provide('handleRefresh', handleRefresh)
|
||||
provide('activatedPage', activatedPage)
|
||||
provide('scrollbarRef', scrollbarRef)
|
||||
provide('mainAppRef', mainAppRef)
|
||||
provide<BewlyAppProvider>('BEWLY_APP', {
|
||||
activatedPage,
|
||||
mainAppRef,
|
||||
scrollbarRef,
|
||||
handleBackToTop,
|
||||
handlePageRefresh,
|
||||
handleReachBottom,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -330,12 +331,12 @@ provide('mainAppRef', mainAppRef)
|
||||
<!-- control button group -->
|
||||
<BackToTopAndRefreshButtons
|
||||
v-if="activatedPage !== AppPage.Search" :show-refresh-button="!showTopBarMask"
|
||||
@refresh="handleRefresh"
|
||||
@refresh="handleThrottledPageRefresh"
|
||||
@back-to-top="handleBackToTop"
|
||||
/>
|
||||
|
||||
<Transition name="page-fade">
|
||||
<Component :is="pages[activatedPage]" :key="dynamicComponentKey" />
|
||||
<Component :is="pages[activatedPage]" />
|
||||
</Transition>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -19,7 +19,7 @@ const activatedCategoryCover = ref<string>('')
|
||||
const shouldMoveCtrlBarUp = ref<boolean>(false)
|
||||
const currentPageNum = ref<number>(1)
|
||||
const keyword = ref<string>('')
|
||||
|
||||
const { handlePageRefresh, handleReachBottom } = useBewlyApp()
|
||||
const isLoading = ref<boolean>(true)
|
||||
const isFullPageLoading = ref<boolean>(false)
|
||||
const noMoreContent = ref<boolean>()
|
||||
@@ -28,20 +28,8 @@ onMounted(async () => {
|
||||
await getFavoriteCategories()
|
||||
changeCategory(favoriteCategories[0])
|
||||
|
||||
emitter.off('reachBottom')
|
||||
emitter.on('reachBottom', () => {
|
||||
if (isLoading.value)
|
||||
return
|
||||
initPageAction()
|
||||
|
||||
if (!noMoreContent.value)
|
||||
getFavoriteResources(selectedCategory.value!.id, ++currentPageNum.value, keyword.value)
|
||||
})
|
||||
|
||||
emitter.off('pageRefresh')
|
||||
emitter.on('pageRefresh', async () => {
|
||||
favoriteResources.length = 0
|
||||
handleSearch()
|
||||
})
|
||||
emitter.off('topBarVisibleChange')
|
||||
emitter.on('topBarVisibleChange', (val) => {
|
||||
shouldMoveCtrlBarUp.value = false
|
||||
@@ -60,11 +48,29 @@ onMounted(async () => {
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off('reachBottom')
|
||||
emitter.off('pageRefresh')
|
||||
emitter.off('topBarVisibleChange')
|
||||
})
|
||||
|
||||
function initPageAction() {
|
||||
handleReachBottom.value = async () => {
|
||||
if (isLoading.value)
|
||||
return
|
||||
if (noMoreContent.value)
|
||||
return
|
||||
|
||||
await getFavoriteResources(selectedCategory.value!.id, ++currentPageNum.value, keyword.value)
|
||||
}
|
||||
|
||||
handlePageRefresh.value = () => {
|
||||
if (isLoading.value)
|
||||
return
|
||||
favoriteResources.length = 0
|
||||
currentPageNum.value = 1
|
||||
noMoreContent.value = false
|
||||
handleSearch()
|
||||
}
|
||||
}
|
||||
|
||||
async function getFavoriteCategories() {
|
||||
await browser.runtime
|
||||
.sendMessage({
|
||||
@@ -128,14 +134,17 @@ async function getFavoriteResources(
|
||||
async function changeCategory(categoryItem: FavoriteCategory) {
|
||||
currentPageNum.value = 1
|
||||
selectedCategory.value = categoryItem
|
||||
|
||||
noMoreContent.value = false
|
||||
favoriteResources.length = 0
|
||||
|
||||
getFavoriteResources(categoryItem.id, 1)
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
currentPageNum.value = 1
|
||||
favoriteResources.length = 0
|
||||
noMoreContent.value = false
|
||||
|
||||
getFavoriteResources(selectedCategory.value!.id, currentPageNum.value, keyword.value)
|
||||
}
|
||||
|
||||
@@ -225,6 +234,9 @@ function handleUnfavorite(favoriteResource: FavoriteResource) {
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
|
||||
<!-- no more content -->
|
||||
<Empty v-if="noMoreContent" class="py-4" :description="$t('common.no_more_content')" />
|
||||
|
||||
<!-- loading -->
|
||||
<Transition name="fade">
|
||||
<Loading
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { getCSRF, openLinkToNewTab, removeHttpFromUrl } from '~/utils/main'
|
||||
import { calcCurrentTime } from '~/utils/dataFormatter'
|
||||
import emitter from '~/utils/mitt'
|
||||
import { Business } from '~/models/video/history'
|
||||
import type { List as HistoryItem, HistoryResult } from '~/models/video/history'
|
||||
import type { List as HistorySearchItem, HistorySearchResult } from '~/models/video/historySearch'
|
||||
@@ -12,61 +11,44 @@ import type { List as HistorySearchItem, HistorySearchResult } from '~/models/vi
|
||||
const { t } = useI18n()
|
||||
|
||||
const isLoading = ref<boolean>()
|
||||
const noMoreContent = ref<boolean>()
|
||||
const noMoreContent = ref<boolean>(false)
|
||||
const historyList = reactive<Array<HistoryItem>>([])
|
||||
const currentPageNum = ref<number>(1)
|
||||
const keyword = ref<string>()
|
||||
const historyStatus = ref<boolean>()
|
||||
const { handlePageRefresh, handleReachBottom } = useBewlyApp()
|
||||
|
||||
const HistoryBusiness = computed(() => {
|
||||
return Business
|
||||
})
|
||||
|
||||
watch(
|
||||
() => keyword.value,
|
||||
(newValue, oldValue) => {
|
||||
if (newValue === oldValue)
|
||||
return
|
||||
emitter.on('reachBottom', () => {
|
||||
if (isLoading.value)
|
||||
return
|
||||
|
||||
if (!noMoreContent.value) {
|
||||
if (keyword.value)
|
||||
searchHistoryList()
|
||||
else getHistoryList()
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
getHistoryList()
|
||||
getHistoryPauseStatus()
|
||||
|
||||
emitter.off('reachBottom')
|
||||
emitter.on('reachBottom', () => {
|
||||
initPageAction()
|
||||
})
|
||||
|
||||
function initPageAction() {
|
||||
handleReachBottom.value = () => {
|
||||
if (isLoading.value)
|
||||
return
|
||||
if (noMoreContent.value)
|
||||
return
|
||||
|
||||
if (!noMoreContent.value) {
|
||||
if (keyword.value)
|
||||
searchHistoryList()
|
||||
else getHistoryList()
|
||||
}
|
||||
})
|
||||
if (keyword.value)
|
||||
searchHistoryList()
|
||||
else
|
||||
getHistoryList()
|
||||
}
|
||||
|
||||
emitter.off('pageRefresh')
|
||||
emitter.on('pageRefresh', async () => {
|
||||
handlePageRefresh.value = () => {
|
||||
historyList.length = 0
|
||||
currentPageNum.value = 1
|
||||
noMoreContent.value = false
|
||||
getHistoryList()
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off('reachBottom')
|
||||
emitter.off('pageRefresh')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get history list
|
||||
@@ -128,6 +110,7 @@ function searchHistoryList() {
|
||||
function handleSearch() {
|
||||
historyList.length = 0
|
||||
currentPageNum.value = 1
|
||||
noMoreContent.value = false
|
||||
if (keyword.value)
|
||||
searchHistoryList()
|
||||
else getHistoryList()
|
||||
@@ -151,7 +134,7 @@ function deleteHistoryItem(index: number, historyItem: HistoryItem) {
|
||||
* @param item history item
|
||||
* @return {string} url
|
||||
*/
|
||||
function getHistoryUrl(item: HistoryItem) {
|
||||
function getHistoryUrl(item: HistoryItem): string {
|
||||
// anime
|
||||
if (item.history.business === 'pgc') {
|
||||
return removeHttpFromUrl(item.uri)
|
||||
@@ -220,7 +203,6 @@ function clearAllHistory() {
|
||||
}
|
||||
|
||||
function handleClearAllWatchHistory() {
|
||||
// eslint-disable-next-line no-alert
|
||||
const result = confirm(
|
||||
t('history.clear_all_watch_history_confirm'),
|
||||
)
|
||||
@@ -229,7 +211,6 @@ function handleClearAllWatchHistory() {
|
||||
}
|
||||
|
||||
function handlePauseWatchHistory() {
|
||||
// eslint-disable-next-line no-alert
|
||||
const result = confirm(
|
||||
t('history.pause_watch_history_confirm'),
|
||||
)
|
||||
@@ -238,7 +219,6 @@ function handlePauseWatchHistory() {
|
||||
}
|
||||
|
||||
function handleTurnOnWatchHistory() {
|
||||
// eslint-disable-next-line no-alert
|
||||
const result = confirm(
|
||||
t('history.turn_on_watch_history_confirm'),
|
||||
)
|
||||
@@ -258,7 +238,7 @@ function jumpToLoginPage() {
|
||||
{{ $t('history.title') }}
|
||||
</h3>
|
||||
<!-- historyList -->
|
||||
<transition-group name="list">
|
||||
<TransitionGroup name="list">
|
||||
<a
|
||||
v-for="(historyItem, index) in historyList"
|
||||
:key="historyItem.kid"
|
||||
@@ -479,7 +459,11 @@ function jumpToLoginPage() {
|
||||
</div>
|
||||
</section>
|
||||
</a>
|
||||
</transition-group>
|
||||
</TransitionGroup>
|
||||
|
||||
<!-- no more content -->
|
||||
<Empty v-if="noMoreContent" class="py-4" :description="$t('common.no_more_content')" />
|
||||
|
||||
<!-- loading -->
|
||||
<Transition name="fade">
|
||||
<loading
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import ForYou from './components/ForYou.vue'
|
||||
import Following from './components/Following.vue'
|
||||
import Trending from './components/Trending.vue'
|
||||
import Ranking from './components/Ranking.vue'
|
||||
import SubscribedSeries from './components/SubscribedSeries.vue'
|
||||
import type { HomeTab } from './types'
|
||||
import { HomeSubPage } from './types'
|
||||
import emitter from '~/utils/mitt'
|
||||
@@ -12,13 +7,19 @@ import { settings } from '~/logic'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const handleBackToTop = inject('handleBackToTop') as (targetScrollTop: number) => void
|
||||
const { handleBackToTop } = useBewlyApp()
|
||||
|
||||
const recommendContentKey = ref<string>(`recommendContent${Number(new Date())}`)
|
||||
const activatedPage = ref<HomeSubPage>(HomeSubPage.ForYou)
|
||||
const pages = { ForYou, Following, SubscribedSeries, Trending, Ranking }
|
||||
const pages = {
|
||||
[HomeSubPage.ForYou]: defineAsyncComponent(() => import('./components/ForYou.vue')),
|
||||
[HomeSubPage.Following]: defineAsyncComponent(() => import('./components/Following.vue')),
|
||||
[HomeSubPage.SubscribedSeries]: defineAsyncComponent(() => import('./components/SubscribedSeries.vue')),
|
||||
[HomeSubPage.Trending]: defineAsyncComponent(() => import('./components/Trending.vue')),
|
||||
[HomeSubPage.Ranking]: defineAsyncComponent(() => import('./components/Ranking.vue')),
|
||||
}
|
||||
const showSearchPageMode = ref<boolean>(false)
|
||||
const shouldMoveTabsUp = ref<boolean>(false)
|
||||
const tabContentLoading = ref<boolean>(false)
|
||||
|
||||
const tabs = computed((): HomeTab[] => {
|
||||
return [
|
||||
@@ -51,10 +52,6 @@ watch(() => activatedPage.value, () => {
|
||||
|
||||
onMounted(() => {
|
||||
showSearchPageMode.value = true
|
||||
emitter.off('pageRefresh')
|
||||
emitter.on('pageRefresh', async () => {
|
||||
recommendContentKey.value = `recommendContent${Number(new Date())}`
|
||||
})
|
||||
emitter.off('topBarVisibleChange')
|
||||
emitter.on('topBarVisibleChange', (val) => {
|
||||
shouldMoveTabsUp.value = false
|
||||
@@ -73,9 +70,18 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off('pageRefresh')
|
||||
emitter.off('topBarVisibleChange')
|
||||
})
|
||||
|
||||
function handleChangeTab(tab: HomeTab) {
|
||||
// When the content of a tab is loading, prevent switching to another tab.
|
||||
// Since `initPageAction()` within the tab replaces the `handleReachBottom` and `handlePageRefresh` functions.
|
||||
// Therefore, this will lead to a failure in refreshing the data of the current tab
|
||||
// because `handlePageRefresh` and `handleReachBottom` has been replaced
|
||||
// now they are set to refresh the data of the tab you switched to
|
||||
if (!tabContentLoading.value)
|
||||
activatedPage.value = tab.value
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -150,7 +156,8 @@ onUnmounted(() => {
|
||||
px-4 lh-35px bg="$bew-elevated-1 hover:$bew-elevated-1-hover" backdrop-glass rounded="$bew-radius"
|
||||
cursor-pointer shadow="$bew-shadow-1" box-border border="1 $bew-border-color" duration-300
|
||||
:class="{ 'tab-activated': activatedPage === tab.value }"
|
||||
@click="activatedPage = tab.value"
|
||||
:style="{ opacity: activatedPage !== tab.value && tabContentLoading ? 0.4 : 1 }"
|
||||
@click="handleChangeTab(tab)"
|
||||
>
|
||||
<span class="text-center">{{ tab.label }}</span>
|
||||
</li>
|
||||
@@ -158,7 +165,13 @@ onUnmounted(() => {
|
||||
</header>
|
||||
|
||||
<Transition name="page-fade">
|
||||
<Component :is="pages[activatedPage]" :key="recommendContentKey" />
|
||||
<KeepAlive include="ForYou">
|
||||
<Component
|
||||
:is="pages[activatedPage]" :key="activatedPage"
|
||||
@before-loading="tabContentLoading = true"
|
||||
@after-loading="tabContentLoading = false"
|
||||
/>
|
||||
</KeepAlive>
|
||||
</Transition>
|
||||
<!-- <RecommendContent :key="recommendContentKey" /> -->
|
||||
</main>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import type { Ref } from 'vue'
|
||||
import type { DataItem as MomentItem, MomentResult } from '~/models/moment/moment'
|
||||
import emitter from '~/utils/mitt'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'beforeLoading'): void
|
||||
(e: 'afterLoading'): void
|
||||
}>()
|
||||
|
||||
const videoList = reactive<MomentItem[]>([])
|
||||
const isLoading = ref<boolean>(false)
|
||||
@@ -10,28 +14,52 @@ const containerRef = ref<HTMLElement>() as Ref<HTMLElement>
|
||||
const offset = ref<string>('')
|
||||
const updateBaseline = ref<string>('')
|
||||
const noMoreContent = ref<boolean>(false)
|
||||
const { handleReachBottom, handlePageRefresh } = useBewlyApp()
|
||||
|
||||
onMounted(async () => {
|
||||
for (let i = 0; i < 3; i++)
|
||||
await getFollowedUsersVideos()
|
||||
|
||||
emitter.off('reachBottom')
|
||||
emitter.on('reachBottom', async () => {
|
||||
if (!isLoading.value) {
|
||||
for (let i = 0; i < 3; i++)
|
||||
await getFollowedUsersVideos()
|
||||
}
|
||||
})
|
||||
initPageAction()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off('reachBottom')
|
||||
onActivated(() => {
|
||||
initPageAction()
|
||||
})
|
||||
|
||||
function initPageAction() {
|
||||
handleReachBottom.value = async () => {
|
||||
if (isLoading.value)
|
||||
return
|
||||
if (noMoreContent.value)
|
||||
return
|
||||
|
||||
for (let i = 0; i < 3; i++)
|
||||
await getFollowedUsersVideos()
|
||||
}
|
||||
handlePageRefresh.value = async () => {
|
||||
if (isLoading.value)
|
||||
return
|
||||
|
||||
offset.value = ''
|
||||
updateBaseline.value = ''
|
||||
videoList.length = 0
|
||||
noMoreContent.value = false
|
||||
|
||||
for (let i = 0; i < 3; i++)
|
||||
await getFollowedUsersVideos()
|
||||
}
|
||||
}
|
||||
|
||||
async function getFollowedUsersVideos() {
|
||||
if (noMoreContent.value)
|
||||
return
|
||||
|
||||
if (offset.value === '0') {
|
||||
noMoreContent.value = true
|
||||
return
|
||||
}
|
||||
|
||||
emit('beforeLoading')
|
||||
isLoading.value = true
|
||||
try {
|
||||
const response: MomentResult = await browser.runtime.sendMessage({
|
||||
@@ -72,6 +100,7 @@ async function getFollowedUsersVideos() {
|
||||
}
|
||||
finally {
|
||||
isLoading.value = false
|
||||
emit('afterLoading')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +145,9 @@ function jumpToLoginPage() {
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- no more content -->
|
||||
<Empty v-if="noMoreContent" class="pb-4" :description="$t('common.no_more_content')" />
|
||||
|
||||
<Transition name="fade">
|
||||
<Loading v-if="isLoading" />
|
||||
</Transition>
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
import type { Ref } from 'vue'
|
||||
import type { AppForYouResult, Item as AppVideoItem } from '~/models/video/appForYou'
|
||||
import type { Item as VideoItem, forYouResult } from '~/models/video/forYou'
|
||||
import emitter from '~/utils/mitt'
|
||||
import { accessKey, settings } from '~/logic'
|
||||
import { LanguageType } from '~/enums/appEnums'
|
||||
import { TVAppKey } from '~/utils/authProvider'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'beforeLoading'): void
|
||||
(e: 'afterLoading'): void
|
||||
}>()
|
||||
|
||||
const videoList = reactive<VideoItem[]>([])
|
||||
const appVideoList = reactive<AppVideoItem[]>([])
|
||||
const isLoading = ref<boolean>(true)
|
||||
@@ -14,6 +18,7 @@ const needToLoginFirst = ref<boolean>(false)
|
||||
const containerRef = ref<HTMLElement>() as Ref<HTMLElement>
|
||||
const refreshIdx = ref<number>(1)
|
||||
const noMoreContent = ref<boolean>(false)
|
||||
const { handleReachBottom, handlePageRefresh } = useBewlyApp()
|
||||
|
||||
watch(() => settings.value.recommendationMode, (newValue) => {
|
||||
videoList.length = 0
|
||||
@@ -41,28 +46,48 @@ onMounted(async () => {
|
||||
}
|
||||
}, 200)
|
||||
|
||||
emitter.off('reachBottom')
|
||||
emitter.on('reachBottom', async () => {
|
||||
if (!isLoading.value) {
|
||||
if (settings.value.recommendationMode === 'web') {
|
||||
getRecommendVideos()
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < 3; i++)
|
||||
await getAppRecommendVideos()
|
||||
}
|
||||
}
|
||||
})
|
||||
initPageAction()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off('reachBottom')
|
||||
onActivated(() => {
|
||||
initPageAction()
|
||||
})
|
||||
|
||||
function initPageAction() {
|
||||
handleReachBottom.value = async () => {
|
||||
if (isLoading.value)
|
||||
return
|
||||
if (noMoreContent.value)
|
||||
return
|
||||
|
||||
if (settings.value.recommendationMode === 'web') {
|
||||
getRecommendVideos()
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < 3; i++)
|
||||
await getAppRecommendVideos()
|
||||
}
|
||||
}
|
||||
|
||||
handlePageRefresh.value = async () => {
|
||||
if (isLoading.value)
|
||||
return
|
||||
|
||||
videoList.length = 0
|
||||
appVideoList.length = 0
|
||||
noMoreContent.value = false
|
||||
if (settings.value.recommendationMode === 'web') {
|
||||
await getRecommendVideos()
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < 3; i++)
|
||||
await getAppRecommendVideos()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getRecommendVideos() {
|
||||
if (noMoreContent.value)
|
||||
return
|
||||
|
||||
emit('beforeLoading')
|
||||
isLoading.value = true
|
||||
try {
|
||||
const response: forYouResult = await browser.runtime.sendMessage({
|
||||
@@ -97,10 +122,12 @@ async function getRecommendVideos() {
|
||||
}
|
||||
finally {
|
||||
isLoading.value = false
|
||||
emit('afterLoading')
|
||||
}
|
||||
}
|
||||
|
||||
async function getAppRecommendVideos() {
|
||||
emit('beforeLoading')
|
||||
isLoading.value = true
|
||||
try {
|
||||
const response: AppForYouResult = await browser.runtime.sendMessage({
|
||||
@@ -136,6 +163,7 @@ async function getAppRecommendVideos() {
|
||||
}
|
||||
finally {
|
||||
isLoading.value = false
|
||||
emit('afterLoading')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +230,9 @@ function jumpToLoginPage() {
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- no more content -->
|
||||
<Empty v-if="noMoreContent" class="pb-4" :description="$t('common.no_more_content')" />
|
||||
|
||||
<Transition name="fade">
|
||||
<Loading v-if="isLoading" />
|
||||
</Transition>
|
||||
|
||||
@@ -6,9 +6,13 @@ import type { List as RankingPgcItem, RankingPgcResult } from '~/models/video/ra
|
||||
import { settings } from '~/logic'
|
||||
import emitter from '~/utils/mitt'
|
||||
|
||||
const { t } = useI18n()
|
||||
const emit = defineEmits<{
|
||||
(e: 'beforeLoading'): void
|
||||
(e: 'afterLoading'): void
|
||||
}>()
|
||||
|
||||
const handleBackToTop = inject('handleBackToTop') as (targetScrollTop: number) => void
|
||||
const { t } = useI18n()
|
||||
const { handleBackToTop, handlePageRefresh } = useBewlyApp()
|
||||
|
||||
const rankingTypes = computed((): RankingType[] => {
|
||||
return [
|
||||
@@ -72,14 +76,30 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
getRankingVideos()
|
||||
initPageAction()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
initPageAction()
|
||||
})
|
||||
|
||||
function initPageAction() {
|
||||
handlePageRefresh.value = async () => {
|
||||
videoList.length = 0
|
||||
PgcList.length = 0
|
||||
if (isLoading.value)
|
||||
return
|
||||
getRankingVideos()
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
emitter.off('topBarVisibleChange')
|
||||
})
|
||||
|
||||
function getRankingVideos() {
|
||||
videoList.length = 0
|
||||
emit('beforeLoading')
|
||||
isLoading.value = true
|
||||
browser.runtime.sendMessage({
|
||||
contentScriptQuery: 'getRankingVideos',
|
||||
@@ -90,7 +110,10 @@ function getRankingVideos() {
|
||||
const { list } = response.data
|
||||
Object.assign(videoList, list)
|
||||
}
|
||||
}).finally(() => isLoading.value = false)
|
||||
}).finally(() => {
|
||||
isLoading.value = false
|
||||
emit('afterLoading')
|
||||
})
|
||||
}
|
||||
|
||||
function getRankingPgc() {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import type { Ref } from 'vue'
|
||||
import type { DataItem as MomentItem, MomentResult } from '~/models/moment/moment'
|
||||
import emitter from '~/utils/mitt'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'beforeLoading'): void
|
||||
(e: 'afterLoading'): void
|
||||
}>()
|
||||
|
||||
const momentList = reactive<MomentItem[]>([])
|
||||
const isLoading = ref<boolean>(false)
|
||||
@@ -10,28 +14,57 @@ const containerRef = ref<HTMLElement>() as Ref<HTMLElement>
|
||||
const offset = ref<string>('')
|
||||
const updateBaseline = ref<string>('')
|
||||
const noMoreContent = ref<boolean>(false)
|
||||
const noMoreContentWarning = ref<boolean>(false)
|
||||
const { handleReachBottom, handlePageRefresh } = useBewlyApp()
|
||||
|
||||
function initPageAction() {
|
||||
handleReachBottom.value = async () => {
|
||||
if (isLoading.value)
|
||||
return
|
||||
if (noMoreContent.value) {
|
||||
noMoreContentWarning.value = true
|
||||
return
|
||||
}
|
||||
for (let i = 0; i < 3; i++)
|
||||
await getFollowedUsersVideos()
|
||||
}
|
||||
handlePageRefresh.value = async () => {
|
||||
if (isLoading.value)
|
||||
return
|
||||
|
||||
offset.value = ''
|
||||
updateBaseline.value = ''
|
||||
momentList.length = 0
|
||||
noMoreContent.value = false
|
||||
noMoreContentWarning.value = false
|
||||
if (isLoading.value)
|
||||
return
|
||||
for (let i = 0; i < 3; i++)
|
||||
await getFollowedUsersVideos()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
for (let i = 0; i < 3; i++)
|
||||
await getFollowedUsersVideos()
|
||||
|
||||
emitter.off('reachBottom')
|
||||
emitter.on('reachBottom', async () => {
|
||||
if (!isLoading.value) {
|
||||
for (let i = 0; i < 3; i++)
|
||||
await getFollowedUsersVideos()
|
||||
}
|
||||
})
|
||||
initPageAction()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off('reachBottom')
|
||||
onActivated(() => {
|
||||
initPageAction()
|
||||
})
|
||||
|
||||
async function getFollowedUsersVideos() {
|
||||
if (noMoreContent.value)
|
||||
return
|
||||
|
||||
if (offset.value === '0') {
|
||||
noMoreContent.value = true
|
||||
return
|
||||
}
|
||||
|
||||
emit('beforeLoading')
|
||||
isLoading.value = true
|
||||
try {
|
||||
const response: MomentResult = await browser.runtime.sendMessage({
|
||||
@@ -72,6 +105,7 @@ async function getFollowedUsersVideos() {
|
||||
}
|
||||
finally {
|
||||
isLoading.value = false
|
||||
emit('afterLoading')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +149,9 @@ function jumpToLoginPage() {
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- no more content -->
|
||||
<Empty v-if="noMoreContentWarning" class="pb-4" :description="$t('common.no_more_content')" />
|
||||
|
||||
<Transition name="fade">
|
||||
<Loading v-if="isLoading" />
|
||||
</Transition>
|
||||
|
||||
@@ -1,28 +1,43 @@
|
||||
<script setup lang="ts">
|
||||
import type { Ref } from 'vue'
|
||||
import type { TrendingResult, List as VideoItem } from '~/models/video/trending'
|
||||
import emitter from '~/utils/mitt'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'beforeLoading'): void
|
||||
(e: 'afterLoading'): void
|
||||
}>()
|
||||
|
||||
const videoList = reactive<VideoItem[]>([])
|
||||
const isLoading = ref<boolean>(false)
|
||||
const containerRef = ref<HTMLElement>() as Ref<HTMLElement>
|
||||
const pn = ref<number>(1)
|
||||
const { handleReachBottom, handlePageRefresh } = useBewlyApp()
|
||||
|
||||
onMounted(async () => {
|
||||
await getTrendingVideos()
|
||||
|
||||
emitter.off('reachBottom')
|
||||
emitter.on('reachBottom', async () => {
|
||||
initPageAction()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
initPageAction()
|
||||
})
|
||||
|
||||
function initPageAction() {
|
||||
handleReachBottom.value = async () => {
|
||||
if (!isLoading.value)
|
||||
await getTrendingVideos()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off('reachBottom')
|
||||
})
|
||||
handlePageRefresh.value = async () => {
|
||||
videoList.length = 0
|
||||
pn.value = 1
|
||||
await getTrendingVideos()
|
||||
}
|
||||
}
|
||||
|
||||
async function getTrendingVideos() {
|
||||
emit('beforeLoading')
|
||||
isLoading.value = true
|
||||
try {
|
||||
const response: TrendingResult = await browser.runtime.sendMessage({
|
||||
@@ -50,6 +65,7 @@ async function getTrendingVideos() {
|
||||
}
|
||||
finally {
|
||||
isLoading.value = false
|
||||
emit('afterLoading')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -3,7 +3,6 @@ import { useDateFormat } from '@vueuse/core'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { getCSRF, openLinkToNewTab, removeHttpFromUrl } from '~/utils/main'
|
||||
import { calcCurrentTime } from '~/utils/dataFormatter'
|
||||
import emitter from '~/utils/mitt'
|
||||
import type { List as VideoItem, WatchLaterResult } from '~/models/video/watchLater'
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -11,20 +10,22 @@ const { t } = useI18n()
|
||||
const isLoading = ref<boolean>()
|
||||
const noMoreContent = ref<boolean>()
|
||||
const watchLaterList = reactive<VideoItem[]>([])
|
||||
const { handlePageRefresh } = useBewlyApp()
|
||||
|
||||
onMounted(() => {
|
||||
getAllWatchLaterList()
|
||||
initPageAction()
|
||||
})
|
||||
|
||||
function initPageAction() {
|
||||
handlePageRefresh.value = () => {
|
||||
if (isLoading.value)
|
||||
return
|
||||
|
||||
emitter.off('pageRefresh')
|
||||
emitter.on('pageRefresh', async () => {
|
||||
watchLaterList.length = 0
|
||||
getAllWatchLaterList()
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off('pageRefresh')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get watch later list
|
||||
@@ -58,7 +59,6 @@ function deleteWatchLaterItem(index: number, aid: number) {
|
||||
}
|
||||
|
||||
function handleClearAllWatchLater() {
|
||||
// eslint-disable-next-line no-alert
|
||||
const result = confirm(
|
||||
t('watch_later.clear_all_confirm'),
|
||||
)
|
||||
@@ -77,7 +77,6 @@ function handleClearAllWatchLater() {
|
||||
}
|
||||
|
||||
function handleRemoveWatchedVideos() {
|
||||
// eslint-disable-next-line no-alert
|
||||
const result = confirm(
|
||||
t('watch_later.remove_watched_videos_confirm'),
|
||||
)
|
||||
|
||||
@@ -4,7 +4,6 @@ import Toast, { POSITION } from 'vue-toastification'
|
||||
import { createPinia } from 'pinia'
|
||||
import { i18n } from '~/utils/i18n'
|
||||
import 'vue-toastification/dist/index.css'
|
||||
import 'overlayscrollbars/overlayscrollbars.css'
|
||||
|
||||
const pinia = createPinia()
|
||||
|
||||
@@ -29,6 +28,4 @@ export async function setupApp(app: App) {
|
||||
position: POSITION.TOP_CENTER,
|
||||
})
|
||||
app.use(pinia)
|
||||
const { OverlayScrollbarsComponent } = await import('overlayscrollbars-vue')
|
||||
app.component('OverlayScrollbarsComponent', OverlayScrollbarsComponent)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
/**
|
||||
* 感謝這份專案給出的獲取accessKey的方法
|
||||
* https://github.com/indefined/UserScripts/blob/42e20281d2e4d7bce16b5c8033b67ccb6ad312e9/bilibiliHome/bilibiliHome.user.js#L1149
|
||||
* TODO: 解耦,避免直接操作DOM
|
||||
*/
|
||||
|
||||
/* eslint-disable no-throw-literal */
|
||||
import browser from 'webextension-polyfill'
|
||||
// import browser from 'webextension-polyfill'
|
||||
import { appSign } from './appSign'
|
||||
import { accessKey } from '~/logic/storage'
|
||||
|
||||
@@ -14,63 +7,66 @@ export function revokeAccessKey() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param t
|
||||
* @param element
|
||||
* 感謝這份專案給出的獲取accessKey的方法
|
||||
* https://github.com/indefined/UserScripts/blob/42e20281d2e4d7bce16b5c8033b67ccb6ad312e9/bilibiliHome/bilibiliHome.user.js#L1149
|
||||
*/
|
||||
export function grantAccessKey(t: any, element: HTMLButtonElement): void {
|
||||
const originalInnerHTML = element.innerHTML
|
||||
element.innerHTML = `
|
||||
<span class="animate-pulse">Loading...</span>
|
||||
`
|
||||
element.style.pointerEvents = 'none'
|
||||
element.disabled = true
|
||||
// /**
|
||||
// * @deprecated
|
||||
// * @param t
|
||||
// * @param element
|
||||
// */
|
||||
// export function grantAccessKey(t: any, element: HTMLButtonElement): void {
|
||||
// const originalInnerHTML = element.innerHTML
|
||||
// element.innerHTML = `
|
||||
// <span class="animate-pulse">Loading...</span>
|
||||
// `
|
||||
// element.style.pointerEvents = 'none'
|
||||
// element.disabled = true
|
||||
|
||||
const tip = t('auth.err_tip')
|
||||
fetch(
|
||||
'https://passport.bilibili.com/login/app/third?appkey=5fd5a7d8bfd9b0e6'
|
||||
+ '&api=https%3A%2F%2Fwww.mcbbs.net%2Ftemplate%2Fmcbbs%2Fimage%2Fspecial_photo_bg.png&sign=04224646d1fea004e79606d3b038c84a',
|
||||
{
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
},
|
||||
)
|
||||
.then(res => res.json())
|
||||
.then((data) => {
|
||||
if (data.code || !data.data)
|
||||
throw { tip, msg: data.msg || data.message || data.code, data }
|
||||
else if (!data.data.has_login)
|
||||
throw { tip, msg: t('auth.plz_login_first'), data }
|
||||
else if (!data.data.confirm_uri)
|
||||
throw { tip, msg: t('auth.receive_verified_url_err'), data }
|
||||
else return data.data.confirm_uri
|
||||
})
|
||||
.then(
|
||||
url =>
|
||||
browser.runtime
|
||||
.sendMessage({
|
||||
contentScriptQuery: 'getAccessKey',
|
||||
confirmUri: url,
|
||||
})
|
||||
.then((res: { accessKey: string }) => {
|
||||
accessKey.value = res.accessKey
|
||||
return Promise.resolve()
|
||||
})
|
||||
.catch((err: any) => {
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
return Promise.reject({ tip, msg: t('auth.failed_to_get_accesskey'), data: err })
|
||||
}),
|
||||
)
|
||||
.catch((error) => {
|
||||
element.innerHTML = originalInnerHTML
|
||||
element.style.pointerEvents = 'auto'
|
||||
element.disabled = false
|
||||
// const tip = t('auth.err_tip')
|
||||
// fetch(
|
||||
// 'https://passport.bilibili.com/login/app/third?appkey=5fd5a7d8bfd9b0e6'
|
||||
// + '&api=https%3A%2F%2Fwww.mcbbs.net%2Ftemplate%2Fmcbbs%2Fimage%2Fspecial_photo_bg.png&sign=04224646d1fea004e79606d3b038c84a',
|
||||
// {
|
||||
// method: 'GET',
|
||||
// credentials: 'include',
|
||||
// },
|
||||
// )
|
||||
// .then(res => res.json())
|
||||
// .then((data) => {
|
||||
// if (data.code || !data.data)
|
||||
// throw { tip, msg: data.msg || data.message || data.code, data }
|
||||
// else if (!data.data.has_login)
|
||||
// throw { tip, msg: t('auth.plz_login_first'), data }
|
||||
// else if (!data.data.confirm_uri)
|
||||
// throw { tip, msg: t('auth.receive_verified_url_err'), data }
|
||||
// else return data.data.confirm_uri
|
||||
// })
|
||||
// .then(
|
||||
// url =>
|
||||
// browser.runtime
|
||||
// .sendMessage({
|
||||
// contentScriptQuery: 'getAccessKey',
|
||||
// confirmUri: url,
|
||||
// })
|
||||
// .then((res: { accessKey: string }) => {
|
||||
// accessKey.value = res.accessKey
|
||||
// return Promise.resolve()
|
||||
// })
|
||||
// .catch((err: any) => {
|
||||
// // eslint-disable-next-line prefer-promise-reject-errors
|
||||
// return Promise.reject({ tip, msg: t('auth.failed_to_get_accesskey'), data: err })
|
||||
// }),
|
||||
// )
|
||||
// .catch((error) => {
|
||||
// element.innerHTML = originalInnerHTML
|
||||
// element.style.pointerEvents = 'auto'
|
||||
// element.disabled = false
|
||||
|
||||
// eslint-disable-next-line no-alert
|
||||
alert(`${error.tip}: ${error.msg}`)
|
||||
console.error(`${error.msg}: `, error.data)
|
||||
})
|
||||
}
|
||||
// alert(`${error.tip}: ${error.msg}`)
|
||||
// console.error(`${error.msg}: `, error.data)
|
||||
// })
|
||||
// }
|
||||
|
||||
// https://socialsisteryi.github.io/bilibili-API-collect/docs/misc/sign/APPKey.html#appkey
|
||||
export const TVAppKey = {
|
||||
|
||||
@@ -100,11 +100,11 @@ export function injectCSS(css: string): HTMLStyleElement {
|
||||
|
||||
/**
|
||||
* delay
|
||||
* @param time delay time
|
||||
* @param ms milliseconds delay time
|
||||
*/
|
||||
export function delay(time: number) {
|
||||
export function delay(ms: number) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, time)
|
||||
setTimeout(resolve, ms)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -33,11 +33,13 @@ export const sharedConfig: UserConfig = {
|
||||
],
|
||||
},
|
||||
],
|
||||
dirs: [r('src/composables')],
|
||||
dts: r('src/auto-imports.d.ts'),
|
||||
}),
|
||||
|
||||
// https://github.com/antfu/unplugin-vue-components
|
||||
Components({
|
||||
extensions: ['vue', 'ts'],
|
||||
dirs: [r('src/components')],
|
||||
// generate `components.d.ts` for ts support with Volar
|
||||
dts: r('src/components.d.ts'),
|
||||
|
||||
Reference in New Issue
Block a user