From a707fd359490d032bc65782cc1009d1e40e4b51a Mon Sep 17 00:00:00 2001 From: Gauthier Roebroeck Date: Mon, 21 Jun 2021 14:53:06 +0800 Subject: [PATCH] feat(webui): the UI is now dynamic to events from the server closes #124 --- komga-webui/src/App.vue | 14 +++ komga-webui/src/components/Dialogs.vue | 72 +---------- komga-webui/src/components/ItemCard.vue | 70 +++++++---- .../src/components/LibraryNavigation.vue | 37 ++++-- komga-webui/src/components/Toaster.vue | 117 ++++++++++++++++++ .../dialogs/CollectionAddToDialog.vue | 2 - .../dialogs/CollectionDeleteDialog.vue | 1 - .../dialogs/CollectionEditDialog.vue | 1 - .../components/dialogs/EditBooksDialog.vue | 1 - .../components/dialogs/EditSeriesDialog.vue | 1 - .../dialogs/LibraryDeleteDialog.vue | 1 - .../components/dialogs/LibraryEditDialog.vue | 3 - .../dialogs/ReadListAddToDialog.vue | 2 - .../dialogs/ReadListDeleteDialog.vue | 1 - .../components/dialogs/ReadListEditDialog.vue | 1 - .../src/components/menus/BookActionsMenu.vue | 3 - .../components/menus/SeriesActionsMenu.vue | 5 +- komga-webui/src/locales/en.json | 17 ++- komga-webui/src/main.ts | 7 +- .../src/plugins/komga-libraries.plugin.ts | 7 +- komga-webui/src/plugins/komga-sse.plugin.ts | 31 +++++ komga-webui/src/services/komga-sse.service.ts | 99 +++++++++++++++ komga-webui/src/types/enum-books.ts | 2 +- komga-webui/src/types/events-payloads.ts | 37 ------ komga-webui/src/types/events.ts | 76 +++--------- komga-webui/src/types/komga-sse.ts | 49 ++++++++ komga-webui/src/views/BrowseBook.vue | 50 ++++++-- komga-webui/src/views/BrowseCollection.vue | 29 +++-- komga-webui/src/views/BrowseCollections.vue | 9 +- komga-webui/src/views/BrowseLibraries.vue | 25 ++-- komga-webui/src/views/BrowseReadList.vue | 41 ++++-- komga-webui/src/views/BrowseReadLists.vue | 9 +- komga-webui/src/views/BrowseSeries.vue | 77 +++++++++--- komga-webui/src/views/Dashboard.vue | 69 ++++++++--- komga-webui/src/views/Home.vue | 24 +++- komga-webui/src/views/Search.vue | 62 ++++++++-- komga-webui/src/views/Welcome.vue | 19 ++- 37 files changed, 741 insertions(+), 330 deletions(-) create mode 100644 komga-webui/src/components/Toaster.vue create mode 100644 komga-webui/src/plugins/komga-sse.plugin.ts create mode 100644 komga-webui/src/services/komga-sse.service.ts delete mode 100644 komga-webui/src/types/events-payloads.ts create mode 100644 komga-webui/src/types/komga-sse.ts diff --git a/komga-webui/src/App.vue b/komga-webui/src/App.vue index 58c31c103..2fdd0cf5f 100644 --- a/komga-webui/src/App.vue +++ b/komga-webui/src/App.vue @@ -6,6 +6,8 @@ diff --git a/komga-webui/src/components/Dialogs.vue b/komga-webui/src/components/Dialogs.vue index 2f1f04e6d..a2465b8cb 100644 --- a/komga-webui/src/components/Dialogs.vue +++ b/komga-webui/src/components/Dialogs.vue @@ -3,39 +3,31 @@ @@ -72,27 +61,11 @@ import EditBooksDialog from '@/components/dialogs/EditBooksDialog.vue' import EditSeriesDialog from '@/components/dialogs/EditSeriesDialog.vue' import LibraryDeleteDialog from '@/components/dialogs/LibraryDeleteDialog.vue' import LibraryEditDialog from '@/components/dialogs/LibraryEditDialog.vue' -import { - BOOK_CHANGED, - bookToEventBookChanged, - COLLECTION_CHANGED, - COLLECTION_DELETED, - collectionToEventCollectionChanged, - collectionToEventCollectionDeleted, - LIBRARY_DELETED, - libraryToEventLibraryDeleted, - READLIST_CHANGED, - READLIST_DELETED, - readListToEventReadListChanged, - readListToEventReadListDeleted, - SERIES_CHANGED, - seriesToEventSeriesChanged, -} from '@/types/events' import Vue from 'vue' import ReadListAddToDialog from '@/components/dialogs/ReadListAddToDialog.vue' import ReadListDeleteDialog from '@/components/dialogs/ReadListDeleteDialog.vue' import ReadListEditDialog from '@/components/dialogs/ReadListEditDialog.vue' -import { BookDto } from '@/types/komga-books' +import {BookDto} from '@/types/komga-books' import {SeriesDto} from "@/types/komga-series"; export default Vue.extend({ @@ -226,49 +199,6 @@ export default Vue.extend({ return this.$store.state.updateSeries }, }, - methods: { - collectionAdded (collection: CollectionDto) { - if (Array.isArray(this.addToCollectionSeries)) { - this.addToCollectionSeries.forEach(s => { - this.$eventHub.$emit(SERIES_CHANGED, seriesToEventSeriesChanged(s)) - }) - } else { - this.$eventHub.$emit(SERIES_CHANGED, seriesToEventSeriesChanged(this.addToCollectionSeries)) - } - this.$eventHub.$emit(COLLECTION_CHANGED, collectionToEventCollectionChanged(collection)) - }, - collectionUpdated () { - this.$eventHub.$emit(COLLECTION_CHANGED, collectionToEventCollectionChanged(this.editCollection)) - }, - collectionDeleted () { - this.$eventHub.$emit(COLLECTION_DELETED, collectionToEventCollectionDeleted(this.deleteCollection)) - }, - readListAdded (readList: ReadListDto) { - if (Array.isArray(this.addToReadListBooks)) { - this.addToReadListBooks.forEach(b => { - this.$eventHub.$emit(BOOK_CHANGED, bookToEventBookChanged(b)) - }) - } else { - this.$eventHub.$emit(BOOK_CHANGED, bookToEventBookChanged(this.addToReadListBooks)) - } - this.$eventHub.$emit(READLIST_CHANGED, readListToEventReadListChanged(readList)) - }, - readListUpdated () { - this.$eventHub.$emit(READLIST_CHANGED, readListToEventReadListChanged(this.editReadList)) - }, - readListDeleted () { - this.$eventHub.$emit(READLIST_DELETED, readListToEventReadListDeleted(this.deleteReadList)) - }, - libraryDeleted () { - this.$eventHub.$emit(LIBRARY_DELETED, libraryToEventLibraryDeleted(this.deleteLibrary)) - }, - bookUpdated (book: BookDto) { - this.$eventHub.$emit(BOOK_CHANGED, bookToEventBookChanged(book)) - }, - seriesUpdated (series: SeriesDto) { - this.$eventHub.$emit(SERIES_CHANGED, seriesToEventSeriesChanged(series)) - }, - }, }) diff --git a/komga-webui/src/components/ItemCard.vue b/komga-webui/src/components/ItemCard.vue index d43710504..c793519cb 100644 --- a/komga-webui/src/components/ItemCard.vue +++ b/komga-webui/src/components/ItemCard.vue @@ -13,6 +13,7 @@ lazy-src="../assets/cover.svg" aspect-ratio="0.7071" contain + @error="thumbnailError = true" >
@@ -39,7 +40,8 @@ :style="'position: absolute; top: 5px; ' + ($vuetify.rtl ? 'right' : 'left') + ': 10px'" @click.stop="selectItem" > - {{ selected || (preselect && hover) ? 'mdi-checkbox-marked-circle' : 'mdi-checkbox-blank-circle-outline' + {{ + selected || (preselect && hover) ? 'mdi-checkbox-marked-circle' : 'mdi-checkbox-blank-circle-outline' }} @@ -126,10 +128,12 @@ import {RawLocation} from 'vue-router' import ReadListActionsMenu from '@/components/menus/ReadListActionsMenu.vue' import {BookDto} from '@/types/komga-books' import {SeriesDto} from "@/types/komga-series"; +import {THUMBNAILBOOK_ADDED, THUMBNAILSERIES_ADDED} from "@/types/events"; +import {ThumbnailBookSseDto, ThumbnailSeriesSseDto} from "@/types/komga-sse"; export default Vue.extend({ name: 'ItemCard', - components: { BookActionsMenu, SeriesActionsMenu, CollectionActionsMenu, ReadListActionsMenu }, + components: {BookActionsMenu, SeriesActionsMenu, CollectionActionsMenu, ReadListActionsMenu}, props: { item: { type: Object as () => BookDto | SeriesDto | CollectionDto | ReadListDto, @@ -182,79 +186,101 @@ export default Vue.extend({ return { ItemTypes, actionMenuState: false, + thumbnailError: false, + thumbnailCacheBust: '', } }, + created() { + this.$eventHub.$on(THUMBNAILBOOK_ADDED, this.thumbnailBookAdded) + this.$eventHub.$on(THUMBNAILSERIES_ADDED, this.thumbnailSeriesAdded) + }, + beforeDestroy() { + this.$eventHub.$off(THUMBNAILBOOK_ADDED, this.thumbnailBookAdded) + this.$eventHub.$off(THUMBNAILSERIES_ADDED, this.thumbnailSeriesAdded) + }, computed: { - canReadPages (): boolean { + canReadPages(): boolean { return this.$store.getters.mePageStreaming && this.computedItem.type() === ItemTypes.BOOK }, - overlay (): boolean { + overlay(): boolean { return this.onEdit !== undefined || this.onSelected !== undefined || this.bookReady || this.canReadPages || this.actionMenu }, - computedItem (): Item { + computedItem(): Item { return createItem(this.item) }, - disableHover (): boolean { + disableHover(): boolean { return !this.overlay }, - thumbnailUrl (): string { - return this.computedItem.thumbnailUrl() + thumbnailUrl(): string { + return this.computedItem.thumbnailUrl() + this.thumbnailCacheBust }, - title (): string { + title(): string { return this.computedItem.title() }, - subtitleProps (): Object { + subtitleProps(): Object { return this.computedItem.subtitleProps() }, - body (): string { + body(): string { return this.computedItem.body() }, - isInProgress (): boolean { + isInProgress(): boolean { if (this.computedItem.type() === ItemTypes.BOOK) return getReadProgress(this.item as BookDto) === ReadStatus.IN_PROGRESS return false }, - isUnread (): boolean { + isUnread(): boolean { if (this.computedItem.type() === ItemTypes.BOOK) return getReadProgress(this.item as BookDto) === ReadStatus.UNREAD return false }, - unreadCount (): number | undefined { + unreadCount(): number | undefined { if (this.computedItem.type() === ItemTypes.SERIES) return (this.item as SeriesDto).booksUnreadCount + (this.item as SeriesDto).booksInProgressCount return undefined }, - readProgressPercentage (): number { + readProgressPercentage(): number { if (this.computedItem.type() === ItemTypes.BOOK) return getReadProgressPercentage(this.item as BookDto) return 0 }, - bookReady (): boolean { + bookReady(): boolean { if (this.computedItem.type() === ItemTypes.BOOK) { return (this.item as BookDto).media.status === 'READY' } return false }, - to (): RawLocation { + to(): RawLocation { return this.computedItem.to() }, - fabTo (): RawLocation { + fabTo(): RawLocation { return this.computedItem.fabTo() }, }, methods: { - onClick () { + thumbnailBookAdded(event: ThumbnailBookSseDto) { + if (this.thumbnailError && + ((this.computedItem.type() === ItemTypes.BOOK && event.bookId === this.item.id) || (this.computedItem.type() === ItemTypes.SERIES && event.seriesId === this.item.id)) + ) { + this.thumbnailCacheBust = '?' + this.$_.random(1000) + } + }, + thumbnailSeriesAdded(event: ThumbnailSeriesSseDto) { + if (this.thumbnailError && (this.computedItem.type() === ItemTypes.SERIES && event.seriesId === this.item.id)) { + this.thumbnailCacheBust = '?' + this.$_.random(1000) + } + }, + onClick() { if (this.preselect && this.onSelected !== undefined) { this.selectItem() } else if (!this.noLink) { this.goto() } }, - goto () { + goto() { this.$router.push(this.computedItem.to()) }, - selectItem () { + selectItem() { if (this.onSelected !== undefined) { this.onSelected() } }, - editItem () { + editItem() { if (this.onEdit !== undefined) { this.onEdit(this.item) } diff --git a/komga-webui/src/components/LibraryNavigation.vue b/komga-webui/src/components/LibraryNavigation.vue index f0558c0bd..5509a4159 100644 --- a/komga-webui/src/components/LibraryNavigation.vue +++ b/komga-webui/src/components/LibraryNavigation.vue @@ -77,7 +77,7 @@ + + diff --git a/komga-webui/src/components/dialogs/CollectionAddToDialog.vue b/komga-webui/src/components/dialogs/CollectionAddToDialog.vue index 374e377f2..6080b9d81 100644 --- a/komga-webui/src/components/dialogs/CollectionAddToDialog.vue +++ b/komga-webui/src/components/dialogs/CollectionAddToDialog.vue @@ -155,7 +155,6 @@ export default Vue.extend({ try { await this.$komgaCollections.patchCollection(collection.id, toUpdate) - this.$emit('added', collection) this.dialogClose() } catch (e) { this.showSnack(e.message) @@ -170,7 +169,6 @@ export default Vue.extend({ try { const created = await this.$komgaCollections.postCollection(toCreate) - this.$emit('created', created) this.dialogClose() } catch (e) { this.showSnack(e.message) diff --git a/komga-webui/src/components/dialogs/CollectionDeleteDialog.vue b/komga-webui/src/components/dialogs/CollectionDeleteDialog.vue index 1ecc104de..011873be1 100644 --- a/komga-webui/src/components/dialogs/CollectionDeleteDialog.vue +++ b/komga-webui/src/components/dialogs/CollectionDeleteDialog.vue @@ -95,7 +95,6 @@ export default Vue.extend({ async deleteCollection() { try { await this.$komgaCollections.deleteCollection(this.collection.id) - this.$emit('deleted', true) } catch (e) { this.showSnack(e.message) } diff --git a/komga-webui/src/components/dialogs/CollectionEditDialog.vue b/komga-webui/src/components/dialogs/CollectionEditDialog.vue index 63f1a617d..cfdd7b4fd 100644 --- a/komga-webui/src/components/dialogs/CollectionEditDialog.vue +++ b/komga-webui/src/components/dialogs/CollectionEditDialog.vue @@ -138,7 +138,6 @@ export default Vue.extend({ } as CollectionUpdateDto await this.$komgaCollections.patchCollection(this.collection.id, update) - this.$emit('updated', true) } catch (e) { this.showSnack(e.message) } diff --git a/komga-webui/src/components/dialogs/EditBooksDialog.vue b/komga-webui/src/components/dialogs/EditBooksDialog.vue index 5def7d935..50c1a1a5a 100644 --- a/komga-webui/src/components/dialogs/EditBooksDialog.vue +++ b/komga-webui/src/components/dialogs/EditBooksDialog.vue @@ -532,7 +532,6 @@ export default Vue.extend({ for (const b of toUpdate) { try { await this.$komgaBooks.updateMetadata(b.id, metadata) - this.$emit('updated', b) } catch (e) { this.showSnack(e.message) } diff --git a/komga-webui/src/components/dialogs/EditSeriesDialog.vue b/komga-webui/src/components/dialogs/EditSeriesDialog.vue index c346844c8..9369369e5 100644 --- a/komga-webui/src/components/dialogs/EditSeriesDialog.vue +++ b/komga-webui/src/components/dialogs/EditSeriesDialog.vue @@ -591,7 +591,6 @@ export default Vue.extend({ for (const s of toUpdate) { try { await this.$komgaSeries.updateMetadata(s.id, metadata) - this.$emit('updated', s) } catch (e) { this.showSnack(e.message) } diff --git a/komga-webui/src/components/dialogs/LibraryDeleteDialog.vue b/komga-webui/src/components/dialogs/LibraryDeleteDialog.vue index 0d487cac8..ef9536576 100644 --- a/komga-webui/src/components/dialogs/LibraryDeleteDialog.vue +++ b/komga-webui/src/components/dialogs/LibraryDeleteDialog.vue @@ -94,7 +94,6 @@ export default Vue.extend({ async deleteLibrary() { try { await this.$store.dispatch('deleteLibrary', this.library) - this.$emit('deleted', true) } catch (e) { this.showSnack(e.message) } diff --git a/komga-webui/src/components/dialogs/LibraryEditDialog.vue b/komga-webui/src/components/dialogs/LibraryEditDialog.vue index 9206bde5e..67e120ee1 100644 --- a/komga-webui/src/components/dialogs/LibraryEditDialog.vue +++ b/komga-webui/src/components/dialogs/LibraryEditDialog.vue @@ -245,7 +245,6 @@