From 378f99b5585692012bf84acd22b195754f6cc3ff Mon Sep 17 00:00:00 2001 From: Gauthier Roebroeck Date: Mon, 20 Jan 2025 15:41:11 +0800 Subject: [PATCH] feat(webui): allow readlist import if duplicates are present Closes: #1671 --- komga-webui/src/locales/en.json | 1 + komga-webui/src/views/ImportReadList.vue | 44 +++++++++++++++++------- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/komga-webui/src/locales/en.json b/komga-webui/src/locales/en.json index 673a64a7ce..a8f1dc0947 100644 --- a/komga-webui/src/locales/en.json +++ b/komga-webui/src/locales/en.json @@ -283,6 +283,7 @@ "comicrack_preambule_html": "You can import existing ComicRack Reading Lists in .cbl format.
Komga will try to match the provided series and book number with series and books in your libraries.", "dialog_confirmation": { "body": "{unmatched} / {total} books are unmatched", + "body2": "{duplicates} / {total} books are duplicated", "create": "Create anyway", "title": "Some books are not matched" }, diff --git a/komga-webui/src/views/ImportReadList.vue b/komga-webui/src/views/ImportReadList.vue index bf868613e2..d92bd6b572 100644 --- a/komga-webui/src/views/ImportReadList.vue +++ b/komga-webui/src/views/ImportReadList.vue @@ -105,7 +105,7 @@ @@ -128,11 +128,6 @@ import {ERROR, NOTIFICATION, NotificationEvent} from '@/types/events' import {helpers, required} from 'vuelidate/lib/validators' import ConfirmationDialog from '@/components/dialogs/ConfirmationDialog.vue' -function duplicateBooks(this: any, value: any[]) { - const ids = value.filter(Boolean).map(b => b.bookId) - return ids.length === [...new Set(ids)].length -} - function validName(this: any, value: string) { return !helpers.req(value) || !this.readLists.some((e: ReadListDto) => e.name.toLowerCase() === value.toLowerCase()) } @@ -163,15 +158,37 @@ export default Vue.extend({ name: {required, validName}, ordered: {}, summary: {}, - books: {duplicateBooks}, + books: {}, }, }, computed: { + totalCount(): number { + return this.result!!.requests.length + }, + matchedCount(): number { + return this.form.books.filter(Boolean).length + }, + unmatchedCount(): number { + return this.totalCount - this.matchedCount + }, + duplicatesCount(): number { + return this.form.books.filter(this.isDuplicateBook).length + }, missingDialogText(): string { - const total = this.result!!.requests.length - const matched = this.form.books.filter(Boolean).length - const unmatched = total - matched - return this.$t('data_import.dialog_confirmation.body', {unmatched: unmatched, total: total}).toString() + let s = '' + if (this.unmatchedCount > 0) + s += this.$t('data_import.dialog_confirmation.body', { + unmatched: this.unmatchedCount, + total: this.totalCount, + }).toString() + if (this.duplicatesCount > 0) { + if (s !== '') s += '

' + s += this.$t('data_import.dialog_confirmation.body2', { + duplicates: this.duplicatesCount, + total: this.totalCount, + }).toString() + } + return s }, importRules(): any { return [ @@ -193,6 +210,7 @@ export default Vue.extend({ }, methods: { isDuplicateBook(book?: ReadListRequestBookMatchBookDto): boolean { + if (book == undefined) return false return this.form.books.filter((b) => b?.bookId === book?.bookId).length > 1 }, isErrorBook(series?: ReadListRequestBookMatchSeriesDto, book?: ReadListRequestBookMatchBookDto): string { @@ -234,14 +252,14 @@ export default Vue.extend({ if (this.$v.$invalid) return undefined return { name: this.form.name, - bookIds: this.form.books.map(b => b?.bookId).filter(Boolean) as string[], + bookIds: [...new Set(this.form.books.map(b => b?.bookId).filter(Boolean))] as string[], ordered: this.form.ordered, summary: this.form.summary, } }, async create(bypassMissing: boolean) { if (!this.creationFinished) { - if (!bypassMissing && this.form.books.filter(Boolean).length !== this.result?.requests.length) { + if (!bypassMissing && (this.duplicatesCount > 0 || this.unmatchedCount > 0)) { this.modalConfirmation = true return }