@@ -40,7 +40,7 @@
diff --git a/client/components/widgets/ItemSlider.vue b/client/components/widgets/ItemSlider.vue
index 15051fb1..fd14a202 100644
--- a/client/components/widgets/ItemSlider.vue
+++ b/client/components/widgets/ItemSlider.vue
@@ -65,7 +65,7 @@ export default {
},
authors: {
component: 'cards-author-card',
- itemPropName: 'author',
+ itemPropName: 'author-mount',
itemIdFunc: (item) => item.id
},
narrators: {
diff --git a/client/mixins/bookshelfCardsHelpers.js b/client/mixins/bookshelfCardsHelpers.js
index 31fa76ba..802b2cc8 100644
--- a/client/mixins/bookshelfCardsHelpers.js
+++ b/client/mixins/bookshelfCardsHelpers.js
@@ -4,6 +4,7 @@ import LazySeriesCard from '@/components/cards/LazySeriesCard'
import LazyCollectionCard from '@/components/cards/LazyCollectionCard'
import LazyPlaylistCard from '@/components/cards/LazyPlaylistCard'
import LazyAlbumCard from '@/components/cards/LazyAlbumCard'
+import AuthorCard from '@/components/cards/AuthorCard'
export default {
data() {
@@ -20,6 +21,7 @@ export default {
if (this.entityName === 'collections') return Vue.extend(LazyCollectionCard)
if (this.entityName === 'playlists') return Vue.extend(LazyPlaylistCard)
if (this.entityName === 'albums') return Vue.extend(LazyAlbumCard)
+ if (this.entityName === 'authors') return Vue.extend(AuthorCard)
return Vue.extend(LazyBookCard)
},
getComponentName() {
@@ -27,6 +29,7 @@ export default {
if (this.entityName === 'collections') return 'cards-lazy-collection-card'
if (this.entityName === 'playlists') return 'cards-lazy-playlist-card'
if (this.entityName === 'albums') return 'cards-lazy-album-card'
+ if (this.entityName === 'authors') return 'cards-author-card'
return 'cards-lazy-book-card'
},
async setCardSize() {
@@ -46,13 +49,14 @@ export default {
props.orderBy = this.seriesSortBy
}
const instance = new ComponentClass({
- propsData: props
+ propsData: props,
+ parent: this
})
instance.$mount()
this.resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
- this.cardWidth = entry.contentRect.width
- this.cardHeight = entry.contentRect.height
+ this.cardWidth = entry.borderBoxSize[0].inlineSize
+ this.cardHeight = entry.borderBoxSize[0].blockSize
this.resizeObserver.disconnect()
this.$refs.bookshelf.removeChild(instance.$el)
}
@@ -72,7 +76,7 @@ export default {
})
const timeAfter = performance.now()
},
- async mountEntityCard(index) {
+ mountEntityCard(index) {
var shelf = Math.floor(index / this.entitiesPerShelf)
var shelfEl = document.getElementById(`shelf-${shelf}`)
if (!shelfEl) {
@@ -114,6 +118,7 @@ export default {
const _this = this
const instance = new ComponentClass({
propsData: props,
+ parent: this,
created() {
this.$on('edit', (entity) => {
if (_this.editEntity) _this.editEntity(entity)
diff --git a/client/pages/author/_id.vue b/client/pages/author/_id.vue
index 0618f8a7..950312ab 100644
--- a/client/pages/author/_id.vue
+++ b/client/pages/author/_id.vue
@@ -53,7 +53,7 @@ export default {
})
if (!author) {
- return redirect(`/library/${store.state.libraries.currentLibraryId}/authors`)
+ return redirect(`/library/${store.state.libraries.currentLibraryId}/bookshelf/authors`)
}
if (store.state.libraries.currentLibraryId !== author.libraryId || !store.state.libraries.filterData) {
@@ -109,7 +109,7 @@ export default {
authorRemoved(author) {
if (author.id === this.author.id) {
console.warn('Author was removed')
- this.$router.replace(`/library/${this.currentLibraryId}/authors`)
+ this.$router.replace(`/library/${this.currentLibraryId}/bookshelf/authors`)
}
}
},
diff --git a/client/pages/library/_library/authors/index.vue b/client/pages/library/_library/authors/index.vue
deleted file mode 100644
index 9820fbce..00000000
--- a/client/pages/library/_library/authors/index.vue
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
-
-
-
diff --git a/client/pages/library/_library/bookshelf/_id.vue b/client/pages/library/_library/bookshelf/_id.vue
index 7037a012..72cfba4a 100644
--- a/client/pages/library/_library/bookshelf/_id.vue
+++ b/client/pages/library/_library/bookshelf/_id.vue
@@ -27,7 +27,7 @@ export default {
// Redirect podcast libraries
const library = libraryData.library
- if (library.mediaType === 'podcast' && (params.id === 'collections' || params.id === 'series')) {
+ if (library.mediaType === 'podcast' && (params.id === 'collections' || params.id === 'series' || params.id === 'authors')) {
return redirect(`/library/${libraryId}`)
}
diff --git a/client/strings/en-us.json b/client/strings/en-us.json
index a6026ab4..862ed117 100644
--- a/client/strings/en-us.json
+++ b/client/strings/en-us.json
@@ -920,6 +920,7 @@
"ToastLibraryScanFailedToStart": "Failed to start scan",
"ToastLibraryScanStarted": "Library scan started",
"ToastLibraryUpdateSuccess": "Library \"{0}\" updated",
+ "ToastMatchAllAuthorsFailed": "Failed to match all authors",
"ToastNameEmailRequired": "Name and email are required",
"ToastNameRequired": "Name is required",
"ToastNewUserCreatedFailed": "Failed to create account: \"{0}\"",
diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js
index 7705428b..927f9a94 100644
--- a/server/controllers/LibraryController.js
+++ b/server/controllers/LibraryController.js
@@ -873,8 +873,40 @@ class LibraryController {
* @param {Response} res
*/
async getAuthors(req, res) {
+ const isPaginated = req.query.limit && !isNaN(req.query.limit) && req.query.page && !isNaN(req.query.page)
+
+ const payload = {
+ results: [],
+ total: 0,
+ limit: isPaginated ? Number(req.query.limit) : 0,
+ page: isPaginated ? Number(req.query.page) : 0,
+ sortBy: req.query.sort,
+ sortDesc: req.query.desc === '1',
+ filterBy: req.query.filter,
+ minified: req.query.minified === '1',
+ include: req.query.include
+ }
+
+ // create order, limit and offset for pagination
+ let offset = isPaginated ? payload.page * payload.limit : undefined
+ let limit = isPaginated ? payload.limit : undefined
+ let order = undefined
+ const direction = payload.sortDesc ? 'DESC' : 'ASC'
+ if (payload.sortBy === 'name') {
+ order = [[Sequelize.literal('name COLLATE NOCASE'), direction]]
+ } else if (payload.sortBy === 'lastFirst') {
+ order = [[Sequelize.literal('lastFirst COLLATE NOCASE'), direction]]
+ } else if (payload.sortBy === 'addedAt') {
+ order = [['createdAt', direction]]
+ } else if (payload.sortBy === 'updatedAt') {
+ order = [['updatedAt', direction]]
+ } else if (payload.sortBy === 'numBooks') {
+ offset = undefined
+ limit = undefined
+ }
+
const { bookWhere, replacements } = libraryItemsBookFilters.getUserPermissionBookWhereQuery(req.user)
- const authors = await Database.authorModel.findAll({
+ const { rows: authors, count } = await Database.authorModel.findAndCountAll({
where: {
libraryId: req.library.id
},
@@ -888,10 +920,13 @@ class LibraryController {
attributes: []
}
},
- order: [[Sequelize.literal('name COLLATE NOCASE'), 'ASC']]
+ order: order,
+ limit: limit,
+ offset: offset,
+ distinct: true
})
- const oldAuthors = []
+ let oldAuthors = []
for (const author of authors) {
const oldAuthor = author.toOldJSONExpanded(author.books.length)
@@ -899,9 +934,25 @@ class LibraryController {
oldAuthors.push(oldAuthor)
}
- res.json({
- authors: oldAuthors
- })
+ // numBooks sort is handled post-query
+ if (payload.sortBy === 'numBooks') {
+ oldAuthors.sort((a, b) => (payload.sortDesc ? b.numBooks - a.numBooks : a.numBooks - b.numBooks))
+ if (isPaginated) {
+ const startIndex = payload.page * payload.limit
+ const endIndex = startIndex + payload.limit
+ oldAuthors = oldAuthors.slice(startIndex, endIndex)
+ }
+ }
+
+ payload.results = oldAuthors
+ if (isPaginated) {
+ payload.total = count
+ res.json(payload)
+ } else {
+ res.json({
+ authors: payload.results
+ })
+ }
}
/**