mirror of
https://github.com/gotson/komga.git
synced 2026-05-09 05:10:19 +02:00
perf(webui): readlist/collection expansion panels load data by page
Refs: #817
This commit is contained in:
parent
ff70fea71a
commit
0b57dc9c96
3 changed files with 63 additions and 28 deletions
|
|
@ -5,14 +5,18 @@
|
||||||
>
|
>
|
||||||
<v-expansion-panel-header>{{ $t('collections_expansion_panel.title', {name: c.name}) }}</v-expansion-panel-header>
|
<v-expansion-panel-header>{{ $t('collections_expansion_panel.title', {name: c.name}) }}</v-expansion-panel-header>
|
||||||
<v-expansion-panel-content>
|
<v-expansion-panel-content>
|
||||||
<horizontal-scroller>
|
<horizontal-scroller
|
||||||
|
:tick="collectionsLoaders[index].tick"
|
||||||
|
@scroll-changed="(percent) => scrollChanged(collectionsLoaders[index], percent)"
|
||||||
|
>
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<router-link class="text-overline"
|
<router-link class="text-overline"
|
||||||
:to="{name: 'browse-collection', params: {collectionId: c.id}}"
|
:to="{name: 'browse-collection', params: {collectionId: c.id}}"
|
||||||
>{{ $t('collections_expansion_panel.manage_collection') }}</router-link>
|
>{{ $t('collections_expansion_panel.manage_collection') }}
|
||||||
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template v-slot:content>
|
||||||
<item-browser :items="collectionsContent[index]"
|
<item-browser :items="collectionsLoaders[index].items"
|
||||||
nowrap
|
nowrap
|
||||||
:selectable="false"
|
:selectable="false"
|
||||||
:action-menu="false"
|
:action-menu="false"
|
||||||
|
|
@ -31,6 +35,7 @@ import ItemBrowser from '@/components/ItemBrowser.vue'
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import {ContextOrigin} from '@/types/context'
|
import {ContextOrigin} from '@/types/context'
|
||||||
import {SeriesDto} from '@/types/komga-series'
|
import {SeriesDto} from '@/types/komga-series'
|
||||||
|
import {PageLoader} from '@/types/pageLoader'
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'CollectionsExpansionPanels',
|
name: 'CollectionsExpansionPanels',
|
||||||
|
|
@ -47,27 +52,36 @@ export default Vue.extend({
|
||||||
data: () => {
|
data: () => {
|
||||||
return {
|
return {
|
||||||
collectionPanel: undefined as number | undefined,
|
collectionPanel: undefined as number | undefined,
|
||||||
collectionsContent: [[]] as any[],
|
collectionsLoaders: [] as PageLoader<SeriesDto>[],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
collections: {
|
collections: {
|
||||||
handler (val) {
|
handler(val: CollectionDto[]) {
|
||||||
this.collectionPanel = undefined
|
this.collectionPanel = undefined
|
||||||
this.collectionsContent = [...Array(val.length)].map(elem => new Array(0))
|
this.collectionsLoaders = val.map(coll => new PageLoader<SeriesDto>(
|
||||||
|
{},
|
||||||
|
(pageable: PageRequest) => this.$komgaCollections.getSeries(coll.id, pageable),
|
||||||
|
(x: SeriesDto) => {
|
||||||
|
x.context = {origin: ContextOrigin.COLLECTION, id: coll.id}
|
||||||
|
return x
|
||||||
|
},
|
||||||
|
))
|
||||||
},
|
},
|
||||||
immediate: true,
|
immediate: true,
|
||||||
},
|
},
|
||||||
async collectionPanel (val) {
|
async collectionPanel(val) {
|
||||||
if (val !== undefined) {
|
if (val !== undefined) {
|
||||||
const collId = this.collections[val].id
|
if (!this.collectionsLoaders[val].hasLoadedAny) {
|
||||||
if (this.$_.isEmpty(this.collectionsContent[val])) {
|
this.collectionsLoaders[val].loadNext()
|
||||||
const content = (await this.$komgaCollections.getSeries(collId, { unpaged: true } as PageRequest)).content
|
|
||||||
content.forEach((x: SeriesDto) => x.context = { origin: ContextOrigin.COLLECTION, id: collId })
|
|
||||||
this.collectionsContent.splice(val, 1, content)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
async scrollChanged(loader: PageLoader<any>, percent: number) {
|
||||||
|
if (percent > 0.95) await loader.loadNext()
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,18 @@
|
||||||
>
|
>
|
||||||
<v-expansion-panel-header>{{ $t('readlists_expansion_panel.title', {name: r.name}) }}</v-expansion-panel-header>
|
<v-expansion-panel-header>{{ $t('readlists_expansion_panel.title', {name: r.name}) }}</v-expansion-panel-header>
|
||||||
<v-expansion-panel-content>
|
<v-expansion-panel-content>
|
||||||
<horizontal-scroller>
|
<horizontal-scroller
|
||||||
|
:tick="readListsLoaders[index].tick"
|
||||||
|
@scroll-changed="(percent) => scrollChanged(readListsLoaders[index], percent)"
|
||||||
|
>
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<router-link class="text-overline"
|
<router-link class="text-overline"
|
||||||
:to="{name: 'browse-readlist', params: {readListId: r.id}}"
|
:to="{name: 'browse-readlist', params: {readListId: r.id}}"
|
||||||
>{{ $t('readlists_expansion_panel.manage_readlist') }}</router-link>
|
>{{ $t('readlists_expansion_panel.manage_readlist') }}
|
||||||
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template v-slot:content>
|
||||||
<item-browser :items="readListsContent[index]"
|
<item-browser :items="readListsLoaders[index].items"
|
||||||
:item-context="[ItemContext.SHOW_SERIES]"
|
:item-context="[ItemContext.SHOW_SERIES]"
|
||||||
nowrap
|
nowrap
|
||||||
:selectable="false"
|
:selectable="false"
|
||||||
|
|
@ -33,6 +37,7 @@ import Vue from 'vue'
|
||||||
import {BookDto} from '@/types/komga-books'
|
import {BookDto} from '@/types/komga-books'
|
||||||
import {ContextOrigin} from '@/types/context'
|
import {ContextOrigin} from '@/types/context'
|
||||||
import {ItemContext} from '@/types/items'
|
import {ItemContext} from '@/types/items'
|
||||||
|
import {PageLoader} from '@/types/pageLoader'
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'ReadListsExpansionPanels',
|
name: 'ReadListsExpansionPanels',
|
||||||
|
|
@ -50,27 +55,36 @@ export default Vue.extend({
|
||||||
return {
|
return {
|
||||||
ItemContext,
|
ItemContext,
|
||||||
readListPanel: undefined as number | undefined,
|
readListPanel: undefined as number | undefined,
|
||||||
readListsContent: [[]] as any[],
|
readListsLoaders: [] as PageLoader<BookDto>[],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
readLists: {
|
readLists: {
|
||||||
handler (val) {
|
handler(val: ReadListDto[]) {
|
||||||
this.readListPanel = undefined
|
this.readListPanel = undefined
|
||||||
this.readListsContent = [...Array(val.length)].map(elem => new Array(0))
|
this.readListsLoaders = val.map(rl => new PageLoader<BookDto>(
|
||||||
|
{},
|
||||||
|
(pageable: PageRequest) => this.$komgaReadLists.getBooks(rl.id, pageable),
|
||||||
|
(x: BookDto) => {
|
||||||
|
x.context = {origin: ContextOrigin.READLIST, id: rl.id}
|
||||||
|
return x
|
||||||
|
},
|
||||||
|
))
|
||||||
},
|
},
|
||||||
immediate: true,
|
immediate: true,
|
||||||
},
|
},
|
||||||
async readListPanel (val) {
|
async readListPanel(val) {
|
||||||
if (val !== undefined) {
|
if (val !== undefined) {
|
||||||
const rlId = this.readLists[val].id
|
if (!this.readListsLoaders[val].hasLoadedAny) {
|
||||||
if (this.$_.isEmpty(this.readListsContent[val])) {
|
this.readListsLoaders[val].loadNext()
|
||||||
const content = (await this.$komgaReadLists.getBooks(rlId, { unpaged: true } as PageRequest)).content
|
|
||||||
content.forEach((x: BookDto) => x.context = { origin: ContextOrigin.READLIST, id: rlId })
|
|
||||||
this.readListsContent.splice(val, 1, content)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
async scrollChanged(loader: PageLoader<any>, percent: number) {
|
||||||
|
if (percent > 0.95) await loader.loadNext()
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
export class PageLoader<T> {
|
export class PageLoader<T> {
|
||||||
private readonly pageable: PageRequest
|
private readonly pageable: PageRequest
|
||||||
private readonly loader: (pageRequest: PageRequest) => Promise<Page<T>>
|
private readonly loader: (pageRequest: PageRequest) => Promise<Page<T>>
|
||||||
|
private readonly postProcessor: (item: T) => T
|
||||||
|
|
||||||
private currentPage = undefined as unknown as Page<T>
|
private currentPage = undefined as unknown as Page<T>
|
||||||
private loadedPages: number[] = []
|
private loadedPages: number[] = []
|
||||||
|
|
@ -15,9 +16,15 @@ export class PageLoader<T> {
|
||||||
return this._tick
|
return this._tick
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(pageable: PageRequest, loader: (pageRequest: PageRequest) => Promise<Page<T>>) {
|
// whether anything has been loaded yet
|
||||||
|
get hasLoadedAny() {
|
||||||
|
return this.currentPage
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(pageable: PageRequest, loader: (pageRequest: PageRequest) => Promise<Page<T>>, postProcessor: (item: T) => T = (item) => item) {
|
||||||
this.pageable = pageable
|
this.pageable = pageable
|
||||||
this.loader = loader
|
this.loader = loader
|
||||||
|
this.postProcessor = postProcessor
|
||||||
}
|
}
|
||||||
|
|
||||||
async reload() {
|
async reload() {
|
||||||
|
|
@ -29,7 +36,7 @@ export class PageLoader<T> {
|
||||||
page: 0,
|
page: 0,
|
||||||
})
|
})
|
||||||
const page = await this.loader(pageable)
|
const page = await this.loader(pageable)
|
||||||
this.items.splice(0, this.items.length, ...page.content)
|
this.items.splice(0, this.items.length, ...page.content.map(this.postProcessor))
|
||||||
this._tick++
|
this._tick++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,7 +45,7 @@ export class PageLoader<T> {
|
||||||
if (!this.currentPage) {
|
if (!this.currentPage) {
|
||||||
this.loadedPages.push(this.pageable.page || 0)
|
this.loadedPages.push(this.pageable.page || 0)
|
||||||
this.currentPage = await this.loader(this.pageable)
|
this.currentPage = await this.loader(this.pageable)
|
||||||
this.items.push(...this.currentPage.content)
|
this.items.push(...this.currentPage.content.map(this.postProcessor))
|
||||||
this._tick++
|
this._tick++
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -50,7 +57,7 @@ export class PageLoader<T> {
|
||||||
this.loadedPages.push(nextPage)
|
this.loadedPages.push(nextPage)
|
||||||
const pageable = Object.assign({}, this.pageable, {page: nextPage})
|
const pageable = Object.assign({}, this.pageable, {page: nextPage})
|
||||||
this.currentPage = await this.loader(pageable)
|
this.currentPage = await this.loader(pageable)
|
||||||
this.items.push(...this.currentPage.content)
|
this.items.push(...this.currentPage.content.map(this.postProcessor))
|
||||||
this._tick++
|
this._tick++
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue