diff --git a/client/assets/app.css b/client/assets/app.css index 2a000975..6decbb29 100644 --- a/client/assets/app.css +++ b/client/assets/app.css @@ -42,32 +42,17 @@ height: 8px; } -/* ::-webkit-scrollbar:horizontal { */ -/* height: 16px; */ -/* height: 24px; -} */ /* Track */ ::-webkit-scrollbar-track { background-color: rgba(0, 0, 0, 0); } -/* ::-webkit-scrollbar-track:horizontal { */ -/* background: rgb(149, 119, 90); */ -/* background: linear-gradient(180deg, rgba(149, 119, 90, 1) 0%, rgba(103, 70, 37, 1) 17%, rgba(103, 70, 37, 1) 88%, rgba(71, 48, 25, 1) 100%); */ -/* background: linear-gradient(180deg, rgb(117, 88, 60) 0%, rgb(65, 41, 17) 17%, rgb(71, 43, 15) 88%, rgb(3, 2, 1) 100%); - box-shadow: 2px 14px 8px #111111aa; -} */ /* Handle */ ::-webkit-scrollbar-thumb { background: #855620; border-radius: 4px; } -/* ::-webkit-scrollbar-thumb:horizontal { */ -/* background: linear-gradient(180deg, rgba(149, 119, 90, 1) 0%, rgba(103, 70, 37, 1) 17%, rgba(103, 70, 37, 1) 88%, rgba(71, 48, 25, 1) 100%); */ -/* box-shadow: 2px 14px 8px #111111aa; - border-radius: 4px; -} */ /* Handle on hover */ ::-webkit-scrollbar-thumb:hover { background: #704922; @@ -78,6 +63,13 @@ opacity: 0; } +.no-scroll { + -ms-overflow-style: none; + /* IE and Edge */ + scrollbar-width: none; + /* Firefox */ +} + /* Chrome, Safari, Edge, Opera */ .no-spinner::-webkit-outer-spin-button, .no-spinner::-webkit-inner-spin-button { diff --git a/client/components/app/BookShelfRow.vue b/client/components/app/BookShelfRow.vue index 8fa90ee8..54f455b0 100644 --- a/client/components/app/BookShelfRow.vue +++ b/client/components/app/BookShelfRow.vue @@ -199,10 +199,7 @@ export default { scroll-behavior: smooth; width: calc(100vw - 80px); - /* background-color: rgb(214, 116, 36); */ background-image: var(--bookshelf-texture-img); - /* background-position: center; */ - /* background-size: contain; */ background-repeat: repeat-x; } @media (max-width: 768px) { @@ -213,9 +210,7 @@ export default { .bookshelfDividerCategorized { background: rgb(149, 119, 90); - /* background: linear-gradient(180deg, rgba(149, 119, 90, 1) 0%, rgba(103, 70, 37, 1) 17%, rgba(103, 70, 37, 1) 88%, rgba(71, 48, 25, 1) 100%); */ background: linear-gradient(180deg, rgb(122, 94, 68) 0%, rgb(92, 62, 31) 17%, rgb(82, 54, 26) 88%, rgba(71, 48, 25, 1) 100%); - /* background: linear-gradient(180deg, rgb(114, 85, 59) 0%, rgb(73, 48, 22) 17%, rgb(71, 43, 15) 88%, rgb(61, 41, 20) 100%); */ box-shadow: 2px 14px 8px #111111aa; } diff --git a/client/components/modals/authors/EditModal.vue b/client/components/modals/authors/EditModal.vue index 36d9b726..4ff04e8d 100644 --- a/client/components/modals/authors/EditModal.vue +++ b/client/components/modals/authors/EditModal.vue @@ -43,13 +43,13 @@ \ No newline at end of file diff --git a/client/layouts/default.vue b/client/layouts/default.vue index c7c170e8..450a1bb5 100644 --- a/client/layouts/default.vue +++ b/client/layouts/default.vue @@ -11,6 +11,7 @@ + diff --git a/client/pages/author/_id.vue b/client/pages/author/_id.vue new file mode 100644 index 00000000..c800669b --- /dev/null +++ b/client/pages/author/_id.vue @@ -0,0 +1,105 @@ + + + \ No newline at end of file diff --git a/client/pages/library/_library/authors/index.vue b/client/pages/library/_library/authors/index.vue index 34e819c1..485bff24 100644 --- a/client/pages/library/_library/authors/index.vue +++ b/client/pages/library/_library/authors/index.vue @@ -7,7 +7,8 @@
@@ -15,7 +16,6 @@
- @@ -40,9 +40,7 @@ export default { data() { return { loading: true, - authors: [], - showAuthorModal: false, - selectedAuthor: null + authors: [] } }, computed: { @@ -51,6 +49,9 @@ export default { }, currentLibraryId() { return this.$store.state.libraries.currentLibraryId + }, + selectedAuthor() { + return this.$store.state.globals.selectedAuthor } }, methods: { @@ -68,7 +69,7 @@ export default { }, authorUpdated(author) { if (this.selectedAuthor && this.selectedAuthor.id === author.id) { - this.selectedAuthor = author + this.$store.commit('globals/setSelectedAuthor', author) } this.authors = this.authors.map((au) => { if (au.id === author.id) { @@ -81,8 +82,7 @@ export default { this.authors = this.authors.filter((au) => au.id !== author.id) }, editAuthor(author) { - this.selectedAuthor = author - this.showAuthorModal = true + this.$store.commit('globals/showEditAuthorModal', author) } }, mounted() { diff --git a/client/store/globals.js b/client/store/globals.js index 2e6a1c5c..bbc383dd 100644 --- a/client/store/globals.js +++ b/client/store/globals.js @@ -6,8 +6,10 @@ export const state = () => ({ showUserCollectionsModal: false, showEditCollectionModal: false, showEditPodcastEpisode: false, + showEditAuthorModal: false, selectedEpisode: null, selectedCollection: null, + selectedAuthor: null, showBookshelfTextureModal: false, isCasting: false, // Actively casting isChromecastInitialized: false // Script loaded @@ -61,6 +63,16 @@ export const mutations = { setShowBookshelfTextureModal(state, val) { state.showBookshelfTextureModal = val }, + showEditAuthorModal(state, author) { + state.selectedAuthor = author + state.showEditAuthorModal = true + }, + setShowEditAuthorModal(state, val) { + state.showEditAuthorModal = val + }, + setSelectedAuthor(state, author) { + state.selectedAuthor = author + }, setChromecastInitialized(state, val) { state.isChromecastInitialized = val }, diff --git a/server/controllers/AuthorController.js b/server/controllers/AuthorController.js index 0da182ea..23b62ae7 100644 --- a/server/controllers/AuthorController.js +++ b/server/controllers/AuthorController.js @@ -1,11 +1,59 @@ const Logger = require('../Logger') const { reqSupportsWebp } = require('../utils/index') +const { createNewSortInstance } = require('fast-sort') +const naturalSort = createNewSortInstance({ + comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare +}) class AuthorController { constructor() { } async findOne(req, res) { - return res.json(req.author) + const include = (req.query.include || '').split(',') + + const authorJson = req.author.toJSON() + + // Used on author landing page to include library items and items grouped in series + if (include.includes('items')) { + authorJson.libraryItems = this.db.libraryItems.filter(li => { + return li.media.metadata.hasAuthor && li.media.metadata.hasAuthor(req.author.id) + }) + + if (include.includes('series')) { + const seriesMap = {} + // Group items into series + authorJson.libraryItems.forEach((li) => { + if (li.media.metadata.series) { + li.media.metadata.series.forEach((series) => { + + const itemWithSeries = li.toJSONMinified() + itemWithSeries.media.metadata.series = series + + if (seriesMap[series.id]) { + seriesMap[series.id].items.push(itemWithSeries) + } else { + seriesMap[series.id] = { + id: series.id, + name: series.name, + items: [itemWithSeries] + } + } + }) + } + }) + // Sort series items + for (const key in seriesMap) { + seriesMap[key].items = naturalSort(seriesMap[key].items).asc(li => li.media.metadata.series.sequence) + } + + authorJson.series = Object.values(seriesMap) + } + + // Minify library items + authorJson.libraryItems = authorJson.libraryItems.map(li => li.toJSONMinified()) + } + + return res.json(authorJson) } async update(req, res) { @@ -41,6 +89,7 @@ class AuthorController { }).length this.emitter('author_updated', req.author.toJSONExpanded(numBooks)) } + res.json({ author: req.author.toJSON(), updated: hasUpdated