feat(webui): filter collection content

closes #270
This commit is contained in:
Gauthier Roebroeck 2020-08-28 11:47:45 +08:00
parent dc064f20ec
commit fc905ef9b5
3 changed files with 160 additions and 9 deletions

View file

@ -78,11 +78,24 @@ export default class KomgaCollectionsService {
}
}
async getSeries (collectionId: string, pageRequest?: PageRequest): Promise<Page<SeriesDto>> {
async getSeries (collectionId: string, pageRequest?: PageRequest,
libraryId?: string[], status?: string[],
readStatus?: string[], genre?: string[], tag?: string[], language?: string[],
publisher?: string[], ageRating?: string[]): Promise<Page<SeriesDto>> {
try {
const params = { ...pageRequest }
const params = { ...pageRequest } as any
if (libraryId) params.library_id = libraryId
if (status) params.status = status
if (readStatus) params.read_status = readStatus
if (genre) params.genre = genre
if (tag) params.tag = tag
if (language) params.language = language
if (publisher) params.publisher = publisher
if (ageRating) params.age_rating = ageRating
return (await this.http.get(`${API_COLLECTIONS}/${collectionId}/series`, {
params: params,
paramsSerializer: params => qs.stringify(params, { indices: false }),
})).data
} catch (e) {
let msg = 'An error occurred while trying to retrieve series'

View file

@ -29,10 +29,11 @@ export default class KomgaReferentialService {
}
}
async getGenres (libraryId?: string): Promise<string[]> {
async getGenres (libraryId?: string, collectionId?: string): Promise<string[]> {
try {
const params = {} as any
if (libraryId) params.library_id = libraryId
if (collectionId) params.collection_id = collectionId
return (await this.http.get('/api/v1/genres', {
params: params,
@ -47,11 +48,12 @@ export default class KomgaReferentialService {
}
}
async getTags (libraryId?: string, seriesId?: string): Promise<string[]> {
async getTags (libraryId?: string, seriesId?: string, collectionId?: string): Promise<string[]> {
try {
const params = {} as any
if (libraryId) params.library_id = libraryId
if (seriesId) params.series_id = seriesId
if (collectionId) params.collection_id = collectionId
return (await this.http.get('/api/v1/tags', {
params: params,
@ -66,10 +68,11 @@ export default class KomgaReferentialService {
}
}
async getPublishers (libraryId?: string): Promise<string[]> {
async getPublishers (libraryId?: string, collectionId?: string): Promise<string[]> {
try {
const params = {} as any
if (libraryId) params.library_id = libraryId
if (collectionId) params.collection_id = collectionId
return (await this.http.get('/api/v1/publishers', {
params: params,
@ -84,10 +87,11 @@ export default class KomgaReferentialService {
}
}
async getAgeRatings (libraryId?: string): Promise<string[]> {
async getAgeRatings (libraryId?: string, collectionId?: string): Promise<string[]> {
try {
const params = {} as any
if (libraryId) params.library_id = libraryId
if (collectionId) params.collection_id = collectionId
return (await this.http.get('/api/v1/age-ratings', {
params: params,
@ -102,10 +106,11 @@ export default class KomgaReferentialService {
}
}
async getLanguages (libraryId?: string): Promise<NameValue[]> {
async getLanguages (libraryId?: string, collectionId?: string): Promise<NameValue[]> {
try {
const params = {} as any
if (libraryId) params.library_id = libraryId
if (collectionId) params.collection_id = collectionId
const data = (await this.http.get('/api/v1/languages', {
params: params,

View file

@ -36,6 +36,10 @@
</v-tooltip>
</v-btn>
<v-btn icon @click="drawer = !drawer">
<v-icon :color="filterActive ? 'secondary' : ''">mdi-filter-variant</v-icon>
</v-btn>
</toolbar-sticky>
<series-multi-select-bar
@ -61,9 +65,34 @@
</toolbar-sticky>
</v-scroll-y-transition>
<filter-drawer v-model="drawer">
<template v-slot:default>
<filter-list
:filters-options="filterOptionsList"
:filters-active.sync="filters"
/>
</template>
<template v-slot:filter>
<filter-panels
:filters-options="filterOptionsPanel"
:filters-active.sync="filters"
/>
</template>
</filter-drawer>
<v-container fluid>
<empty-state
v-if="series.length === 0"
title="The active filter has no matches"
sub-title="Use the filter panel to change the active filter"
icon="mdi-book-multiple"
icon-color="secondary"
>
</empty-state>
<item-browser
v-else
:items.sync="series"
:selected.sync="selectedSeries"
:edit-function="editSingleSeries"
@ -84,6 +113,15 @@ import { COLLECTION_CHANGED, COLLECTION_DELETED, SERIES_CHANGED } from '@/types/
import Vue from 'vue'
import SeriesMultiSelectBar from '@/components/bars/SeriesMultiSelectBar.vue'
import { LIBRARIES_ALL } from '@/types/library'
import { ReadStatus } from '@/types/enum-books'
import { SeriesStatus, SeriesStatusKeyValue } from '@/types/enum-series'
import { mergeFilterParams, toNameValue } from '@/functions/filter'
import FilterDrawer from '@/components/FilterDrawer.vue'
import FilterPanels from '@/components/FilterPanels.vue'
import FilterList from '@/components/FilterList.vue'
import { Location } from 'vue-router'
import EmptyState from '@/components/EmptyState.vue'
import { parseQueryFilter } from '@/functions/query-params'
export default Vue.extend({
name: 'BrowseCollection',
@ -92,6 +130,10 @@ export default Vue.extend({
ItemBrowser,
CollectionActionsMenu,
SeriesMultiSelectBar,
FilterDrawer,
FilterPanels,
FilterList,
EmptyState,
},
data: () => {
return {
@ -100,6 +142,21 @@ export default Vue.extend({
seriesCopy: [] as SeriesDto[],
selectedSeries: [] as SeriesDto[],
editElements: false,
filterOptionsList: {
readStatus: { values: [{ name: 'Unread', value: ReadStatus.UNREAD }] },
} as FiltersOptions,
filterOptionsPanel: {
library: { name: 'LIBRARY', values: [] },
status: { name: 'STATUS', values: SeriesStatusKeyValue },
genre: { name: 'GENRE', values: [] },
tag: { name: 'TAG', values: [] },
publisher: { name: 'PUBLISHER', values: [] },
language: { name: 'LANGUAGE', values: [] },
ageRating: { name: 'AGE RATING', values: [] },
} as FiltersOptions,
filters: {} as FiltersActive,
filterUnwatch: null as any,
drawer: false,
}
},
props: {
@ -134,14 +191,29 @@ export default Vue.extend({
},
mounted () {
this.loadCollection(this.collectionId)
this.resetParams(this.$route)
this.setWatches()
},
beforeRouteUpdate (to, from, next) {
if (to.params.collectionId !== from.params.collectionId) {
this.unsetWatches()
// reset
this.resetParams(this.$route)
this.series = []
this.editElements = false
this.filterOptionsPanel.library.values = []
this.filterOptionsPanel.genre.values = []
this.filterOptionsPanel.tag.values = []
this.filterOptionsPanel.publisher.values = []
this.filterOptionsPanel.language.values = []
this.filterOptionsPanel.ageRating.values = []
this.loadCollection(to.params.collectionId)
this.setWatches()
}
next()
@ -150,17 +222,77 @@ export default Vue.extend({
isAdmin (): boolean {
return this.$store.getters.meAdmin
},
filterActive (): boolean {
return Object.keys(this.filters).some(x => this.filters[x].length !== 0)
},
},
methods: {
resetParams (route: any) {
if (route.query.status || route.query.readStatus || route.query.genre || route.query.tag || route.query.language || route.query.ageRating) {
this.filters.status = parseQueryFilter(route.query.status, Object.keys(SeriesStatus))
this.filters.readStatus = parseQueryFilter(route.query.readStatus, Object.keys(ReadStatus))
this.filters.genre = parseQueryFilter(route.query.genre, this.filterOptionsPanel.genre.values.map(x => x.value))
this.filters.tag = parseQueryFilter(route.query.tag, this.filterOptionsPanel.tag.values.map(x => x.value))
this.filters.language = parseQueryFilter(route.query.language, this.filterOptionsPanel.language.values.map(x => x.value))
this.filters.ageRating = parseQueryFilter(route.query.ageRating, this.filterOptionsPanel.ageRating.values.map(x => x.value))
} else {
this.filters = this.$cookies.get(this.cookieFilter(route.params.collectionId)) || {} as FiltersActive
}
},
cookieFilter (collectionId: string): string {
return `collection.filter.${collectionId}`
},
setWatches () {
this.filterUnwatch = this.$watch('filters', (val) => {
this.$cookies.set(this.cookieFilter(this.collectionId), val, Infinity)
this.updateRouteAndReload()
})
},
unsetWatches () {
this.filterUnwatch()
},
collectionChanged (event: EventCollectionChanged) {
if (event.id === this.collectionId) {
this.loadCollection(this.collectionId)
}
},
updateRouteAndReload () {
this.unsetWatches()
this.selectedSeries = []
this.updateRoute()
this.loadSeries(this.collectionId)
this.setWatches()
},
async loadSeries (collectionId: string) {
this.series = (await this.$komgaCollections.getSeries(collectionId, { unpaged: true } as PageRequest, this.filters.library, this.filters.status, this.filters.readStatus, this.filters.genre, this.filters.tag, this.filters.language, this.filters.publisher, this.filters.ageRating)).content
this.seriesCopy = [...this.series]
},
async loadCollection (collectionId: string) {
this.collection = await this.$komgaCollections.getOneCollection(collectionId)
this.series = (await this.$komgaCollections.getSeries(collectionId, { unpaged: true } as PageRequest)).content
this.seriesCopy = [...this.series]
await this.loadSeries(collectionId)
this.filterOptionsPanel.library.values.push(...this.$store.state.komgaLibraries.libraries.map((x: LibraryDto) => ({
name: x.name,
value: x.id,
})))
this.filterOptionsPanel.genre.values.push(...toNameValue(await this.$komgaReferential.getGenres(undefined, collectionId)))
this.filterOptionsPanel.tag.values.push(...toNameValue(await this.$komgaReferential.getTags(undefined, undefined, collectionId)))
this.filterOptionsPanel.publisher.values.push(...toNameValue(await this.$komgaReferential.getPublishers(undefined, collectionId)))
this.filterOptionsPanel.language.values.push(...(await this.$komgaReferential.getLanguages(undefined, collectionId)))
this.filterOptionsPanel.ageRating.values.push(...toNameValue(await this.$komgaReferential.getAgeRatings(undefined, collectionId)))
},
updateRoute () {
const loc = {
name: this.$route.name,
params: { collectionId: this.$route.params.collectionId },
query: {},
} as Location
mergeFilterParams(this.filters, loc.query)
this.$router.replace(loc).catch((_: any) => {
})
},
editSingleSeries (series: SeriesDto) {
this.$store.dispatch('dialogUpdateSeries', series)
@ -188,6 +320,7 @@ export default Vue.extend({
this.$store.dispatch('dialogAddSeriesToCollection', this.selectedSeries)
},
startEditElements () {
this.filters = {}
this.editElements = true
},
cancelEditElements () {