Add tag filter (#145)

This commit is contained in:
Teven Feng
2022-09-09 13:58:07 +08:00
committed by GitHub
parent 5e19572ba3
commit f0b8fa16d6
7 changed files with 90 additions and 2 deletions

View File

@@ -65,7 +65,7 @@ import Vue from 'vue';
import { mapGetters } from 'vuex';
import { tr } from '@/locale';
import { Torrent, Category } from '@/types';
import { Torrent, Category, Tag } from '@/types';
import FilterGroup from './drawer/FilterGroup.vue';
import api from '../Api';
import { formatSize } from '@/filters';
@@ -141,7 +141,9 @@ interface MenuChildrenItem extends MenuItem {
'isDataReady',
'allTorrents',
'allCategories',
'allTags',
'torrentGroupByCategory',
'torrentGroupByTag',
'torrentGroupBySite',
'torrentGroupByState',
]),
@@ -165,7 +167,9 @@ export default class Drawer extends Vue {
isDataReady!: boolean
allTorrents!: Torrent[]
allCategories!: Category[]
allTags!: Tag[]
torrentGroupByCategory!: {[category: string]: Torrent[]}
torrentGroupByTag!: {[tag: string]: Torrent[]}
torrentGroupBySite!: {[site: string]: Torrent[]}
torrentGroupByState!: {[state: string]: Torrent[]}
@@ -214,6 +218,24 @@ export default class Drawer extends Vue {
});
}
buildTagGroup(): MenuChildrenItem[] {
return [{
key: '',
name: tr('untagged'),
}].concat(this.allTags).map((tag) => {
let value = this.torrentGroupByTag[tag.key];
if (isUndefined(value)) {
value = [];
}
const size = formatSize(sumBy(value, 'size'));
const title = `${tag.name} (${value.length})`;
const append = `[${size}]`;
return {
icon: 'mdi-folder', title, key: tag.key, append,
};
});
}
buildSiteGroup(): MenuChildrenItem[] {
return sortBy(Object.entries(this.torrentGroupBySite).map(([key, value]) => {
const size = formatSize(sumBy(value, 'size'));
@@ -263,6 +285,20 @@ export default class Drawer extends Vue {
],
});
filterGroups.push({
icon: 'mdi-menu-up',
'icon-alt': 'mdi-menu-down',
title: tr('tag', 0),
model: null,
select: 'tag',
children: [
{
icon: 'mdi-folder', title: `${tr('all')} (${this.allTorrents.length})`, key: null, append: `[${totalSize}]`,
},
...this.buildTagGroup(),
],
});
filterGroups.push({
icon: 'mdi-menu-up',
'icon-alt': 'mdi-menu-down',

View File

@@ -248,7 +248,7 @@ import api from '../Api'
import { formatSize } from '@/filters'
import { DialogType, TorrentFilter, ConfigPayload, DialogConfig, SnackBarConfig } from '@/store/types'
import Component from 'vue-class-component'
import { Torrent, Category } from '@/types'
import { Torrent, Category, Tag } from '@/types'
import { Watch } from 'vue-property-decorator'
function getStateInfo(state: string) {
@@ -337,8 +337,10 @@ function getStateInfo(state: string) {
...mapGetters([
'isDataReady',
'allTorrents',
'allTags',
'allCategories',
'torrentGroupByCategory',
'torrentGroupByTag',
'torrentGroupBySite',
'torrentGroupByState',
]),
@@ -413,7 +415,9 @@ export default class Torrents extends Vue {
isDataReady!: boolean
allTorrents!: Torrent[]
allCategories!: Category[]
allTags!: Tag[]
torrentGroupByCategory!: {[category: string]: Torrent[]}
torrentGroupByTag!: {[tag: string]: Torrent[]}
torrentGroupBySite!: {[site: string]: Torrent[]}
torrentGroupByState!: {[state: string]: Torrent[]}
filter!: TorrentFilter
@@ -444,6 +448,9 @@ export default class Torrents extends Vue {
if (this.filter.category !== null) {
list = intersection(list, this.torrentGroupByCategory[this.filter.category]);
}
if (this.filter.tag !== null) {
list = intersection(list, this.torrentGroupByTag[this.filter.tag]);
}
if (this.filter.state !== null) {
list = intersection(list, this.torrentGroupByState[this.filter.state]);
}

View File

@@ -53,6 +53,8 @@ export default {
all: '全部',
category: '分类',
uncategorized: '未分类',
tag: '标签',
untagged: '无标签',
others: '其他',
sites: '站点',
files: '文件',

View File

@@ -53,6 +53,12 @@ const store = new Vuex.Store<RootState>({
}
delete payload.categories_removed;
}
if (payload.tags_removed) {
for (const key of payload.tags_removed) {
Vue.delete(mainData, key);
}
delete payload.categories_removed;
}
stateMerge(mainData, payload);
}
},
@@ -94,9 +100,38 @@ const store = new Vuex.Store<RootState>({
(value, key) => merge({}, value, { key }));
return sortBy(categories, 'name');
},
allTags(state) {
if (!state.mainData) {
return [];
}
const finalTags: any[] = []
for (const tag of state.mainData.tags) {
finalTags.push({
"key": tag,
"name": tag,
});
}
return sortBy(finalTags, 'name');
},
torrentGroupByCategory(state, getters) {
return groupBy(getters.allTorrents, torrent => torrent.category);
},
torrentGroupByTag(state, getters) {
const result: any = {}
for (const torrent of getters.allTorrents) {
const tags: any[] = torrent.tags.split(",");
tags.forEach(tag => {
let list: any[] = result[tag]
if (!list) {
list = []
result[tag] = list;
}
list.push(torrent);
});
}
return result;
},
torrentGroupBySite(state, getters) {
return groupBy(getters.allTorrents, (torrent) => {
if (!torrent.tracker) {

View File

@@ -25,6 +25,7 @@ export interface AddFormState {
export interface TorrentFilter {
state: string;
category: string;
tag: string;
site: string;
query: string;
}

View File

@@ -66,6 +66,11 @@ export interface SimpleCategory {
savePath?: string;
}
export interface Tag {
key: string;
name: string;
}
export interface ServerState {
alltime_dl: number;
alltime_ul: number;
@@ -95,6 +100,7 @@ export interface ServerState {
export interface MainData {
categories: Record<string, Category>;
tags: [string];
server_state: ServerState;
torrents: Record<string, BaseTorrent>;
}

View File

@@ -48,6 +48,7 @@ describe('all torrents getter', () => {
store.replaceState(mockState({
mainData: {
categories: {},
tags: [""],
// eslint-disable-next-line @typescript-eslint/camelcase
server_state: undefined as any,
torrents: {