mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-11-07 16:44:16 +01:00
Add:Experimental collapse series for library option #322
This commit is contained in:
parent
5bd6c8f553
commit
d8cc0b57a5
@ -142,14 +142,6 @@ 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
|
||||
// }
|
||||
// },
|
||||
toggleBatchRead() {
|
||||
this.$store.commit('setProcessingBatch', true)
|
||||
var newIsRead = !this.selectedIsRead
|
||||
|
@ -29,6 +29,7 @@
|
||||
<div class="flex-grow hidden md:inline-block" />
|
||||
|
||||
<!-- <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" /> -->
|
||||
<ui-checkbox v-if="showExperimentalFeatures" v-model="settings.collapseSeries" label="Collapse Series" checkbox-bg="bg" check-color="white" small class="mr-2" @input="updateCollapseSeries" />
|
||||
<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">
|
||||
@ -72,6 +73,9 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showExperimentalFeatures() {
|
||||
return this.$store.state.showExperimentalFeatures
|
||||
},
|
||||
isGridMode() {
|
||||
return this.viewMode === 'grid'
|
||||
},
|
||||
@ -87,14 +91,6 @@ export default {
|
||||
if (this.page === 'collections') return 'Collections'
|
||||
return ''
|
||||
},
|
||||
// _keywordFilter: {
|
||||
// get() {
|
||||
// return this.$store.state.audiobooks.keywordFilter
|
||||
// },
|
||||
// set(val) {
|
||||
// this.$store.commit('audiobooks/setKeywordFilter', val)
|
||||
// }
|
||||
// },
|
||||
paramId() {
|
||||
return this.$route.params ? this.$route.params.id || '' : ''
|
||||
},
|
||||
@ -124,6 +120,9 @@ export default {
|
||||
updateFilter() {
|
||||
this.saveSettings()
|
||||
},
|
||||
updateCollapseSeries() {
|
||||
this.saveSettings()
|
||||
},
|
||||
saveSettings() {
|
||||
this.$store.dispatch('user/updateUserSettings', this.settings)
|
||||
},
|
||||
|
@ -105,6 +105,9 @@ export default {
|
||||
filterBy() {
|
||||
return this.$store.getters['user/getUserSetting']('filterBy')
|
||||
},
|
||||
collapseSeries() {
|
||||
return this.$store.getters['user/getUserSetting']('collapseSeries')
|
||||
},
|
||||
coverAspectRatio() {
|
||||
return this.$store.getters['getServerSetting']('coverAspectRatio')
|
||||
},
|
||||
@ -207,19 +210,6 @@ export default {
|
||||
this.isSelectionMode = false
|
||||
this.isSelectAll = false
|
||||
},
|
||||
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
|
||||
}
|
||||
}
|
||||
},
|
||||
selectEntity(entity) {
|
||||
if (this.entityName === 'books' || this.entityName === 'series-books') {
|
||||
this.$store.commit('toggleAudiobookSelected', entity.id)
|
||||
@ -255,6 +245,7 @@ export default {
|
||||
console.error('failed to fetch books', error)
|
||||
return null
|
||||
})
|
||||
console.log('payload', payload)
|
||||
this.isFetchingEntities = false
|
||||
if (this.pendingReset) {
|
||||
this.pendingReset = false
|
||||
@ -392,6 +383,9 @@ export default {
|
||||
searchParams.set('sort', this.orderBy)
|
||||
searchParams.set('desc', this.orderDesc ? 1 : 0)
|
||||
}
|
||||
if (this.collapseSeries) {
|
||||
searchParams.set('collapseseries', 1)
|
||||
}
|
||||
}
|
||||
return searchParams.toString()
|
||||
},
|
||||
@ -524,7 +518,6 @@ export default {
|
||||
})
|
||||
|
||||
this.$eventBus.$on('bookshelf-clear-selection', this.clearSelectedEntities)
|
||||
this.$eventBus.$on('bookshelf-select-all', this.selectAllEntities)
|
||||
this.$eventBus.$on('socket_init', this.socketInit)
|
||||
|
||||
this.$store.commit('user/addSettingsListener', { id: 'lazy-bookshelf', meth: this.settingsUpdated })
|
||||
@ -546,7 +539,6 @@ export default {
|
||||
bookshelf.removeEventListener('scroll', this.scroll)
|
||||
}
|
||||
this.$eventBus.$off('bookshelf-clear-selection', this.clearSelectedEntities)
|
||||
this.$eventBus.$off('bookshelf-select-all', this.selectAllEntities)
|
||||
this.$eventBus.$off('socket_init', this.socketInit)
|
||||
|
||||
this.$store.commit('user/removeSettingsListener', 'lazy-bookshelf')
|
||||
|
@ -12,6 +12,8 @@
|
||||
<p class="truncate text-gray-400" :style="{ fontSize: 0.8 * sizeMultiplier + 'rem' }">{{ authorFL }}</p>
|
||||
</div>
|
||||
|
||||
<div v-if="booksInSeries" class="absolute z-20 top-1.5 right-1.5 rounded-md leading-3 text-sm p-1 font-semibold text-white flex items-center justify-center" style="background-color: #cd9d49dd">{{ booksInSeries }}</div>
|
||||
|
||||
<div class="w-full h-full absolute top-0 left-0 rounded overflow-hidden z-10">
|
||||
<div v-show="audiobook && !imageReady" class="absolute top-0 left-0 w-full h-full flex items-center justify-center" :style="{ padding: sizeMultiplier * 0.5 + 'rem' }">
|
||||
<p :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }" class="font-book text-gray-300 text-center">{{ title }}</p>
|
||||
@ -113,6 +115,9 @@ export default {
|
||||
_audiobook() {
|
||||
return this.audiobook || {}
|
||||
},
|
||||
book() {
|
||||
return this._audiobook.book || {}
|
||||
},
|
||||
placeholderUrl() {
|
||||
return '/book_placeholder.jpg'
|
||||
},
|
||||
@ -122,6 +127,12 @@ export default {
|
||||
audiobookId() {
|
||||
return this._audiobook.id
|
||||
},
|
||||
series() {
|
||||
return this.book.series
|
||||
},
|
||||
libraryId() {
|
||||
return this._audiobook.libraryId
|
||||
},
|
||||
hasEbook() {
|
||||
return this._audiobook.numEbooks
|
||||
},
|
||||
@ -131,8 +142,9 @@ export default {
|
||||
processingBatch() {
|
||||
return this.store.state.processingBatch
|
||||
},
|
||||
book() {
|
||||
return this._audiobook.book || {}
|
||||
booksInSeries() {
|
||||
// Only added to audiobook object when collapseSeries is enabled
|
||||
return this._audiobook.booksInSeries
|
||||
},
|
||||
hasCover() {
|
||||
return !!this.book.cover
|
||||
@ -321,7 +333,10 @@ export default {
|
||||
this.selectBtnClick()
|
||||
} else {
|
||||
var router = this.$router || this.$nuxt.$router
|
||||
if (router) router.push(`/audiobook/${this.audiobookId}`)
|
||||
if (router) {
|
||||
if (this.booksInSeries) router.push(`/library/${this.libraryId}/series/${this.$encode(this.series)}`)
|
||||
else router.push(`/audiobook/${this.audiobookId}`)
|
||||
}
|
||||
}
|
||||
},
|
||||
editClick() {
|
||||
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<label class="flex justify-start items-start">
|
||||
<div class="bg-white border-2 rounded border-gray-400 flex flex-shrink-0 justify-center items-center focus-within:border-blue-500" :class="wrapperClass">
|
||||
<input v-model="selected" type="checkbox" class="opacity-0 absolute" />
|
||||
<svg v-if="selected" class="fill-current text-green-500 pointer-events-none" :class="svgClass" viewBox="0 0 20 20"><path d="M0 11l2-2 5 5L18 3l2 2L7 18z" /></svg>
|
||||
<label class="flex justify-start items-center cursor-pointer">
|
||||
<div class="border-2 rounded border-gray-400 flex flex-shrink-0 justify-center items-center" :class="wrapperClass">
|
||||
<input v-model="selected" type="checkbox" class="opacity-0 absolute cursor-pointer" />
|
||||
<svg v-if="selected" class="fill-current pointer-events-none" :class="svgClass" viewBox="0 0 20 20"><path d="M0 11l2-2 5 5L18 3l2 2L7 18z" /></svg>
|
||||
</div>
|
||||
<div v-if="label" class="select-none">{{ label }}</div>
|
||||
<div v-if="label" class="select-none pl-1 text-gray-100" :class="labelClass">{{ label }}</div>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
@ -12,8 +12,16 @@
|
||||
export default {
|
||||
props: {
|
||||
value: Boolean,
|
||||
label: Boolean,
|
||||
small: Boolean
|
||||
label: String,
|
||||
small: Boolean,
|
||||
checkboxBg: {
|
||||
type: String,
|
||||
default: 'white'
|
||||
},
|
||||
checkColor: {
|
||||
type: String,
|
||||
default: 'green-500'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
@ -28,12 +36,22 @@ export default {
|
||||
}
|
||||
},
|
||||
wrapperClass() {
|
||||
if (this.small) return 'w-4 h-4'
|
||||
return 'w-6 h-6'
|
||||
var classes = [`bg-${this.checkboxBg}`]
|
||||
if (this.small) classes.push('w-4 h-4')
|
||||
else classes.push('w-6 h-6')
|
||||
|
||||
return classes.join(' ')
|
||||
},
|
||||
labelClass() {
|
||||
if (this.small) return 'text-sm'
|
||||
return ''
|
||||
},
|
||||
svgClass() {
|
||||
if (this.small) return 'w-3 h-3'
|
||||
return 'w-4 h-4'
|
||||
var classes = [`text-${this.checkColor}`]
|
||||
if (this.small) classes.push('w-3 h-3')
|
||||
else classes.push('w-4 h-4')
|
||||
|
||||
return classes.join(' ')
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
|
@ -8,7 +8,8 @@ export const state = () => ({
|
||||
orderDesc: false,
|
||||
filterBy: 'all',
|
||||
playbackRate: 1,
|
||||
bookshelfCoverSize: 120
|
||||
bookshelfCoverSize: 120,
|
||||
collapseSeries: false
|
||||
},
|
||||
settingsListeners: [],
|
||||
collections: [],
|
||||
|
@ -98,7 +98,6 @@ class LibraryController {
|
||||
audiobooks = libraryHelpers.getFiltered(audiobooks, req.query.filter, req.user)
|
||||
}
|
||||
|
||||
|
||||
if (req.query.sort) {
|
||||
var orderByNumber = req.query.sort === 'book.volumeNumber'
|
||||
var direction = req.query.desc === '1' ? 'desc' : 'asc'
|
||||
@ -120,6 +119,7 @@ class LibraryController {
|
||||
}
|
||||
|
||||
// api/libraries/:id/books/all
|
||||
// TODO: Optimize this method, audiobooks are iterated through several times but can be combined
|
||||
getBooksForLibrary2(req, res) {
|
||||
var libraryId = req.library.id
|
||||
|
||||
@ -132,7 +132,8 @@ class LibraryController {
|
||||
sortBy: req.query.sort,
|
||||
sortDesc: req.query.desc === '1',
|
||||
filterBy: req.query.filter,
|
||||
minified: req.query.minified === '1'
|
||||
minified: req.query.minified === '1',
|
||||
collapseseries: req.query.collapseseries === '1'
|
||||
}
|
||||
|
||||
if (payload.filterBy) {
|
||||
@ -148,11 +149,38 @@ class LibraryController {
|
||||
})
|
||||
}
|
||||
|
||||
if (payload.collapseseries) {
|
||||
var series = {}
|
||||
// Group abs by series
|
||||
for (let i = 0; i < audiobooks.length; i++) {
|
||||
var ab = audiobooks[i]
|
||||
if (ab.book.series) {
|
||||
if (!series[ab.book.series]) series[ab.book.series] = []
|
||||
series[ab.book.series].push(ab)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort series by volume number and filter out all but the first book in series
|
||||
var seriesBooksToKeep = Object.values(series).map((_series) => {
|
||||
var sorted = naturalSort(_series).asc(_ab => _ab.book.volumeNumber)
|
||||
return sorted[0].id
|
||||
})
|
||||
// Add "booksInSeries" field to audiobook payload
|
||||
audiobooks = audiobooks.filter(ab => !ab.book.series || seriesBooksToKeep.includes(ab.id)).map(ab => {
|
||||
var abJson = payload.minified ? ab.toJSONMinified() : ab.toJSONExpanded()
|
||||
if (ab.book.series) abJson.booksInSeries = series[ab.book.series].length
|
||||
return abJson
|
||||
})
|
||||
payload.total = audiobooks.length
|
||||
} else {
|
||||
audiobooks = audiobooks.map(ab => payload.minified ? ab.toJSONMinified() : ab.toJSONExpanded())
|
||||
}
|
||||
|
||||
if (payload.limit) {
|
||||
var startIndex = payload.page * payload.limit
|
||||
audiobooks = audiobooks.slice(startIndex, startIndex + payload.limit)
|
||||
}
|
||||
payload.results = audiobooks.map(ab => payload.minified ? ab.toJSONMinified() : ab.toJSONExpanded())
|
||||
payload.results = audiobooks
|
||||
res.json(payload)
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,8 @@ class User {
|
||||
orderDesc: false,
|
||||
filterBy: 'all',
|
||||
playbackRate: 1,
|
||||
bookshelfCoverSize: 120
|
||||
bookshelfCoverSize: 120,
|
||||
collapseSeries: false
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user