Skip to content

Commit

Permalink
feat(webui): prefill author selection when bulk editing books
Browse files Browse the repository at this point in the history
  • Loading branch information
noaione committed Nov 28, 2023
1 parent 6e5e7a9 commit df22609
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 11 deletions.
25 changes: 21 additions & 4 deletions komga-webui/src/components/dialogs/EditBooksDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -481,6 +481,7 @@ export default Vue.extend({
authorSearch: [],
authorSearchResults: [] as string[],
tagsAvailable: [] as string[],
isMultiBookAuthorDirty: false, // workaround for author consistency in bulk mode
}
},
props: {
Expand Down Expand Up @@ -626,11 +627,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 = []
Expand Down Expand Up @@ -664,7 +681,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})),
Expand Down
19 changes: 15 additions & 4 deletions komga-webui/src/components/dialogs/EditOneshotDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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 = []
Expand Down Expand Up @@ -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})),
Expand Down
24 changes: 21 additions & 3 deletions komga-webui/src/functions/authors.ts
Original file line number Diff line number Diff line change
@@ -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
}, {})
}

0 comments on commit df22609

Please sign in to comment.