Refactor navbar history filters (fix #3676, #2729)

This commit is contained in:
the1812 2022-12-10 22:10:56 +08:00
parent c658cd67e5
commit 9dc2ad58f4
10 changed files with 90 additions and 47 deletions

View File

@ -155,6 +155,8 @@ export default Vue.extend({
</script>
<style lang="scss">
@import 'common';
.custom-navbar-item {
color: inherit;
position: relative;
@ -216,7 +218,7 @@ export default Vue.extend({
}
&.active .main-content {
font-weight: bold;
@include semi-bold();
font-size: 11pt;
}

View File

@ -409,7 +409,7 @@ export default Vue.extend({
.title {
grid-area: title;
font-size: 13px;
font-weight: bold;
@include semi-bold();
@include max-line(2);
-webkit-box-align: start;
margin: 0;

View File

@ -25,15 +25,15 @@
<div class="row-title">过滤:</div>
<div class="type-filters">
<div v-for="t of types" :key="t.name" class="type-filter">
<VButton
round
<RadioButton
:title="(t.checked ? '不显示' : '显示') + t.displayName"
:class="{ checked: t.checked }"
@click="toggleTypeFilter(t)"
:checked="t.checked"
:disabled="loading"
@change="toggleTypeFilter(t)"
>
<VIcon :icon="t.icon" :size="18"></VIcon>
{{ t.displayName }}
</VButton>
</RadioButton>
</div>
</div>
</div>
@ -108,14 +108,24 @@
import { bilibiliApi, getJsonWithCredentials, postTextWithCredentials } from '@/core/ajax'
import { formData, getCsrf } from '@/core/utils'
import { descendingSort } from '@/core/utils/sort'
import { VButton, VIcon, TextBox, VLoading, VEmpty, ScrollTrigger, DpiImage } from '@/ui'
import {
VButton,
VIcon,
RadioButton,
TextBox,
VLoading,
VEmpty,
ScrollTrigger,
DpiImage,
} from '@/ui'
import { popperMixin } from '../mixins'
import { types, TypeFilter, HistoryItem, getHistoryItems, group } from './types'
import { types, TypeFilter, HistoryItem, getHistoryItems, group, HistoryType } from './types'
export default Vue.extend({
components: {
VButton,
VIcon,
RadioButton,
TextBox,
VLoading,
VEmpty,
@ -137,17 +147,12 @@ export default Vue.extend({
},
computed: {
canNextPage() {
return (
this.search === '' &&
!this.loading &&
this.hasMorePage &&
this.types.every((t: TypeFilter) => t.checked)
)
return this.search === '' && !this.loading && this.hasMorePage
},
},
watch: {
search: lodash.debounce(function search() {
this.updateGroups()
this.reloadHistoryItems()
}, 200),
},
async created() {
@ -159,11 +164,22 @@ export default Vue.extend({
},
methods: {
toggleTypeFilter(typeFilter: TypeFilter) {
typeFilter.checked = !typeFilter.checked
this.updateGroups()
types.forEach(t => (t.checked = t.name === typeFilter.name))
this.reloadHistoryItems()
},
async reloadHistoryItems() {
this.viewTime = 0
this.hasMorePage = true
this.loading = true
try {
await this.nextPage()
} finally {
this.loading = false
}
},
filterFunc(item: HistoryItem) {
if (types.some(t => t.name === item.type && !t.checked)) {
const isAllType = types.find(it => it.name === HistoryType.All).checked
if (!isAllType && types.some(t => t.name === item.type && !t.checked)) {
return false
}
if (
@ -178,7 +194,10 @@ export default Vue.extend({
this.groups = group(this.cards.filter(this.filterFunc))
},
async nextPage() {
const items = await getHistoryItems(this.viewTime)
const items = await getHistoryItems(
this.viewTime,
types.find(t => t.checked),
)
const cards: HistoryItem[] = lodash.uniqBy(
this.cards.concat(items).sort(descendingSort((item: HistoryItem) => item.viewAt)),
item => item.id,
@ -188,7 +207,10 @@ export default Vue.extend({
if (cards.length > 0) {
this.viewTime = lodash.last(cards).viewAt
}
this.hasMorePage = cards.length !== 0
this.hasMorePage = items.length !== 0
if (this.hasMorePage && this.groups.length === 0) {
await this.nextPage()
}
},
async updateHistoryPauseState() {
const result = await bilibiliApi(
@ -223,7 +245,7 @@ export default Vue.extend({
@import '../popup';
.custom-navbar-history-list {
width: 350px;
width: 400px;
@include navbar-popup-height();
font-size: 12px;
padding: 0;
@ -251,23 +273,22 @@ export default Vue.extend({
margin: 16px 12px 4px 12px;
.header-row {
@include h-stretch(8px);
justify-content: space-between;
.row-title {
@include h-center();
}
}
.type-filters {
@include h-center(8px);
@include h-center(6px);
.type-filter {
.be-button {
padding: 4px 8px 4px 6px;
color: #8888;
.be-icon {
margin-right: 6px;
}
&.checked {
color: inherit;
}
// color: #8888;
// .be-icon {
// margin-right: 6px;
// }
// &.checked {
// color: inherit;
// }
}
}
}
@ -402,8 +423,8 @@ export default Vue.extend({
}
}
.title {
@include semi-bold();
grid-area: title;
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;

View File

@ -11,7 +11,7 @@ export const history: CustomNavbarItemInit = {
active: document.URL.replace(/\?.*$/, '') === href,
loginRequired: true,
boundingWidth: 350,
boundingWidth: 400,
noPopupPadding: true,
popupContent: () => import('./NavbarHistory.vue').then(m => m.default),
}

View File

@ -4,6 +4,7 @@ import { formatDuration } from '@/core/utils/formatters'
/** 历史项目类型, 值为 API 中的 `history.business` */
export enum HistoryType {
All = 'all',
Video = 'archive',
Live = 'live',
Article = 'article',
@ -48,32 +49,44 @@ export interface TypeFilter {
displayName: string
icon: string
checked: boolean
apiType: string
}
/** 所有的历史记录类型 */
export const types = [
{
name: HistoryType.All,
displayName: '全部',
icon: '',
checked: true,
apiType: '',
},
{
name: HistoryType.Video,
displayName: '视频',
icon: 'mdi-play-circle-outline',
checked: true,
checked: false,
apiType: 'archive',
},
{
name: HistoryType.Bangumi,
displayName: '番剧',
icon: 'mdi-television-classic',
checked: true,
checked: false,
apiType: 'archive',
},
{
name: HistoryType.Live,
displayName: '直播',
icon: 'mdi-video-wireless-outline',
checked: true,
checked: false,
apiType: 'live',
},
{
name: HistoryType.Article,
displayName: '专栏',
icon: 'mdi-newspaper-variant-outline',
checked: true,
checked: false,
apiType: 'article',
},
] as TypeFilter[]
@ -188,12 +201,17 @@ const parseHistoryItem = (item: any): HistoryItem => {
* ,
* @param viewTime
*/
export const getHistoryItems = async (viewTime?: number) => {
let api = 'https://api.bilibili.com/x/web-interface/history/cursor'
export const getHistoryItems = async (viewTime?: number, type?: TypeFilter) => {
const api = 'https://api.bilibili.com/x/web-interface/history/cursor'
const params = new URLSearchParams()
if (viewTime) {
api += `?view_at=${Math.round(viewTime / 1000)}`
params.set('view_at', Math.round(viewTime / 1000).toString())
}
const { list } = await bilibiliApi(getJsonWithCredentials(api), '获取历史记录失败')
params.set('type', type?.apiType ?? '')
const { list } = await bilibiliApi(
getJsonWithCredentials(`${api}?${params.toString()}`),
'获取历史记录失败',
)
if (!Array.isArray(list)) {
return []
}

View File

@ -186,7 +186,7 @@ export default Vue.extend({
.title {
margin-left: 6px;
font-size: 18px;
font-weight: bold;
@include semi-bold();
}
.grow {
flex: 1;

View File

@ -233,7 +233,7 @@ export default Vue.extend({
}
.title {
font-size: 14px;
font-weight: bold;
@include semi-bold();
padding-top: 4px;
color: inherit;
white-space: nowrap;

View File

@ -325,6 +325,8 @@ export default Vue.extend({
</script>
<style lang="scss">
@import 'common';
.user-info-panel {
border-radius: 8px;
overflow: hidden;
@ -441,7 +443,7 @@ export default Vue.extend({
.welcome,
.name {
font-size: 16px;
font-weight: bold;
@include semi-bold();
margin: 46px 0 16px 0;
text-align: center;
color: inherit;
@ -495,7 +497,7 @@ export default Vue.extend({
color: var(--theme-color) !important;
}
.stats-number {
font-weight: bold;
@include semi-bold();
margin-bottom: 4px;
font-size: 14px;
transition: none;

View File

@ -326,7 +326,7 @@ export default Vue.extend({
.title {
grid-area: title;
font-size: 13px;
font-weight: bold;
@include semi-bold();
margin: 0;
margin-top: 8px;
padding: 0 10px;

View File

@ -91,7 +91,7 @@ export default Vue.extend({
&.left-icon {
.icon-container {
order: -1;
margin: 2px 8px 2px 0;
margin: 2px 6px 2px 0;
}
}
}