diff --git a/komga-webui/src/services/komga-sse.service.ts b/komga-webui/src/services/komga-sse.service.ts index db0912776..416eb345a 100644 --- a/komga-webui/src/services/komga-sse.service.ts +++ b/komga-webui/src/services/komga-sse.service.ts @@ -15,6 +15,8 @@ import { READLIST_DELETED, READPROGRESS_CHANGED, READPROGRESS_DELETED, + READPROGRESS_SERIES_CHANGED, + READPROGRESS_SERIES_DELETED, SERIES_ADDED, SERIES_CHANGED, SERIES_DELETED, @@ -76,6 +78,8 @@ export default class KomgaSseService { // Read Progress this.eventSource.addEventListener('ReadProgressChanged', (event: any) => this.emit(READPROGRESS_CHANGED, event)) this.eventSource.addEventListener('ReadProgressDeleted', (event: any) => this.emit(READPROGRESS_DELETED, event)) + this.eventSource.addEventListener('ReadProgressSeriesChanged', (event: any) => this.emit(READPROGRESS_SERIES_CHANGED, event)) + this.eventSource.addEventListener('ReadProgressSeriesDeleted', (event: any) => this.emit(READPROGRESS_SERIES_DELETED, event)) // Thumbnails this.eventSource.addEventListener('ThumbnailBookAdded', (event: any) => this.emit(THUMBNAILBOOK_ADDED, event)) diff --git a/komga-webui/src/types/events.ts b/komga-webui/src/types/events.ts index 7e42f407e..0e7294330 100644 --- a/komga-webui/src/types/events.ts +++ b/komga-webui/src/types/events.ts @@ -21,6 +21,8 @@ export const READLIST_DELETED = 'readlist-deleted' export const READPROGRESS_CHANGED = 'readprogress-changed' export const READPROGRESS_DELETED = 'readprogress-deleted' +export const READPROGRESS_SERIES_CHANGED = 'readprogress-series-changed' +export const READPROGRESS_SERIES_DELETED = 'readprogress-series-deleted' export const THUMBNAILBOOK_ADDED = 'thumbnailbook-added' export const THUMBNAILSERIES_ADDED = 'thumbnailbook-added' diff --git a/komga-webui/src/types/komga-sse.ts b/komga-webui/src/types/komga-sse.ts index 5c09d714d..90b8d9603 100644 --- a/komga-webui/src/types/komga-sse.ts +++ b/komga-webui/src/types/komga-sse.ts @@ -28,6 +28,11 @@ export interface ReadProgressSseDto { userId: string, } +export interface ReadProgressSeriesSseDto { + seriesId: string, + userId: string, +} + export interface ThumbnailBookSseDto { bookId: string, seriesId: string, diff --git a/komga-webui/src/views/BrowseCollection.vue b/komga-webui/src/views/BrowseCollection.vue index 195e5ae31..b4126de52 100644 --- a/komga-webui/src/views/BrowseCollection.vue +++ b/komga-webui/src/views/BrowseCollection.vue @@ -47,7 +47,7 @@ kind="series" show-select-all @unselect-all="selectedSeries = []" - @select-all="selectedSeries = $_.cloneDeep(series)" + @select-all="selectedSeries = series" @mark-read="markSelectedRead" @mark-unread="markSelectedUnread" @add-to-collection="addToCollection" @@ -117,7 +117,14 @@ import CollectionActionsMenu from '@/components/menus/CollectionActionsMenu.vue' import ItemBrowser from '@/components/ItemBrowser.vue' import ToolbarSticky from '@/components/bars/ToolbarSticky.vue' -import {COLLECTION_CHANGED, COLLECTION_DELETED, SERIES_CHANGED, SERIES_DELETED} from '@/types/events' +import { + COLLECTION_CHANGED, + COLLECTION_DELETED, + READPROGRESS_SERIES_CHANGED, + READPROGRESS_SERIES_DELETED, + SERIES_CHANGED, + SERIES_DELETED, +} from '@/types/events' import Vue from 'vue' import MultiSelectBar from '@/components/bars/MultiSelectBar.vue' import {LIBRARIES_ALL} from '@/types/library' @@ -132,7 +139,8 @@ 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 {CollectionSseDto, SeriesSseDto} from "@/types/komga-sse" +import {CollectionSseDto, ReadProgressSeriesSseDto, SeriesSseDto} from "@/types/komga-sse" +import {throttle} from 'lodash' export default Vue.extend({ name: 'BrowseCollection', @@ -173,31 +181,21 @@ export default Vue.extend({ required: true, }, }, - watch: { - selectedSeries(val: SeriesDto[]) { - val.forEach(s => { - let index = this.series.findIndex(x => x.id === s.id) - if (index !== -1) { - this.series.splice(index, 1, s) - } - index = this.seriesCopy.findIndex(x => x.id === s.id) - if (index !== -1) { - this.seriesCopy.splice(index, 1, s) - } - }) - }, - }, created() { this.$eventHub.$on(COLLECTION_CHANGED, this.collectionChanged) this.$eventHub.$on(COLLECTION_DELETED, this.collectionDeleted) this.$eventHub.$on(SERIES_CHANGED, this.seriesChanged) this.$eventHub.$on(SERIES_DELETED, this.seriesChanged) + this.$eventHub.$on(READPROGRESS_SERIES_CHANGED, this.readProgressChanged) + this.$eventHub.$on(READPROGRESS_SERIES_DELETED, this.readProgressChanged) }, beforeDestroy() { this.$eventHub.$off(COLLECTION_CHANGED, this.collectionChanged) this.$eventHub.$off(COLLECTION_DELETED, this.collectionDeleted) this.$eventHub.$off(SERIES_CHANGED, this.seriesChanged) this.$eventHub.$off(SERIES_DELETED, this.seriesChanged) + this.$eventHub.$off(READPROGRESS_SERIES_CHANGED, this.readProgressChanged) + this.$eventHub.$off(READPROGRESS_SERIES_DELETED, this.readProgressChanged) }, async mounted() { await this.resetParams(this.$route, this.collectionId) @@ -368,6 +366,9 @@ export default Vue.extend({ this.setWatches() }, + reloadSeries: throttle(function (this: any) { + this.loadSeries(this.collectionId) + }, 5000), async loadSeries(collectionId: string) { let authorsFilter = [] as AuthorDto[] authorRoles.forEach((role: string) => { @@ -407,17 +408,13 @@ export default Vue.extend({ await Promise.all(this.selectedSeries.map(s => this.$komgaSeries.markAsRead(s.id), )) - this.selectedSeries = await Promise.all(this.selectedSeries.map(s => - this.$komgaSeries.getOneSeries(s.id), - )) + this.selectedSeries = [] }, async markSelectedUnread() { await Promise.all(this.selectedSeries.map(s => this.$komgaSeries.markAsUnread(s.id), )) - this.selectedSeries = await Promise.all(this.selectedSeries.map(s => - this.$komgaSeries.getOneSeries(s.id), - )) + this.selectedSeries = [] }, addToCollection() { this.$store.dispatch('dialogAddSeriesToCollection', this.selectedSeries) @@ -436,13 +433,15 @@ export default Vue.extend({ seriesIds: this.series.map(x => x.id), } as CollectionUpdateDto this.$komgaCollections.patchCollection(this.collectionId, update) - this.loadCollection(this.collectionId) }, editCollection() { this.$store.dispatch('dialogEditCollection', this.collection) }, seriesChanged(event: SeriesSseDto) { - if (this.series.some(s => s.id === event.seriesId)) this.loadCollection(this.collectionId) + if (this.series.some(s => s.id === event.seriesId)) this.reloadSeries() + }, + readProgressChanged(event: ReadProgressSeriesSseDto) { + if (this.series.some(b => b.id === event.seriesId)) this.reloadSeries() }, }, }) diff --git a/komga-webui/src/views/BrowseLibraries.vue b/komga-webui/src/views/BrowseLibraries.vue index e563e45d5..17f9ca81e 100644 --- a/komga-webui/src/views/BrowseLibraries.vue +++ b/komga-webui/src/views/BrowseLibraries.vue @@ -30,7 +30,7 @@ kind="series" show-select-all @unselect-all="selectedSeries = []" - @select-all="selectedSeries = $_.cloneDeep(series)" + @select-all="selectedSeries = series" @mark-read="markSelectedRead" @mark-unread="markSelectedUnread" @add-to-collection="addToCollection" @@ -115,7 +115,15 @@ import PageSizeSelect from '@/components/PageSizeSelect.vue' import {parseQuerySort} from '@/functions/query-params' import {ReadStatus, replaceCompositeReadStatus} from '@/types/enum-books' import {SeriesStatus, SeriesStatusKeyValue} from '@/types/enum-series' -import {LIBRARY_CHANGED, LIBRARY_DELETED, SERIES_ADDED, SERIES_CHANGED, SERIES_DELETED} from '@/types/events' +import { + LIBRARY_CHANGED, + LIBRARY_DELETED, + READPROGRESS_SERIES_CHANGED, + READPROGRESS_SERIES_DELETED, + SERIES_ADDED, + SERIES_CHANGED, + SERIES_DELETED, +} from '@/types/events' import Vue from 'vue' import {Location} from 'vue-router' import {LIBRARIES_ALL, LIBRARY_ROUTE} from '@/types/library' @@ -127,7 +135,8 @@ import {mergeFilterParams, sortOrFilterActive, toNameValue} from '@/functions/fi import {SeriesDto} from "@/types/komga-series"; import {AuthorDto} from "@/types/komga-books"; import {authorRoles} from "@/types/author-roles"; -import {LibrarySseDto, SeriesSseDto} from "@/types/komga-sse"; +import {LibrarySseDto, ReadProgressSeriesSseDto, SeriesSseDto} from "@/types/komga-sse"; +import {throttle} from "lodash"; export default Vue.extend({ name: 'BrowseLibraries', @@ -177,22 +186,14 @@ export default Vue.extend({ default: LIBRARIES_ALL, }, }, - watch: { - selectedSeries(val: SeriesDto[]) { - val.forEach(s => { - const index = this.series.findIndex(x => x.id === s.id) - if (index !== -1) { - this.series.splice(index, 1, s) - } - }) - }, - }, created() { this.$eventHub.$on(SERIES_ADDED, this.seriesChanged) this.$eventHub.$on(SERIES_CHANGED, this.seriesChanged) this.$eventHub.$on(SERIES_DELETED, this.seriesChanged) this.$eventHub.$on(LIBRARY_DELETED, this.libraryDeleted) this.$eventHub.$on(LIBRARY_CHANGED, this.libraryChanged) + this.$eventHub.$on(READPROGRESS_SERIES_CHANGED, this.readProgressChanged) + this.$eventHub.$on(READPROGRESS_SERIES_DELETED, this.readProgressChanged) }, beforeDestroy() { this.$eventHub.$off(SERIES_ADDED, this.seriesChanged) @@ -200,6 +201,8 @@ export default Vue.extend({ this.$eventHub.$off(SERIES_DELETED, this.seriesChanged) this.$eventHub.$off(LIBRARY_DELETED, this.libraryDeleted) this.$eventHub.$off(LIBRARY_CHANGED, this.libraryChanged) + this.$eventHub.$off(READPROGRESS_SERIES_CHANGED, this.readProgressChanged) + this.$eventHub.$off(READPROGRESS_SERIES_DELETED, this.readProgressChanged) }, async mounted() { this.$store.commit('setLibraryRoute', {id: this.libraryId, route: LIBRARY_ROUTE.BROWSE}) @@ -417,7 +420,7 @@ export default Vue.extend({ }, seriesChanged(event: SeriesSseDto) { if (this.libraryId === LIBRARIES_ALL || event.libraryId === this.libraryId) { - this.loadPage(this.libraryId, this.page, this.sortActive) + this.reloadPage() } }, libraryChanged(event: LibrarySseDto) { @@ -425,6 +428,9 @@ export default Vue.extend({ this.loadLibrary(this.libraryId) } }, + readProgressChanged(event: ReadProgressSeriesSseDto) { + if (this.series.some(b => b.id === event.seriesId)) this.reloadPage() + }, async loadLibrary(libraryId: string) { this.library = this.getLibraryLazy(libraryId) @@ -444,6 +450,9 @@ export default Vue.extend({ this.$router.replace(loc).catch((_: any) => { }) }, + reloadPage: throttle(function (this: any) { + this.loadPage(this.libraryId, this.page, this.sortActive) + }, 5000), async loadPage(libraryId: string, page: number, sort: SortActive) { this.selectedSeries = [] @@ -482,17 +491,13 @@ export default Vue.extend({ await Promise.all(this.selectedSeries.map(s => this.$komgaSeries.markAsRead(s.id), )) - this.selectedSeries = await Promise.all(this.selectedSeries.map(s => - this.$komgaSeries.getOneSeries(s.id), - )) + this.selectedSeries = [] }, async markSelectedUnread() { await Promise.all(this.selectedSeries.map(s => this.$komgaSeries.markAsUnread(s.id), )) - this.selectedSeries = await Promise.all(this.selectedSeries.map(s => - this.$komgaSeries.getOneSeries(s.id), - )) + this.selectedSeries = [] }, addToCollection() { this.$store.dispatch('dialogAddSeriesToCollection', this.selectedSeries) diff --git a/komga-webui/src/views/BrowseReadList.vue b/komga-webui/src/views/BrowseReadList.vue index 7b7628440..7fad0347e 100644 --- a/komga-webui/src/views/BrowseReadList.vue +++ b/komga-webui/src/views/BrowseReadList.vue @@ -40,7 +40,7 @@ kind="books" show-select-all @unselect-all="selectedBooks = []" - @select-all="selectedBooks = $_.cloneDeep(books)" + @select-all="selectedBooks = books" @mark-read="markSelectedRead" @mark-unread="markSelectedUnread" @add-to-readlist="addToReadList" @@ -93,6 +93,7 @@ import MultiSelectBar from '@/components/bars/MultiSelectBar.vue' import {BookDto, ReadProgressUpdateDto} from '@/types/komga-books' import {ContextOrigin} from '@/types/context' import {BookSseDto, ReadListSseDto, ReadProgressSseDto} from "@/types/komga-sse"; +import {throttle} from "lodash"; export default Vue.extend({ name: 'BrowseReadList', @@ -117,20 +118,6 @@ export default Vue.extend({ required: true, }, }, - watch: { - selectedBooks (val: BookDto[]) { - val.forEach(s => { - let index = this.books.findIndex(x => x.id === s.id) - if (index !== -1) { - this.books.splice(index, 1, s) - } - index = this.booksCopy.findIndex(x => x.id === s.id) - if (index !== -1) { - this.booksCopy.splice(index, 1, s) - } - }) - }, - }, created () { this.$eventHub.$on(READLIST_CHANGED, this.readListChanged) this.$eventHub.$on(READLIST_DELETED, this.readListDeleted) @@ -180,11 +167,17 @@ export default Vue.extend({ async loadReadList (readListId: string) { this.$komgaReadLists.getOneReadList(readListId) .then(v => this.readList = v) + await this.loadBooks(readListId) + }, + async loadBooks (readListId: string) { this.books = (await this.$komgaReadLists.getBooks(readListId, { unpaged: true } as PageRequest)).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) + }, 5000), editSingleBook (book: BookDto) { this.$store.dispatch('dialogUpdateBooks', book) }, @@ -195,17 +188,11 @@ export default Vue.extend({ await Promise.all(this.selectedBooks.map(b => this.$komgaBooks.updateReadProgress(b.id, { completed: true } as ReadProgressUpdateDto), )) - this.selectedBooks = await Promise.all(this.selectedBooks.map(b => - this.$komgaBooks.getBook(b.id), - )) }, async markSelectedUnread () { await Promise.all(this.selectedBooks.map(b => this.$komgaBooks.deleteReadProgress(b.id), )) - this.selectedBooks = await Promise.all(this.selectedBooks.map(b => - this.$komgaBooks.getBook(b.id), - )) }, addToReadList () { this.$store.dispatch('dialogAddBooksToReadList', this.selectedBooks) @@ -223,16 +210,15 @@ export default Vue.extend({ bookIds: this.books.map(x => x.id), } as ReadListUpdateDto this.$komgaReadLists.patchReadList(this.readListId, update) - this.loadReadList(this.readListId) }, editReadList () { this.$store.dispatch('dialogEditReadList', this.readList) }, bookChanged (event: BookSseDto) { - if (this.books.some(b => b.id === event.bookId)) this.loadReadList(this.readListId) + if (this.books.some(b => b.id === event.bookId)) this.reloadBooks() }, readProgressChanged(event: ReadProgressSseDto){ - if (this.books.some(b => b.id === event.bookId)) this.loadReadList(this.readListId) + if (this.books.some(b => b.id === event.bookId)) this.reloadBooks() }, }, }) diff --git a/komga-webui/src/views/Dashboard.vue b/komga-webui/src/views/Dashboard.vue index 5f36d8a15..b58df30cb 100644 --- a/komga-webui/src/views/Dashboard.vue +++ b/komga-webui/src/views/Dashboard.vue @@ -343,6 +343,7 @@ export default Vue.extend({ }, addToCollection() { this.$store.dispatch('dialogAddSeriesToCollection', this.selectedSeries) + this.selectedSeries = [] }, editMultipleSeries() { this.$store.dispatch('dialogUpdateSeries', this.selectedSeries) @@ -352,6 +353,7 @@ export default Vue.extend({ }, addToReadList() { this.$store.dispatch('dialogAddBooksToReadList', this.selectedBooks) + this.selectedBooks = [] }, async markSelectedBooksRead() { await Promise.all(this.selectedBooks.map(b => diff --git a/komga-webui/src/views/Search.vue b/komga-webui/src/views/Search.vue index 720f65199..a787b944a 100644 --- a/komga-webui/src/views/Search.vue +++ b/komga-webui/src/views/Search.vue @@ -135,12 +135,22 @@ import { READLIST_DELETED, READPROGRESS_CHANGED, READPROGRESS_DELETED, + READPROGRESS_SERIES_CHANGED, + READPROGRESS_SERIES_DELETED, SERIES_CHANGED, SERIES_DELETED, } from '@/types/events' import Vue from 'vue' import {SeriesDto} from "@/types/komga-series"; -import {BookSseDto, CollectionSseDto, ReadListSseDto, ReadProgressSseDto, SeriesSseDto} from "@/types/komga-sse"; +import { + BookSseDto, + CollectionSseDto, + ReadListSseDto, + ReadProgressSeriesSseDto, + ReadProgressSseDto, + SeriesSseDto, +} from "@/types/komga-sse"; +import {throttle} from "lodash"; export default Vue.extend({ name: 'Search', @@ -177,6 +187,8 @@ export default Vue.extend({ this.$eventHub.$on(READLIST_DELETED, this.readListChanged) this.$eventHub.$on(READPROGRESS_CHANGED, this.readProgressChanged) this.$eventHub.$on(READPROGRESS_DELETED, this.readProgressChanged) + this.$eventHub.$on(READPROGRESS_SERIES_CHANGED, this.readProgressSeriesChanged) + this.$eventHub.$on(READPROGRESS_SERIES_DELETED, this.readProgressSeriesChanged) }, beforeDestroy () { this.$eventHub.$off(LIBRARY_DELETED, this.reloadResults) @@ -190,6 +202,8 @@ export default Vue.extend({ this.$eventHub.$off(READLIST_DELETED, this.readListChanged) this.$eventHub.$off(READPROGRESS_CHANGED, this.readProgressChanged) this.$eventHub.$off(READPROGRESS_DELETED, this.readProgressChanged) + this.$eventHub.$off(READPROGRESS_SERIES_CHANGED, this.readProgressSeriesChanged) + this.$eventHub.$off(READPROGRESS_SERIES_DELETED, this.readProgressSeriesChanged) }, watch: { '$route.query.q': { @@ -231,6 +245,11 @@ export default Vue.extend({ this.reloadResults() } }, + readProgressSeriesChanged(event: ReadProgressSeriesSseDto){ + if(this.series.map(x => x.id).includes(event.seriesId)){ + this.reloadResults() + } + }, collectionChanged (event: CollectionSseDto) { if (this.collections.map(x => x.id).includes(event.collectionId)) { this.reloadResults() @@ -297,9 +316,9 @@ export default Vue.extend({ this.$komgaBooks.deleteReadProgress(b.id), )) }, - reloadResults () { + reloadResults: throttle(function (this: any) { this.loadResults(this.$route.query.q.toString()) - }, + }, 500), async loadResults (search: string) { this.selectedBooks = [] this.selectedSeries = []