diff --git a/komga-webui/src/views/BrowseCollection.vue b/komga-webui/src/views/BrowseCollection.vue
index 4eb586840..474be8a38 100644
--- a/komga-webui/src/views/BrowseCollection.vue
+++ b/komga-webui/src/views/BrowseCollection.vue
@@ -36,6 +36,8 @@
+
+
mdi-filter-variant
@@ -90,7 +92,7 @@
{{ $t('common.reset_filters') }}
-
+
+
+
+
+
+
+
@@ -138,16 +155,18 @@ import {Location} from 'vue-router'
import EmptyState from '@/components/EmptyState.vue'
import {SeriesDto} from '@/types/komga-series'
import {authorRoles} from '@/types/author-roles'
-import {AuthorDto} from '@/types/komga-books'
+import {AuthorDto, BookDto} from '@/types/komga-books'
import {CollectionSseDto, ReadProgressSeriesSseDto, SeriesSseDto} from '@/types/komga-sse'
import {throttle} from 'lodash'
import {LibraryDto} from '@/types/komga-libraries'
import {parseBooleanFilter} from '@/functions/query-params'
import {ContextOrigin} from '@/types/context'
+import PageSizeSelect from '@/components/PageSizeSelect.vue'
export default Vue.extend({
name: 'BrowseCollection',
components: {
+ PageSizeSelect,
ToolbarSticky,
ItemBrowser,
CollectionActionsMenu,
@@ -163,9 +182,16 @@ export default Vue.extend({
series: [] as SeriesDto[],
seriesCopy: [] as SeriesDto[],
selectedSeries: [] as SeriesDto[],
+ page: 1,
+ pageSize: 20,
+ unpaged: false,
+ totalPages: 1,
+ totalElements: null as number | null,
editElements: false,
filters: {} as FiltersActive,
filterUnwatch: null as any,
+ pageUnwatch: null as any,
+ pageSizeUnwatch: null as any,
drawer: false,
filterOptions: {
library: [] as NameValue[],
@@ -201,7 +227,12 @@ export default Vue.extend({
this.$eventHub.$off(READPROGRESS_SERIES_DELETED, this.readProgressChanged)
},
async mounted() {
+ this.pageSize = this.$store.state.persistedState.browsingPageSize || this.pageSize
+
+ // restore from query param
await this.resetParams(this.$route, this.collectionId)
+ if (this.$route.query.page) this.page = Number(this.$route.query.page)
+ if (this.$route.query.pageSize) this.pageSize = Number(this.$route.query.pageSize)
this.loadCollection(this.collectionId)
@@ -213,6 +244,9 @@ export default Vue.extend({
// reset
await this.resetParams(this.$route, to.params.collectionId)
+ this.page = 1
+ this.totalPages = 1
+ this.totalElements = null
this.series = []
this.editElements = false
@@ -224,6 +258,19 @@ export default Vue.extend({
next()
},
computed: {
+ paginationVisible(): number {
+ switch (this.$vuetify.breakpoint.name) {
+ case 'xs':
+ return 5
+ case 'sm':
+ case 'md':
+ return 10
+ case 'lg':
+ case 'xl':
+ default:
+ return 15
+ }
+ },
filterOptionsList(): FiltersOptions {
return {
readStatus: {
@@ -352,9 +399,20 @@ export default Vue.extend({
this.$store.commit('setCollectionFilter', {id: this.collectionId, filter: val})
this.updateRouteAndReload()
})
+ this.pageSizeUnwatch = this.$watch('pageSize', (val) => {
+ this.$store.commit('setBrowsingPageSize', val)
+ this.updateRouteAndReload()
+ })
+
+ this.pageUnwatch = this.$watch('page', (val) => {
+ this.updateRoute()
+ this.loadPage(this.collectionId, val)
+ })
},
unsetWatches() {
this.filterUnwatch()
+ this.pageUnwatch()
+ this.pageSizeUnwatch()
},
collectionChanged(event: CollectionSseDto) {
if (event.collectionId === this.collectionId) {
@@ -369,15 +427,25 @@ export default Vue.extend({
updateRouteAndReload() {
this.unsetWatches()
+ this.page = 1
+
this.updateRoute()
- this.loadSeries(this.collectionId)
+ this.loadPage(this.collectionId, this.page)
this.setWatches()
},
- reloadSeries: throttle(function (this: any) {
- this.loadSeries(this.collectionId)
- }, 1000),
- async loadSeries(collectionId: string) {
+ // reloadSeries: throttle(function (this: any) {
+ // this.loadSeries(this.collectionId)
+ // }, 1000),
+ async loadPage(collectionId: string, page: number) {
+ this.selectedSeries = []
+
+ const pageRequest = {
+ page: page - 1,
+ size: this.pageSize,
+ unpaged: this.unpaged,
+ } as PageRequest
+
let authorsFilter = [] as AuthorDto[]
authorRoles.forEach((role: string) => {
if (role in this.filters) this.filters[role].forEach((name: string) => authorsFilter.push({
@@ -387,22 +455,48 @@ export default Vue.extend({
})
const complete = parseBooleanFilter(this.filters.complete)
- this.series = (await this.$komgaCollections.getSeries(collectionId, {unpaged: true} as PageRequest, this.filters.library, this.filters.status, replaceCompositeReadStatus(this.filters.readStatus), this.filters.genre, this.filters.tag, this.filters.language, this.filters.publisher, this.filters.ageRating, this.filters.releaseDate, authorsFilter, complete)).content
+ const seriesPage = await this.$komgaCollections.getSeries(collectionId, pageRequest, this.filters.library, this.filters.status, replaceCompositeReadStatus(this.filters.readStatus), this.filters.genre, this.filters.tag, this.filters.language, this.filters.publisher, this.filters.ageRating, this.filters.releaseDate, authorsFilter, complete)
+
+ this.totalPages = seriesPage.totalPages
+ this.totalElements = seriesPage.totalElements
+ this.series = seriesPage.content
+
this.series.forEach((x: SeriesDto) => x.context = {origin: ContextOrigin.COLLECTION, id: collectionId})
this.seriesCopy = [...this.series]
this.selectedSeries = []
},
+ reloadPage: throttle(function (this: any) {
+ this.loadPage(this.collectionId, this.page)
+ }, 1000),
+ // async loadSeries(collectionId: string) {
+ // let authorsFilter = [] as AuthorDto[]
+ // authorRoles.forEach((role: string) => {
+ // if (role in this.filters) this.filters[role].forEach((name: string) => authorsFilter.push({
+ // name: name,
+ // role: role,
+ // }))
+ // })
+ //
+ // const complete = parseBooleanFilter(this.filters.complete)
+ // this.series = (await this.$komgaCollections.getSeries(collectionId, {unpaged: true} as PageRequest, this.filters.library, this.filters.status, replaceCompositeReadStatus(this.filters.readStatus), this.filters.genre, this.filters.tag, this.filters.language, this.filters.publisher, this.filters.ageRating, this.filters.releaseDate, authorsFilter, complete)).content
+ // this.series.forEach((x: SeriesDto) => x.context = {origin: ContextOrigin.COLLECTION, id: collectionId})
+ // this.seriesCopy = [...this.series]
+ // this.selectedSeries = []
+ // },
async loadCollection(collectionId: string) {
this.$komgaCollections.getOneCollection(collectionId)
.then(v => this.collection = v)
- await this.loadSeries(collectionId)
+ await this.loadPage(collectionId, this.page)
},
updateRoute() {
const loc = {
name: this.$route.name,
params: {collectionId: this.$route.params.collectionId},
- query: {},
+ query: {
+ page: `${this.page}`,
+ pageSize: `${this.pageSize}`,
+ },
} as Location
mergeFilterParams(this.filters, loc.query)
this.$router.replace(loc).catch((_: any) => {
@@ -429,13 +523,17 @@ export default Vue.extend({
addToCollection() {
this.$store.dispatch('dialogAddSeriesToCollection', this.selectedSeries)
},
- startEditElements() {
+ async startEditElements() {
this.filters = {}
+ this.unpaged = true
+ await this.reloadPage()
this.editElements = true
},
cancelEditElements() {
this.editElements = false
this.series = [...this.seriesCopy]
+ this.unpaged = false
+ this.reloadPage()
},
doEditElements() {
this.editElements = false
@@ -443,15 +541,17 @@ export default Vue.extend({
seriesIds: this.series.map(x => x.id),
} as CollectionUpdateDto
this.$komgaCollections.patchCollection(this.collectionId, update)
+ this.unpaged = false
+ this.reloadPage()
},
editCollection() {
this.$store.dispatch('dialogEditCollection', this.collection)
},
seriesChanged(event: SeriesSseDto) {
- if (this.series.some(s => s.id === event.seriesId)) this.reloadSeries()
+ if (this.series.some(s => s.id === event.seriesId)) this.reloadPage()
},
readProgressChanged(event: ReadProgressSeriesSseDto) {
- if (this.series.some(b => b.id === event.seriesId)) this.reloadSeries()
+ if (this.series.some(b => b.id === event.seriesId)) this.reloadPage()
},
},
})
diff --git a/komga-webui/src/views/BrowseReadList.vue b/komga-webui/src/views/BrowseReadList.vue
index d2ea2a9be..449204430 100644
--- a/komga-webui/src/views/BrowseReadList.vue
+++ b/komga-webui/src/views/BrowseReadList.vue
@@ -33,6 +33,8 @@
+
+
mdi-filter-variant
@@ -108,14 +110,40 @@
-
+
+ {{ $t('common.reset_filters') }}
+
+
+
+
+
+
+
+
+
@@ -151,10 +179,14 @@ import {mergeFilterParams, toNameValue} from '@/functions/filter'
import {Location} from 'vue-router'
import {readListFileUrl} from '@/functions/urls'
import {ItemContext} from '@/types/items'
+import PageSizeSelect from '@/components/PageSizeSelect.vue'
+import EmptyState from '@/components/EmptyState.vue'
export default Vue.extend({
name: 'BrowseReadList',
components: {
+ EmptyState,
+ PageSizeSelect,
ToolbarSticky,
ItemBrowser,
ReadListActionsMenu,
@@ -171,9 +203,16 @@ export default Vue.extend({
books: [] as BookDto[],
booksCopy: [] as BookDto[],
selectedBooks: [] as BookDto[],
+ page: 1,
+ pageSize: 20,
+ unpaged: false,
+ totalPages: 1,
+ totalElements: null as number | null,
editElements: false,
filters: {} as FiltersActive,
filterUnwatch: null as any,
+ pageUnwatch: null as any,
+ pageSizeUnwatch: null as any,
drawer: false,
filterOptions: {
library: [] as NameValue[],
@@ -204,7 +243,12 @@ export default Vue.extend({
this.$eventHub.$off(READPROGRESS_DELETED, this.readProgressChanged)
},
async mounted() {
+ this.pageSize = this.$store.state.persistedState.browsingPageSize || this.pageSize
+
+ // restore from query param
await this.resetParams(this.$route, this.readListId)
+ if (this.$route.query.page) this.page = Number(this.$route.query.page)
+ if (this.$route.query.pageSize) this.pageSize = Number(this.$route.query.pageSize)
this.loadReadList(this.readListId)
@@ -216,6 +260,9 @@ export default Vue.extend({
// reset
await this.resetParams(this.$route, this.readListId)
+ this.page = 1
+ this.totalPages = 1
+ this.totalElements = null
this.books = []
this.editElements = false
@@ -227,6 +274,19 @@ export default Vue.extend({
next()
},
computed: {
+ paginationVisible(): number {
+ switch (this.$vuetify.breakpoint.name) {
+ case 'xs':
+ return 5
+ case 'sm':
+ case 'md':
+ return 10
+ case 'lg':
+ case 'xl':
+ default:
+ return 15
+ }
+ },
filterOptionsList(): FiltersOptions {
return {
readStatus: {
@@ -319,15 +379,28 @@ export default Vue.extend({
this.$store.commit('setReadListFilter', {id: this.readListId, filter: val})
this.updateRouteAndReload()
})
+ this.pageSizeUnwatch = this.$watch('pageSize', (val) => {
+ this.$store.commit('setBrowsingPageSize', val)
+ this.updateRouteAndReload()
+ })
+
+ this.pageUnwatch = this.$watch('page', (val) => {
+ this.updateRoute()
+ this.loadPage(this.readListId, val)
+ })
},
unsetWatches() {
this.filterUnwatch()
+ this.pageUnwatch()
+ this.pageSizeUnwatch()
},
updateRouteAndReload() {
this.unsetWatches()
+ this.page = 1
+
this.updateRoute()
- this.loadBooks(this.readListId)
+ this.loadPage(this.readListId, this.page)
this.setWatches()
},
@@ -335,7 +408,10 @@ export default Vue.extend({
const loc = {
name: this.$route.name,
params: {readListId: this.$route.params.readListId},
- query: {},
+ query: {
+ page: `${this.page}`,
+ pageSize: `${this.pageSize}`,
+ },
} as Location
mergeFilterParams(this.filters, loc.query)
this.$router.replace(loc).catch((_: any) => {
@@ -354,9 +430,18 @@ export default Vue.extend({
async loadReadList(readListId: string) {
this.$komgaReadLists.getOneReadList(readListId)
.then(v => this.readList = v)
- await this.loadBooks(readListId)
+
+ await this.loadPage(readListId, this.page)
},
- async loadBooks(readListId: string) {
+ async loadPage(readListId: string, page: number) {
+ this.selectedBooks = []
+
+ const pageRequest = {
+ page: page - 1,
+ size: this.pageSize,
+ unpaged: this.unpaged,
+ } as PageRequest
+
let authorsFilter = [] as AuthorDto[]
authorRoles.forEach((role: string) => {
if (role in this.filters) this.filters[role].forEach((name: string) => authorsFilter.push({
@@ -365,13 +450,18 @@ export default Vue.extend({
}))
})
- this.books = (await this.$komgaReadLists.getBooks(readListId, {unpaged: true} as PageRequest, this.filters.library, replaceCompositeReadStatus(this.filters.readStatus), this.filters.tag, authorsFilter)).content
+ const booksPage = await this.$komgaReadLists.getBooks(readListId, pageRequest, this.filters.library, replaceCompositeReadStatus(this.filters.readStatus), this.filters.tag, authorsFilter)
+
+ this.totalPages = booksPage.totalPages
+ this.totalElements = booksPage.totalElements
+ this.books = booksPage.content
+
this.books.forEach((x: BookDto) => x.context = {origin: ContextOrigin.READLIST, id: readListId})
this.booksCopy = [...this.books]
this.selectedBooks = []
},
- reloadBooks: throttle(function (this: any) {
- this.loadBooks(this.readListId)
+ reloadPage: throttle(function (this: any) {
+ this.loadPage(this.readListId, this.page)
}, 1000),
editSingleBook(book: BookDto) {
this.$store.dispatch('dialogUpdateBooks', book)
@@ -397,13 +487,17 @@ export default Vue.extend({
addToReadList() {
this.$store.dispatch('dialogAddBooksToReadList', this.selectedBooks)
},
- startEditElements() {
+ async startEditElements() {
this.filters = {}
+ this.unpaged = true
+ await this.reloadPage()
this.editElements = true
},
cancelEditElements() {
this.editElements = false
this.books = [...this.booksCopy]
+ this.unpaged = false
+ this.reloadPage()
},
doEditElements() {
this.editElements = false
@@ -411,15 +505,17 @@ export default Vue.extend({
bookIds: this.books.map(x => x.id),
} as ReadListUpdateDto
this.$komgaReadLists.patchReadList(this.readListId, update)
+ this.unpaged = false
+ this.reloadPage()
},
editReadList() {
this.$store.dispatch('dialogEditReadList', this.readList)
},
bookChanged(event: BookSseDto) {
- if (this.books.some(b => b.id === event.bookId)) this.reloadBooks()
+ if (this.books.some(b => b.id === event.bookId)) this.reloadPage()
},
readProgressChanged(event: ReadProgressSseDto) {
- if (this.books.some(b => b.id === event.bookId)) this.reloadBooks()
+ if (this.books.some(b => b.id === event.bookId)) this.reloadPage()
},
},
})