mirror of
https://github.com/gotson/komga.git
synced 2025-12-20 23:45:11 +01:00
feat(webui): the UI is now dynamic to events from the server
closes #124
This commit is contained in:
parent
691c7f0071
commit
a707fd3594
37 changed files with 741 additions and 330 deletions
|
|
@ -6,6 +6,8 @@
|
|||
<script lang="ts">
|
||||
import Vue from 'vue'
|
||||
import {Theme} from "@/types/themes";
|
||||
import {LIBRARY_ADDED, LIBRARY_CHANGED, LIBRARY_DELETED} from "@/types/events";
|
||||
import {LibrarySseDto} from "@/types/komga-sse";
|
||||
|
||||
const cookieLocale = 'locale'
|
||||
const cookieTheme = 'theme'
|
||||
|
|
@ -73,9 +75,18 @@ export default Vue.extend({
|
|||
this.$cookies.keys()
|
||||
.filter(x => x.startsWith('collection.filter') || x.startsWith('library.filter') || x.startsWith('library.sort'))
|
||||
.forEach(x => this.$cookies.remove(x))
|
||||
|
||||
|
||||
this.$eventHub.$on(LIBRARY_ADDED, this.reloadLibraries)
|
||||
this.$eventHub.$on(LIBRARY_DELETED, this.reloadLibraries)
|
||||
this.$eventHub.$on(LIBRARY_CHANGED, this.reloadLibraries)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.matchMedia('(prefers-color-scheme: dark)').removeEventListener('change', this.systemThemeChange)
|
||||
|
||||
this.$eventHub.$off(LIBRARY_ADDED, this.reloadLibraries)
|
||||
this.$eventHub.$off(LIBRARY_DELETED, this.reloadLibraries)
|
||||
this.$eventHub.$off(LIBRARY_CHANGED, this.reloadLibraries)
|
||||
},
|
||||
watch: {
|
||||
"$store.state.persistedState.locale": {
|
||||
|
|
@ -117,6 +128,9 @@ export default Vue.extend({
|
|||
break
|
||||
}
|
||||
},
|
||||
reloadLibraries(event: LibrarySseDto) {
|
||||
this.$store.dispatch('getLibraries')
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -3,39 +3,31 @@
|
|||
<collection-add-to-dialog
|
||||
v-model="addToCollectionDialog"
|
||||
:series="addToCollectionSeries"
|
||||
@added="collectionAdded"
|
||||
@created="collectionAdded"
|
||||
/>
|
||||
|
||||
<collection-edit-dialog
|
||||
v-model="editCollectionDialog"
|
||||
:collection="editCollection"
|
||||
@updated="collectionUpdated"
|
||||
/>
|
||||
|
||||
<collection-delete-dialog
|
||||
v-model="deleteCollectionDialog"
|
||||
:collection="deleteCollection"
|
||||
@deleted="collectionDeleted"
|
||||
/>
|
||||
|
||||
<read-list-add-to-dialog
|
||||
v-model="addToReadListDialog"
|
||||
:books="addToReadListBooks"
|
||||
@added="readListAdded"
|
||||
@created="readListAdded"
|
||||
/>
|
||||
|
||||
<read-list-edit-dialog
|
||||
v-model="editReadListDialog"
|
||||
:read-list="editReadList"
|
||||
@updated="readListUpdated"
|
||||
/>
|
||||
|
||||
<read-list-delete-dialog
|
||||
v-model="deleteReadListDialog"
|
||||
:read-list="deleteReadList"
|
||||
@deleted="readListDeleted"
|
||||
/>
|
||||
|
||||
<library-edit-dialog
|
||||
|
|
@ -46,19 +38,16 @@
|
|||
<library-delete-dialog
|
||||
v-model="deleteLibraryDialog"
|
||||
:library="deleteLibrary"
|
||||
@deleted="libraryDeleted"
|
||||
/>
|
||||
|
||||
<edit-books-dialog
|
||||
v-model="updateBooksDialog"
|
||||
:books="updateBooks"
|
||||
@updated="bookUpdated"
|
||||
/>
|
||||
|
||||
<edit-series-dialog
|
||||
v-model="updateSeriesDialog"
|
||||
:series="updateSeries"
|
||||
@updated="seriesUpdated"
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
|
@ -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))
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
lazy-src="../assets/cover.svg"
|
||||
aspect-ratio="0.7071"
|
||||
contain
|
||||
@error="thumbnailError = true"
|
||||
>
|
||||
<!-- unread tick for book -->
|
||||
<div class="unread" v-if="isUnread"/>
|
||||
|
|
@ -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'
|
||||
}}
|
||||
</v-icon>
|
||||
|
||||
|
|
@ -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<BookDto | SeriesDto | CollectionDto | ReadListDto> {
|
||||
computedItem(): Item<BookDto | SeriesDto | CollectionDto | ReadListDto> {
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@
|
|||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue'
|
||||
import {COLLECTION_CHANGED, READLIST_CHANGED} from '@/types/events'
|
||||
import {COLLECTION_ADDED, COLLECTION_DELETED, READLIST_ADDED, READLIST_DELETED} from '@/types/events'
|
||||
import {LIBRARIES_ALL} from '@/types/library'
|
||||
|
||||
export default Vue.extend({
|
||||
|
|
@ -101,18 +101,23 @@ export default Vue.extend({
|
|||
watch: {
|
||||
libraryId: {
|
||||
handler(val) {
|
||||
this.loadCounts(val)
|
||||
this.loadReadListCounts(val)
|
||||
this.loadCollectionCounts(val)
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$eventHub.$on(COLLECTION_CHANGED, this.reloadCounts)
|
||||
this.$eventHub.$on(READLIST_CHANGED, this.reloadCounts)
|
||||
this.$eventHub.$on(COLLECTION_ADDED, this.collectionAdded)
|
||||
this.$eventHub.$on(COLLECTION_DELETED, this.collectionDeleted)
|
||||
this.$eventHub.$on(READLIST_ADDED, this.readListAdded)
|
||||
this.$eventHub.$on(READLIST_DELETED, this.readListDeleted)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$eventHub.$off(COLLECTION_CHANGED, this.reloadCounts)
|
||||
this.$eventHub.$off(READLIST_CHANGED, this.reloadCounts)
|
||||
this.$eventHub.$off(COLLECTION_ADDED, this.collectionAdded)
|
||||
this.$eventHub.$off(COLLECTION_DELETED, this.collectionDeleted)
|
||||
this.$eventHub.$off(READLIST_ADDED, this.readListAdded)
|
||||
this.$eventHub.$off(READLIST_DELETED, this.readListDeleted)
|
||||
},
|
||||
computed: {
|
||||
showRecommended(): boolean {
|
||||
|
|
@ -123,15 +128,27 @@ export default Vue.extend({
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
reloadCounts() {
|
||||
this.loadCounts(this.libraryId)
|
||||
readListAdded() {
|
||||
if(this.readListsCount === 0) this.loadReadListCounts(this.libraryId)
|
||||
},
|
||||
async loadCounts(libraryId: string) {
|
||||
readListDeleted() {
|
||||
if(this.readListsCount === 1) this.loadReadListCounts(this.libraryId)
|
||||
},
|
||||
collectionAdded() {
|
||||
if(this.collectionsCount === 0) this.loadCollectionCounts(this.libraryId)
|
||||
},
|
||||
collectionDeleted() {
|
||||
if(this.collectionsCount === 1) this.loadCollectionCounts(this.libraryId)
|
||||
},
|
||||
async loadCollectionCounts(libraryId: string) {
|
||||
const lib = libraryId !== LIBRARIES_ALL ? [libraryId] : undefined
|
||||
this.$komgaCollections.getCollections(lib, {size: 0})
|
||||
.then(v => this.collectionsCount = v.totalElements)
|
||||
},
|
||||
async loadReadListCounts(libraryId: string) {
|
||||
const lib = libraryId !== LIBRARIES_ALL ? [libraryId] : undefined
|
||||
await this.$komgaReadLists.getReadLists(lib, {size: 0})
|
||||
.then(v => this.readListsCount = v.totalElements)
|
||||
.then(v => this.readListsCount = v.totalElements)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
117
komga-webui/src/components/Toaster.vue
Normal file
117
komga-webui/src/components/Toaster.vue
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<template>
|
||||
<v-snackbar
|
||||
v-model="snackbar.show"
|
||||
:color="snackbar.color"
|
||||
bottom
|
||||
multi-line
|
||||
vertical
|
||||
:timeout="snackbar.timeout"
|
||||
>
|
||||
<p>{{ snackbar.text }}</p>
|
||||
<p>{{ snackbar.text2 }}</p>
|
||||
<template v-slot:action="{ attrs }">
|
||||
<v-btn
|
||||
v-if="snackbar.goTo"
|
||||
color="secondary"
|
||||
text
|
||||
v-bind="attrs"
|
||||
@click="snackbar.goTo.click"
|
||||
>
|
||||
{{ snackbar.goTo.text }}
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
text
|
||||
v-bind="attrs"
|
||||
@click="close"
|
||||
>{{ $t('common.dismiss') }}</v-btn>
|
||||
</template>
|
||||
</v-snackbar>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue'
|
||||
import {BOOK_IMPORTED} from "@/types/events";
|
||||
import {convertErrorCodes} from "@/functions/error-codes";
|
||||
import {BookImportSseDto} from "@/types/komga-sse";
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'Toaster',
|
||||
data: function () {
|
||||
return {
|
||||
queue: [] as any[],
|
||||
snackbar: {
|
||||
show: false,
|
||||
text: '',
|
||||
text2: '',
|
||||
color: undefined,
|
||||
timeout: 5000,
|
||||
goTo: {
|
||||
text: '',
|
||||
click: () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$eventHub.$on(BOOK_IMPORTED, this.bookImported)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$eventHub.$off(BOOK_IMPORTED, this.bookImported)
|
||||
},
|
||||
watch: {
|
||||
'snackbar.show'(val) {
|
||||
if (!val) {
|
||||
setTimeout(() => this.next(), 1000)
|
||||
}
|
||||
},
|
||||
queue(val) {
|
||||
if (val.length > 0) {
|
||||
this.next()
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.snackbar.show = false
|
||||
},
|
||||
next() {
|
||||
if (this.snackbar.show) {
|
||||
return
|
||||
}
|
||||
if (this.queue.length > 0) {
|
||||
const snack = this.queue.shift()
|
||||
this.snackbar.text = snack.text
|
||||
this.snackbar.text2 = snack.text2
|
||||
this.snackbar.goTo = snack.goTo
|
||||
this.snackbar.color = snack.color
|
||||
this.snackbar.show = true
|
||||
}
|
||||
},
|
||||
async bookImported(event: BookImportSseDto) {
|
||||
if (event.success && event.bookId) {
|
||||
const book = await this.$komgaBooks.getBook(event.bookId)
|
||||
this.queue.push({
|
||||
text: this.$t('book_import.notification.import_successful', {book: book.metadata.title}).toString(),
|
||||
text2: this.$t('book_import.notification.source_file', {file: event.sourceFile}).toString(),
|
||||
goTo: {
|
||||
text: this.$t('book_import.notification.go_to_book').toString(),
|
||||
click: () => this.$router.push({name: 'browse-book', params: {bookId: book.id}}),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
this.queue.push({
|
||||
text: this.$t('book_import.notification.import_failure', {file: event.sourceFile}).toString(),
|
||||
text2: convertErrorCodes(event.message || ''),
|
||||
color: 'error',
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -245,7 +245,6 @@
|
|||
|
||||
<script lang="ts">
|
||||
import FileBrowserDialog from '@/components/dialogs/FileBrowserDialog.vue'
|
||||
import {LIBRARY_ADDED, LIBRARY_CHANGED, libraryToEventLibraryChanged} from '@/types/events'
|
||||
import Vue from 'vue'
|
||||
import {required} from 'vuelidate/lib/validators'
|
||||
|
||||
|
|
@ -435,10 +434,8 @@ export default Vue.extend({
|
|||
try {
|
||||
if (this.library) {
|
||||
await this.$store.dispatch('updateLibrary', {libraryId: this.library.id, library: library})
|
||||
this.$eventHub.$emit(LIBRARY_CHANGED, libraryToEventLibraryChanged(this.library))
|
||||
} else {
|
||||
await this.$store.dispatch('postLibrary', library)
|
||||
this.$eventHub.$emit(LIBRARY_ADDED)
|
||||
}
|
||||
this.dialogClose()
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -151,7 +151,6 @@ export default Vue.extend({
|
|||
|
||||
try {
|
||||
await this.$komgaReadLists.patchReadList(readList.id, toUpdate)
|
||||
this.$emit('added', readList)
|
||||
this.dialogClose()
|
||||
} catch (e) {
|
||||
this.showSnack(e.message)
|
||||
|
|
@ -165,7 +164,6 @@ export default Vue.extend({
|
|||
|
||||
try {
|
||||
const created = await this.$komgaReadLists.postReadList(toCreate)
|
||||
this.$emit('created', created)
|
||||
this.dialogClose()
|
||||
} catch (e) {
|
||||
this.showSnack(e.message)
|
||||
|
|
|
|||
|
|
@ -93,7 +93,6 @@ export default Vue.extend({
|
|||
async delete() {
|
||||
try {
|
||||
await this.$komgaReadLists.deleteReadList(this.readList.id)
|
||||
this.$emit('deleted', true)
|
||||
} catch (e) {
|
||||
this.showSnack(e.message)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,7 +122,6 @@ export default Vue.extend({
|
|||
} as ReadListUpdateDto
|
||||
|
||||
await this.$komgaReadLists.patchReadList(this.readList.id, update)
|
||||
this.$emit('updated', true)
|
||||
} catch (e) {
|
||||
this.showSnack(e.message)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
<script lang="ts">
|
||||
import {getReadProgress} from '@/functions/book-progress'
|
||||
import {ReadStatus} from '@/types/enum-books'
|
||||
import {BOOK_CHANGED, bookToEventBookChanged} from '@/types/events'
|
||||
import Vue from 'vue'
|
||||
import {BookDto, ReadProgressUpdateDto} from '@/types/komga-books'
|
||||
|
||||
|
|
@ -79,11 +78,9 @@ export default Vue.extend({
|
|||
async markRead () {
|
||||
const readProgress = { completed: true } as ReadProgressUpdateDto
|
||||
await this.$komgaBooks.updateReadProgress(this.book.id, readProgress)
|
||||
this.$eventHub.$emit(BOOK_CHANGED, bookToEventBookChanged(this.book))
|
||||
},
|
||||
async markUnread () {
|
||||
await this.$komgaBooks.deleteReadProgress(this.book.id)
|
||||
this.$eventHub.$emit(BOOK_CHANGED, bookToEventBookChanged(this.book))
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@
|
|||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import {SERIES_CHANGED, seriesToEventSeriesChanged} from '@/types/events'
|
||||
import Vue from 'vue'
|
||||
import {SeriesDto} from "@/types/komga-series";
|
||||
|
||||
|
|
@ -76,11 +75,11 @@ export default Vue.extend({
|
|||
},
|
||||
async markRead () {
|
||||
await this.$komgaSeries.markAsRead(this.series.id)
|
||||
this.$eventHub.$emit(SERIES_CHANGED, seriesToEventSeriesChanged(this.series))
|
||||
// this.$eventHub.$emit(SERIES_CHANGED, seriesToEventSeriesChanged(this.series))
|
||||
},
|
||||
async markUnread () {
|
||||
await this.$komgaSeries.markAsUnread(this.series.id)
|
||||
this.$eventHub.$emit(SERIES_CHANGED, seriesToEventSeriesChanged(this.series))
|
||||
// this.$eventHub.$emit(SERIES_CHANGED, seriesToEventSeriesChanged(this.series))
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -39,6 +39,12 @@
|
|||
"field_import_path": "Import from folder",
|
||||
"info_part1": "This screen lets you import files that are outside your existing libraries. You can only import files into existing Series, in which case Komga will move or copy the files into the directory of the chosen Series.",
|
||||
"info_part2": "If you choose a number for a book, and a book already exists with that number, then you will be able to compare the 2 books. If you decide to import the book, Komga will upgrade the existing book with the new one, effectively replacing the old file with the new.",
|
||||
"notification": {
|
||||
"go_to_book": "Go to book",
|
||||
"import_failure": "Failed to import book: {file}",
|
||||
"import_successful": "Book imported successfully: {book}",
|
||||
"source_file": "Source file: {file}"
|
||||
},
|
||||
"row": {
|
||||
"error_analyze_first": "Book needs to be analyzed first",
|
||||
"error_choose_series": "Choose a series",
|
||||
|
|
@ -159,6 +165,7 @@
|
|||
"collections": "Collections",
|
||||
"create": "Create",
|
||||
"delete": "Delete",
|
||||
"dismiss": "Dismiss",
|
||||
"download": "Download",
|
||||
"email": "Email",
|
||||
"filter_no_matches": "The active filter has no matches",
|
||||
|
|
@ -177,12 +184,13 @@
|
|||
"read": "Read",
|
||||
"readlists": "Read Lists",
|
||||
"required": "Required",
|
||||
"reset_filters": "Reset filters",
|
||||
"roles": "Roles",
|
||||
"series": "Series",
|
||||
"tags": "Tags",
|
||||
"use_filter_panel_to_change_filter": "Use the filter panel to change the active filter",
|
||||
"year": "year",
|
||||
"reset_filters": "Reset filters"
|
||||
"pending_tasks": "No pending tasks | 1 pending task | {count} pending tasks"
|
||||
},
|
||||
"dashboard": {
|
||||
"keep_reading": "Keep Reading",
|
||||
|
|
@ -469,7 +477,12 @@
|
|||
"ERR_1014": "No match for book number within series",
|
||||
"ERR_1015": "Error while deserializing ComicRack ReadingList",
|
||||
"ERR_1016": "Directory not accessible or not a directory",
|
||||
"ERR_1017": "Cannot scan folder that is part of an existing library"
|
||||
"ERR_1017": "Cannot scan folder that is part of an existing library",
|
||||
"ERR_1018": "File not found",
|
||||
"ERR_1019": "Cannot import file that is part of an existing library",
|
||||
"ERR_1020": "Book to upgrade does not belong to provided series",
|
||||
"ERR_1021": "Destination file already exists",
|
||||
"ERR_1022": "Newly imported book could not be scanned"
|
||||
},
|
||||
"filter": {
|
||||
"age_rating": "age rating",
|
||||
|
|
|
|||
|
|
@ -18,12 +18,16 @@ import komgaReferential from './plugins/komga-referential.plugin'
|
|||
import komgaSeries from './plugins/komga-series.plugin'
|
||||
import komgaUsers from './plugins/komga-users.plugin'
|
||||
import komgaTransientBooks from './plugins/komga-transientbooks.plugin'
|
||||
import komgaSse from './plugins/komga-sse.plugin'
|
||||
import vuetify from './plugins/vuetify'
|
||||
import './public-path'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import i18n from './i18n'
|
||||
|
||||
Vue.prototype.$_ = _
|
||||
Vue.prototype.$eventHub = new Vue()
|
||||
|
||||
Vue.use(Vuelidate)
|
||||
Vue.use(lineClamp)
|
||||
Vue.use(VueCookies)
|
||||
|
|
@ -39,10 +43,9 @@ Vue.use(komgaClaim, {http: Vue.prototype.$http})
|
|||
Vue.use(komgaTransientBooks, {http: Vue.prototype.$http})
|
||||
Vue.use(komgaUsers, {store: store, http: Vue.prototype.$http})
|
||||
Vue.use(komgaLibraries, {store: store, http: Vue.prototype.$http})
|
||||
Vue.use(komgaSse, {eventHub: Vue.prototype.$eventHub, store: store})
|
||||
Vue.use(actuator, {http: Vue.prototype.$http})
|
||||
|
||||
Vue.prototype.$_ = _
|
||||
Vue.prototype.$eventHub = new Vue()
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import KomgaLibrariesService from '@/services/komga-libraries.service'
|
||||
import { AxiosInstance } from 'axios'
|
||||
import {AxiosInstance} from 'axios'
|
||||
import _Vue from 'vue'
|
||||
import { Module } from 'vuex/types'
|
||||
import {Module} from 'vuex/types'
|
||||
|
||||
let service: KomgaLibrariesService
|
||||
|
||||
|
|
@ -25,15 +25,12 @@ const vuexModule: Module<any, any> = {
|
|||
},
|
||||
async postLibrary ({ dispatch }, library) {
|
||||
await service.postLibrary(library)
|
||||
await dispatch('getLibraries')
|
||||
},
|
||||
async updateLibrary ({ dispatch }, { libraryId, library }) {
|
||||
await service.updateLibrary(libraryId, library)
|
||||
await dispatch('getLibraries')
|
||||
},
|
||||
async deleteLibrary ({ dispatch }, library) {
|
||||
await service.deleteLibrary(library)
|
||||
await dispatch('getLibraries')
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
31
komga-webui/src/plugins/komga-sse.plugin.ts
Normal file
31
komga-webui/src/plugins/komga-sse.plugin.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import _Vue from 'vue'
|
||||
import KomgaSseService from "@/services/komga-sse.service"
|
||||
import {Module} from "vuex";
|
||||
|
||||
const vuexModule: Module<any, any> = {
|
||||
state: {
|
||||
taskCount: 0,
|
||||
},
|
||||
mutations: {
|
||||
setTaskCount (state, val) {
|
||||
state.taskCount = val
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
install(
|
||||
Vue: typeof _Vue,
|
||||
{eventHub, store}: { eventHub: _Vue, store: any },
|
||||
) {
|
||||
store.registerModule('komgaSse', vuexModule)
|
||||
|
||||
Vue.prototype.$komgaSse = new KomgaSseService(eventHub, store)
|
||||
},
|
||||
}
|
||||
|
||||
declare module 'vue/types/vue' {
|
||||
interface Vue {
|
||||
$komgaSse: KomgaSseService;
|
||||
}
|
||||
}
|
||||
99
komga-webui/src/services/komga-sse.service.ts
Normal file
99
komga-webui/src/services/komga-sse.service.ts
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
import urls from '@/functions/urls'
|
||||
import {
|
||||
BOOK_ADDED,
|
||||
BOOK_CHANGED,
|
||||
BOOK_DELETED,
|
||||
BOOK_IMPORTED,
|
||||
COLLECTION_ADDED,
|
||||
COLLECTION_CHANGED,
|
||||
COLLECTION_DELETED,
|
||||
LIBRARY_ADDED,
|
||||
LIBRARY_CHANGED,
|
||||
LIBRARY_DELETED,
|
||||
READLIST_ADDED,
|
||||
READLIST_CHANGED,
|
||||
READLIST_DELETED,
|
||||
READPROGRESS_CHANGED,
|
||||
READPROGRESS_DELETED,
|
||||
SERIES_ADDED,
|
||||
SERIES_CHANGED,
|
||||
SERIES_DELETED,
|
||||
THUMBNAILBOOK_ADDED,
|
||||
THUMBNAILSERIES_ADDED,
|
||||
} from "@/types/events";
|
||||
import Vue from "vue";
|
||||
import {TaskQueueSseDto} from "@/types/komga-sse";
|
||||
|
||||
const API_SSE = '/sse/v1/events'
|
||||
|
||||
export default class KomgaSseService {
|
||||
private eventSource: EventSource | undefined
|
||||
private eventHub: Vue
|
||||
private store: any
|
||||
|
||||
constructor(eventHub: Vue, store: any) {
|
||||
this.eventHub = eventHub
|
||||
this.store = store
|
||||
|
||||
this.eventHub.$watch(
|
||||
() => this.store.getters.authenticated,
|
||||
(val) => {
|
||||
if (val) this.connect()
|
||||
else this.disconnect()
|
||||
})
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.eventSource = new EventSource(urls.originNoSlash + API_SSE, {withCredentials: true})
|
||||
|
||||
// Libraries
|
||||
this.eventSource.addEventListener('LibraryAdded', (event: any) => this.emit(LIBRARY_ADDED, event))
|
||||
this.eventSource.addEventListener('LibraryChanged', (event: any) => this.emit(LIBRARY_CHANGED, event))
|
||||
this.eventSource.addEventListener('LibraryDeleted', (event: any) => this.emit(LIBRARY_DELETED, event))
|
||||
|
||||
// Series
|
||||
this.eventSource.addEventListener('SeriesAdded', (event: any) => this.emit(SERIES_ADDED, event))
|
||||
this.eventSource.addEventListener('SeriesChanged', (event: any) => this.emit(SERIES_CHANGED, event))
|
||||
this.eventSource.addEventListener('SeriesDeleted', (event: any) => this.emit(SERIES_DELETED, event))
|
||||
|
||||
// Books
|
||||
this.eventSource.addEventListener('BookAdded', (event: any) => this.emit(BOOK_ADDED, event))
|
||||
this.eventSource.addEventListener('BookChanged', (event: any) => this.emit(BOOK_CHANGED, event))
|
||||
this.eventSource.addEventListener('BookDeleted', (event: any) => this.emit(BOOK_DELETED, event))
|
||||
|
||||
this.eventSource.addEventListener('BookImported', (event: any) => this.emit(BOOK_IMPORTED, event))
|
||||
|
||||
// Collections
|
||||
this.eventSource.addEventListener('CollectionAdded', (event: any) => this.emit(COLLECTION_ADDED, event))
|
||||
this.eventSource.addEventListener('CollectionChanged', (event: any) => this.emit(COLLECTION_CHANGED, event))
|
||||
this.eventSource.addEventListener('CollectionDeleted', (event: any) => this.emit(COLLECTION_DELETED, event))
|
||||
|
||||
// Read Lists
|
||||
this.eventSource.addEventListener('ReadListAdded', (event: any) => this.emit(READLIST_ADDED, event))
|
||||
this.eventSource.addEventListener('ReadListChanged', (event: any) => this.emit(READLIST_CHANGED, event))
|
||||
this.eventSource.addEventListener('ReadListDeleted', (event: any) => this.emit(READLIST_DELETED, event))
|
||||
|
||||
// Read Progress
|
||||
this.eventSource.addEventListener('ReadProgressChanged', (event: any) => this.emit(READPROGRESS_CHANGED, event))
|
||||
this.eventSource.addEventListener('ReadProgressDeleted', (event: any) => this.emit(READPROGRESS_DELETED, event))
|
||||
|
||||
// Thumbnails
|
||||
this.eventSource.addEventListener('ThumbnailBookAdded', (event: any) => this.emit(THUMBNAILBOOK_ADDED, event))
|
||||
this.eventSource.addEventListener('ThumbnailSeriesAdded', (event: any) => this.emit(THUMBNAILSERIES_ADDED, event))
|
||||
|
||||
this.eventSource.addEventListener('TaskQueueStatus', (event: any) => this.updateTaskCount(event))
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.eventSource?.close()
|
||||
}
|
||||
|
||||
private emit(name: string, event: any) {
|
||||
this.eventHub.$emit(name, JSON.parse(event.data))
|
||||
}
|
||||
|
||||
private updateTaskCount(event: any) {
|
||||
const data = JSON.parse(event.data) as TaskQueueSseDto
|
||||
this.store.commit('setTaskCount', data.count)
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ export enum ReadStatus {
|
|||
}
|
||||
|
||||
export function replaceCompositeReadStatus(list: string[]): string[] {
|
||||
if(list.includes(ReadStatus.UNREAD_AND_IN_PROGRESS)){
|
||||
if(list?.includes(ReadStatus.UNREAD_AND_IN_PROGRESS)){
|
||||
return [...without(list, ReadStatus.UNREAD_AND_IN_PROGRESS), ReadStatus.UNREAD, ReadStatus.IN_PROGRESS]
|
||||
}
|
||||
else return list
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
interface EventBookChanged {
|
||||
id: string,
|
||||
seriesId: string
|
||||
}
|
||||
|
||||
interface EventSeriesChanged {
|
||||
id: string,
|
||||
libraryId: string
|
||||
}
|
||||
|
||||
interface EventCollectionChanged {
|
||||
id: string
|
||||
}
|
||||
|
||||
interface EventCollectionDeleted {
|
||||
id: string
|
||||
}
|
||||
|
||||
interface EventReadListChanged {
|
||||
id: string
|
||||
}
|
||||
|
||||
interface EventReadListDeleted {
|
||||
id: string
|
||||
}
|
||||
|
||||
interface EventLibraryAdded {
|
||||
id: string
|
||||
}
|
||||
|
||||
interface EventLibraryChanged {
|
||||
id: string
|
||||
}
|
||||
|
||||
interface EventLibraryDeleted {
|
||||
id: string
|
||||
}
|
||||
|
|
@ -1,68 +1,26 @@
|
|||
import { BookDto } from '@/types/komga-books'
|
||||
import {SeriesDto} from "@/types/komga-series";
|
||||
|
||||
export const BOOK_CHANGED = 'book-changed'
|
||||
export const SERIES_CHANGED = 'series-changed'
|
||||
export const COLLECTION_DELETED = 'collection-deleted'
|
||||
export const COLLECTION_CHANGED = 'collection-changed'
|
||||
export const READLIST_DELETED = 'readlist-deleted'
|
||||
export const READLIST_CHANGED = 'readlist-changed'
|
||||
export const LIBRARY_ADDED = 'library-added'
|
||||
export const LIBRARY_CHANGED = 'library-changed'
|
||||
export const LIBRARY_DELETED = 'library-deleted'
|
||||
|
||||
export function bookToEventBookChanged (book: BookDto): EventBookChanged {
|
||||
return {
|
||||
id: book.id,
|
||||
seriesId: book.seriesId,
|
||||
} as EventBookChanged
|
||||
}
|
||||
export const BOOK_ADDED = 'book-added'
|
||||
export const BOOK_CHANGED = 'book-changed'
|
||||
export const BOOK_DELETED = 'book-deleted'
|
||||
export const BOOK_IMPORTED = 'book-imported'
|
||||
|
||||
export function seriesToEventSeriesChanged (series: SeriesDto): EventSeriesChanged {
|
||||
return {
|
||||
id: series.id,
|
||||
libraryId: series.libraryId,
|
||||
} as EventSeriesChanged
|
||||
}
|
||||
export const SERIES_ADDED = 'series-added'
|
||||
export const SERIES_CHANGED = 'series-changed'
|
||||
export const SERIES_DELETED = 'series-deleted'
|
||||
|
||||
export function collectionToEventCollectionChanged (collection: CollectionDto): EventCollectionChanged {
|
||||
return {
|
||||
id: collection.id,
|
||||
} as EventCollectionChanged
|
||||
}
|
||||
export const COLLECTION_ADDED = 'collection-added'
|
||||
export const COLLECTION_CHANGED = 'collection-changed'
|
||||
export const COLLECTION_DELETED = 'collection-deleted'
|
||||
|
||||
export function collectionToEventCollectionDeleted (collection: CollectionDto): EventCollectionDeleted {
|
||||
return {
|
||||
id: collection.id,
|
||||
} as EventCollectionDeleted
|
||||
}
|
||||
export const READLIST_ADDED = 'readlist-added'
|
||||
export const READLIST_CHANGED = 'readlist-changed'
|
||||
export const READLIST_DELETED = 'readlist-deleted'
|
||||
|
||||
export function readListToEventReadListChanged (readList: ReadListDto): EventReadListChanged {
|
||||
return {
|
||||
id: readList.id,
|
||||
} as EventReadListChanged
|
||||
}
|
||||
export const READPROGRESS_CHANGED = 'readprogress-changed'
|
||||
export const READPROGRESS_DELETED = 'readprogress-deleted'
|
||||
|
||||
export function readListToEventReadListDeleted (readList: ReadListDto): EventReadListDeleted {
|
||||
return {
|
||||
id: readList.id,
|
||||
} as EventReadListDeleted
|
||||
}
|
||||
|
||||
export function libraryToEventLibraryAdded (library: LibraryDto): EventLibraryAdded {
|
||||
return {
|
||||
id: library.id,
|
||||
} as EventLibraryAdded
|
||||
}
|
||||
|
||||
export function libraryToEventLibraryChanged (library: LibraryDto): EventLibraryChanged {
|
||||
return {
|
||||
id: library.id,
|
||||
} as EventLibraryChanged
|
||||
}
|
||||
|
||||
export function libraryToEventLibraryDeleted (library: LibraryDto): EventLibraryDeleted {
|
||||
return {
|
||||
id: library.id,
|
||||
} as EventLibraryDeleted
|
||||
}
|
||||
export const THUMBNAILBOOK_ADDED = 'thumbnailbook-added'
|
||||
export const THUMBNAILSERIES_ADDED = 'thumbnailbook-added'
|
||||
|
|
|
|||
49
komga-webui/src/types/komga-sse.ts
Normal file
49
komga-webui/src/types/komga-sse.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
export interface LibrarySseDto {
|
||||
libraryId: string,
|
||||
}
|
||||
|
||||
export interface SeriesSseDto {
|
||||
seriesId: string,
|
||||
libraryId: string,
|
||||
}
|
||||
|
||||
export interface BookSseDto {
|
||||
bookId: string,
|
||||
seriesId: string,
|
||||
libraryId: string,
|
||||
}
|
||||
|
||||
export interface CollectionSseDto {
|
||||
collectionId: string,
|
||||
seriesIds: string[],
|
||||
}
|
||||
|
||||
export interface ReadListSseDto {
|
||||
readListId: string,
|
||||
bookIds: string[],
|
||||
}
|
||||
|
||||
export interface ReadProgressSseDto {
|
||||
bookId: string,
|
||||
userId: string,
|
||||
}
|
||||
|
||||
export interface ThumbnailBookSseDto {
|
||||
bookId: string,
|
||||
seriesId: string,
|
||||
}
|
||||
|
||||
export interface ThumbnailSeriesSseDto {
|
||||
seriesId: string,
|
||||
}
|
||||
|
||||
export interface TaskQueueSseDto {
|
||||
count: number,
|
||||
}
|
||||
|
||||
export interface BookImportSseDto {
|
||||
bookId?: string,
|
||||
sourceFile: string,
|
||||
success: boolean,
|
||||
message?: string,
|
||||
}
|
||||
|
|
@ -322,7 +322,16 @@ import {getReadProgress, getReadProgressPercentage} from '@/functions/book-progr
|
|||
import {getBookTitleCompact} from '@/functions/book-title'
|
||||
import {bookFileUrl, bookThumbnailUrl} from '@/functions/urls'
|
||||
import {ReadStatus} from '@/types/enum-books'
|
||||
import {BOOK_CHANGED, LIBRARY_DELETED} from '@/types/events'
|
||||
import {
|
||||
BOOK_CHANGED,
|
||||
BOOK_DELETED,
|
||||
LIBRARY_DELETED,
|
||||
READLIST_ADDED,
|
||||
READLIST_CHANGED,
|
||||
READLIST_DELETED,
|
||||
READPROGRESS_CHANGED,
|
||||
READPROGRESS_DELETED,
|
||||
} from '@/types/events'
|
||||
import Vue from 'vue'
|
||||
import ReadListsExpansionPanels from '@/components/ReadListsExpansionPanels.vue'
|
||||
import {BookDto, BookFormat} from '@/types/komga-books'
|
||||
|
|
@ -333,6 +342,7 @@ import VueHorizontal from "vue-horizontal";
|
|||
import {authorRoles} from "@/types/author-roles";
|
||||
import {convertErrorCodes} from "@/functions/error-codes";
|
||||
import RtlIcon from "@/components/RtlIcon.vue";
|
||||
import {BookSseDto, LibrarySseDto, ReadListSseDto, ReadProgressSseDto} from "@/types/komga-sse";
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'BrowseBook',
|
||||
|
|
@ -351,12 +361,24 @@ export default Vue.extend({
|
|||
},
|
||||
async created() {
|
||||
this.loadBook(this.bookId)
|
||||
this.$eventHub.$on(BOOK_CHANGED, this.reloadBook)
|
||||
this.$eventHub.$on(BOOK_CHANGED, this.bookChanged)
|
||||
this.$eventHub.$on(BOOK_DELETED, this.bookDeleted)
|
||||
this.$eventHub.$on(READPROGRESS_CHANGED, this.readProgressChanged)
|
||||
this.$eventHub.$on(READPROGRESS_DELETED, this.readProgressChanged)
|
||||
this.$eventHub.$on(LIBRARY_DELETED, this.libraryDeleted)
|
||||
this.$eventHub.$on(READLIST_ADDED, this.readListChanged)
|
||||
this.$eventHub.$on(READLIST_CHANGED, this.readListChanged)
|
||||
this.$eventHub.$on(READLIST_DELETED, this.readListChanged)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$eventHub.$off(BOOK_CHANGED, this.reloadBook)
|
||||
this.$eventHub.$off(BOOK_CHANGED, this.bookChanged)
|
||||
this.$eventHub.$off(BOOK_DELETED, this.bookDeleted)
|
||||
this.$eventHub.$off(READPROGRESS_CHANGED, this.readProgressChanged)
|
||||
this.$eventHub.$off(READPROGRESS_DELETED, this.readProgressChanged)
|
||||
this.$eventHub.$off(LIBRARY_DELETED, this.libraryDeleted)
|
||||
this.$eventHub.$off(READLIST_ADDED, this.readListChanged)
|
||||
this.$eventHub.$off(READLIST_CHANGED, this.readListChanged)
|
||||
this.$eventHub.$off(READLIST_DELETED, this.readListChanged)
|
||||
},
|
||||
props: {
|
||||
bookId: {
|
||||
|
|
@ -434,13 +456,27 @@ export default Vue.extend({
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
libraryDeleted(event: EventLibraryDeleted) {
|
||||
if (event.id === this.book.libraryId) {
|
||||
libraryDeleted(event: LibrarySseDto) {
|
||||
if (event.libraryId === this.book.libraryId) {
|
||||
this.$router.push({name: 'home'})
|
||||
}
|
||||
},
|
||||
reloadBook(event: EventBookChanged) {
|
||||
if (event.id === this.bookId) this.loadBook(this.bookId)
|
||||
readListChanged(event: ReadListSseDto) {
|
||||
if(event.bookIds.includes(this.bookId) || this.readLists.map(x => x.id).includes(event.readListId)){
|
||||
this.$komgaBooks.getReadLists(this.bookId)
|
||||
.then(v => this.readLists = v)
|
||||
}
|
||||
},
|
||||
bookChanged(event: BookSseDto) {
|
||||
if (event.bookId === this.bookId) this.loadBook(this.bookId)
|
||||
},
|
||||
bookDeleted(event: BookSseDto) {
|
||||
if (event.bookId === this.bookId){
|
||||
this.$router.push({name:'browse-series', params: {seriesId: this.series.id }})
|
||||
}
|
||||
},
|
||||
readProgressChanged(event: ReadProgressSseDto){
|
||||
if (event.bookId === this.bookId) this.loadBook(this.bookId)
|
||||
},
|
||||
async loadBook(bookId: string) {
|
||||
this.book = await this.$komgaBooks.getBook(bookId)
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@
|
|||
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} from '@/types/events'
|
||||
import {COLLECTION_CHANGED, COLLECTION_DELETED, SERIES_CHANGED, SERIES_DELETED} from '@/types/events'
|
||||
import Vue from 'vue'
|
||||
import SeriesMultiSelectBar from '@/components/bars/SeriesMultiSelectBar.vue'
|
||||
import {LIBRARIES_ALL} from '@/types/library'
|
||||
|
|
@ -130,6 +130,7 @@ import {parseQueryParam} from '@/functions/query-params'
|
|||
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";
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'BrowseCollection',
|
||||
|
|
@ -186,13 +187,15 @@ export default Vue.extend({
|
|||
},
|
||||
created() {
|
||||
this.$eventHub.$on(COLLECTION_CHANGED, this.collectionChanged)
|
||||
this.$eventHub.$on(COLLECTION_DELETED, this.afterDelete)
|
||||
this.$eventHub.$on(SERIES_CHANGED, this.reloadSeries)
|
||||
this.$eventHub.$on(COLLECTION_DELETED, this.collectionDeleted)
|
||||
this.$eventHub.$on(SERIES_CHANGED, this.seriesChanged)
|
||||
this.$eventHub.$on(SERIES_DELETED, this.seriesChanged)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$eventHub.$off(COLLECTION_CHANGED, this.collectionChanged)
|
||||
this.$eventHub.$off(COLLECTION_DELETED, this.afterDelete)
|
||||
this.$eventHub.$off(SERIES_CHANGED, this.reloadSeries)
|
||||
this.$eventHub.$off(COLLECTION_DELETED, this.collectionDeleted)
|
||||
this.$eventHub.$off(SERIES_CHANGED, this.seriesChanged)
|
||||
this.$eventHub.$off(SERIES_DELETED, this.seriesChanged)
|
||||
},
|
||||
async mounted() {
|
||||
await this.resetParams(this.$route, this.collectionId)
|
||||
|
|
@ -345,11 +348,16 @@ export default Vue.extend({
|
|||
unsetWatches() {
|
||||
this.filterUnwatch()
|
||||
},
|
||||
collectionChanged(event: EventCollectionChanged) {
|
||||
if (event.id === this.collectionId) {
|
||||
collectionChanged(event: CollectionSseDto) {
|
||||
if (event.collectionId === this.collectionId) {
|
||||
this.loadCollection(this.collectionId)
|
||||
}
|
||||
},
|
||||
collectionDeleted(event: CollectionSseDto) {
|
||||
if(event.collectionId === this.collectionId) {
|
||||
this.$router.push({name: 'browse-collections', params: {libraryId: LIBRARIES_ALL}})
|
||||
}
|
||||
},
|
||||
updateRouteAndReload() {
|
||||
this.unsetWatches()
|
||||
|
||||
|
|
@ -431,11 +439,8 @@ export default Vue.extend({
|
|||
editCollection() {
|
||||
this.$store.dispatch('dialogEditCollection', this.collection)
|
||||
},
|
||||
afterDelete() {
|
||||
this.$router.push({name: 'browse-collections', params: {libraryId: LIBRARIES_ALL}})
|
||||
},
|
||||
reloadSeries(event: EventSeriesChanged) {
|
||||
if (this.series.some(s => s.id === event.id)) this.loadCollection(this.collectionId)
|
||||
seriesChanged(event: SeriesSseDto) {
|
||||
if (this.series.some(s => s.id === event.seriesId)) this.loadCollection(this.collectionId)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -54,10 +54,11 @@ import ItemBrowser from '@/components/ItemBrowser.vue'
|
|||
import LibraryNavigation from '@/components/LibraryNavigation.vue'
|
||||
import LibraryActionsMenu from '@/components/menus/LibraryActionsMenu.vue'
|
||||
import PageSizeSelect from '@/components/PageSizeSelect.vue'
|
||||
import {COLLECTION_CHANGED, COLLECTION_DELETED, LIBRARY_CHANGED} from '@/types/events'
|
||||
import {COLLECTION_ADDED, COLLECTION_CHANGED, COLLECTION_DELETED, LIBRARY_CHANGED} from '@/types/events'
|
||||
import Vue from 'vue'
|
||||
import {Location} from 'vue-router'
|
||||
import {LIBRARIES_ALL, LIBRARY_ROUTE} from '@/types/library'
|
||||
import {LibrarySseDto} from "@/types/komga-sse";
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'BrowseCollections',
|
||||
|
|
@ -87,11 +88,13 @@ export default Vue.extend({
|
|||
},
|
||||
},
|
||||
created() {
|
||||
this.$eventHub.$on(COLLECTION_ADDED, this.reloadCollections)
|
||||
this.$eventHub.$on(COLLECTION_CHANGED, this.reloadCollections)
|
||||
this.$eventHub.$on(COLLECTION_DELETED, this.reloadCollections)
|
||||
this.$eventHub.$on(LIBRARY_CHANGED, this.reloadLibrary)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$eventHub.$off(COLLECTION_ADDED, this.reloadCollections)
|
||||
this.$eventHub.$off(COLLECTION_CHANGED, this.reloadCollections)
|
||||
this.$eventHub.$off(COLLECTION_DELETED, this.reloadCollections)
|
||||
this.$eventHub.$off(LIBRARY_CHANGED, this.reloadLibrary)
|
||||
|
|
@ -179,8 +182,8 @@ export default Vue.extend({
|
|||
reloadCollections() {
|
||||
this.loadLibrary(this.libraryId)
|
||||
},
|
||||
reloadLibrary(event: EventLibraryChanged) {
|
||||
if (event.id === this.libraryId) {
|
||||
reloadLibrary(event: LibrarySseDto) {
|
||||
if (event.libraryId === this.libraryId) {
|
||||
this.loadLibrary(this.libraryId)
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ import PageSizeSelect from '@/components/PageSizeSelect.vue'
|
|||
import {parseQueryParam, 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_CHANGED} from '@/types/events'
|
||||
import {LIBRARY_CHANGED, LIBRARY_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'
|
||||
|
|
@ -124,6 +124,7 @@ 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";
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'BrowseLibraries',
|
||||
|
|
@ -184,14 +185,18 @@ export default Vue.extend({
|
|||
},
|
||||
},
|
||||
created() {
|
||||
this.$eventHub.$on(SERIES_CHANGED, this.reloadSeries)
|
||||
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.reloadLibrary)
|
||||
this.$eventHub.$on(LIBRARY_CHANGED, this.libraryChanged)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$eventHub.$off(SERIES_CHANGED, this.reloadSeries)
|
||||
this.$eventHub.$off(SERIES_ADDED, this.seriesChanged)
|
||||
this.$eventHub.$off(SERIES_CHANGED, this.seriesChanged)
|
||||
this.$eventHub.$off(SERIES_DELETED, this.seriesChanged)
|
||||
this.$eventHub.$off(LIBRARY_DELETED, this.libraryDeleted)
|
||||
this.$eventHub.$off(LIBRARY_CHANGED, this.reloadLibrary)
|
||||
this.$eventHub.$off(LIBRARY_CHANGED, this.libraryChanged)
|
||||
},
|
||||
async mounted() {
|
||||
this.$store.commit('setLibraryRoute', {id: this.libraryId, route: LIBRARY_ROUTE.BROWSE})
|
||||
|
|
@ -365,8 +370,8 @@ export default Vue.extend({
|
|||
})
|
||||
return validFilter
|
||||
},
|
||||
libraryDeleted(event: EventLibraryDeleted) {
|
||||
if (event.id === this.libraryId) {
|
||||
libraryDeleted(event: LibrarySseDto) {
|
||||
if (event.libraryId === this.libraryId) {
|
||||
this.$router.push({name: 'home'})
|
||||
} else if (this.libraryId === LIBRARIES_ALL) {
|
||||
this.loadLibrary(this.libraryId)
|
||||
|
|
@ -407,13 +412,13 @@ export default Vue.extend({
|
|||
|
||||
this.setWatches()
|
||||
},
|
||||
reloadSeries(event: EventSeriesChanged) {
|
||||
seriesChanged(event: SeriesSseDto) {
|
||||
if (this.libraryId === LIBRARIES_ALL || event.libraryId === this.libraryId) {
|
||||
this.loadPage(this.libraryId, this.page, this.sortActive)
|
||||
}
|
||||
},
|
||||
reloadLibrary(event: EventLibraryChanged) {
|
||||
if (this.libraryId === LIBRARIES_ALL || event.id === this.libraryId) {
|
||||
libraryChanged(event: LibrarySseDto) {
|
||||
if (this.libraryId === LIBRARIES_ALL || event.libraryId === this.libraryId) {
|
||||
this.loadLibrary(this.libraryId)
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -76,12 +76,20 @@
|
|||
<script lang="ts">
|
||||
import ItemBrowser from '@/components/ItemBrowser.vue'
|
||||
import ToolbarSticky from '@/components/bars/ToolbarSticky.vue'
|
||||
import {BOOK_CHANGED, READLIST_CHANGED, READLIST_DELETED} from '@/types/events'
|
||||
import {
|
||||
BOOK_CHANGED,
|
||||
BOOK_DELETED,
|
||||
READLIST_CHANGED,
|
||||
READLIST_DELETED,
|
||||
READPROGRESS_CHANGED,
|
||||
READPROGRESS_DELETED,
|
||||
} from '@/types/events'
|
||||
import Vue from 'vue'
|
||||
import ReadListActionsMenu from '@/components/menus/ReadListActionsMenu.vue'
|
||||
import BooksMultiSelectBar from '@/components/bars/BooksMultiSelectBar.vue'
|
||||
import {BookDto, ReadProgressUpdateDto} from '@/types/komga-books'
|
||||
import {ContextOrigin} from '@/types/context'
|
||||
import {BookSseDto, ReadListSseDto, ReadProgressSseDto} from "@/types/komga-sse";
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'BrowseReadList',
|
||||
|
|
@ -122,13 +130,19 @@ export default Vue.extend({
|
|||
},
|
||||
created () {
|
||||
this.$eventHub.$on(READLIST_CHANGED, this.readListChanged)
|
||||
this.$eventHub.$on(READLIST_DELETED, this.afterDelete)
|
||||
this.$eventHub.$on(BOOK_CHANGED, this.reloadBook)
|
||||
this.$eventHub.$on(READLIST_DELETED, this.readListDeleted)
|
||||
this.$eventHub.$on(BOOK_CHANGED, this.bookChanged)
|
||||
this.$eventHub.$on(BOOK_DELETED, this.bookChanged)
|
||||
this.$eventHub.$on(READPROGRESS_CHANGED, this.readProgressChanged)
|
||||
this.$eventHub.$on(READPROGRESS_DELETED, this.readProgressChanged)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$eventHub.$off(READLIST_CHANGED, this.readListChanged)
|
||||
this.$eventHub.$off(READLIST_DELETED, this.afterDelete)
|
||||
this.$eventHub.$off(BOOK_CHANGED, this.reloadBook)
|
||||
this.$eventHub.$off(READLIST_DELETED, this.readListDeleted)
|
||||
this.$eventHub.$off(BOOK_CHANGED, this.bookChanged)
|
||||
this.$eventHub.$off(BOOK_DELETED, this.bookChanged)
|
||||
this.$eventHub.$off(READPROGRESS_CHANGED, this.readProgressChanged)
|
||||
this.$eventHub.$off(READPROGRESS_DELETED, this.readProgressChanged)
|
||||
},
|
||||
mounted () {
|
||||
this.loadReadList(this.readListId)
|
||||
|
|
@ -150,11 +164,16 @@ export default Vue.extend({
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
readListChanged (event: EventReadListChanged) {
|
||||
if (event.id === this.readListId) {
|
||||
readListChanged (event: ReadListSseDto) {
|
||||
if (event.readListId === this.readListId) {
|
||||
this.loadReadList(this.readListId)
|
||||
}
|
||||
},
|
||||
readListDeleted (event: ReadListSseDto) {
|
||||
if (event.readListId === this.readListId) {
|
||||
this.$router.push({name: 'browse-readlists', params: {libraryId: 'all'}})
|
||||
}
|
||||
},
|
||||
async loadReadList (readListId: string) {
|
||||
this.$komgaReadLists.getOneReadList(readListId)
|
||||
.then(v => this.readList = v)
|
||||
|
|
@ -206,11 +225,11 @@ export default Vue.extend({
|
|||
editReadList () {
|
||||
this.$store.dispatch('dialogEditReadList', this.readList)
|
||||
},
|
||||
afterDelete () {
|
||||
this.$router.push({ name: 'browse-readlists', params: { libraryId: 'all' } })
|
||||
bookChanged (event: BookSseDto) {
|
||||
if (this.books.some(b => b.id === event.bookId)) this.loadReadList(this.readListId)
|
||||
},
|
||||
reloadBook (event: EventBookChanged) {
|
||||
if (this.books.some(b => b.id === event.id)) this.loadReadList(this.readListId)
|
||||
readProgressChanged(event: ReadProgressSseDto){
|
||||
if (this.books.some(b => b.id === event.bookId)) this.loadReadList(this.readListId)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -54,10 +54,11 @@ import ItemBrowser from '@/components/ItemBrowser.vue'
|
|||
import LibraryNavigation from '@/components/LibraryNavigation.vue'
|
||||
import LibraryActionsMenu from '@/components/menus/LibraryActionsMenu.vue'
|
||||
import PageSizeSelect from '@/components/PageSizeSelect.vue'
|
||||
import {LIBRARY_CHANGED, READLIST_CHANGED, READLIST_DELETED} from '@/types/events'
|
||||
import {LIBRARY_CHANGED, READLIST_ADDED, READLIST_CHANGED, READLIST_DELETED} from '@/types/events'
|
||||
import Vue from 'vue'
|
||||
import {Location} from 'vue-router'
|
||||
import {LIBRARIES_ALL, LIBRARY_ROUTE} from '@/types/library'
|
||||
import {LibrarySseDto} from "@/types/komga-sse";
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'BrowseReadLists',
|
||||
|
|
@ -87,11 +88,13 @@ export default Vue.extend({
|
|||
},
|
||||
},
|
||||
created () {
|
||||
this.$eventHub.$on(READLIST_ADDED, this.reloadElements)
|
||||
this.$eventHub.$on(READLIST_CHANGED, this.reloadElements)
|
||||
this.$eventHub.$on(READLIST_DELETED, this.reloadElements)
|
||||
this.$eventHub.$on(LIBRARY_CHANGED, this.reloadLibrary)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$eventHub.$off(READLIST_ADDED, this.reloadElements)
|
||||
this.$eventHub.$off(READLIST_CHANGED, this.reloadElements)
|
||||
this.$eventHub.$off(READLIST_DELETED, this.reloadElements)
|
||||
this.$eventHub.$off(LIBRARY_CHANGED, this.reloadLibrary)
|
||||
|
|
@ -179,8 +182,8 @@ export default Vue.extend({
|
|||
reloadElements () {
|
||||
this.loadLibrary(this.libraryId)
|
||||
},
|
||||
reloadLibrary (event: EventLibraryChanged) {
|
||||
if (event.id === this.libraryId) {
|
||||
reloadLibrary (event: LibrarySseDto) {
|
||||
if (event.libraryId === this.libraryId) {
|
||||
this.loadLibrary(this.libraryId)
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -381,7 +381,19 @@ import PageSizeSelect from '@/components/PageSizeSelect.vue'
|
|||
import {parseQueryParam, parseQueryParamAndFilter, parseQuerySort} from '@/functions/query-params'
|
||||
import {seriesFileUrl, seriesThumbnailUrl} from '@/functions/urls'
|
||||
import {ReadStatus, replaceCompositeReadStatus} from '@/types/enum-books'
|
||||
import {BOOK_CHANGED, LIBRARY_DELETED, READLIST_CHANGED, SERIES_CHANGED} from '@/types/events'
|
||||
import {
|
||||
BOOK_ADDED,
|
||||
BOOK_CHANGED,
|
||||
BOOK_DELETED,
|
||||
COLLECTION_ADDED,
|
||||
COLLECTION_CHANGED,
|
||||
COLLECTION_DELETED,
|
||||
LIBRARY_DELETED,
|
||||
READPROGRESS_CHANGED,
|
||||
READPROGRESS_DELETED,
|
||||
SERIES_CHANGED,
|
||||
SERIES_DELETED,
|
||||
} from '@/types/events'
|
||||
import Vue from 'vue'
|
||||
import {Location} from 'vue-router'
|
||||
import {AuthorDto, BookDto} from '@/types/komga-books'
|
||||
|
|
@ -397,6 +409,8 @@ import ReadMore from "@/components/ReadMore.vue";
|
|||
import {authorRoles, authorRolesSeries} from "@/types/author-roles";
|
||||
import VueHorizontal from "vue-horizontal";
|
||||
import RtlIcon from "@/components/RtlIcon.vue";
|
||||
import {throttle} from "lodash";
|
||||
import {BookSseDto, CollectionSseDto, LibrarySseDto, ReadProgressSseDto, SeriesSseDto} from "@/types/komga-sse";
|
||||
|
||||
const tags = require('language-tags')
|
||||
|
||||
|
|
@ -542,16 +556,30 @@ export default Vue.extend({
|
|||
},
|
||||
},
|
||||
created() {
|
||||
this.$eventHub.$on(SERIES_CHANGED, this.reloadSeries)
|
||||
this.$eventHub.$on(READLIST_CHANGED, this.reloadSeries)
|
||||
this.$eventHub.$on(BOOK_CHANGED, this.reloadBooks)
|
||||
this.$eventHub.$on(SERIES_CHANGED, this.seriesChanged)
|
||||
this.$eventHub.$on(SERIES_DELETED, this.seriesDeleted)
|
||||
this.$eventHub.$on(BOOK_ADDED, this.bookChanged)
|
||||
this.$eventHub.$on(BOOK_CHANGED, this.bookChanged)
|
||||
this.$eventHub.$on(BOOK_DELETED, this.bookChanged)
|
||||
this.$eventHub.$on(READPROGRESS_CHANGED, this.readProgressChanged)
|
||||
this.$eventHub.$on(READPROGRESS_DELETED, this.readProgressChanged)
|
||||
this.$eventHub.$on(LIBRARY_DELETED, this.libraryDeleted)
|
||||
this.$eventHub.$on(COLLECTION_ADDED, this.collectionChanged)
|
||||
this.$eventHub.$on(COLLECTION_CHANGED, this.collectionChanged)
|
||||
this.$eventHub.$on(COLLECTION_DELETED, this.collectionChanged)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$eventHub.$off(SERIES_CHANGED, this.reloadSeries)
|
||||
this.$eventHub.$off(READLIST_CHANGED, this.reloadSeries)
|
||||
this.$eventHub.$off(BOOK_CHANGED, this.reloadBooks)
|
||||
this.$eventHub.$off(SERIES_CHANGED, this.seriesChanged)
|
||||
this.$eventHub.$off(SERIES_DELETED, this.seriesDeleted)
|
||||
this.$eventHub.$off(BOOK_ADDED, this.bookChanged)
|
||||
this.$eventHub.$off(BOOK_CHANGED, this.bookChanged)
|
||||
this.$eventHub.$off(BOOK_DELETED, this.bookChanged)
|
||||
this.$eventHub.$off(READPROGRESS_CHANGED, this.readProgressChanged)
|
||||
this.$eventHub.$off(READPROGRESS_DELETED, this.readProgressChanged)
|
||||
this.$eventHub.$off(LIBRARY_DELETED, this.libraryDeleted)
|
||||
this.$eventHub.$off(COLLECTION_ADDED, this.collectionChanged)
|
||||
this.$eventHub.$off(COLLECTION_CHANGED, this.collectionChanged)
|
||||
this.$eventHub.$off(COLLECTION_DELETED, this.collectionChanged)
|
||||
},
|
||||
async mounted() {
|
||||
this.pageSize = this.$store.state.persistedState.browsingPageSize || this.pageSize
|
||||
|
|
@ -636,22 +664,41 @@ export default Vue.extend({
|
|||
|
||||
this.setWatches()
|
||||
},
|
||||
libraryDeleted(event: EventLibraryDeleted) {
|
||||
if (event.id === this.series.libraryId) {
|
||||
libraryDeleted(event: LibrarySseDto) {
|
||||
if (event.libraryId === this.series.libraryId) {
|
||||
this.$router.push({name: 'home'})
|
||||
}
|
||||
},
|
||||
reloadSeries(event: EventSeriesChanged) {
|
||||
if (event.id === this.seriesId) this.loadSeries(this.seriesId)
|
||||
seriesChanged(event: SeriesSseDto) {
|
||||
if (event.seriesId === this.seriesId)
|
||||
this.$komgaSeries.getOneSeries(this.seriesId)
|
||||
.then(v => this.series = v)
|
||||
},
|
||||
reloadBooks(event: EventBookChanged) {
|
||||
if (event.seriesId === this.seriesId) this.loadSeries(this.seriesId)
|
||||
seriesDeleted(event: SeriesSseDto) {
|
||||
if (event.seriesId === this.seriesId) {
|
||||
this.$router.push({name: 'browse-libraries', params: {libraryId: this.series.libraryId}})
|
||||
}
|
||||
},
|
||||
bookChanged(event: BookSseDto) {
|
||||
if (event.seriesId === this.seriesId) this.reloadPage()
|
||||
},
|
||||
readProgressChanged(event: ReadProgressSseDto) {
|
||||
if (this.books.some(b => b.id === event.bookId)) this.reloadPage()
|
||||
},
|
||||
collectionChanged(event: CollectionSseDto) {
|
||||
if (event.seriesIds.includes(this.seriesId) || this.collections.map(x => x.id).includes(event.collectionId)) {
|
||||
this.$komgaSeries.getCollections(this.seriesId)
|
||||
.then(v => this.collections = v)
|
||||
}
|
||||
},
|
||||
reloadPage: throttle(function (this: any) {
|
||||
this.loadPage(this.seriesId, this.page, this.sortActive)
|
||||
}, 5000),
|
||||
async loadSeries(seriesId: string) {
|
||||
this.$komgaSeries.getOneSeries(seriesId)
|
||||
.then(v => this.series = v)
|
||||
.then(v => this.series = v)
|
||||
this.$komgaSeries.getCollections(seriesId)
|
||||
.then(v => this.collections = v)
|
||||
.then(v => this.collections = v)
|
||||
|
||||
await this.loadPage(seriesId, this.page, this.sortActive)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@
|
|||
|
||||
</toolbar-sticky>
|
||||
|
||||
<library-navigation v-if="individualLibrary && $vuetify.breakpoint.name === 'xs'" :libraryId="libraryId" bottom-navigation/>
|
||||
<library-navigation v-if="individualLibrary && $vuetify.breakpoint.name === 'xs'" :libraryId="libraryId"
|
||||
bottom-navigation/>
|
||||
|
||||
<series-multi-select-bar
|
||||
v-model="selectedSeries"
|
||||
|
|
@ -134,10 +135,21 @@ import LibraryActionsMenu from '@/components/menus/LibraryActionsMenu.vue'
|
|||
import LibraryNavigation from '@/components/LibraryNavigation.vue'
|
||||
import {ReadStatus} from '@/types/enum-books'
|
||||
import {BookDto} from '@/types/komga-books'
|
||||
import {BOOK_CHANGED, LIBRARY_DELETED, SERIES_CHANGED} from '@/types/events'
|
||||
import {
|
||||
BOOK_ADDED,
|
||||
BOOK_CHANGED,
|
||||
BOOK_DELETED,
|
||||
READPROGRESS_CHANGED,
|
||||
READPROGRESS_DELETED,
|
||||
SERIES_ADDED,
|
||||
SERIES_CHANGED,
|
||||
SERIES_DELETED,
|
||||
} from '@/types/events'
|
||||
import Vue from 'vue'
|
||||
import {SeriesDto} from "@/types/komga-series";
|
||||
import {LIBRARIES_ALL, LIBRARY_ROUTE} from "@/types/library";
|
||||
import {throttle} from 'lodash'
|
||||
import {BookSseDto, ReadProgressSseDto, SeriesSseDto} from "@/types/komga-sse";
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'Dashboard',
|
||||
|
|
@ -164,17 +176,30 @@ export default Vue.extend({
|
|||
}
|
||||
},
|
||||
created() {
|
||||
this.$eventHub.$on(LIBRARY_DELETED, this.libraryDeleted)
|
||||
this.$eventHub.$on(SERIES_CHANGED, this.reload)
|
||||
this.$eventHub.$on(BOOK_CHANGED, this.reload)
|
||||
this.$eventHub.$on(SERIES_ADDED, this.seriesChanged)
|
||||
this.$eventHub.$on(SERIES_CHANGED, this.seriesChanged)
|
||||
this.$eventHub.$on(SERIES_DELETED, this.seriesChanged)
|
||||
this.$eventHub.$on(BOOK_ADDED, this.bookChanged)
|
||||
this.$eventHub.$on(BOOK_CHANGED, this.bookChanged)
|
||||
this.$eventHub.$on(BOOK_DELETED, this.bookChanged)
|
||||
this.$eventHub.$on(READPROGRESS_CHANGED, this.readProgressChanged)
|
||||
this.$eventHub.$on(READPROGRESS_DELETED, this.readProgressChanged)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$eventHub.$off(LIBRARY_DELETED, this.libraryDeleted)
|
||||
this.$eventHub.$off(SERIES_CHANGED, this.reload)
|
||||
this.$eventHub.$off(BOOK_CHANGED, this.reload)
|
||||
this.$eventHub.$off(SERIES_ADDED, this.seriesChanged)
|
||||
this.$eventHub.$off(SERIES_CHANGED, this.seriesChanged)
|
||||
this.$eventHub.$off(SERIES_DELETED, this.seriesChanged)
|
||||
this.$eventHub.$off(BOOK_ADDED, this.bookChanged)
|
||||
this.$eventHub.$off(BOOK_CHANGED, this.bookChanged)
|
||||
this.$eventHub.$off(BOOK_DELETED, this.bookChanged)
|
||||
this.$eventHub.$off(READPROGRESS_CHANGED, this.readProgressChanged)
|
||||
this.$eventHub.$off(READPROGRESS_DELETED, this.readProgressChanged)
|
||||
},
|
||||
mounted() {
|
||||
if(this.individualLibrary) this.$store.commit('setLibraryRoute', {id: this.libraryId, route: LIBRARY_ROUTE.RECOMMENDED})
|
||||
if (this.individualLibrary) this.$store.commit('setLibraryRoute', {
|
||||
id: this.libraryId,
|
||||
route: LIBRARY_ROUTE.RECOMMENDED,
|
||||
})
|
||||
this.reload()
|
||||
},
|
||||
props: {
|
||||
|
|
@ -193,6 +218,12 @@ export default Vue.extend({
|
|||
libraryId(val) {
|
||||
this.loadAll(val)
|
||||
},
|
||||
'$store.state.komgaLibraries.libraries': {
|
||||
handler(val){
|
||||
if(val.length === 0) this.$router.push({name: 'welcome'})
|
||||
else this.reload()
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
fixedCardWidth(): number {
|
||||
|
|
@ -213,16 +244,24 @@ export default Vue.extend({
|
|||
getRequestLibraryId(libraryId: string): string | undefined {
|
||||
return libraryId !== LIBRARIES_ALL ? libraryId : undefined
|
||||
},
|
||||
libraryDeleted() {
|
||||
if (this.$store.state.komgaLibraries.libraries.length === 0) {
|
||||
this.$router.push({name: 'welcome'})
|
||||
} else {
|
||||
seriesChanged(event: SeriesSseDto) {
|
||||
if (this.libraryId === LIBRARIES_ALL || event.libraryId === this.libraryId) {
|
||||
this.reload()
|
||||
}
|
||||
},
|
||||
reload() {
|
||||
this.loadAll(this.libraryId)
|
||||
bookChanged(event: BookSseDto){
|
||||
if (this.libraryId === LIBRARIES_ALL || event.libraryId === this.libraryId) {
|
||||
this.reload()
|
||||
}
|
||||
},
|
||||
readProgressChanged(event: ReadProgressSseDto){
|
||||
if (this.inProgressBooks.some(b => b.id === event.bookId)) this.reload()
|
||||
else if (this.latestBooks.some(b => b.id === event.bookId)) this.reload()
|
||||
else if (this.onDeckBooks.some(b => b.id === event.bookId)) this.reload()
|
||||
},
|
||||
reload: throttle(function(this: any) {
|
||||
this.loadAll(this.libraryId)
|
||||
}, 5000),
|
||||
loadAll(libraryId: string) {
|
||||
this.library = this.getLibraryLazy(libraryId)
|
||||
this.selectedSeries = []
|
||||
|
|
|
|||
|
|
@ -20,6 +20,21 @@
|
|||
Komga
|
||||
</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
|
||||
<v-tooltip left>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-progress-linear
|
||||
:active="taskCount > 0"
|
||||
indeterminate
|
||||
absolute
|
||||
bottom
|
||||
height="2"
|
||||
color="secondary"
|
||||
v-on="on"
|
||||
/>
|
||||
</template>
|
||||
<span>{{ $tc('common.pending_tasks', taskCount) }}</span>
|
||||
</v-tooltip>
|
||||
</v-list-item>
|
||||
|
||||
<v-divider/>
|
||||
|
|
@ -141,6 +156,7 @@
|
|||
|
||||
<v-main class="fill-height">
|
||||
<dialogs/>
|
||||
<toaster/>
|
||||
<router-view/>
|
||||
</v-main>
|
||||
</div>
|
||||
|
|
@ -152,11 +168,12 @@ import LibraryActionsMenu from '@/components/menus/LibraryActionsMenu.vue'
|
|||
import SearchBox from '@/components/SearchBox.vue'
|
||||
import {Theme} from '@/types/themes'
|
||||
import Vue from 'vue'
|
||||
import {LIBRARIES_ALL} from "@/types/library";
|
||||
import {LIBRARIES_ALL} from "@/types/library"
|
||||
import Toaster from "@/components/Toaster.vue"
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'home',
|
||||
components: {LibraryActionsMenu, SearchBox, Dialogs},
|
||||
components: {Toaster, LibraryActionsMenu, SearchBox, Dialogs},
|
||||
data: function () {
|
||||
return {
|
||||
LIBRARIES_ALL,
|
||||
|
|
@ -171,6 +188,9 @@ export default Vue.extend({
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
taskCount(): number {
|
||||
return this.$store.state.komgaSse.taskCount
|
||||
},
|
||||
libraries(): LibraryDto[] {
|
||||
return this.$store.state.komgaLibraries.libraries
|
||||
},
|
||||
|
|
|
|||
|
|
@ -110,15 +110,20 @@ import ItemBrowser from '@/components/ItemBrowser.vue'
|
|||
import {BookDto} from '@/types/komga-books'
|
||||
import {
|
||||
BOOK_CHANGED,
|
||||
BOOK_DELETED,
|
||||
COLLECTION_CHANGED,
|
||||
COLLECTION_DELETED,
|
||||
LIBRARY_DELETED,
|
||||
READLIST_CHANGED,
|
||||
READLIST_DELETED,
|
||||
READPROGRESS_CHANGED,
|
||||
READPROGRESS_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";
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'Search',
|
||||
|
|
@ -144,21 +149,29 @@ export default Vue.extend({
|
|||
},
|
||||
created () {
|
||||
this.$eventHub.$on(LIBRARY_DELETED, this.reloadResults)
|
||||
this.$eventHub.$on(SERIES_CHANGED, this.reloadResults)
|
||||
this.$eventHub.$on(BOOK_CHANGED, this.reloadResults)
|
||||
this.$eventHub.$on(COLLECTION_CHANGED, this.reloadResults)
|
||||
this.$eventHub.$on(COLLECTION_DELETED, this.reloadResults)
|
||||
this.$eventHub.$on(READLIST_CHANGED, this.reloadResults)
|
||||
this.$eventHub.$on(READLIST_DELETED, this.reloadResults)
|
||||
this.$eventHub.$on(SERIES_CHANGED, this.seriesChanged)
|
||||
this.$eventHub.$on(SERIES_DELETED, this.seriesChanged)
|
||||
this.$eventHub.$on(BOOK_CHANGED, this.bookChanged)
|
||||
this.$eventHub.$on(BOOK_DELETED, this.bookChanged)
|
||||
this.$eventHub.$on(COLLECTION_CHANGED, this.collectionChanged)
|
||||
this.$eventHub.$on(COLLECTION_DELETED, this.collectionChanged)
|
||||
this.$eventHub.$on(READLIST_CHANGED, this.readListChanged)
|
||||
this.$eventHub.$on(READLIST_DELETED, this.readListChanged)
|
||||
this.$eventHub.$on(READPROGRESS_CHANGED, this.readProgressChanged)
|
||||
this.$eventHub.$on(READPROGRESS_DELETED, this.readProgressChanged)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$eventHub.$off(LIBRARY_DELETED, this.reloadResults)
|
||||
this.$eventHub.$off(SERIES_CHANGED, this.reloadResults)
|
||||
this.$eventHub.$off(BOOK_CHANGED, this.reloadResults)
|
||||
this.$eventHub.$off(COLLECTION_CHANGED, this.reloadResults)
|
||||
this.$eventHub.$off(COLLECTION_DELETED, this.reloadResults)
|
||||
this.$eventHub.$off(READLIST_CHANGED, this.reloadResults)
|
||||
this.$eventHub.$off(READLIST_DELETED, this.reloadResults)
|
||||
this.$eventHub.$off(SERIES_CHANGED, this.seriesChanged)
|
||||
this.$eventHub.$off(SERIES_DELETED, this.seriesChanged)
|
||||
this.$eventHub.$off(BOOK_CHANGED, this.bookChanged)
|
||||
this.$eventHub.$off(BOOK_DELETED, this.bookChanged)
|
||||
this.$eventHub.$off(COLLECTION_CHANGED, this.collectionChanged)
|
||||
this.$eventHub.$off(COLLECTION_DELETED, this.collectionChanged)
|
||||
this.$eventHub.$off(READLIST_CHANGED, this.readListChanged)
|
||||
this.$eventHub.$off(READLIST_DELETED, this.readListChanged)
|
||||
this.$eventHub.$off(READPROGRESS_CHANGED, this.readProgressChanged)
|
||||
this.$eventHub.$off(READPROGRESS_DELETED, this.readProgressChanged)
|
||||
},
|
||||
watch: {
|
||||
'$route.query.q': {
|
||||
|
|
@ -199,6 +212,31 @@ export default Vue.extend({
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
seriesChanged(event: SeriesSseDto){
|
||||
if(this.series.map(x => x.id).includes(event.seriesId)){
|
||||
this.reloadResults()
|
||||
}
|
||||
},
|
||||
bookChanged(event: BookSseDto){
|
||||
if(this.books.map(x => x.id).includes(event.bookId)){
|
||||
this.reloadResults()
|
||||
}
|
||||
},
|
||||
readProgressChanged(event: ReadProgressSseDto){
|
||||
if(this.books.map(x => x.id).includes(event.bookId)){
|
||||
this.reloadResults()
|
||||
}
|
||||
},
|
||||
collectionChanged (event: CollectionSseDto) {
|
||||
if (this.collections.map(x => x.id).includes(event.collectionId)) {
|
||||
this.reloadResults()
|
||||
}
|
||||
},
|
||||
readListChanged (event: ReadListSseDto) {
|
||||
if (this.readLists.map(x => x.id).includes(event.readListId)) {
|
||||
this.reloadResults()
|
||||
}
|
||||
},
|
||||
singleEditSeries (series: SeriesDto) {
|
||||
this.$store.dispatch('dialogUpdateSeries', series)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="ma-3">
|
||||
<div class="pa-6">
|
||||
<v-row align="center" justify="center">
|
||||
<v-img src="../assets/logo.svg"
|
||||
max-width="400"
|
||||
|
|
@ -16,7 +16,6 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {LIBRARY_ADDED} from '@/types/events'
|
||||
import Vue from 'vue'
|
||||
|
||||
export default Vue.extend({
|
||||
|
|
@ -26,21 +25,19 @@ export default Vue.extend({
|
|||
return this.$store.getters.meAdmin
|
||||
},
|
||||
},
|
||||
created () {
|
||||
this.$eventHub.$on(LIBRARY_ADDED, this.libraryAdded)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$eventHub.$off(LIBRARY_ADDED, this.libraryAdded)
|
||||
},
|
||||
mounted () {
|
||||
if (this.$store.state.komgaLibraries.libraries.length !== 0) {
|
||||
this.$router.push({ name: 'dashboard' })
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
libraryAdded () {
|
||||
this.$router.push({ name: 'dashboard' })
|
||||
watch: {
|
||||
'$store.state.komgaLibraries.libraries': {
|
||||
handler(val){
|
||||
if(val.length !== 0) this.$router.push({name: 'dashboard'})
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
addLibrary () {
|
||||
this.$store.dispatch('dialogAddLibrary')
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue