diff --git a/komga-webui/src/services/komga-collections.service.ts b/komga-webui/src/services/komga-collections.service.ts index baba576a3..2ce31d9ce 100644 --- a/komga-webui/src/services/komga-collections.service.ts +++ b/komga-webui/src/services/komga-collections.service.ts @@ -78,11 +78,24 @@ export default class KomgaCollectionsService { } } - async getSeries (collectionId: string, pageRequest?: PageRequest): Promise> { + async getSeries (collectionId: string, pageRequest?: PageRequest, + libraryId?: string[], status?: string[], + readStatus?: string[], genre?: string[], tag?: string[], language?: string[], + publisher?: string[], ageRating?: string[]): Promise> { 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' diff --git a/komga-webui/src/services/komga-referential.service.ts b/komga-webui/src/services/komga-referential.service.ts index fc4cc88b4..84395c206 100644 --- a/komga-webui/src/services/komga-referential.service.ts +++ b/komga-webui/src/services/komga-referential.service.ts @@ -29,10 +29,11 @@ export default class KomgaReferentialService { } } - async getGenres (libraryId?: string): Promise { + async getGenres (libraryId?: string, collectionId?: string): Promise { 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 { + async getTags (libraryId?: string, seriesId?: string, collectionId?: string): Promise { 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 { + async getPublishers (libraryId?: string, collectionId?: string): Promise { 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 { + async getAgeRatings (libraryId?: string, collectionId?: string): Promise { 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 { + async getLanguages (libraryId?: string, collectionId?: string): Promise { 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, diff --git a/komga-webui/src/views/BrowseCollection.vue b/komga-webui/src/views/BrowseCollection.vue index bd6185457..897a9ec1d 100644 --- a/komga-webui/src/views/BrowseCollection.vue +++ b/komga-webui/src/views/BrowseCollection.vue @@ -36,6 +36,10 @@ + + mdi-filter-variant + + + + + + + + + + { 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 () {