Add:Series sort #712

This commit is contained in:
advplyr 2022-10-29 11:17:51 -05:00
parent dc4c30d791
commit ce133cd6f2
8 changed files with 93 additions and 34 deletions

View File

@ -45,7 +45,7 @@
<ui-checkbox v-if="isLibraryPage && !isPodcastLibrary" v-model="settings.collapseSeries" label="Collapse Series" checkbox-bg="bg" check-color="white" small class="mr-2" @input="updateCollapseSeries" /> <ui-checkbox v-if="isLibraryPage && !isPodcastLibrary" v-model="settings.collapseSeries" label="Collapse Series" checkbox-bg="bg" check-color="white" small class="mr-2" @input="updateCollapseSeries" />
<controls-filter-select v-if="isLibraryPage" v-model="settings.filterBy" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateFilter" /> <controls-filter-select v-if="isLibraryPage" v-model="settings.filterBy" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateFilter" />
<controls-order-select v-if="isLibraryPage" v-model="settings.orderBy" :descending.sync="settings.orderDesc" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateOrder" /> <controls-order-select v-if="isLibraryPage" v-model="settings.orderBy" :descending.sync="settings.orderDesc" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateOrder" />
<controls-sort-select v-if="isSeriesPage" v-model="seriesSort" :descending.sync="seriesSortDesc" :items="seriesSortItems" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateSeriesSort" /> <controls-sort-select v-if="isSeriesPage" v-model="seriesSortBy" :descending.sync="seriesSortDesc" :items="seriesSortItems" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateSeriesSort" />
<ui-btn v-if="isIssuesFilter && userCanDelete" :loading="processingIssues" color="error" small class="ml-4" @click="removeAllIssues">Remove All {{ numShowing }} {{ entityName }}</ui-btn> <ui-btn v-if="isIssuesFilter && userCanDelete" :loading="processingIssues" color="error" small class="ml-4" @click="removeAllIssues">Remove All {{ numShowing }} {{ entityName }}</ui-btn>
</template> </template>
@ -85,19 +85,17 @@ export default {
processingSeries: false, processingSeries: false,
processingIssues: false, processingIssues: false,
processingAuthors: false, processingAuthors: false,
seriesSort: 'name',
seriesSortDesc: false,
seriesSortItems: [ seriesSortItems: [
{ {
text: 'Name', text: 'Name',
value: 'name' value: 'name'
}, },
{ {
text: '# of Books', text: 'Number of Books',
value: 'numBooks' value: 'numBooks'
}, },
{ {
text: 'Added At', text: 'Date Added',
value: 'addedAt' value: 'addedAt'
}, },
{ {
@ -169,6 +167,22 @@ export default {
}, },
isIssuesFilter() { isIssuesFilter() {
return this.filterBy === 'issues' && this.$route.query.filter === 'issues' return this.filterBy === 'issues' && this.$route.query.filter === 'issues'
},
seriesSortBy: {
get() {
return this.$store.state.libraries.seriesSortBy
},
set(val) {
this.$store.commit('libraries/setSeriesSortBy', val)
}
},
seriesSortDesc: {
get() {
return this.$store.state.libraries.seriesSortDesc
},
set(val) {
this.$store.commit('libraries/setSeriesSortDesc', val)
}
} }
}, },
methods: { methods: {
@ -249,7 +263,6 @@ export default {
this.saveSettings() this.saveSettings()
}, },
updateSeriesSort() { updateSeriesSort() {
this.$store.commit('libraries/setSeriesSort', { sort: this.seriesSort, desc: this.seriesSortDesc })
this.$eventBus.$emit('series-sort-updated') this.$eventBus.$emit('series-sort-updated')
}, },
updateCollapseSeries() { updateCollapseSeries() {

View File

@ -99,7 +99,7 @@ export default {
return this.page return this.page
}, },
seriesSortBy() { seriesSortBy() {
return this.$store.state.libraries.seriesSort return this.$store.state.libraries.seriesSortBy
}, },
seriesSortDesc() { seriesSortDesc() {
return this.$store.state.libraries.seriesSortDesc return this.$store.state.libraries.seriesSortDesc

View File

@ -20,6 +20,7 @@
</div> </div>
<div v-else class="absolute z-30 left-0 right-0 mx-auto -bottom-8 h-8 py-1 rounded-md text-center"> <div v-else class="absolute z-30 left-0 right-0 mx-auto -bottom-8 h-8 py-1 rounded-md text-center">
<p class="truncate" :style="{ fontSize: labelFontSize + 'rem' }">{{ displayTitle }}</p> <p class="truncate" :style="{ fontSize: labelFontSize + 'rem' }">{{ displayTitle }}</p>
<p v-if="displaySortLine" class="truncate text-gray-400" :style="{ fontSize: 0.8 * sizeMultiplier + 'rem' }">{{ displaySortLine }}</p>
</div> </div>
</div> </div>
</template> </template>
@ -40,7 +41,8 @@ export default {
type: Object, type: Object,
default: () => null default: () => null
}, },
sortingIgnorePrefix: Boolean sortingIgnorePrefix: Boolean,
orderBy: String
}, },
data() { data() {
return { return {
@ -52,6 +54,9 @@ export default {
} }
}, },
computed: { computed: {
dateFormat() {
return this.store.state.serverSettings.dateFormat
},
labelFontSize() { labelFontSize() {
if (this.width < 160) return 0.75 if (this.width < 160) return 0.75
return 0.875 return 0.875
@ -73,12 +78,24 @@ export default {
if (this.sortingIgnorePrefix) return this.nameIgnorePrefix || this.title if (this.sortingIgnorePrefix) return this.nameIgnorePrefix || this.title
return this.title return this.title
}, },
displaySortLine() {
if (this.orderBy === 'addedAt') {
// return this.addedAt
return 'Added ' + this.$formatDate(this.addedAt, this.dateFormat)
} else if (this.orderBy === 'totalDuration') {
return 'Duration: ' + this.$elapsedPrettyExtended(this.totalDuration, false)
}
return null
},
books() { books() {
return this.series ? this.series.books || [] : [] return this.series ? this.series.books || [] : []
}, },
addedAt() { addedAt() {
return this.series ? this.series.addedAt : 0 return this.series ? this.series.addedAt : 0
}, },
totalDuration() {
return this.series ? this.series.totalDuration : 0
},
seriesBookProgress() { seriesBookProgress() {
return this.books return this.books
.map((libraryItem) => { .map((libraryItem) => {

View File

@ -59,6 +59,8 @@ export default {
if (this.entityName === 'books') { if (this.entityName === 'books') {
props.filterBy = this.filterBy props.filterBy = this.filterBy
props.orderBy = this.orderBy props.orderBy = this.orderBy
} else if (this.entityName === 'series') {
props.orderBy = this.seriesSortBy
} }
var _this = this var _this = this

View File

@ -14,8 +14,13 @@ export default {
return redirect('/oops?message=Library not found') return redirect('/oops?message=Library not found')
} }
// Set series sort by
if (query.sort && params.id === 'series') {
store.commit('libraries/setSeriesSortBy', query.sort)
store.commit('libraries/setSeriesSortDesc', !!query.desc)
}
// Set filter by // Set filter by
if (query.filter) { if (query.filter && params.id !== 'series') {
store.dispatch('user/updateUserSettings', { filterBy: query.filter }) store.dispatch('user/updateUserSettings', { filterBy: query.filter })
} }

View File

@ -9,7 +9,7 @@ export const state = () => ({
issues: 0, issues: 0,
folderLastUpdate: 0, folderLastUpdate: 0,
filterData: null, filterData: null,
seriesSort: 'name', seriesSortBy: 'name',
seriesSortDesc: false seriesSortDesc: false
}) })
@ -292,8 +292,10 @@ export const mutations = {
} }
} }
}, },
setSeriesSort(state, { sort, desc }) { setSeriesSortBy(state, sortBy) {
state.seriesSort = sort state.seriesSortBy = sortBy
state.seriesSortDesc = desc },
setSeriesSortDesc(state, sortDesc) {
state.seriesSortDesc = sortDesc
} }
} }

View File

@ -222,7 +222,7 @@ class LibraryController {
} }
if (payload.collapseseries) { if (payload.collapseseries) {
libraryItems = libraryHelpers.collapseBookSeries(libraryItems) libraryItems = libraryHelpers.collapseBookSeries(libraryItems, this.db.series)
payload.total = libraryItems.length payload.total = libraryItems.length
} else if (filterSeries) { } else if (filterSeries) {
// Book media when filtering series will include series object on media metadata // Book media when filtering series will include series object on media metadata
@ -275,10 +275,24 @@ class LibraryController {
minified: req.query.minified === '1' minified: req.query.minified === '1'
} }
var series = libraryHelpers.getSeriesFromBooks(libraryItems, payload.minified) var series = libraryHelpers.getSeriesFromBooks(libraryItems, this.db.series, payload.minified)
series = sort(series).asc(s => { const direction = payload.sortDesc ? 'desc' : 'asc'
return this.db.serverSettings.sortingIgnorePrefix ? s.nameIgnorePrefix : s.name series = naturalSort(series).by([
}) {
[direction]: (se) => {
if (payload.sortBy === 'numBooks') {
return se.books.length
} else if (payload.sortBy === 'totalDuration') {
return se.totalDuration
} else if (payload.sortBy === 'addedAt') {
return se.addedAt
} else { // sort by name
return this.db.serverSettings.sortingIgnorePrefix ? se.nameIgnorePrefix : se.name
}
}
}
])
payload.total = series.length payload.total = series.length
if (payload.limit) { if (payload.limit) {

View File

@ -1,5 +1,5 @@
const { sort, createNewSortInstance } = require('../libs/fastSort') const { sort, createNewSortInstance } = require('../libs/fastSort')
const { getTitleIgnorePrefix } = require('../utils/index') const { getTitleIgnorePrefix, isNullOrNaN } = require('../utils/index')
const naturalSort = createNewSortInstance({ const naturalSort = createNewSortInstance({
comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare
}) })
@ -114,23 +114,29 @@ module.exports = {
return data return data
}, },
getSeriesFromBooks(books, minified = false) { getSeriesFromBooks(books, allSeries, minified = false) {
var _series = {} const _series = {}
books.forEach((libraryItem) => { books.forEach((libraryItem) => {
var bookSeries = libraryItem.media.metadata.series || [] const bookSeries = libraryItem.media.metadata.series || []
bookSeries.forEach((series) => { bookSeries.forEach((bookSeriesObj) => {
var abJson = minified ? libraryItem.toJSONMinified() : libraryItem.toJSONExpanded() const series = allSeries.find(se => se.id === bookSeriesObj.id)
abJson.sequence = series.sequence
if (!_series[series.id]) { const abJson = minified ? libraryItem.toJSONMinified() : libraryItem.toJSONExpanded()
_series[series.id] = { abJson.sequence = bookSeriesObj.sequence
id: series.id, if (!_series[bookSeriesObj.id]) {
name: series.name, _series[bookSeriesObj.id] = {
nameIgnorePrefix: getTitleIgnorePrefix(series.name), id: bookSeriesObj.id,
name: bookSeriesObj.name,
nameIgnorePrefix: getTitleIgnorePrefix(bookSeriesObj.name),
type: 'series', type: 'series',
books: [abJson] books: [abJson],
addedAt: series ? series.addedAt : 0,
totalDuration: isNullOrNaN(abJson.media.duration) ? 0 : Number(abJson.media.duration)
} }
} else { } else {
_series[series.id].books.push(abJson) _series[bookSeriesObj.id].books.push(abJson)
_series[bookSeriesObj.id].totalDuration += isNullOrNaN(abJson.media.duration) ? 0 : Number(abJson.media.duration)
} }
}) })
}) })
@ -209,8 +215,8 @@ module.exports = {
return totalSize return totalSize
}, },
collapseBookSeries(libraryItems) { collapseBookSeries(libraryItems, series) {
var seriesObjects = this.getSeriesFromBooks(libraryItems, true) var seriesObjects = this.getSeriesFromBooks(libraryItems, series, true)
var seriesToUse = {} var seriesToUse = {}
var libraryItemIdsToHide = [] var libraryItemIdsToHide = []
seriesObjects.forEach((series) => { seriesObjects.forEach((series) => {