Fix:Batch selecting ebooks showing play button in appbar #1235

This commit is contained in:
advplyr 2022-11-30 17:09:00 -06:00
parent d917f0e37d
commit 725f8eecdb
16 changed files with 117 additions and 89 deletions

View File

@ -45,10 +45,10 @@
</span>
</nuxt-link>
</div>
<div v-show="numLibraryItemsSelected" class="absolute top-0 left-0 w-full h-full px-4 bg-primary flex items-center">
<h1 class="text-lg md:text-2xl px-4">{{ $getString('MessageItemsSelected', [numLibraryItemsSelected]) }}</h1>
<div v-show="numMediaItemsSelected" class="absolute top-0 left-0 w-full h-full px-4 bg-primary flex items-center">
<h1 class="text-lg md:text-2xl px-4">{{ $getString('MessageItemsSelected', [numMediaItemsSelected]) }}</h1>
<div class="flex-grow" />
<ui-btn v-if="!isPodcastLibrary" color="success" :padding-x="4" small class="flex items-center h-9 mr-2" @click="playSelectedItems">
<ui-btn v-if="!isPodcastLibrary && selectedMediaItemsArePlayable" color="success" :padding-x="4" small class="flex items-center h-9 mr-2" @click="playSelectedItems">
<span class="material-icons text-2xl -ml-2 pr-1 text-white">play_arrow</span>
{{ $strings.ButtonPlay }}
</ui-btn>
@ -109,11 +109,14 @@ export default {
username() {
return this.user ? this.user.username : 'err'
},
numLibraryItemsSelected() {
return this.selectedLibraryItems.length
numMediaItemsSelected() {
return this.selectedMediaItems.length
},
selectedLibraryItems() {
return this.$store.state.selectedLibraryItems
selectedMediaItems() {
return this.$store.state.globals.selectedMediaItems
},
selectedMediaItemsArePlayable() {
return !this.selectedMediaItems.some(i => !i.hasTracks)
},
userMediaProgress() {
return this.$store.state.user.user.mediaProgress || []
@ -129,8 +132,8 @@ export default {
},
selectedIsFinished() {
// Find an item that is not finished, if none then all items finished
return !this.selectedLibraryItems.find((libraryItemId) => {
var itemProgress = this.userMediaProgress.find((lip) => lip.libraryItemId === libraryItemId)
return !this.selectedMediaItems.find((item) => {
const itemProgress = this.userMediaProgress.find((lip) => lip.libraryItemId === item.id)
return !itemProgress || !itemProgress.isFinished
})
},
@ -154,8 +157,9 @@ export default {
async playSelectedItems() {
this.$store.commit('setProcessingBatch', true)
var libraryItems = await this.$axios.$post(`/api/items/batch/get`, { libraryItemIds: this.selectedLibraryItems }).catch((error) => {
var errorMsg = error.response.data || 'Failed to get items'
const libraryItemIds = this.selectedMediaItems.map((i) => i.id)
const libraryItems = await this.$axios.$post(`/api/items/batch/get`, { libraryItemIds }).catch((error) => {
const errorMsg = error.response.data || 'Failed to get items'
console.error(errorMsg, error)
this.$toast.error(errorMsg)
return []
@ -185,20 +189,20 @@ export default {
queueItems
})
this.$store.commit('setProcessingBatch', false)
this.$store.commit('setSelectedLibraryItems', [])
this.$store.commit('globals/resetSelectedMediaItems', [])
this.$eventBus.$emit('bookshelf_clear_selection')
},
cancelSelectionMode() {
if (this.processingBatch) return
this.$store.commit('setSelectedLibraryItems', [])
this.$store.commit('globals/resetSelectedMediaItems', [])
this.$eventBus.$emit('bookshelf_clear_selection')
},
toggleBatchRead() {
this.$store.commit('setProcessingBatch', true)
var newIsFinished = !this.selectedIsFinished
var updateProgressPayloads = this.selectedLibraryItems.map((lid) => {
const newIsFinished = !this.selectedIsFinished
const updateProgressPayloads = this.selectedMediaItems.map((item) => {
return {
libraryItemId: lid,
libraryItemId: item.id,
isFinished: newIsFinished
}
})
@ -208,7 +212,7 @@ export default {
.then(() => {
this.$toast.success('Batch update success!')
this.$store.commit('setProcessingBatch', false)
this.$store.commit('setSelectedLibraryItems', [])
this.$store.commit('globals/resetSelectedMediaItems', [])
this.$eventBus.$emit('bookshelf_clear_selection')
})
.catch((error) => {
@ -218,18 +222,18 @@ export default {
})
},
batchDeleteClick() {
var audiobookText = this.numLibraryItemsSelected > 1 ? `these ${this.numLibraryItemsSelected} items` : 'this item'
var confirmMsg = `Are you sure you want to remove ${audiobookText}?\n\n*Does not delete your files, only removes the items from Audiobookshelf`
const audiobookText = this.numMediaItemsSelected > 1 ? `these ${this.numMediaItemsSelected} items` : 'this item'
const confirmMsg = `Are you sure you want to remove ${audiobookText}?\n\n*Does not delete your files, only removes the items from Audiobookshelf`
if (confirm(confirmMsg)) {
this.$store.commit('setProcessingBatch', true)
this.$axios
.$post(`/api/items/batch/delete`, {
libraryItemIds: this.selectedLibraryItems
libraryItemIds: this.selectedMediaItems.map((i) => i.id)
})
.then(() => {
this.$toast.success('Batch delete success!')
this.$store.commit('setProcessingBatch', false)
this.$store.commit('setSelectedLibraryItems', [])
this.$store.commit('globals/resetSelectedMediaItems', [])
this.$eventBus.$emit('bookshelf_clear_selection')
})
.catch((error) => {

View File

@ -89,8 +89,8 @@ export default {
var baseSize = this.isCoverSquareAspectRatio ? 192 : 120
return this.bookCoverWidth / baseSize
},
selectedLibraryItems() {
return this.$store.state.selectedLibraryItems || []
selectedMediaItems() {
return this.$store.state.globals.selectedMediaItems || []
}
},
methods: {
@ -100,15 +100,15 @@ export default {
const indexOf = shelf.shelfStartIndex + entityShelfIndex
const lastLastItemIndexSelected = this.lastItemIndexSelected
if (!this.selectedLibraryItems.includes(entity.id)) {
if (!this.selectedMediaItems.some((i) => i.id === entity.id)) {
this.lastItemIndexSelected = indexOf
} else {
this.lastItemIndexSelected = -1
}
if (shiftKey && lastLastItemIndexSelected >= 0) {
var loopStart = indexOf
var loopEnd = lastLastItemIndexSelected
let loopStart = indexOf
let loopEnd = lastLastItemIndexSelected
if (indexOf > lastLastItemIndexSelected) {
loopStart = lastLastItemIndexSelected
loopEnd = indexOf
@ -117,12 +117,12 @@ export default {
const flattenedEntitiesArray = []
this.shelves.map((s) => flattenedEntitiesArray.push(...s.entities))
var isSelecting = false
let isSelecting = false
// If any items in this range is not selected then select all otherwise unselect all
for (let i = loopStart; i <= loopEnd; i++) {
const thisEntity = flattenedEntitiesArray[i]
if (thisEntity) {
if (!this.selectedLibraryItems.includes(thisEntity.id)) {
if (!this.selectedMediaItems.some((i) => i.id === thisEntity.id)) {
isSelecting = true
break
}
@ -133,13 +133,23 @@ export default {
for (let i = loopStart; i <= loopEnd; i++) {
const thisEntity = flattenedEntitiesArray[i]
if (thisEntity) {
this.$store.commit('setLibraryItemSelected', { libraryItemId: thisEntity.id, selected: isSelecting })
const mediaItem = {
id: thisEntity.id,
mediaType: thisEntity.mediaType,
hasTracks: thisEntity.mediaType === 'podcast' || thisEntity.media.numTracks || (thisEntity.media.tracks && thisEntity.media.tracks.length)
}
this.$store.commit('globals/setMediaItemSelected', { item: mediaItem, selected: isSelecting })
} else {
console.error('Invalid entity index', i)
}
}
} else {
this.$store.commit('toggleLibraryItemSelected', entity.id)
const mediaItem = {
id: entity.id,
mediaType: entity.mediaType,
hasTracks: entity.mediaType === 'podcast' || entity.media.numTracks || (entity.media.tracks && entity.media.tracks.length)
}
this.$store.commit('globals/toggleMediaItemSelected', mediaItem)
}
this.$nextTick(() => {

View File

@ -98,7 +98,7 @@ export default {
return this.$store.state.libraries.currentLibraryId
},
isSelectionMode() {
return this.$store.getters['getNumLibraryItemsSelected'] > 0
return this.$store.getters['globals/getIsBatchSelectingMediaItems']
}
},
methods: {
@ -119,14 +119,14 @@ export default {
this.$store.commit('globals/setShowEditPodcastEpisodeModal', true)
},
updateSelectionMode(val) {
var selectedLibraryItems = this.$store.state.selectedLibraryItems
const selectedMediaItems = this.$store.state.globals.selectedMediaItems
if (this.shelf.type === 'book' || this.shelf.type === 'podcast') {
this.shelf.entities.forEach((ent) => {
var component = this.$refs[`shelf-book-${ent.id}`]
if (!component || !component.length) return
component = component[0]
component.setSelectionMode(val)
component.selected = selectedLibraryItems.includes(ent.id)
component.selected = selectedMediaItems.some((i) => i.id === ent.id)
})
} else if (this.shelf.type === 'episode') {
this.shelf.entities.forEach((ent) => {
@ -134,7 +134,7 @@ export default {
if (!component || !component.length) return
component = component[0]
component.setSelectionMode(val)
component.selected = selectedLibraryItems.includes(ent.id)
component.selected = selectedMediaItems.some((i) => i.id === ent.id)
})
}
},

View File

@ -205,7 +205,7 @@ export default {
return this.seriesProgress.libraryItemIds || []
},
isBatchSelecting() {
return this.$store.state.selectedLibraryItems.length
return this.$store.getters['globals/getIsBatchSelectingMediaItems']
},
isSeriesFinished() {
return this.seriesProgress && !!this.seriesProgress.isFinished

View File

@ -201,8 +201,8 @@ export default {
// Includes margin
return this.entityWidth + 24
},
selectedLibraryItems() {
return this.$store.state.selectedLibraryItems || []
selectedMediaItems() {
return this.$store.state.globals.selectedMediaItems || []
},
sizeMultiplier() {
var baseSize = this.isCoverSquareAspectRatio ? 192 : 120
@ -232,7 +232,7 @@ export default {
if (this.entityName === 'books' || this.entityName === 'series-books') {
var indexOf = this.entities.findIndex((ent) => ent && ent.id === entity.id)
const lastLastItemIndexSelected = this.lastItemIndexSelected
if (!this.selectedLibraryItems.includes(entity.id)) {
if (!this.selectedMediaItems.some((i) => i.id === entity.id)) {
this.lastItemIndexSelected = indexOf
} else {
this.lastItemIndexSelected = -1
@ -251,7 +251,7 @@ export default {
for (let i = loopStart; i <= loopEnd; i++) {
const thisEntity = this.entities[i]
if (thisEntity && !thisEntity.collapsedSeries) {
if (!this.selectedLibraryItems.includes(thisEntity.id)) {
if (!this.selectedMediaItems.some((i) => i.id === thisEntity.id)) {
isSelecting = true
break
}
@ -269,16 +269,27 @@ export default {
const entityComponentRef = this.entityComponentRefs[i]
if (thisEntity && entityComponentRef) {
entityComponentRef.selected = isSelecting
this.$store.commit('setLibraryItemSelected', { libraryItemId: thisEntity.id, selected: isSelecting })
const mediaItem = {
id: thisEntity.id,
mediaType: thisEntity.mediaType,
hasTracks: thisEntity.mediaType === 'podcast' || thisEntity.media.numTracks || (thisEntity.media.tracks && thisEntity.media.tracks.length)
}
this.$store.commit('globals/setMediaItemSelected', { item: mediaItem, selected: isSelecting })
} else {
console.error('Invalid entity index', i)
}
}
} else {
this.$store.commit('toggleLibraryItemSelected', entity.id)
const mediaItem = {
id: entity.id,
mediaType: entity.mediaType,
hasTracks: entity.mediaType === 'podcast' || entity.media.numTracks || (entity.media.tracks && entity.media.tracks.length)
}
this.$store.commit('globals/toggleMediaItemSelected', mediaItem)
}
var newIsSelectionMode = !!this.selectedLibraryItems.length
const newIsSelectionMode = !!this.selectedMediaItems.length
if (this.isSelectionMode !== newIsSelectionMode) {
this.isSelectionMode = newIsSelectionMode
this.updateBookSelectionMode(newIsSelectionMode)

View File

@ -82,7 +82,7 @@ export default {
return this.$store.state.globals.showBatchQuickMatchModal
},
selectedBookIds() {
return this.$store.state.selectedLibraryItems || []
return (this.$store.state.globals.selectedMediaItems || []).map((i) => i.id)
},
currentLibraryId() {
return this.$store.state.libraries.currentLibraryId

View File

@ -104,7 +104,7 @@ export default {
return this.$store.state.globals.showBatchCollectionModal
},
selectedBookIds() {
return this.$store.state.selectedLibraryItems || []
return (this.$store.state.globals.selectedMediaItems || []).map((i) => i.id)
},
currentLibraryId() {
return this.$store.state.libraries.currentLibraryId

View File

@ -61,7 +61,7 @@ export default {
return Math.floor(this.clientWidth / (this.cardWidth + 16))
},
isSelectionMode() {
return this.$store.getters['getNumLibraryItemsSelected'] > 0
return this.$store.getters['globals/getIsBatchSelectingMediaItems']
}
},
methods: {

View File

@ -77,7 +77,7 @@ export default {
return Math.floor(this.clientWidth / (this.cardWidth + 16))
},
isSelectionMode() {
return this.$store.getters['getNumLibraryItemsSelected'] > 0
return this.$store.getters['globals/getIsBatchSelectingMediaItems']
}
},
methods: {
@ -101,14 +101,14 @@ export default {
this.updateSelectionMode(this.isSelectionMode)
},
updateSelectionMode(val) {
var selectedLibraryItems = this.$store.state.selectedLibraryItems
const selectedMediaItems = this.$store.state.globals.selectedMediaItems
this.items.forEach((ent) => {
var component = this.$refs[`slider-episode-${ent.recentEpisode.id}`]
let component = this.$refs[`slider-episode-${ent.recentEpisode.id}`]
if (!component || !component.length) return
component = component[0]
component.setSelectionMode(val)
component.selected = selectedLibraryItems.includes(ent.id)
component.selected = selectedMediaItems.some((i) => i.id === ent.id)
})
},
scrolled() {

View File

@ -63,7 +63,7 @@ export default {
return Math.floor(this.clientWidth / (this.cardWidth + 16))
},
isSelectionMode() {
return this.$store.getters['getNumLibraryItemsSelected'] > 0
return this.$store.getters['globals/getIsBatchSelectingMediaItems']
}
},
methods: {
@ -82,14 +82,14 @@ export default {
this.updateSelectionMode(this.isSelectionMode)
},
updateSelectionMode(val) {
var selectedLibraryItems = this.$store.state.selectedLibraryItems
const selectedMediaItems = this.$store.state.globals.selectedMediaItems
this.items.forEach((item) => {
var component = this.$refs[`slider-item-${item.id}`]
let component = this.$refs[`slider-item-${item.id}`]
if (!component || !component.length) return
component = component[0]
component.setSelectionMode(val)
component.selected = selectedLibraryItems.includes(item.id)
component.selected = selectedMediaItems.some((i) => i.id === item.id)
})
},
scrolled() {

View File

@ -61,7 +61,7 @@ export default {
return Math.floor(this.clientWidth / (this.cardWidth + 16))
},
isSelectionMode() {
return this.$store.getters['getNumLibraryItemsSelected'] > 0
return this.$store.getters['globals/getIsBatchSelectingMediaItems']
}
},
methods: {

View File

@ -42,9 +42,8 @@ export default {
if (this.$store.state.showEditModal) {
this.$store.commit('setShowEditModal', false)
}
if (this.$store.state.selectedLibraryItems) {
this.$store.commit('setSelectedLibraryItems', [])
}
this.$store.commit('globals/resetSelectedMediaItems', [])
this.updateBodyClass()
}
},
@ -504,9 +503,9 @@ export default {
}
// Batch selecting
if (this.$store.getters['getNumLibraryItemsSelected'] && name === 'Escape') {
if (this.$store.getters['globals/getIsBatchSelectingMediaItems'] && name === 'Escape') {
// ESCAPE key cancels batch selection
this.$store.commit('setSelectedLibraryItems', [])
this.$store.commit('globals/resetSelectedMediaItems', [])
this.$eventBus.$emit('bookshelf_clear_selection')
e.preventDefault()
return

View File

@ -32,7 +32,7 @@ export default {
shelfEl.appendChild(bookComponent.$el)
if (this.isSelectionMode) {
bookComponent.setSelectionMode(true)
if (this.selectedLibraryItems.includes(bookComponent.libraryItemId) || this.isSelectAll) {
if (this.selectedMediaItems.some(i => i.id === bookComponent.libraryItemId) || this.isSelectAll) {
bookComponent.selected = true
} else {
bookComponent.selected = false
@ -89,7 +89,7 @@ export default {
}
if (this.isSelectionMode) {
instance.setSelectionMode(true)
if (instance.libraryItemId && this.selectedLibraryItems.includes(instance.libraryItemId) || this.isSelectAll) {
if (instance.libraryItemId && this.selectedMediaItems.some(i => i.id === instance.libraryItemId) || this.isSelectAll) {
instance.selected = true
}
}

View File

@ -91,11 +91,13 @@
<script>
export default {
async asyncData({ store, redirect, app }) {
if (!store.state.selectedLibraryItems.length) {
if (!store.state.globals.selectedMediaItems.length) {
return redirect('/')
}
var libraryItems = await app.$axios.$post(`/api/items/batch/get`, { libraryItemIds: store.state.selectedLibraryItems }).catch((error) => {
var errorMsg = error.response.data || 'Failed to get items'
const libraryItemIds = store.state.globals.selectedMediaItems.map((i) => i.id)
const libraryItems = await app.$axios.$post(`/api/items/batch/get`, { libraryItemIds }).catch((error) => {
const errorMsg = error.response.data || 'Failed to get items'
console.error(errorMsg, error)
return []
})

View File

@ -16,6 +16,7 @@ export const state = () => ({
selectedPlaylist: null,
selectedCollection: null,
selectedAuthor: null,
selectedMediaItems: [],
isCasting: false, // Actively casting
isChromecastInitialized: false, // Script loadeds
showBatchQuickMatchModal: false,
@ -64,6 +65,9 @@ export const getters = {
return `http://localhost:3333${rootState.routerBasePath}/api/items/${libraryItemId}/cover?token=${userToken}`
}
return `${rootState.routerBasePath}/api/items/${libraryItemId}/cover?token=${userToken}`
},
getIsBatchSelectingMediaItems: (state) => {
return state.selectedMediaItems.length
}
}
@ -134,5 +138,28 @@ export const mutations = {
},
setShowBatchQuickMatchModal(state, val) {
state.showBatchQuickMatchModal = val
},
resetSelectedMediaItems(state) {
// Vue.set(state, 'selectedMediaItems', [])
state.selectedMediaItems = []
},
toggleMediaItemSelected(state, item) {
if (state.selectedMediaItems.some(i => i.id === item.id)) {
state.selectedMediaItems = state.selectedMediaItems.filter(i => i.id !== item.id)
} else {
// const newSel = state.selectedMediaItems.concat([{...item}])
// Vue.set(state, 'selectedMediaItems', newSel)
state.selectedMediaItems.push(item)
}
},
setMediaItemSelected(state, { item, selected }) {
const index = state.selectedMediaItems.findIndex(i => i.id === item.id)
if (index && !selected) {
state.selectedMediaItems = state.selectedMediaItems.filter(i => i.id !== item.id)
} else if (selected && !index) {
state.selectedMediaItems.splice(index, 1, item)
// var newSel = state.selectedMediaItems.concat([libraryItemId])
// Vue.set(state, 'selectedMediaItems', newSel)
}
}
}

View File

@ -17,7 +17,6 @@ export const state = () => ({
showEReader: false,
selectedLibraryItem: null,
developerMode: false,
selectedLibraryItems: [],
processingBatch: false,
previousPath: '/',
showExperimentalFeatures: false,
@ -29,14 +28,10 @@ export const state = () => ({
})
export const getters = {
getIsLibraryItemSelected: state => libraryItemId => {
return !!state.selectedLibraryItems.includes(libraryItemId)
},
getServerSetting: state => key => {
if (!state.serverSettings) return null
return state.serverSettings[key]
},
getNumLibraryItemsSelected: state => state.selectedLibraryItems.length,
getLibraryItemIdStreaming: state => {
return state.streamLibraryItem ? state.streamLibraryItem.id : null
},
@ -217,26 +212,6 @@ export const mutations = {
setSelectedLibraryItem(state, val) {
Vue.set(state, 'selectedLibraryItem', val)
},
setSelectedLibraryItems(state, items) {
Vue.set(state, 'selectedLibraryItems', items)
},
toggleLibraryItemSelected(state, itemId) {
if (state.selectedLibraryItems.includes(itemId)) {
state.selectedLibraryItems = state.selectedLibraryItems.filter(a => a !== itemId)
} else {
var newSel = state.selectedLibraryItems.concat([itemId])
Vue.set(state, 'selectedLibraryItems', newSel)
}
},
setLibraryItemSelected(state, { libraryItemId, selected }) {
var isThere = state.selectedLibraryItems.includes(libraryItemId)
if (isThere && !selected) {
state.selectedLibraryItems = state.selectedLibraryItems.filter(a => a !== libraryItemId)
} else if (selected && !isThere) {
var newSel = state.selectedLibraryItems.concat([libraryItemId])
Vue.set(state, 'selectedLibraryItems', newSel)
}
},
setProcessingBatch(state, val) {
state.processingBatch = val
},