mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-06-29 16:47:45 +02:00
Add:Collapse series and collapse sub-series settings #99
This commit is contained in:
parent
a81f50878e
commit
33bdee66a3
4 changed files with 76 additions and 10 deletions
|
@ -85,6 +85,12 @@ export default {
|
||||||
filterBy() {
|
filterBy() {
|
||||||
return this.$store.getters['user/getUserSetting']('mobileFilterBy')
|
return this.$store.getters['user/getUserSetting']('mobileFilterBy')
|
||||||
},
|
},
|
||||||
|
collapseSeries() {
|
||||||
|
return this.$store.getters['user/getUserSetting']('collapseSeries')
|
||||||
|
},
|
||||||
|
collapseBookSeries() {
|
||||||
|
return this.$store.getters['user/getUserSetting']('collapseBookSeries')
|
||||||
|
},
|
||||||
isCoverSquareAspectRatio() {
|
isCoverSquareAspectRatio() {
|
||||||
return this.bookCoverAspectRatio === 1
|
return this.bookCoverAspectRatio === 1
|
||||||
},
|
},
|
||||||
|
@ -356,6 +362,9 @@ export default {
|
||||||
let searchParams = new URLSearchParams()
|
let searchParams = new URLSearchParams()
|
||||||
if (this.page === 'series-books') {
|
if (this.page === 'series-books') {
|
||||||
searchParams.set('filter', `series.${this.$encode(this.seriesId)}`)
|
searchParams.set('filter', `series.${this.$encode(this.seriesId)}`)
|
||||||
|
if (this.collapseBookSeries) {
|
||||||
|
searchParams.set('collapseseries', 1)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.filterBy && this.filterBy !== 'all') {
|
if (this.filterBy && this.filterBy !== 'all') {
|
||||||
searchParams.set('filter', this.filterBy)
|
searchParams.set('filter', this.filterBy)
|
||||||
|
|
|
@ -10,11 +10,16 @@
|
||||||
<p class="truncate" :style="{ fontSize: 0.9 * sizeMultiplier + 'rem' }">
|
<p class="truncate" :style="{ fontSize: 0.9 * sizeMultiplier + 'rem' }">
|
||||||
{{ displayTitle }}
|
{{ displayTitle }}
|
||||||
</p>
|
</p>
|
||||||
<p class="truncate text-gray-400" :style="{ fontSize: 0.8 * sizeMultiplier + 'rem' }">{{ displayAuthor || ' ' }}</p>
|
<p class="truncate text-gray-400" :style="{ fontSize: 0.8 * sizeMultiplier + 'rem' }">{{ displayLineTwo || ' ' }}</p>
|
||||||
<p v-if="displaySortLine" class="truncate text-gray-400" :style="{ fontSize: 0.8 * sizeMultiplier + 'rem' }">{{ displaySortLine }}</p>
|
<p v-if="displaySortLine" class="truncate text-gray-400" :style="{ fontSize: 0.8 * sizeMultiplier + 'rem' }">{{ displaySortLine }}</p>
|
||||||
</div>
|
</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 v-if="seriesSequenceList" class="absolute rounded-lg bg-black bg-opacity-90 box-shadow-md z-20 text-right" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem', padding: `${0.1 * sizeMultiplier}rem ${0.25 * sizeMultiplier}rem` }" style="background-color: #78350f">
|
||||||
|
<p :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }">#{{ seriesSequenceList }}</p>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="booksInSeries" class="absolute rounded-lg bg-black bg-opacity-90 box-shadow-md z-20" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem', padding: `${0.1 * sizeMultiplier}rem ${0.25 * sizeMultiplier}rem` }" style="background-color: #cd9d49dd">
|
||||||
|
<p :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }">{{ booksInSeries }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="w-full h-full absolute top-0 left-0 rounded overflow-hidden z-10">
|
<div class="w-full h-full absolute top-0 left-0 rounded overflow-hidden z-10">
|
||||||
<div v-show="libraryItem && !imageReady" class="absolute top-0 left-0 w-full h-full flex items-center justify-center" :style="{ padding: sizeMultiplier * 0.5 + 'rem' }">
|
<div v-show="libraryItem && !imageReady" class="absolute top-0 left-0 w-full h-full flex items-center justify-center" :style="{ padding: sizeMultiplier * 0.5 + 'rem' }">
|
||||||
|
@ -226,6 +231,13 @@ export default {
|
||||||
// Only added to item object when collapseSeries is enabled
|
// Only added to item object when collapseSeries is enabled
|
||||||
return this.collapsedSeries ? this.collapsedSeries.numBooks : 0
|
return this.collapsedSeries ? this.collapsedSeries.numBooks : 0
|
||||||
},
|
},
|
||||||
|
seriesSequenceList() {
|
||||||
|
return this.collapsedSeries ? this.collapsedSeries.seriesSequenceList : null
|
||||||
|
},
|
||||||
|
libraryItemIdsInSeries() {
|
||||||
|
// Only added to item object when collapseSeries is enabled
|
||||||
|
return this.collapsedSeries ? this.collapsedSeries.libraryItemIds || [] : []
|
||||||
|
},
|
||||||
displayTitle() {
|
displayTitle() {
|
||||||
if (this.recentEpisode) return this.recentEpisode.title
|
if (this.recentEpisode) return this.recentEpisode.title
|
||||||
if (this.orderBy === 'media.metadata.title' && this.sortingIgnorePrefix && this.title.toLowerCase().startsWith('the ')) {
|
if (this.orderBy === 'media.metadata.title' && this.sortingIgnorePrefix && this.title.toLowerCase().startsWith('the ')) {
|
||||||
|
@ -233,16 +245,22 @@ export default {
|
||||||
}
|
}
|
||||||
return this.title
|
return this.title
|
||||||
},
|
},
|
||||||
displayAuthor() {
|
displayLineTwo() {
|
||||||
|
if (this.recentEpisode) return this.title
|
||||||
|
if (this.collapsedSeries) return ''
|
||||||
|
if (this.isPodcast) return this.author
|
||||||
|
|
||||||
if (this.orderBy === 'media.metadata.authorNameLF') return this.authorLF
|
if (this.orderBy === 'media.metadata.authorNameLF') return this.authorLF
|
||||||
return this.author
|
return this.author
|
||||||
},
|
},
|
||||||
displaySortLine() {
|
displaySortLine() {
|
||||||
|
if (this.collapsedSeries) return null
|
||||||
if (this.orderBy === 'mtimeMs') return 'Modified ' + this.$formatDate(this._libraryItem.mtimeMs)
|
if (this.orderBy === 'mtimeMs') return 'Modified ' + this.$formatDate(this._libraryItem.mtimeMs)
|
||||||
if (this.orderBy === 'birthtimeMs') return 'Born ' + this.$formatDate(this._libraryItem.birthtimeMs)
|
if (this.orderBy === 'birthtimeMs') return 'Born ' + this.$formatDate(this._libraryItem.birthtimeMs)
|
||||||
if (this.orderBy === 'addedAt') return 'Added ' + this.$formatDate(this._libraryItem.addedAt)
|
if (this.orderBy === 'addedAt') return 'Added ' + this.$formatDate(this._libraryItem.addedAt)
|
||||||
if (this.orderBy === 'media.duration') return 'Duration: ' + this.$elapsedPrettyExtended(this.media.duration, false)
|
if (this.orderBy === 'media.duration') return 'Duration: ' + this.$elapsedPrettyExtended(this.media.duration, false)
|
||||||
if (this.orderBy === 'size') return 'Size: ' + this.$bytesPretty(this._libraryItem.size)
|
if (this.orderBy === 'size') return 'Size: ' + this.$bytesPretty(this._libraryItem.size)
|
||||||
|
if (this.orderBy === 'media.numTracks') return `${this.numEpisodes} Episodes`
|
||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
episodeProgress() {
|
episodeProgress() {
|
||||||
|
@ -435,7 +453,7 @@ export default {
|
||||||
const router = this.$router || this.$nuxt.$router
|
const router = this.$router || this.$nuxt.$router
|
||||||
if (router) {
|
if (router) {
|
||||||
if (this.recentEpisode) router.push(`/item/${this.libraryItemId}/${this.recentEpisode.id}`)
|
if (this.recentEpisode) router.push(`/item/${this.libraryItemId}/${this.recentEpisode.id}`)
|
||||||
else if (this.collapsedSeries) router.push(`/library/${this.libraryId}/series/${this.collapsedSeries.id}`)
|
else if (this.collapsedSeries) router.push(`/bookshelf/series/${this.collapsedSeries.id}`)
|
||||||
else router.push(`/item/${this.libraryItemId}`)
|
else router.push(`/item/${this.libraryItemId}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,8 @@
|
||||||
<div class="w-full h-9 bg-bg relative z-20">
|
<div class="w-full h-9 bg-bg relative z-20">
|
||||||
<div id="bookshelf-toolbar" class="absolute top-0 left-0 w-full h-full z-20 flex items-center px-2">
|
<div id="bookshelf-toolbar" class="absolute top-0 left-0 w-full h-full z-20 flex items-center px-2">
|
||||||
<div class="flex items-center w-full text-sm">
|
<div class="flex items-center w-full text-sm">
|
||||||
<nuxt-link to="/bookshelf/series" v-if="selectedSeriesName" class="pt-1">
|
|
||||||
<span class="material-icons">arrow_back</span>
|
|
||||||
</nuxt-link>
|
|
||||||
<p v-show="!selectedSeriesName" class="pt-1">{{ totalEntities }} {{ entityTitle }}</p>
|
<p v-show="!selectedSeriesName" class="pt-1">{{ totalEntities }} {{ entityTitle }}</p>
|
||||||
<p v-show="selectedSeriesName" class="ml-2pt-1">{{ selectedSeriesName }} ({{ totalEntities }})</p>
|
<p v-show="selectedSeriesName" class="ml-2 pt-1">{{ selectedSeriesName }} ({{ totalEntities }})</p>
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<span v-if="page == 'library' || seriesBookPage" class="material-icons px-2" @click="changeView">{{ !bookshelfListView ? 'view_list' : 'grid_view' }}</span>
|
<span v-if="page == 'library' || seriesBookPage" class="material-icons px-2" @click="changeView">{{ !bookshelfListView ? 'view_list' : 'grid_view' }}</span>
|
||||||
<template v-if="page === 'library'">
|
<template v-if="page === 'library'">
|
||||||
|
@ -16,11 +13,13 @@
|
||||||
</div>
|
</div>
|
||||||
<span class="material-icons px-2" @click="showSortModal = true">sort</span>
|
<span class="material-icons px-2" @click="showSortModal = true">sort</span>
|
||||||
</template>
|
</template>
|
||||||
|
<span v-if="(page == 'library' && isBookLibrary) || seriesBookPage" class="material-icons px-2" @click="showMoreMenuDialog = true">more_vert</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<modals-order-modal v-model="showSortModal" :order-by.sync="settings.mobileOrderBy" :descending.sync="settings.mobileOrderDesc" @change="updateOrder" />
|
<modals-order-modal v-model="showSortModal" :order-by.sync="settings.mobileOrderBy" :descending.sync="settings.mobileOrderDesc" @change="updateOrder" />
|
||||||
<modals-filter-modal v-model="showFilterModal" :filter-by.sync="settings.mobileFilterBy" @change="updateFilter" />
|
<modals-filter-modal v-model="showFilterModal" :filter-by.sync="settings.mobileFilterBy" @change="updateFilter" />
|
||||||
|
<modals-dialog v-model="showMoreMenuDialog" :items="menuItems" @action="clickMenuAction" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -31,7 +30,8 @@ export default {
|
||||||
showSortModal: false,
|
showSortModal: false,
|
||||||
showFilterModal: false,
|
showFilterModal: false,
|
||||||
settings: {},
|
settings: {},
|
||||||
totalEntities: 0
|
totalEntities: 0,
|
||||||
|
showMoreMenuDialog: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -44,6 +44,12 @@ export default {
|
||||||
this.$store.commit('globals/setBookshelfListView', val)
|
this.$store.commit('globals/setBookshelfListView', val)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
currentLibraryMediaType() {
|
||||||
|
return this.$store.getters['libraries/getCurrentLibraryMediaType']
|
||||||
|
},
|
||||||
|
isBookLibrary() {
|
||||||
|
return this.currentLibraryMediaType === 'book'
|
||||||
|
},
|
||||||
hasFilters() {
|
hasFilters() {
|
||||||
return this.$store.getters['user/getUserSetting']('mobileFilterBy') !== 'all'
|
return this.$store.getters['user/getUserSetting']('mobileFilterBy') !== 'all'
|
||||||
},
|
},
|
||||||
|
@ -79,9 +85,40 @@ export default {
|
||||||
},
|
},
|
||||||
isPodcast() {
|
isPodcast() {
|
||||||
return this.$store.getters['libraries/getCurrentLibraryMediaType'] === 'podcast'
|
return this.$store.getters['libraries/getCurrentLibraryMediaType'] === 'podcast'
|
||||||
|
},
|
||||||
|
menuItems() {
|
||||||
|
if (!this.isBookLibrary) return []
|
||||||
|
|
||||||
|
if (this.seriesBookPage) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
text: 'Collapse Sub-Series',
|
||||||
|
value: 'collapse_subseries',
|
||||||
|
icon: this.settings.collapseBookSeries ? 'check_box' : 'check_box_outline_blank'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
text: 'Collapse Series',
|
||||||
|
value: 'collapse_series',
|
||||||
|
icon: this.settings.collapseSeries ? 'check_box' : 'check_box_outline_blank'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
clickMenuAction(action) {
|
||||||
|
this.showMoreMenuDialog = false
|
||||||
|
if (action === 'collapse_series') {
|
||||||
|
this.settings.collapseSeries = !this.settings.collapseSeries
|
||||||
|
this.saveSettings()
|
||||||
|
} else if (action === 'collapse_subseries') {
|
||||||
|
this.settings.collapseBookSeries = !this.settings.collapseBookSeries
|
||||||
|
this.saveSettings()
|
||||||
|
}
|
||||||
|
},
|
||||||
updateOrder() {
|
updateOrder() {
|
||||||
this.saveSettings()
|
this.saveSettings()
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,9 @@ export const state = () => ({
|
||||||
mobileOrderBy: 'addedAt',
|
mobileOrderBy: 'addedAt',
|
||||||
mobileOrderDesc: true,
|
mobileOrderDesc: true,
|
||||||
mobileFilterBy: 'all',
|
mobileFilterBy: 'all',
|
||||||
playbackRate: 1
|
playbackRate: 1,
|
||||||
|
collapseSeries: false,
|
||||||
|
collapseBookSeries: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue