mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-08-03 17:54:54 +02:00
Lazy bookshelf finalized
This commit is contained in:
parent
5c92aef048
commit
1ef9a689bc
53 changed files with 914 additions and 795 deletions
|
@ -39,19 +39,18 @@
|
|||
|
||||
<div v-show="numAudiobooksSelected" class="absolute top-0 left-0 w-full h-full px-4 bg-primary flex items-center">
|
||||
<h1 class="text-2xl px-4">{{ numAudiobooksSelected }} Selected</h1>
|
||||
<ui-btn v-show="!isHome" small class="text-sm mx-2" @click="toggleSelectAll"
|
||||
>{{ isAllSelected ? 'Select None' : 'Select All' }}<span class="pl-2">({{ totalBooks }})</span></ui-btn
|
||||
>
|
||||
<!-- <ui-btn v-show="!isHome" small class="text-sm mx-2" @click="toggleSelectAll"
|
||||
>{{ isAllSelected ? 'Select None' : 'Select All' }}<span class="pl-2">({{ entitiesLoaded }})</span></ui-btn
|
||||
> -->
|
||||
|
||||
<div class="flex-grow" />
|
||||
|
||||
<ui-tooltip :text="`Mark as ${selectedIsRead ? 'Not Read' : 'Read'}`" direction="bottom">
|
||||
<ui-read-icon-btn :disabled="processingBatch" :is-read="selectedIsRead" @click="toggleBatchRead" class="mx-1.5" />
|
||||
</ui-tooltip>
|
||||
<ui-tooltip text="Add to Collection" direction="bottom">
|
||||
<ui-tooltip v-if="userCanUpdate" text="Add to Collection" direction="bottom">
|
||||
<ui-icon-btn :disabled="processingBatch" icon="collections_bookmark" @click="batchAddToCollectionClick" class="mx-1.5" />
|
||||
</ui-tooltip>
|
||||
<template v-if="userCanUpdate">
|
||||
<template v-if="userCanUpdate && numAudiobooksSelected < 50">
|
||||
<ui-icon-btn v-show="!processingBatchDelete" icon="edit" bg-color="warning" class="mx-1.5" @click="batchEditClick" />
|
||||
</template>
|
||||
<ui-icon-btn v-show="userCanDelete" :disabled="processingBatchDelete" icon="delete" bg-color="error" class="mx-1.5" @click="batchDeleteClick" />
|
||||
|
@ -66,7 +65,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
processingBatchDelete: false,
|
||||
totalBooks: 0,
|
||||
totalEntities: 0,
|
||||
isAllSelected: false
|
||||
}
|
||||
},
|
||||
|
@ -98,16 +97,9 @@ export default {
|
|||
selectedAudiobooks() {
|
||||
return this.$store.state.selectedAudiobooks
|
||||
},
|
||||
// isAllSelected() {
|
||||
// return this.audiobooksShowing.length === this.selectedAudiobooks.length
|
||||
// },
|
||||
userAudiobooks() {
|
||||
return this.$store.state.user.user.audiobooks || {}
|
||||
},
|
||||
audiobooksShowing() {
|
||||
// return this.$store.getters['audiobooks/getFiltered']()
|
||||
return this.$store.getters['audiobooks/getEntitiesShowing']()
|
||||
},
|
||||
selectedSeries() {
|
||||
return this.$store.state.audiobooks.selectedSeries
|
||||
},
|
||||
|
@ -150,16 +142,14 @@ export default {
|
|||
this.$eventBus.$emit('bookshelf-clear-selection')
|
||||
this.isAllSelected = false
|
||||
},
|
||||
toggleSelectAll() {
|
||||
if (this.isAllSelected) {
|
||||
this.cancelSelectionMode()
|
||||
} else {
|
||||
this.$eventBus.$emit('bookshelf-select-all')
|
||||
this.isAllSelected = true
|
||||
// var audiobookIds = this.audiobooksShowing.map((a) => a.id)
|
||||
// this.$store.commit('setSelectedAudiobooks', audiobookIds)
|
||||
}
|
||||
},
|
||||
// toggleSelectAll() {
|
||||
// if (this.isAllSelected) {
|
||||
// this.cancelSelectionMode()
|
||||
// } else {
|
||||
// this.$eventBus.$emit('bookshelf-select-all')
|
||||
// this.isAllSelected = true
|
||||
// }
|
||||
// },
|
||||
toggleBatchRead() {
|
||||
this.$store.commit('setProcessingBatch', true)
|
||||
var newIsRead = !this.selectedIsRead
|
||||
|
@ -175,6 +165,7 @@ export default {
|
|||
this.$toast.success('Batch update success!')
|
||||
this.$store.commit('setProcessingBatch', false)
|
||||
this.$store.commit('setSelectedAudiobooks', [])
|
||||
this.$eventBus.$emit('bookshelf-clear-selection')
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toast.error('Batch update failed')
|
||||
|
@ -197,6 +188,7 @@ export default {
|
|||
this.processingBatchDelete = false
|
||||
this.$store.commit('setProcessingBatch', false)
|
||||
this.$store.commit('setSelectedAudiobooks', [])
|
||||
this.$eventBus.$emit('bookshelf-clear-selection')
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toast.error('Batch delete failed')
|
||||
|
@ -212,15 +204,15 @@ export default {
|
|||
batchAddToCollectionClick() {
|
||||
this.$store.commit('globals/setShowBatchUserCollectionsModal', true)
|
||||
},
|
||||
setBookshelfTotalBooks(totalBooks) {
|
||||
this.totalBooks = totalBooks
|
||||
setBookshelfTotalEntities(totalEntities) {
|
||||
this.totalEntities = totalEntities
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$eventBus.$on('bookshelf-total-books', this.setBookshelfTotalBooks)
|
||||
this.$eventBus.$on('bookshelf-total-entities', this.setBookshelfTotalEntities)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$eventBus.$off('bookshelf-total-books', this.setBookshelfTotalBooks)
|
||||
this.$eventBus.$off('bookshelf-total-entities', this.setBookshelfTotalEntities)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -72,12 +72,6 @@ export default {
|
|||
audiobookId() {
|
||||
return this.book.id
|
||||
},
|
||||
isSelectionMode() {
|
||||
return !!this.selectedAudiobooks.length
|
||||
},
|
||||
selectedAudiobooks() {
|
||||
return this.$store.state.selectedAudiobooks
|
||||
},
|
||||
selected: {
|
||||
get() {
|
||||
return this.$store.getters['getIsAudiobookSelected'](this.audiobookId)
|
||||
|
@ -114,8 +108,8 @@ export default {
|
|||
isMissing() {
|
||||
return this.book.isMissing
|
||||
},
|
||||
isIncomplete() {
|
||||
return this.book.isIncomplete
|
||||
isInvalid() {
|
||||
return this.book.isInvalid
|
||||
},
|
||||
numEbooks() {
|
||||
return this.book.numEbooks
|
||||
|
@ -130,7 +124,7 @@ export default {
|
|||
return this.showExperimentalFeatures && this.numEbooks
|
||||
},
|
||||
showPlayButton() {
|
||||
return !this.isMissing && !this.isIncomplete && this.numTracks && !this.isStreaming
|
||||
return !this.isMissing && !this.isInvalid && this.numTracks && !this.isStreaming
|
||||
},
|
||||
userIsRead() {
|
||||
return this.userAudiobook ? !!this.userAudiobook.isRead : false
|
||||
|
|
|
@ -101,7 +101,6 @@ export default {
|
|||
},
|
||||
searchResults() {
|
||||
this.$nextTick(() => {
|
||||
// this.$store.commit('audiobooks/setSearchResults', this.searchResults)
|
||||
this.setBookshelfEntities()
|
||||
})
|
||||
},
|
||||
|
@ -215,21 +214,7 @@ export default {
|
|||
return shelves
|
||||
},
|
||||
entities() {
|
||||
if (this.page === '') {
|
||||
return this.$store.getters['audiobooks/getFilteredAndSorted']()
|
||||
} else if (this.page === 'search') {
|
||||
var audiobookSearchResults = this.searchResults ? this.searchResults.audiobooks || [] : []
|
||||
return audiobookSearchResults.map((absr) => absr.audiobook)
|
||||
} else if (this.page === 'collections') {
|
||||
return this.$store.state.user.collections || []
|
||||
} else {
|
||||
var seriesGroups = this.$store.getters['audiobooks/getSeriesGroups']()
|
||||
if (this.selectedSeries) {
|
||||
var group = seriesGroups.find((group) => group.name === this.selectedSeries)
|
||||
return group.books
|
||||
}
|
||||
return seriesGroups
|
||||
}
|
||||
return []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -308,10 +293,10 @@ export default {
|
|||
var sizeIndex = this.availableSizes.findIndex((s) => s === bookshelfCoverSize)
|
||||
if (!isNaN(sizeIndex)) this.selectedSizeIndex = sizeIndex
|
||||
|
||||
var isLoading = await this.$store.dispatch('audiobooks/load')
|
||||
if (!isLoading) {
|
||||
this.setBookshelfEntities()
|
||||
}
|
||||
// var isLoading = await this.$store.dispatch('audiobooks/load')
|
||||
// if (!isLoading) {
|
||||
// this.setBookshelfEntities()
|
||||
// }
|
||||
},
|
||||
resize() {
|
||||
this.$nextTick(this.setBookshelfEntities)
|
||||
|
|
|
@ -1,22 +1,16 @@
|
|||
<template>
|
||||
<div id="bookshelf" ref="wrapper" class="w-full h-full overflow-y-scroll relative">
|
||||
<!-- Cover size widget -->
|
||||
<div class="fixed bottom-4 right-4 z-40">
|
||||
<div class="rounded-full py-1 bg-primary px-2 border border-black-100 text-center flex items-center box-shadow-md" @mousedown.prevent @mouseup.prevent>
|
||||
<span class="material-icons" :class="selectedSizeIndex === 0 ? 'text-gray-400' : 'hover:text-yellow-300 cursor-pointer'" style="font-size: 0.9rem" @mousedown.prevent @click="decreaseSize">remove</span>
|
||||
<p class="px-2 font-mono">{{ bookCoverWidth }}</p>
|
||||
<span class="material-icons" :class="selectedSizeIndex === availableSizes.length - 1 ? 'text-gray-400' : 'hover:text-yellow-300 cursor-pointer'" style="font-size: 0.9rem" @mousedown.prevent @click="increaseSize">add</span>
|
||||
</div>
|
||||
</div>
|
||||
<widgets-cover-size-widget class="fixed bottom-4 right-4 z-30" />
|
||||
<!-- Experimental Bookshelf Texture -->
|
||||
<div v-show="showExperimentalFeatures" class="fixed bottom-4 right-28 z-40">
|
||||
<div class="rounded-full py-1 bg-primary hover:bg-bg cursor-pointer px-2 border border-black-100 text-center flex items-center box-shadow-md" @mousedown.prevent @mouseup.prevent @click="showBookshelfTextureModal"><p class="text-sm py-0.5">Texture</p></div>
|
||||
</div>
|
||||
|
||||
<div v-if="loaded && !shelves.length" class="w-full flex flex-col items-center justify-center py-12">
|
||||
<p class="text-center text-2xl font-book mb-4 py-4">Your Audiobookshelf is empty!</p>
|
||||
<div v-if="loaded && !shelves.length && isRootUser" class="w-full flex flex-col items-center justify-center py-12">
|
||||
<p class="text-center text-2xl font-book mb-4 py-4">Audiobookshelf is empty!</p>
|
||||
<div class="flex">
|
||||
<ui-btn to="/config" color="primary" class="w-52 mr-2" @click="scan">Configure Scanner</ui-btn>
|
||||
<ui-btn to="/config" color="primary" class="w-52 mr-2">Configure Scanner</ui-btn>
|
||||
<ui-btn color="success" class="w-52" @click="scan">Scan Audiobooks</ui-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -40,8 +34,6 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
loaded: false,
|
||||
availableSizes: [60, 80, 100, 120, 140, 160, 180, 200, 220],
|
||||
selectedSizeIndex: 3,
|
||||
keywordFilterTimeout: null,
|
||||
scannerParseSubtitle: false,
|
||||
wrapperClientWidth: 0,
|
||||
|
@ -49,6 +41,9 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
isRootUser() {
|
||||
return this.$store.getters['user/getIsRoot']
|
||||
},
|
||||
showExperimentalFeatures() {
|
||||
return this.$store.state.showExperimentalFeatures
|
||||
},
|
||||
|
@ -56,7 +51,7 @@ export default {
|
|||
return this.$store.state.libraries.currentLibraryId
|
||||
},
|
||||
bookCoverWidth() {
|
||||
return this.availableSizes[this.selectedSizeIndex]
|
||||
return this.$store.getters['user/getUserSetting']('bookshelfCoverSize')
|
||||
},
|
||||
sizeMultiplier() {
|
||||
return this.bookCoverWidth / 120
|
||||
|
@ -66,41 +61,28 @@ export default {
|
|||
showBookshelfTextureModal() {
|
||||
this.$store.commit('globals/setShowBookshelfTextureModal', true)
|
||||
},
|
||||
increaseSize() {
|
||||
this.selectedSizeIndex = Math.min(this.availableSizes.length - 1, this.selectedSizeIndex + 1)
|
||||
this.resize()
|
||||
this.$store.dispatch('user/updateUserSettings', { bookshelfCoverSize: this.bookCoverWidth })
|
||||
},
|
||||
decreaseSize() {
|
||||
this.selectedSizeIndex = Math.max(0, this.selectedSizeIndex - 1)
|
||||
this.resize()
|
||||
this.$store.dispatch('user/updateUserSettings', { bookshelfCoverSize: this.bookCoverWidth })
|
||||
},
|
||||
async init() {
|
||||
this.wrapperClientWidth = this.$refs.wrapper ? this.$refs.wrapper.clientWidth : 0
|
||||
|
||||
var bookshelfCoverSize = this.$store.getters['user/getUserSetting']('bookshelfCoverSize')
|
||||
var sizeIndex = this.availableSizes.findIndex((s) => s === bookshelfCoverSize)
|
||||
if (!isNaN(sizeIndex)) this.selectedSizeIndex = sizeIndex
|
||||
|
||||
// await this.$store.dispatch('audiobooks/load')
|
||||
if (this.search) {
|
||||
this.setShelvesFromSearch()
|
||||
} else {
|
||||
var categories = await this.$axios
|
||||
.$get(`/api/libraries/${this.currentLibraryId}/categories`)
|
||||
.then((data) => {
|
||||
console.log('Category data', data)
|
||||
return data
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to fetch cats', error)
|
||||
})
|
||||
this.shelves = categories
|
||||
await this.fetchCategories()
|
||||
}
|
||||
|
||||
this.loaded = true
|
||||
},
|
||||
async fetchCategories() {
|
||||
var categories = await this.$axios
|
||||
.$get(`/api/libraries/${this.currentLibraryId}/categories`)
|
||||
.then((data) => {
|
||||
return data
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to fetch categories', error)
|
||||
return []
|
||||
})
|
||||
this.shelves = categories
|
||||
},
|
||||
async setShelvesFromSearch() {
|
||||
var shelves = []
|
||||
if (this.results.audiobooks) {
|
||||
|
@ -111,14 +93,17 @@ export default {
|
|||
entities: this.results.audiobooks.map((ab) => ab.audiobook)
|
||||
})
|
||||
}
|
||||
if (this.results.authors) {
|
||||
shelves.push({
|
||||
id: 'authors',
|
||||
label: 'Authors',
|
||||
type: 'authors',
|
||||
entities: this.results.authors.map((a) => a.author)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Author shelves
|
||||
// if (this.results.authors) {
|
||||
// shelves.push({
|
||||
// id: 'authors',
|
||||
// label: 'Authors',
|
||||
// type: 'authors',
|
||||
// entities: this.results.authors.map((a) => a.author)
|
||||
// })
|
||||
// }
|
||||
|
||||
if (this.results.series) {
|
||||
shelves.push({
|
||||
id: 'series',
|
||||
|
@ -149,27 +134,100 @@ export default {
|
|||
}
|
||||
this.shelves = shelves
|
||||
},
|
||||
resize() {},
|
||||
settingsUpdated(settings) {
|
||||
if (settings.bookshelfCoverSize !== this.bookCoverWidth && settings.bookshelfCoverSize !== undefined) {
|
||||
var index = this.availableSizes.indexOf(settings.bookshelfCoverSize)
|
||||
if (index >= 0) {
|
||||
this.selectedSizeIndex = index
|
||||
this.resize()
|
||||
}
|
||||
}
|
||||
},
|
||||
settingsUpdated(settings) {},
|
||||
scan() {
|
||||
this.$root.socket.emit('scan', this.$store.state.libraries.currentLibraryId)
|
||||
},
|
||||
audiobookAdded(audiobook) {
|
||||
console.log('Audiobook added', audiobook)
|
||||
// TODO: Check if audiobook would be on this shelf
|
||||
if (!this.search) {
|
||||
this.fetchCategories()
|
||||
}
|
||||
},
|
||||
audiobookUpdated(audiobook) {
|
||||
console.log('Audiobook updated', audiobook)
|
||||
this.shelves.forEach((shelf) => {
|
||||
if (shelf.type === 'books') {
|
||||
shelf.entities = shelf.entities.map((ent) => {
|
||||
if (ent.id === audiobook.id) {
|
||||
return audiobook
|
||||
}
|
||||
return ent
|
||||
})
|
||||
} else if (shelf.type === 'series') {
|
||||
shelf.entities.forEach((ent) => {
|
||||
ent.books = ent.books.map((book) => {
|
||||
if (book.id === audiobook.id) return audiobook
|
||||
return book
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
removeBookFromShelf(audiobook) {
|
||||
this.shelves.forEach((shelf) => {
|
||||
if (shelf.type === 'books') {
|
||||
shelf.entities = shelf.entities.filter((ent) => {
|
||||
return ent.id !== audiobook.id
|
||||
})
|
||||
} else if (shelf.type === 'series') {
|
||||
shelf.entities.forEach((ent) => {
|
||||
ent.books = ent.books.filter((book) => {
|
||||
return book.id !== audiobook.id
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
audiobookRemoved(audiobook) {
|
||||
this.removeBookFromShelf(audiobook)
|
||||
},
|
||||
audiobooksAdded(audiobooks) {
|
||||
console.log('audiobooks added', audiobooks)
|
||||
// TODO: Check if audiobook would be on this shelf
|
||||
if (!this.search) {
|
||||
this.fetchCategories()
|
||||
}
|
||||
},
|
||||
audiobooksUpdated(audiobooks) {
|
||||
audiobooks.forEach((ab) => {
|
||||
this.audiobookUpdated(ab)
|
||||
})
|
||||
},
|
||||
initListeners() {
|
||||
this.$store.commit('user/addSettingsListener', { id: 'bookshelf', meth: this.settingsUpdated })
|
||||
|
||||
if (this.$root.socket) {
|
||||
this.$root.socket.on('audiobook_updated', this.audiobookUpdated)
|
||||
this.$root.socket.on('audiobook_added', this.audiobookAdded)
|
||||
this.$root.socket.on('audiobook_removed', this.audiobookRemoved)
|
||||
this.$root.socket.on('audiobooks_updated', this.audiobooksUpdated)
|
||||
this.$root.socket.on('audiobooks_added', this.audiobooksAdded)
|
||||
} else {
|
||||
console.error('Error socket not initialized')
|
||||
}
|
||||
},
|
||||
removeListeners() {
|
||||
this.$store.commit('user/removeSettingsListener', 'bookshelf')
|
||||
|
||||
if (this.$root.socket) {
|
||||
this.$root.socket.off('audiobook_updated', this.audiobookUpdated)
|
||||
this.$root.socket.off('audiobook_added', this.audiobookAdded)
|
||||
this.$root.socket.off('audiobook_removed', this.audiobookRemoved)
|
||||
this.$root.socket.off('audiobooks_updated', this.audiobooksUpdated)
|
||||
this.$root.socket.off('audiobooks_added', this.audiobooksAdded)
|
||||
} else {
|
||||
console.error('Error socket not initialized')
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$store.commit('user/addSettingsListener', { id: 'bookshelf', meth: this.settingsUpdated })
|
||||
|
||||
this.initListeners()
|
||||
this.init()
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$store.commit('user/removeSettingsListener', 'bookshelf')
|
||||
this.removeListeners()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -9,25 +9,25 @@
|
|||
</div>
|
||||
<div v-if="shelf.type === 'series'" class="flex items-center -mb-2">
|
||||
<template v-for="entity in shelf.entities">
|
||||
<cards-group-card :key="entity.name" :width="bookCoverWidth" :group="entity" @click="$emit('clickSeries', entity)" />
|
||||
<cards-group-card :key="entity.name" is-categorized :width="bookCoverWidth" :group="entity" @click="$emit('clickSeries', entity)" />
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="shelf.type === 'tags'" class="flex items-center -mb-2">
|
||||
<template v-for="entity in shelf.entities">
|
||||
<nuxt-link :key="entity.name" :to="`/library/${currentLibraryId}/bookshelf?filter=tags.${$encode(entity.name)}`">
|
||||
<cards-group-card is-search :width="bookCoverWidth" :group="entity" />
|
||||
<cards-group-card is-categorized :width="bookCoverWidth" :group="entity" />
|
||||
</nuxt-link>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else-if="shelf.series" class="flex items-center -mb-2">
|
||||
<template v-for="entity in shelf.series">
|
||||
<cards-group-card is-search :key="entity.name" :width="bookCoverWidth" :group="entity" @click="$emit('clickSeries', entity)" />
|
||||
<cards-group-card is-categorized :key="entity.name" :width="bookCoverWidth" :group="entity" @click="$emit('clickSeries', entity)" />
|
||||
</template>
|
||||
</div>
|
||||
<div v-else-if="shelf.tags" class="flex items-center -mb-2">
|
||||
<template v-for="entity in shelf.tags">
|
||||
<nuxt-link :key="entity.name" :to="`/library/${currentLibraryId}/bookshelf?filter=tags.${$encode(entity.name)}`">
|
||||
<cards-group-card is-search :width="bookCoverWidth" :group="entity" />
|
||||
<cards-group-card is-categorized :width="bookCoverWidth" :group="entity" />
|
||||
</nuxt-link>
|
||||
</template>
|
||||
</div>
|
||||
|
|
|
@ -31,16 +31,16 @@
|
|||
<!-- <ui-text-input v-show="showSortFilters" v-model="keywordFilter" @input="keywordFilterInput" placeholder="Keyword Filter" :padding-y="1.5" clearable class="text-xs w-40 hidden md:block" /> -->
|
||||
<controls-filter-select v-show="showSortFilters" v-model="settings.filterBy" class="w-48 h-7.5 ml-4" @change="updateFilter" />
|
||||
<controls-order-select v-show="showSortFilters" v-model="settings.orderBy" :descending.sync="settings.orderDesc" class="w-48 h-7.5 ml-4" @change="updateOrder" />
|
||||
<div v-show="showSortFilters" class="h-7 ml-4 flex border border-white border-opacity-25 rounded-md">
|
||||
<!-- <div v-show="showSortFilters" class="h-7 ml-4 flex border border-white border-opacity-25 rounded-md">
|
||||
<div class="h-full px-2 text-white flex items-center rounded-l-md hover:bg-primary hover:bg-opacity-75 cursor-pointer" :class="isGridMode ? 'bg-primary' : 'text-opacity-70'" @click="$emit('update:viewMode', 'grid')">
|
||||
<span class="material-icons" style="font-size: 1.4rem">view_module</span>
|
||||
</div>
|
||||
<div class="h-full px-2 text-white flex items-center rounded-r-md hover:bg-primary hover:bg-opacity-75 cursor-pointer" :class="!isGridMode ? 'bg-primary' : 'text-opacity-70'" @click="$emit('update:viewMode', 'list')">
|
||||
<span class="material-icons" style="font-size: 1.4rem">view_list</span>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</template>
|
||||
<template v-else-if="!isHome">
|
||||
<template v-else-if="page === 'search'">
|
||||
<div @click="searchBackArrow" class="rounded-full h-10 w-10 flex items-center justify-center hover:bg-white hover:bg-opacity-10 cursor-pointer">
|
||||
<span class="material-icons text-3xl text-white">west</span>
|
||||
</div>
|
||||
|
@ -59,10 +59,6 @@ export default {
|
|||
page: String,
|
||||
isHome: Boolean,
|
||||
selectedSeries: String,
|
||||
searchResults: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
searchQuery: String,
|
||||
viewMode: String
|
||||
},
|
||||
|
@ -84,27 +80,9 @@ export default {
|
|||
},
|
||||
numShowing() {
|
||||
return this.totalEntities
|
||||
|
||||
if (this.page === '') {
|
||||
// return this.$store.getters['audiobooks/getFiltered']().length
|
||||
return this.totalEntities
|
||||
} else if (this.page === 'search') {
|
||||
var audiobookSearchResults = this.searchResults ? this.searchResults.audiobooks || [] : []
|
||||
return audiobookSearchResults.length
|
||||
} else if (this.page === 'collections') {
|
||||
return (this.$store.state.user.collections || []).length
|
||||
} else {
|
||||
var groups = this.$store.getters['audiobooks/getSeriesGroups']()
|
||||
if (this.selectedSeries) {
|
||||
var group = groups.find((g) => g.name === this.selectedSeries)
|
||||
if (group) return group.books.length
|
||||
return 0
|
||||
}
|
||||
return groups.length
|
||||
}
|
||||
},
|
||||
entityName() {
|
||||
if (!this.page) return 'Audiobooks'
|
||||
if (!this.page) return 'Books'
|
||||
if (this.page === 'series') return 'Series'
|
||||
if (this.page === 'collections') return 'Collections'
|
||||
return ''
|
||||
|
@ -139,7 +117,6 @@ export default {
|
|||
},
|
||||
seriesBackArrow() {
|
||||
this.$router.replace(`/library/${this.currentLibraryId}/bookshelf/series`)
|
||||
this.$emit('update:selectedSeries', null)
|
||||
},
|
||||
updateOrder() {
|
||||
this.saveSettings()
|
||||
|
|
|
@ -9,9 +9,22 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="!totalShelves && initialized" class="w-full py-16">
|
||||
<div v-if="initialized && !totalShelves && !hasFilter && isRootUser && entityName === 'books'" class="w-full flex flex-col items-center justify-center py-12">
|
||||
<p class="text-center text-2xl font-book mb-4 py-4">Audiobookshelf is empty!</p>
|
||||
<div class="flex">
|
||||
<ui-btn to="/config" color="primary" class="w-52 mr-2">Configure Scanner</ui-btn>
|
||||
<ui-btn color="success" class="w-52" @click="scan">Scan Audiobooks</ui-btn>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="!totalShelves && initialized" class="w-full py-16">
|
||||
<p class="text-xl text-center">{{ emptyMessage }}</p>
|
||||
</div>
|
||||
|
||||
<widgets-cover-size-widget class="fixed bottom-4 right-4 z-30" />
|
||||
<!-- Experimental Bookshelf Texture -->
|
||||
<div v-show="showExperimentalFeatures" class="fixed bottom-4 right-28 z-40">
|
||||
<div class="rounded-full py-1 bg-primary hover:bg-bg cursor-pointer px-2 border border-black-100 text-center flex items-center box-shadow-md" @mousedown.prevent @mouseup.prevent @click="showBookshelfTextureModal"><p class="text-sm py-0.5">Texture</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -20,7 +33,8 @@ import bookshelfCardsHelpers from '@/mixins/bookshelfCardsHelpers'
|
|||
|
||||
export default {
|
||||
props: {
|
||||
page: String
|
||||
page: String,
|
||||
seriesId: String
|
||||
},
|
||||
mixins: [bookshelfCardsHelpers],
|
||||
data() {
|
||||
|
@ -36,7 +50,7 @@ export default {
|
|||
pagesLoaded: {},
|
||||
entityIndexesMounted: [],
|
||||
entityComponentRefs: {},
|
||||
bookWidth: 120,
|
||||
currentBookWidth: 0,
|
||||
pageLoadQueue: [],
|
||||
isFetchingEntities: false,
|
||||
scrollTimeout: null,
|
||||
|
@ -47,27 +61,34 @@ export default {
|
|||
isSelectAll: false,
|
||||
currentSFQueryString: null,
|
||||
pendingReset: false,
|
||||
keywordFilter: null
|
||||
keywordFilter: null,
|
||||
currScrollTop: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route.query.filter'() {
|
||||
if (this.$route.query.filter && this.$route.query.filter !== this.filterBy) {
|
||||
this.$store.dispatch('user/updateUserSettings', { filterBy: this.$route.query.filter })
|
||||
} else if (!this.$route.query.filter && this.filterBy) {
|
||||
this.$store.dispatch('user/updateUserSettings', { filterBy: 'all' })
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// booksFiltered() {
|
||||
// const keywordFilterKeys = ['title', 'subtitle', 'author', 'series', 'narrator']
|
||||
// const keyworkFilter = state.keywordFilter.toLowerCase()
|
||||
// return this.books.filter((ab) => {
|
||||
// if (!ab.book) return false
|
||||
// return !!keywordFilterKeys.find((key) => ab.book[key] && ab.book[key].toLowerCase().includes(keyworkFilter))
|
||||
// })
|
||||
// },
|
||||
isRootUser() {
|
||||
return this.$store.getters['user/getIsRoot']
|
||||
},
|
||||
showExperimentalFeatures() {
|
||||
return this.$store.state.showExperimentalFeatures
|
||||
},
|
||||
emptyMessage() {
|
||||
if (this.page === 'series') return `You have no series`
|
||||
if (this.page === 'collections') return "You haven't made any collections yet"
|
||||
return 'No results'
|
||||
},
|
||||
entityName() {
|
||||
if (this.page === 'series') return 'series'
|
||||
if (this.page === 'collections') return 'collections'
|
||||
return 'books'
|
||||
if (!this.page) return 'books'
|
||||
return this.page
|
||||
},
|
||||
orderBy() {
|
||||
return this.$store.getters['user/getUserSetting']('orderBy')
|
||||
|
@ -78,9 +99,15 @@ export default {
|
|||
filterBy() {
|
||||
return this.$store.getters['user/getUserSetting']('filterBy')
|
||||
},
|
||||
hasFilter() {
|
||||
return this.filterBy && this.filterBy !== 'all'
|
||||
},
|
||||
currentLibraryId() {
|
||||
return this.$store.state.libraries.currentLibraryId
|
||||
},
|
||||
bookWidth() {
|
||||
return this.$store.getters['user/getUserSetting']('bookshelfCoverSize')
|
||||
},
|
||||
entityWidth() {
|
||||
if (this.entityName === 'series') return this.bookWidth * 1.6
|
||||
if (this.entityName === 'collections') return this.bookWidth * 2
|
||||
|
@ -107,20 +134,30 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
showBookshelfTextureModal() {
|
||||
this.$store.commit('globals/setShowBookshelfTextureModal', true)
|
||||
},
|
||||
editEntity(entity) {
|
||||
if (this.entityName === 'books') {
|
||||
if (this.entityName === 'books' || this.entityName === 'series-books') {
|
||||
var bookIds = this.entities.map((e) => e.id)
|
||||
this.$store.commit('setBookshelfBookIds', bookIds)
|
||||
this.$store.commit('showEditModal', entity)
|
||||
} else if (this.entityName === 'collections') {
|
||||
this.$store.commit('globals/setEditCollection', entity)
|
||||
}
|
||||
},
|
||||
clearSelectedBooks() {
|
||||
clearSelectedEntities() {
|
||||
this.updateBookSelectionMode(false)
|
||||
this.isSelectionMode = false
|
||||
this.isSelectAll = false
|
||||
},
|
||||
selectAllBooks() {
|
||||
selectAllEntities() {
|
||||
this.isSelectAll = true
|
||||
if (this.entityName === 'books' || this.entityName === 'series-books') {
|
||||
var allAvailableEntityIds = this.entities.map((ent) => ent.id).filter((ent) => !!ent)
|
||||
this.$store.commit('setSelectedAudiobooks', allAvailableEntityIds)
|
||||
}
|
||||
|
||||
for (const key in this.entityComponentRefs) {
|
||||
if (this.entityIndexesMounted.includes(Number(key))) {
|
||||
this.entityComponentRefs[key].selected = true
|
||||
|
@ -128,7 +165,7 @@ export default {
|
|||
}
|
||||
},
|
||||
selectEntity(entity) {
|
||||
if (this.entityName === 'books') {
|
||||
if (this.entityName === 'books' || this.entityName === 'series-books') {
|
||||
this.$store.commit('toggleAudiobookSelected', entity.id)
|
||||
|
||||
var newIsSelectionMode = !!this.selectedAudiobooks.length
|
||||
|
@ -155,8 +192,10 @@ export default {
|
|||
}
|
||||
|
||||
var entityPath = this.entityName === 'books' ? `books/all` : this.entityName
|
||||
if (this.entityName === 'series-books') entityPath = `series/${this.seriesId}`
|
||||
var sfQueryString = this.currentSFQueryString ? this.currentSFQueryString + '&' : ''
|
||||
var payload = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/${entityPath}?${sfQueryString}limit=${this.booksPerFetch}&page=${page}`).catch((error) => {
|
||||
var fullQueryString = this.entityName === 'series-books' ? '' : `?${sfQueryString}limit=${this.booksPerFetch}&page=${page}`
|
||||
var payload = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/${entityPath}${fullQueryString}`).catch((error) => {
|
||||
console.error('failed to fetch books', error)
|
||||
return null
|
||||
})
|
||||
|
@ -167,7 +206,7 @@ export default {
|
|||
return
|
||||
}
|
||||
if (payload) {
|
||||
console.log('Received payload', payload)
|
||||
// console.log('Received payload', payload)
|
||||
if (!this.initialized) {
|
||||
this.initialized = true
|
||||
this.totalEntities = payload.total
|
||||
|
@ -202,6 +241,7 @@ export default {
|
|||
}
|
||||
},
|
||||
handleScroll(scrollTop) {
|
||||
this.currScrollTop = scrollTop
|
||||
var firstShelfIndex = Math.floor(scrollTop / this.shelfHeight)
|
||||
var lastShelfIndex = Math.ceil((scrollTop + this.bookshelfHeight) / this.shelfHeight)
|
||||
lastShelfIndex = Math.min(this.totalShelves - 1, lastShelfIndex)
|
||||
|
@ -233,11 +273,9 @@ export default {
|
|||
},
|
||||
async resetEntities() {
|
||||
if (this.isFetchingEntities) {
|
||||
console.warn('RESET BOOKS BUT ALREADY FETCHING, WAIT FOR FETCH')
|
||||
this.pendingReset = true
|
||||
return
|
||||
}
|
||||
|
||||
this.destroyEntityComponents()
|
||||
this.entityIndexesMounted = []
|
||||
this.entityComponentRefs = {}
|
||||
|
@ -250,13 +288,25 @@ export default {
|
|||
this.isSelectAll = false
|
||||
this.initialized = false
|
||||
|
||||
this.initSizeData()
|
||||
this.pagesLoaded[0] = true
|
||||
await this.fetchEntites(0)
|
||||
var lastBookIndex = Math.min(this.totalEntities, this.shelvesPerPage * this.entitiesPerShelf)
|
||||
this.mountEntites(0, lastBookIndex)
|
||||
},
|
||||
remountEntities() {
|
||||
for (const key in this.entityComponentRefs) {
|
||||
if (this.entityComponentRefs[key]) {
|
||||
this.entityComponentRefs[key].destroy()
|
||||
}
|
||||
}
|
||||
this.entityComponentRefs = {}
|
||||
this.entityIndexesMounted.forEach((i) => {
|
||||
this.cardsHelpers.mountEntityCard(i)
|
||||
})
|
||||
},
|
||||
buildSearchParams() {
|
||||
if (this.page === 'search' || this.page === 'series' || this.page === 'collections') {
|
||||
if (this.page === 'search' || this.page === 'series' || this.page === 'collections' || this.page === 'series-books') {
|
||||
return ''
|
||||
}
|
||||
|
||||
|
@ -273,11 +323,11 @@ export default {
|
|||
checkUpdateSearchParams() {
|
||||
var newSearchParams = this.buildSearchParams()
|
||||
var currentQueryString = window.location.search
|
||||
if (currentQueryString && currentQueryString.startsWith('?')) currentQueryString = currentQueryString.slice(1)
|
||||
|
||||
if (newSearchParams === '') {
|
||||
return false
|
||||
}
|
||||
|
||||
if (newSearchParams !== this.currentSFQueryString || newSearchParams !== currentQueryString) {
|
||||
let newurl = window.location.protocol + '//' + window.location.host + window.location.pathname + '?' + newSearchParams
|
||||
window.history.replaceState({ path: newurl }, '', newurl)
|
||||
|
@ -290,6 +340,20 @@ export default {
|
|||
var wasUpdated = this.checkUpdateSearchParams()
|
||||
if (wasUpdated) {
|
||||
this.resetEntities()
|
||||
} else if (settings.bookshelfCoverSize !== this.currentBookWidth) {
|
||||
this.initSizeData()
|
||||
|
||||
var lastBookIndex = Math.min(this.totalEntities, this.shelvesPerPage * this.entitiesPerShelf)
|
||||
this.entityIndexesMounted = []
|
||||
for (let i = 0; i < lastBookIndex; i++) {
|
||||
this.entityIndexesMounted.push(i)
|
||||
}
|
||||
var bookshelfEl = document.getElementById('bookshelf')
|
||||
if (bookshelfEl) {
|
||||
bookshelfEl.scrollTop = 0
|
||||
}
|
||||
|
||||
this.$nextTick(this.remountEntities)
|
||||
}
|
||||
},
|
||||
scroll(e) {
|
||||
|
@ -300,8 +364,51 @@ export default {
|
|||
this.handleScroll(scrollTop)
|
||||
// }, 250)
|
||||
},
|
||||
async init(bookshelf) {
|
||||
this.checkUpdateSearchParams()
|
||||
audiobookAdded(audiobook) {
|
||||
console.log('Audiobook added', audiobook)
|
||||
// TODO: Check if audiobook would be on this shelf
|
||||
this.resetEntities()
|
||||
},
|
||||
audiobookUpdated(audiobook) {
|
||||
console.log('Audiobook updated', audiobook)
|
||||
if (this.entityName === 'books' || this.entityName === 'series-books') {
|
||||
var indexOf = this.entities.findIndex((ent) => ent && ent.id === audiobook.id)
|
||||
if (indexOf >= 0) {
|
||||
this.entities[indexOf] = audiobook
|
||||
if (this.entityComponentRefs[indexOf]) {
|
||||
this.entityComponentRefs[indexOf].setEntity(audiobook)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
audiobookRemoved(audiobook) {
|
||||
if (this.entityName === 'books' || this.entityName === 'series-books') {
|
||||
var indexOf = this.entities.findIndex((ent) => ent && ent.id === audiobook.id)
|
||||
if (indexOf >= 0) {
|
||||
this.entities = this.entities.filter((ent) => ent.id !== audiobook.id)
|
||||
this.totalEntities = this.entities.length
|
||||
this.$eventBus.$emit('bookshelf-total-entities', this.totalEntities)
|
||||
this.remountEntities()
|
||||
}
|
||||
}
|
||||
},
|
||||
audiobooksAdded(audiobooks) {
|
||||
console.log('audiobooks added', audiobooks)
|
||||
// TODO: Check if audiobook would be on this shelf
|
||||
this.resetEntities()
|
||||
},
|
||||
audiobooksUpdated(audiobooks) {
|
||||
audiobooks.forEach((ab) => {
|
||||
this.audiobookUpdated(ab)
|
||||
})
|
||||
},
|
||||
initSizeData(_bookshelf) {
|
||||
var bookshelf = _bookshelf || document.getElementById('bookshelf')
|
||||
if (!bookshelf) {
|
||||
console.error('Failed to init size data')
|
||||
return
|
||||
}
|
||||
var entitiesPerShelfBefore = this.entitiesPerShelf
|
||||
|
||||
var { clientHeight, clientWidth } = bookshelf
|
||||
this.bookshelfHeight = clientHeight
|
||||
|
@ -310,6 +417,16 @@ export default {
|
|||
this.shelvesPerPage = Math.ceil(this.bookshelfHeight / this.shelfHeight) + 2
|
||||
this.bookshelfMarginLeft = (this.bookshelfWidth - this.entitiesPerShelf * this.totalEntityCardWidth) / 2
|
||||
|
||||
this.currentBookWidth = this.bookWidth
|
||||
if (this.totalEntities) {
|
||||
this.totalShelves = Math.ceil(this.totalEntities / this.entitiesPerShelf)
|
||||
}
|
||||
return entitiesPerShelfBefore < this.entitiesPerShelf // Books per shelf has changed
|
||||
},
|
||||
async init(bookshelf) {
|
||||
this.checkUpdateSearchParams()
|
||||
this.initSizeData(bookshelf)
|
||||
|
||||
this.pagesLoaded[0] = true
|
||||
await this.fetchEntites(0)
|
||||
var lastBookIndex = Math.min(this.totalEntities, this.shelvesPerPage * this.entitiesPerShelf)
|
||||
|
@ -321,22 +438,42 @@ export default {
|
|||
this.init(bookshelf)
|
||||
bookshelf.addEventListener('scroll', this.scroll)
|
||||
}
|
||||
this.$eventBus.$on('bookshelf-clear-selection', this.clearSelectedBooks)
|
||||
this.$eventBus.$on('bookshelf-select-all', this.selectAllBooks)
|
||||
this.$eventBus.$on('bookshelf-clear-selection', this.clearSelectedEntities)
|
||||
this.$eventBus.$on('bookshelf-select-all', this.selectAllEntities)
|
||||
this.$eventBus.$on('bookshelf-keyword-filter', this.updateKeywordFilter)
|
||||
|
||||
this.$store.commit('user/addSettingsListener', { id: 'lazy-bookshelf', meth: this.settingsUpdated })
|
||||
|
||||
if (this.$root.socket) {
|
||||
this.$root.socket.on('audiobook_updated', this.audiobookUpdated)
|
||||
this.$root.socket.on('audiobook_added', this.audiobookAdded)
|
||||
this.$root.socket.on('audiobook_removed', this.audiobookRemoved)
|
||||
this.$root.socket.on('audiobooks_updated', this.audiobooksUpdated)
|
||||
this.$root.socket.on('audiobooks_added', this.audiobooksAdded)
|
||||
} else {
|
||||
console.error('Bookshelf - Socket not initialized')
|
||||
}
|
||||
},
|
||||
removeListeners() {
|
||||
var bookshelf = document.getElementById('bookshelf')
|
||||
if (bookshelf) {
|
||||
bookshelf.removeEventListener('scroll', this.scroll)
|
||||
}
|
||||
this.$eventBus.$off('bookshelf-clear-selection', this.clearSelectedBooks)
|
||||
this.$eventBus.$off('bookshelf-select-all', this.selectAllBooks)
|
||||
this.$eventBus.$off('bookshelf-clear-selection', this.clearSelectedEntities)
|
||||
this.$eventBus.$off('bookshelf-select-all', this.selectAllEntities)
|
||||
this.$eventBus.$off('bookshelf-keyword-filter', this.updateKeywordFilter)
|
||||
|
||||
this.$store.commit('user/removeSettingsListener', 'lazy-bookshelf')
|
||||
|
||||
if (this.$root.socket) {
|
||||
this.$root.socket.off('audiobook_updated', this.audiobookUpdated)
|
||||
this.$root.socket.off('audiobook_added', this.audiobookAdded)
|
||||
this.$root.socket.off('audiobook_removed', this.audiobookRemoved)
|
||||
this.$root.socket.off('audiobooks_updated', this.audiobooksUpdated)
|
||||
this.$root.socket.off('audiobooks_added', this.audiobooksAdded)
|
||||
} else {
|
||||
console.error('Bookshelf - Socket not initialized')
|
||||
}
|
||||
},
|
||||
destroyEntityComponents() {
|
||||
for (const key in this.entityComponentRefs) {
|
||||
|
@ -344,6 +481,9 @@ export default {
|
|||
this.entityComponentRefs[key].destroy()
|
||||
}
|
||||
}
|
||||
},
|
||||
scan() {
|
||||
this.$root.socket.emit('scan', this.currentLibraryId)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
@ -21,14 +21,14 @@
|
|||
<div v-show="showLibrary" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
|
||||
</nuxt-link>
|
||||
|
||||
<nuxt-link :to="`/library/${currentLibraryId}/bookshelf/series`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="paramId === 'series' ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
|
||||
<nuxt-link :to="`/library/${currentLibraryId}/bookshelf/series`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isSeriesPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2" />
|
||||
</svg>
|
||||
|
||||
<p class="font-book pt-1.5" style="font-size: 0.9rem">Series</p>
|
||||
|
||||
<div v-show="paramId === 'series'" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
|
||||
<div v-show="isSeriesPage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
|
||||
</nuxt-link>
|
||||
|
||||
<nuxt-link :to="`/library/${currentLibraryId}/bookshelf/collections`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="paramId === 'collections' ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
|
||||
|
@ -103,6 +103,9 @@ export default {
|
|||
homePage() {
|
||||
return this.$route.name === 'library-library'
|
||||
},
|
||||
isSeriesPage() {
|
||||
return this.$route.name === 'library-library-series-id'
|
||||
},
|
||||
libraryBookshelfPage() {
|
||||
return this.$route.name === 'library-library-bookshelf-id'
|
||||
},
|
||||
|
@ -114,7 +117,7 @@ export default {
|
|||
return this.libraryBookshelfPage && this.$route.query.filter === 'issues'
|
||||
},
|
||||
numIssues() {
|
||||
return this.$store.getters['audiobooks/getAudiobooksWithIssues'].length
|
||||
return this.$store.state.libraries.issues || 0
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue