feat(webui): prefill author selection when bulk editing books

This commit is contained in:
noaione 2023-01-19 14:49:20 +08:00
parent d1475864af
commit 5b56f20185
No known key found for this signature in database
GPG key ID: C6AA2F30908B72EC
3 changed files with 57 additions and 11 deletions

View file

@ -425,7 +425,7 @@
</template>
<script lang="ts">
import {groupAuthorsByRole} from '@/functions/authors'
import {buildManyAuthorsByRole, groupAuthorsByRole} from '@/functions/authors'
import {authorRoles} from '@/types/author-roles'
import Vue from 'vue'
import {helpers, requiredIf} from 'vuelidate/lib/validators'
@ -482,6 +482,7 @@ export default Vue.extend({
authorSearch: [],
authorSearchResults: [] as string[],
tagsAvailable: [] as string[],
isMultiBookAuthorDirty: false, // workaround for author consistency in bulk mode
}
},
props: {
@ -627,11 +628,27 @@ export default Vue.extend({
if (Array.isArray(books) && books.length === 0) return
else if (this.$_.isEmpty(books)) return
if (Array.isArray(books) && books.length > 0) {
this.form.authors = {}
this.form.authors = buildManyAuthorsByRole(books)
this.form.links = []
const currentRoles = this.$_.keys(this.form.authors)
// Use authorRoles from computed value, so we can extend if needed
const ignoreRoles = this.authorRoles.map(r => r.value)
.filter((r) => !this.customRoles.includes(r)) // remove old custom roles
.filter((r) => !authorRoles.includes(r)) // remove native roles
this.customRoles = currentRoles.filter((r) => !ignoreRoles.includes(r)) // add new custom roles
let forceAuthorLock = false
for (const book of books) {
const bookAuthor = groupAuthorsByRole(book.metadata.authors)
if (!this.$_.isEqual(bookAuthor, this.form.authors)) {
this.isMultiBookAuthorDirty = true
forceAuthorLock = true
break
}
}
const authorsLock = this.$_.uniq(books.map(x => x.metadata.authorsLock))
this.form.authorsLock = authorsLock.length > 1 ? false : authorsLock[0]
this.form.authorsLock = authorsLock.length > 1 ? false : authorsLock[0] || forceAuthorLock
this.form.tags = []
@ -665,7 +682,7 @@ export default Vue.extend({
tagsLock: this.form.tagsLock,
}
if (this.$v.form?.authors?.$dirty) {
if (this.$v.form?.authors?.$dirty || this.isMultiBookAuthorDirty) {
this.$_.merge(metadata, {
authors: this.$_.keys(this.form.authors).flatMap((role: string) =>
this.$_.get(this.form.authors, role).map((name: string) => ({name: name, role: role})),

View file

@ -573,7 +573,7 @@ import {isMatch} from 'date-fns'
import IsbnVerify from '@saekitominaga/isbn-verify'
import {debounce} from 'lodash'
import {authorRoles} from '@/types/author-roles'
import {groupAuthorsByRole} from '@/functions/authors'
import {buildManyAuthorsByRole, groupAuthorsByRole} from '@/functions/authors'
const tags = require('language-tags')
@ -644,6 +644,7 @@ export default Vue.extend({
genresAvailable: [] as string[],
tagsAvailable: [] as string[],
sharingLabelsAvailable: [] as string[],
isMultiBookAuthorDirty: false, // workaround for author consistency in bulk mode
}
},
props: {
@ -880,10 +881,20 @@ export default Vue.extend({
this.form.series.sharingLabelsLock = sharingLabelsLock.length > 1 ? false : sharingLabelsLock[0]
this.form.book.links = []
this.form.book.authors = {}
this.form.book.authors = buildManyAuthorsByRole(oneshots.map(x => x.book))
let forceAuthorLock = false
for (const oneshot of oneshots) {
const bookAuthor = groupAuthorsByRole(oneshot.book.metadata.authors)
if (!this.$_.isEqual(bookAuthor, this.form.book.authors)) {
this.isMultiBookAuthorDirty = true
forceAuthorLock = true
break
}
}
const authorsLock = this.$_.uniq(oneshots.map(x => x.book.metadata.authorsLock))
this.form.book.authorsLock = authorsLock.length > 1 ? false : authorsLock[0]
this.form.book.authorsLock = authorsLock.length > 1 ? false : authorsLock[0] || forceAuthorLock
} else {
this.form.series.genres = []
this.form.series.sharingLabels = []
@ -925,7 +936,7 @@ export default Vue.extend({
tagsLock: this.form.book.tagsLock,
}
if (this.$v.form?.book?.authors?.$dirty) {
if (this.$v.form?.book?.authors?.$dirty || this.isMultiBookAuthorDirty) {
this.$_.merge(metadataBook, {
authors: this.$_.keys(this.form.book.authors).flatMap((role: string) =>
this.$_.get(this.form.book.authors, role).map((name: string) => ({name: name, role: role})),

View file

@ -1,8 +1,26 @@
import {groupBy, mapValues} from 'lodash'
import {AuthorDto} from '@/types/komga-books'
import {flatMap, groupBy, intersection, mapValues, reduce, uniq} from 'lodash'
import {AuthorDto, BookDto} from '@/types/komga-books'
type AuthorsByRole = {[role: string]: string[]}
// return an object where keys are roles, and values are string[]
export function groupAuthorsByRole (authors: AuthorDto[]): any {
export function groupAuthorsByRole (authors: AuthorDto[]): AuthorsByRole {
return mapValues(groupBy(authors, 'role'),
authors => authors.map((author: AuthorDto) => author.name))
}
// create an object where keys are roles and values are arrays of authors
// we're using intersection to only include authors that are present on all books of each roles
export function buildManyAuthorsByRole(books: BookDto[]): AuthorsByRole {
const allAuthorsByRoles = books.map(book => groupAuthorsByRole(book.metadata.authors))
const roleKeys = uniq(flatMap(allAuthorsByRoles, Object.keys))
return reduce(roleKeys, (acc: {[key: string]: string[]}, key) => {
const values = allAuthorsByRoles.map(authorsByRole => authorsByRole[key] || [])
const intersect = intersection(...values.filter(arr => arr.length > 0))
if (intersect.length > 0) {
acc[key] = intersect
}
return acc
}, {})
}