mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-07-03 18:44:41 +02:00
Add bookshelf list view
This commit is contained in:
parent
119bfd6c98
commit
105451ebf1
8 changed files with 471 additions and 255 deletions
|
@ -273,220 +273,3 @@ class AbsDownloader : Plugin() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//
|
|
||||||
// @PluginMethod
|
|
||||||
// fun download(call: PluginCall) {
|
|
||||||
// var audiobookId = call.data.getString("audiobookId", "audiobook").toString()
|
|
||||||
// var url = call.data.getString("downloadUrl", "unknown").toString()
|
|
||||||
// var coverDownloadUrl = call.data.getString("coverDownloadUrl", "").toString()
|
|
||||||
// var title = call.data.getString("title", "Audiobook").toString()
|
|
||||||
// var filename = call.data.getString("filename", "audiobook.mp3").toString()
|
|
||||||
// var coverFilename = call.data.getString("coverFilename", "cover.png").toString()
|
|
||||||
// var downloadFolderUrl = call.data.getString("downloadFolderUrl", "").toString()
|
|
||||||
// var folder = DocumentFileCompat.fromUri(context, Uri.parse(downloadFolderUrl))!!
|
|
||||||
// Log.d(tag, "Called download: $url | Folder: ${folder.name} | $downloadFolderUrl")
|
|
||||||
//
|
|
||||||
// var dlfilename = audiobookId + "." + File(filename).extension
|
|
||||||
// var coverdlfilename = audiobookId + "." + File(coverFilename).extension
|
|
||||||
// Log.d(tag, "DL Filename $dlfilename | Cover DL Filename $coverdlfilename")
|
|
||||||
//
|
|
||||||
// var canWriteToFolder = folder.canWrite()
|
|
||||||
// if (!canWriteToFolder) {
|
|
||||||
// Log.e(tag, "Error Cannot Write to Folder ${folder.baseName}")
|
|
||||||
// val ret = JSObject()
|
|
||||||
// ret.put("error", "Cannot write to ${folder.baseName}")
|
|
||||||
// call.resolve(ret)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var dlRequest = DownloadManager.Request(Uri.parse(url))
|
|
||||||
// dlRequest.setTitle("Ab: $title")
|
|
||||||
// dlRequest.setDescription("Downloading to ${folder.name}")
|
|
||||||
// dlRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
|
||||||
// dlRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, dlfilename)
|
|
||||||
//
|
|
||||||
// var audiobookDownloadId = downloadManager.enqueue(dlRequest)
|
|
||||||
// var coverDownloadId:Long? = null
|
|
||||||
//
|
|
||||||
// if (coverDownloadUrl != "") {
|
|
||||||
// var coverDlRequest = DownloadManager.Request(Uri.parse(coverDownloadUrl))
|
|
||||||
// coverDlRequest.setTitle("Cover: $title")
|
|
||||||
// coverDlRequest.setDescription("Downloading to ${folder.name}")
|
|
||||||
// coverDlRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION)
|
|
||||||
// coverDlRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, coverdlfilename)
|
|
||||||
// coverDownloadId = downloadManager.enqueue(coverDlRequest)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var progressReceiver : (id:Long, prog: Long) -> Unit = { id:Long, prog: Long ->
|
|
||||||
// if (id == audiobookDownloadId) {
|
|
||||||
// var jsobj = JSObject()
|
|
||||||
// jsobj.put("audiobookId", audiobookId)
|
|
||||||
// jsobj.put("progress", prog)
|
|
||||||
// notifyListeners("onDownloadProgress", jsobj)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var coverDocFile:DocumentFile? = null
|
|
||||||
//
|
|
||||||
// var doneReceiver : (id:Long, success: Boolean) -> Unit = { id:Long, success: Boolean ->
|
|
||||||
// Log.d(tag, "RECEIVER DONE $id, SUCCES? $success")
|
|
||||||
// var docfile:DocumentFile? = null
|
|
||||||
//
|
|
||||||
// // Download was complete, now find downloaded file
|
|
||||||
// if (id == coverDownloadId) {
|
|
||||||
// docfile = DocumentFileCompat.fromPublicFolder(context, PublicDirectory.DOWNLOADS, coverdlfilename)
|
|
||||||
// Log.d(tag, "Move Cover File ${docfile?.name}")
|
|
||||||
//
|
|
||||||
// // For unknown reason, Android 10 test was using the title set in "setTitle" for the dl manager as the filename
|
|
||||||
// // check if this was the case
|
|
||||||
// if (docfile?.name == null) {
|
|
||||||
// docfile = DocumentFileCompat.fromPublicFolder(context, PublicDirectory.DOWNLOADS, "Cover: $title")
|
|
||||||
// Log.d(tag, "Cover File name attempt 2 ${docfile?.name}")
|
|
||||||
// }
|
|
||||||
// } else if (id == audiobookDownloadId) {
|
|
||||||
// docfile = DocumentFileCompat.fromPublicFolder(context, PublicDirectory.DOWNLOADS, dlfilename)
|
|
||||||
// Log.d(tag, "Move Audiobook File ${docfile?.name}")
|
|
||||||
//
|
|
||||||
// if (docfile?.name == null) {
|
|
||||||
// docfile = DocumentFileCompat.fromPublicFolder(context, PublicDirectory.DOWNLOADS, "Ab: $title")
|
|
||||||
// Log.d(tag, "File name attempt 2 ${docfile?.name}")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Callback for moving the downloaded file
|
|
||||||
// var callback = object : FileCallback() {
|
|
||||||
// override fun onPrepare() {
|
|
||||||
// Log.d(tag, "PREPARING MOVE FILE")
|
|
||||||
// }
|
|
||||||
// override fun onFailed(errorCode:ErrorCode) {
|
|
||||||
// Log.e(tag, "FAILED MOVE FILE $errorCode")
|
|
||||||
//
|
|
||||||
// docfile?.delete()
|
|
||||||
// coverDocFile?.delete()
|
|
||||||
//
|
|
||||||
// if (id == audiobookDownloadId) {
|
|
||||||
// var jsobj = JSObject()
|
|
||||||
// jsobj.put("audiobookId", audiobookId)
|
|
||||||
// jsobj.put("error", "Move failed")
|
|
||||||
// notifyListeners("onDownloadFailed", jsobj)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// override fun onCompleted(result:Any) {
|
|
||||||
// var resultDocFile = result as DocumentFile
|
|
||||||
// var simplePath = resultDocFile.getSimplePath(context)
|
|
||||||
// var storageId = resultDocFile.getStorageId(context)
|
|
||||||
// var size = resultDocFile.length()
|
|
||||||
// Log.d(tag, "Finished Moving File, NAME: ${resultDocFile.name} | URI:${resultDocFile.uri} | AbsolutePath:${resultDocFile.getAbsolutePath(context)} | $storageId | SimplePath: $simplePath")
|
|
||||||
//
|
|
||||||
// var abFolder = folder.findFolder(title)
|
|
||||||
// var jsobj = JSObject()
|
|
||||||
// jsobj.put("audiobookId", audiobookId)
|
|
||||||
// jsobj.put("downloadId", id)
|
|
||||||
// jsobj.put("storageId", storageId)
|
|
||||||
// jsobj.put("storageType", resultDocFile.getStorageType(context))
|
|
||||||
// jsobj.put("folderUrl", abFolder?.uri)
|
|
||||||
// jsobj.put("folderName", abFolder?.name)
|
|
||||||
// jsobj.put("downloadFolderUrl", downloadFolderUrl)
|
|
||||||
// jsobj.put("contentUrl", resultDocFile.uri)
|
|
||||||
// jsobj.put("basePath", resultDocFile.getBasePath(context))
|
|
||||||
// jsobj.put("filename", filename)
|
|
||||||
// jsobj.put("simplePath", simplePath)
|
|
||||||
// jsobj.put("size", size)
|
|
||||||
//
|
|
||||||
// if (resultDocFile.name == filename) {
|
|
||||||
// Log.d(tag, "Audiobook Finishing Moving")
|
|
||||||
// } else if (resultDocFile.name == coverFilename) {
|
|
||||||
// coverDocFile = docfile
|
|
||||||
// Log.d(tag, "Audiobook Cover Finished Moving")
|
|
||||||
// jsobj.put("isCover", true)
|
|
||||||
// }
|
|
||||||
// notifyListeners("onDownloadComplete", jsobj)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // After file is downloaded, move the files into an audiobook directory inside the user selected folder
|
|
||||||
// if (id == coverDownloadId) {
|
|
||||||
// docfile?.moveFileTo(context, folder, FileDescription(coverFilename, title, MimeType.IMAGE), callback)
|
|
||||||
// } else if (id == audiobookDownloadId) {
|
|
||||||
// docfile?.moveFileTo(context, folder, FileDescription(filename, title, MimeType.AUDIO), callback)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var progressUpdater = DownloadProgressUpdater(downloadManager, audiobookDownloadId, progressReceiver, doneReceiver)
|
|
||||||
// progressUpdater.run()
|
|
||||||
// if (coverDownloadId != null) {
|
|
||||||
// var coverProgressUpdater = DownloadProgressUpdater(downloadManager, coverDownloadId, progressReceiver, doneReceiver)
|
|
||||||
// coverProgressUpdater.run()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// val ret = JSObject()
|
|
||||||
// ret.put("audiobookDownloadId", audiobookDownloadId)
|
|
||||||
// ret.put("coverDownloadId", coverDownloadId)
|
|
||||||
// call.resolve(ret)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//internal class DownloadProgressUpdater(private val manager: DownloadManager, private val downloadId: Long, private var receiver: (Long, Long) -> Unit, private var doneReceiver: (Long, Boolean) -> Unit) : Thread() {
|
|
||||||
// private val query: DownloadManager.Query = DownloadManager.Query()
|
|
||||||
// private var totalBytes: Int = 0
|
|
||||||
// private var TAG = "DownloadProgressUpdater"
|
|
||||||
//
|
|
||||||
// init {
|
|
||||||
// query.setFilterById(this.downloadId)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun run() {
|
|
||||||
// Log.d(TAG, "RUN FOR ID $downloadId")
|
|
||||||
// var keepRunning = true
|
|
||||||
// var increment = 0
|
|
||||||
// while (keepRunning) {
|
|
||||||
// Thread.sleep(500)
|
|
||||||
// increment++
|
|
||||||
//
|
|
||||||
// if (increment % 4 == 0) {
|
|
||||||
// Log.d(TAG, "Loop $increment : $downloadId")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// manager.query(query).use {
|
|
||||||
// if (it.moveToFirst()) {
|
|
||||||
// //get total bytes of the file
|
|
||||||
// if (totalBytes <= 0) {
|
|
||||||
// totalBytes = it.getInt(it.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
|
|
||||||
// if (totalBytes <= 0) {
|
|
||||||
// Log.e(TAG, "Download Is 0 Bytes $downloadId")
|
|
||||||
// doneReceiver(downloadId, false)
|
|
||||||
// keepRunning = false
|
|
||||||
// this.interrupt()
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// val downloadStatus = it.getInt(it.getColumnIndex(DownloadManager.COLUMN_STATUS))
|
|
||||||
// val bytesDownloadedSoFar = it.getInt(it.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
|
|
||||||
//
|
|
||||||
// if (increment % 4 == 0) {
|
|
||||||
// Log.d(TAG, "BYTES $increment : $downloadId : $bytesDownloadedSoFar : TOTAL: $totalBytes")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (downloadStatus == DownloadManager.STATUS_SUCCESSFUL || downloadStatus == DownloadManager.STATUS_FAILED) {
|
|
||||||
// if (downloadStatus == DownloadManager.STATUS_SUCCESSFUL) {
|
|
||||||
// doneReceiver(downloadId, true)
|
|
||||||
// } else {
|
|
||||||
// doneReceiver(downloadId, false)
|
|
||||||
// }
|
|
||||||
// keepRunning = false
|
|
||||||
// this.interrupt()
|
|
||||||
// } else {
|
|
||||||
// //update progress
|
|
||||||
// val percentProgress = ((bytesDownloadedSoFar * 100L) / totalBytes)
|
|
||||||
// receiver(downloadId, percentProgress)
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// Log.e(TAG, "NOT FOUND IN QUERY")
|
|
||||||
// keepRunning = false
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="bookshelf" class="w-full max-w-full h-full">
|
<div id="bookshelf" class="w-full max-w-full h-full">
|
||||||
<template v-for="shelf in totalShelves">
|
<template v-for="shelf in totalShelves">
|
||||||
<div :key="shelf" class="w-full px-2 bookshelfRow relative" :id="`shelf-${shelf - 1}`" :style="{ height: shelfHeight + 'px' }">
|
<div :key="shelf" class="w-full px-2 relative" :class="showBookshelfListView ? '' : 'bookshelfRow'" :id="`shelf-${shelf - 1}`" :style="{ height: shelfHeight + 'px' }">
|
||||||
<div class="bookshelfDivider w-full absolute bottom-0 left-0 z-30" style="min-height: 16px" :class="`h-${shelfDividerHeightIndex}`" />
|
<div v-if="!showBookshelfListView" class="bookshelfDivider w-full absolute bottom-0 left-0 z-30" style="min-height: 16px" :class="`h-${shelfDividerHeightIndex}`" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -28,9 +28,8 @@ export default {
|
||||||
bookshelfWidth: 0,
|
bookshelfWidth: 0,
|
||||||
bookshelfMarginLeft: 0,
|
bookshelfMarginLeft: 0,
|
||||||
shelvesPerPage: 0,
|
shelvesPerPage: 0,
|
||||||
entitiesPerShelf: 8,
|
entitiesPerShelf: 2,
|
||||||
currentPage: 0,
|
currentPage: 0,
|
||||||
currentBookWidth: 0,
|
|
||||||
booksPerFetch: 20,
|
booksPerFetch: 20,
|
||||||
initialized: false,
|
initialized: false,
|
||||||
currentSFQueryString: null,
|
currentSFQueryString: null,
|
||||||
|
@ -46,6 +45,11 @@ export default {
|
||||||
localLibraryItems: []
|
localLibraryItems: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
showBookshelfListView(newVal) {
|
||||||
|
this.resetEntities()
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
user() {
|
user() {
|
||||||
return this.$store.state.user.user
|
return this.$store.state.user.user
|
||||||
|
@ -57,6 +61,12 @@ export default {
|
||||||
if (this.isBookEntity) return 4
|
if (this.isBookEntity) return 4
|
||||||
return 6
|
return 6
|
||||||
},
|
},
|
||||||
|
bookshelfListView() {
|
||||||
|
return this.$store.state.globals.bookshelfListView
|
||||||
|
},
|
||||||
|
showBookshelfListView() {
|
||||||
|
return this.isBookEntity && this.bookshelfListView
|
||||||
|
},
|
||||||
entityName() {
|
entityName() {
|
||||||
return this.page
|
return this.page
|
||||||
},
|
},
|
||||||
|
@ -93,10 +103,12 @@ export default {
|
||||||
return this.bookWidth * 1.6
|
return this.bookWidth * 1.6
|
||||||
},
|
},
|
||||||
entityWidth() {
|
entityWidth() {
|
||||||
|
if (this.showBookshelfListView) return this.bookshelfWidth - 16
|
||||||
if (this.isBookEntity) return this.bookWidth
|
if (this.isBookEntity) return this.bookWidth
|
||||||
return this.bookWidth * 2
|
return this.bookWidth * 2
|
||||||
},
|
},
|
||||||
entityHeight() {
|
entityHeight() {
|
||||||
|
if (this.showBookshelfListView) return 88
|
||||||
return this.bookHeight
|
return this.bookHeight
|
||||||
},
|
},
|
||||||
currentLibraryId() {
|
currentLibraryId() {
|
||||||
|
@ -106,9 +118,11 @@ export default {
|
||||||
return this.$store.getters['libraries/getCurrentLibraryMediaType']
|
return this.$store.getters['libraries/getCurrentLibraryMediaType']
|
||||||
},
|
},
|
||||||
shelfHeight() {
|
shelfHeight() {
|
||||||
|
if (this.showBookshelfListView) return this.entityHeight
|
||||||
return this.entityHeight + 40
|
return this.entityHeight + 40
|
||||||
},
|
},
|
||||||
totalEntityCardWidth() {
|
totalEntityCardWidth() {
|
||||||
|
if (this.showBookshelfListView) return this.entityWidth
|
||||||
// Includes margin
|
// Includes margin
|
||||||
return this.entityWidth + 24
|
return this.entityWidth + 24
|
||||||
}
|
}
|
||||||
|
@ -154,16 +168,6 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < payload.results.length; i++) {
|
for (let i = 0; i < payload.results.length; i++) {
|
||||||
if (this.entityName === 'books' || this.entityName === 'series-books') {
|
|
||||||
// Check if has download and append download obj
|
|
||||||
// var download = this.downloads.find((dl) => dl.id === payload.results[i].id)
|
|
||||||
// if (download) {
|
|
||||||
// var dl = { ...download }
|
|
||||||
// delete dl.audiobook
|
|
||||||
// payload.results[i].download = dl
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = i + startIndex
|
var index = i + startIndex
|
||||||
this.entities[index] = payload.results[i]
|
this.entities[index] = payload.results[i]
|
||||||
if (this.entityComponentRefs[index]) {
|
if (this.entityComponentRefs[index]) {
|
||||||
|
@ -294,12 +298,11 @@ export default {
|
||||||
var { clientHeight, clientWidth } = bookshelf
|
var { clientHeight, clientWidth } = bookshelf
|
||||||
this.bookshelfHeight = clientHeight
|
this.bookshelfHeight = clientHeight
|
||||||
this.bookshelfWidth = clientWidth
|
this.bookshelfWidth = clientWidth
|
||||||
this.entitiesPerShelf = Math.floor((this.bookshelfWidth - 16) / this.totalEntityCardWidth)
|
this.entitiesPerShelf = this.showBookshelfListView ? 1 : Math.floor((this.bookshelfWidth - 16) / this.totalEntityCardWidth)
|
||||||
|
|
||||||
this.shelvesPerPage = Math.ceil(this.bookshelfHeight / this.shelfHeight) + 2
|
this.shelvesPerPage = Math.ceil(this.bookshelfHeight / this.shelfHeight) + 2
|
||||||
this.bookshelfMarginLeft = (this.bookshelfWidth - this.entitiesPerShelf * this.totalEntityCardWidth) / 2
|
this.bookshelfMarginLeft = (this.bookshelfWidth - this.entitiesPerShelf * this.totalEntityCardWidth) / 2
|
||||||
|
|
||||||
this.currentBookWidth = this.bookWidth
|
|
||||||
if (this.totalEntities) {
|
if (this.totalEntities) {
|
||||||
this.totalShelves = Math.ceil(this.totalEntities / this.entitiesPerShelf)
|
this.totalShelves = Math.ceil(this.totalEntities / this.entitiesPerShelf)
|
||||||
}
|
}
|
||||||
|
|
428
components/cards/LazyListBookCard.vue
Normal file
428
components/cards/LazyListBookCard.vue
Normal file
|
@ -0,0 +1,428 @@
|
||||||
|
<template>
|
||||||
|
<div ref="card" :id="`book-card-${index}`" :style="{ minWidth: width + 'px', maxWidth: width + 'px', height: height + 'px' }" class="rounded-sm z-10 cursor-pointer py-1" @click="clickCard">
|
||||||
|
<div class="h-full flex">
|
||||||
|
<div class="w-20 h-20 relative" style="min-width: 80px; max-width: 80px">
|
||||||
|
<!-- When cover image does not fill -->
|
||||||
|
<div v-show="showCoverBg" class="absolute top-0 left-0 w-full h-full overflow-hidden rounded-sm bg-primary">
|
||||||
|
<div class="absolute cover-bg" ref="coverBg" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full h-full absolute top-0 left-0">
|
||||||
|
<img v-show="libraryItem" ref="cover" :src="bookCoverSrc" class="w-full h-full transition-opacity duration-300" :class="showCoverBg ? 'object-contain' : 'object-fill'" @load="imageLoaded" :style="{ opacity: imageReady ? 1 : 0 }" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- No progress shown for collapsed series in library -->
|
||||||
|
<div class="absolute bottom-0 left-0 h-1 shadow-sm max-w-full z-10 rounded-b" :class="itemIsFinished ? 'bg-success' : 'bg-yellow-400'" :style="{ width: width * userProgressPercent + 'px' }"></div>
|
||||||
|
|
||||||
|
<div v-if="localLibraryItem || isLocal" class="absolute top-0 right-0 z-20" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem', padding: `${0.1 * sizeMultiplier}rem ${0.25 * sizeMultiplier}rem` }">
|
||||||
|
<span class="material-icons text-2xl text-success">{{ isLocalOnly ? 'task' : 'download_done' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow px-2">
|
||||||
|
<p class="whitespace-normal" :style="{ fontSize: 0.8 * sizeMultiplier + 'rem' }">
|
||||||
|
{{ displayTitle }}
|
||||||
|
</p>
|
||||||
|
<p class="truncate text-gray-400" :style="{ fontSize: 0.7 * sizeMultiplier + 'rem' }">{{ displayAuthor }}</p>
|
||||||
|
<p v-if="displaySortLine" class="truncate text-gray-400" :style="{ fontSize: 0.7 * sizeMultiplier + 'rem' }">{{ displaySortLine }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Capacitor } from '@capacitor/core'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
index: Number,
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
default: 120
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
default: 192
|
||||||
|
},
|
||||||
|
bookCoverAspectRatio: Number,
|
||||||
|
showVolumeNumber: Boolean,
|
||||||
|
bookshelfView: Number,
|
||||||
|
bookMount: {
|
||||||
|
// Book can be passed as prop or set with setEntity()
|
||||||
|
type: Object,
|
||||||
|
default: () => null
|
||||||
|
},
|
||||||
|
orderBy: String,
|
||||||
|
filterBy: String,
|
||||||
|
sortingIgnorePrefix: Boolean
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isProcessingReadUpdate: false,
|
||||||
|
libraryItem: null,
|
||||||
|
imageReady: false,
|
||||||
|
rescanning: false,
|
||||||
|
selected: false,
|
||||||
|
isSelectionMode: false,
|
||||||
|
showCoverBg: false,
|
||||||
|
localLibraryItem: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
bookMount: {
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
this.libraryItem = newVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
showExperimentalFeatures() {
|
||||||
|
return this.store.state.showExperimentalFeatures
|
||||||
|
},
|
||||||
|
_libraryItem() {
|
||||||
|
return this.libraryItem || {}
|
||||||
|
},
|
||||||
|
isLocal() {
|
||||||
|
return !!this._libraryItem.isLocal
|
||||||
|
},
|
||||||
|
isLocalOnly() {
|
||||||
|
// Local item with no server match
|
||||||
|
return this.isLocal && !this._libraryItem.libraryItemId
|
||||||
|
},
|
||||||
|
media() {
|
||||||
|
return this._libraryItem.media || {}
|
||||||
|
},
|
||||||
|
mediaMetadata() {
|
||||||
|
return this.media.metadata || {}
|
||||||
|
},
|
||||||
|
placeholderUrl() {
|
||||||
|
return '/book_placeholder.jpg'
|
||||||
|
},
|
||||||
|
bookCoverSrc() {
|
||||||
|
if (this.isLocal) {
|
||||||
|
if (this.libraryItem.coverContentUrl) return Capacitor.convertFileSrc(this.libraryItem.coverContentUrl)
|
||||||
|
return this.placeholderUrl
|
||||||
|
}
|
||||||
|
return this.store.getters['globals/getLibraryItemCoverSrc'](this._libraryItem, this.placeholderUrl)
|
||||||
|
},
|
||||||
|
libraryItemId() {
|
||||||
|
return this._libraryItem.id
|
||||||
|
},
|
||||||
|
series() {
|
||||||
|
return this.mediaMetadata.series
|
||||||
|
},
|
||||||
|
libraryId() {
|
||||||
|
return this._libraryItem.libraryId
|
||||||
|
},
|
||||||
|
hasEbook() {
|
||||||
|
return this.media.ebookFile
|
||||||
|
},
|
||||||
|
numTracks() {
|
||||||
|
return this.media.numTracks
|
||||||
|
},
|
||||||
|
processingBatch() {
|
||||||
|
return this.store.state.processingBatch
|
||||||
|
},
|
||||||
|
booksInSeries() {
|
||||||
|
// Only added to audiobook object when collapseSeries is enabled
|
||||||
|
return this._libraryItem.booksInSeries
|
||||||
|
},
|
||||||
|
hasCover() {
|
||||||
|
return !!this.media.coverPath
|
||||||
|
},
|
||||||
|
squareAspectRatio() {
|
||||||
|
return this.bookCoverAspectRatio === 1
|
||||||
|
},
|
||||||
|
sizeMultiplier() {
|
||||||
|
return this.width / 364
|
||||||
|
},
|
||||||
|
title() {
|
||||||
|
return this.mediaMetadata.title || ''
|
||||||
|
},
|
||||||
|
playIconFontSize() {
|
||||||
|
return Math.max(2, 3 * this.sizeMultiplier)
|
||||||
|
},
|
||||||
|
author() {
|
||||||
|
return this.mediaMetadata.authorName || ''
|
||||||
|
},
|
||||||
|
authorLF() {
|
||||||
|
return this.mediaMetadata.authorNameLF || ''
|
||||||
|
},
|
||||||
|
volumeNumber() {
|
||||||
|
return this.mediaMetadata.volumeNumber || null
|
||||||
|
},
|
||||||
|
displayTitle() {
|
||||||
|
if (this.orderBy === 'media.metadata.title' && this.sortingIgnorePrefix && this.title.toLowerCase().startsWith('the ')) {
|
||||||
|
return this.title.substr(4) + ', The'
|
||||||
|
}
|
||||||
|
return this.title
|
||||||
|
},
|
||||||
|
displayAuthor() {
|
||||||
|
if (this.orderBy === 'media.metadata.authorNameLF') return this.authorLF
|
||||||
|
return this.author
|
||||||
|
},
|
||||||
|
displaySortLine() {
|
||||||
|
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 === 'addedAt') return 'Added ' + this.$formatDate(this._libraryItem.addedAt)
|
||||||
|
if (this.orderBy === 'duration') return 'Duration: ' + this.$elapsedPrettyExtended(this.media.duration, false)
|
||||||
|
if (this.orderBy === 'size') return 'Size: ' + this.$bytesPretty(this._libraryItem.size)
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
userProgress() {
|
||||||
|
return this.store.getters['user/getUserLibraryItemProgress'](this.libraryItemId)
|
||||||
|
},
|
||||||
|
userProgressPercent() {
|
||||||
|
return this.userProgress ? this.userProgress.progress || 0 : 0
|
||||||
|
},
|
||||||
|
itemIsFinished() {
|
||||||
|
return this.userProgress ? !!this.userProgress.isFinished : false
|
||||||
|
},
|
||||||
|
showError() {
|
||||||
|
return this.hasMissingParts || this.hasInvalidParts || this.isMissing || this.isInvalid
|
||||||
|
},
|
||||||
|
isStreaming() {
|
||||||
|
return this.store.getters['getlibraryItemIdStreaming'] === this.libraryItemId
|
||||||
|
},
|
||||||
|
showReadButton() {
|
||||||
|
return !this.isSelectionMode && this.showExperimentalFeatures && !this.showPlayButton && this.hasEbook
|
||||||
|
},
|
||||||
|
showPlayButton() {
|
||||||
|
return !this.isSelectionMode && !this.isMissing && !this.isInvalid && this.numTracks && !this.isStreaming
|
||||||
|
},
|
||||||
|
showSmallEBookIcon() {
|
||||||
|
return !this.isSelectionMode && this.showExperimentalFeatures && this.hasEbook
|
||||||
|
},
|
||||||
|
isMissing() {
|
||||||
|
return this._libraryItem.isMissing
|
||||||
|
},
|
||||||
|
isInvalid() {
|
||||||
|
return this._libraryItem.isInvalid
|
||||||
|
},
|
||||||
|
hasMissingParts() {
|
||||||
|
return this._libraryItem.hasMissingParts
|
||||||
|
},
|
||||||
|
hasInvalidParts() {
|
||||||
|
return this._libraryItem.hasInvalidParts
|
||||||
|
},
|
||||||
|
errorText() {
|
||||||
|
if (this.isMissing) return 'Item directory is missing!'
|
||||||
|
else if (this.isInvalid) return 'Item has no media files'
|
||||||
|
var txt = ''
|
||||||
|
if (this.hasMissingParts) {
|
||||||
|
txt = `${this.hasMissingParts} missing parts.`
|
||||||
|
}
|
||||||
|
if (this.hasInvalidParts) {
|
||||||
|
if (this.hasMissingParts) txt += ' '
|
||||||
|
txt += `${this.hasInvalidParts} invalid parts.`
|
||||||
|
}
|
||||||
|
return txt || 'Unknown Error'
|
||||||
|
},
|
||||||
|
overlayWrapperClasslist() {
|
||||||
|
var classes = []
|
||||||
|
if (this.isSelectionMode) classes.push('bg-opacity-60')
|
||||||
|
else classes.push('bg-opacity-40')
|
||||||
|
if (this.selected) {
|
||||||
|
classes.push('border-2 border-yellow-400')
|
||||||
|
}
|
||||||
|
return classes
|
||||||
|
},
|
||||||
|
store() {
|
||||||
|
return this.$store || this.$nuxt.$store
|
||||||
|
},
|
||||||
|
userCanUpdate() {
|
||||||
|
return this.store.getters['user/getUserCanUpdate']
|
||||||
|
},
|
||||||
|
userCanDelete() {
|
||||||
|
return this.store.getters['user/getUserCanDelete']
|
||||||
|
},
|
||||||
|
userCanDownload() {
|
||||||
|
return this.store.getters['user/getUserCanDownload']
|
||||||
|
},
|
||||||
|
userIsRoot() {
|
||||||
|
return this.store.getters['user/getIsRoot']
|
||||||
|
},
|
||||||
|
_socket() {
|
||||||
|
return this.$root.socket || this.$nuxt.$root.socket
|
||||||
|
},
|
||||||
|
titleFontSize() {
|
||||||
|
return 0.75 * this.sizeMultiplier
|
||||||
|
},
|
||||||
|
authorFontSize() {
|
||||||
|
return 0.6 * this.sizeMultiplier
|
||||||
|
},
|
||||||
|
placeholderCoverPadding() {
|
||||||
|
return 0.8 * this.sizeMultiplier
|
||||||
|
},
|
||||||
|
authorBottom() {
|
||||||
|
return 0.75 * this.sizeMultiplier
|
||||||
|
},
|
||||||
|
titleCleaned() {
|
||||||
|
if (!this.title) return ''
|
||||||
|
if (this.title.length > 60) {
|
||||||
|
return this.title.slice(0, 57) + '...'
|
||||||
|
}
|
||||||
|
return this.title
|
||||||
|
},
|
||||||
|
authorCleaned() {
|
||||||
|
if (!this.author) return ''
|
||||||
|
if (this.author.length > 30) {
|
||||||
|
return this.author.slice(0, 27) + '...'
|
||||||
|
}
|
||||||
|
return this.author
|
||||||
|
},
|
||||||
|
isAlternativeBookshelfView() {
|
||||||
|
return false
|
||||||
|
// var constants = this.$constants || this.$nuxt.$constants
|
||||||
|
// return this.bookshelfView === constants.BookshelfView.TITLES
|
||||||
|
},
|
||||||
|
titleDisplayBottomOffset() {
|
||||||
|
if (!this.isAlternativeBookshelfView) return 0
|
||||||
|
else if (!this.displaySortLine) return 3 * this.sizeMultiplier
|
||||||
|
return 4.25 * this.sizeMultiplier
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setSelectionMode(val) {
|
||||||
|
this.isSelectionMode = val
|
||||||
|
if (!val) this.selected = false
|
||||||
|
},
|
||||||
|
setEntity(libraryItem) {
|
||||||
|
this.libraryItem = libraryItem
|
||||||
|
},
|
||||||
|
setLocalLibraryItem(localLibraryItem) {
|
||||||
|
// Server books may have a local library item
|
||||||
|
this.localLibraryItem = localLibraryItem
|
||||||
|
},
|
||||||
|
clickCard(e) {
|
||||||
|
if (this.isSelectionMode) {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
this.selectBtnClick()
|
||||||
|
} else {
|
||||||
|
var router = this.$router || this.$nuxt.$router
|
||||||
|
if (router) {
|
||||||
|
if (this.booksInSeries) router.push(`/library/${this.libraryId}/series/${this.$encode(this.series)}`)
|
||||||
|
else router.push(`/item/${this.libraryItemId}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
editClick() {
|
||||||
|
this.$emit('edit', this.libraryItem)
|
||||||
|
},
|
||||||
|
toggleFinished() {
|
||||||
|
var updatePayload = {
|
||||||
|
isFinished: !this.itemIsFinished
|
||||||
|
}
|
||||||
|
this.isProcessingReadUpdate = true
|
||||||
|
var toast = this.$toast || this.$nuxt.$toast
|
||||||
|
var axios = this.$axios || this.$nuxt.$axios
|
||||||
|
axios
|
||||||
|
.$patch(`/api/me/progress/${this.libraryItemId}`, updatePayload)
|
||||||
|
.then(() => {
|
||||||
|
this.isProcessingReadUpdate = false
|
||||||
|
toast.success(`Item marked as ${updatePayload.isFinished ? 'Finished' : 'Not Finished'}`)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed', error)
|
||||||
|
this.isProcessingReadUpdate = false
|
||||||
|
toast.error(`Failed to mark as ${updatePayload.isFinished ? 'Finished' : 'Not Finished'}`)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
rescan() {
|
||||||
|
this.rescanning = true
|
||||||
|
this.$axios
|
||||||
|
.$get(`/api/items/${this.libraryItemId}/scan`)
|
||||||
|
.then((data) => {
|
||||||
|
this.rescanning = false
|
||||||
|
var result = data.result
|
||||||
|
if (!result) {
|
||||||
|
this.$toast.error(`Re-Scan Failed for "${this.title}"`)
|
||||||
|
} else if (result === 'UPDATED') {
|
||||||
|
this.$toast.success(`Re-Scan complete item was updated`)
|
||||||
|
} else if (result === 'UPTODATE') {
|
||||||
|
this.$toast.success(`Re-Scan complete item was up to date`)
|
||||||
|
} else if (result === 'REMOVED') {
|
||||||
|
this.$toast.error(`Re-Scan complete item was removed`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to scan library item', error)
|
||||||
|
this.$toast.error('Failed to scan library item')
|
||||||
|
this.rescanning = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
showEditModalTracks() {
|
||||||
|
// More menu func
|
||||||
|
this.store.commit('showEditModalOnTab', { libraryItem: this.libraryItem, tab: 'tracks' })
|
||||||
|
},
|
||||||
|
showEditModalMatch() {
|
||||||
|
// More menu func
|
||||||
|
this.store.commit('showEditModalOnTab', { libraryItem: this.libraryItem, tab: 'match' })
|
||||||
|
},
|
||||||
|
showEditModalDownload() {
|
||||||
|
// More menu func
|
||||||
|
this.store.commit('showEditModalOnTab', { libraryItem: this.libraryItem, tab: 'download' })
|
||||||
|
},
|
||||||
|
openCollections() {
|
||||||
|
this.store.commit('setSelectedLibraryItem', this.libraryItem)
|
||||||
|
this.store.commit('globals/setShowUserCollectionsModal', true)
|
||||||
|
},
|
||||||
|
clickReadEBook() {
|
||||||
|
this.store.commit('showEReader', this.libraryItem)
|
||||||
|
},
|
||||||
|
selectBtnClick() {
|
||||||
|
if (this.processingBatch) return
|
||||||
|
this.selected = !this.selected
|
||||||
|
this.$emit('select', this.libraryItem)
|
||||||
|
},
|
||||||
|
play() {
|
||||||
|
var eventBus = this.$eventBus || this.$nuxt.$eventBus
|
||||||
|
eventBus.$emit('play-item', this.libraryItemId)
|
||||||
|
},
|
||||||
|
destroy() {
|
||||||
|
// destroy the vue listeners, etc
|
||||||
|
this.$destroy()
|
||||||
|
|
||||||
|
// remove the element from the DOM
|
||||||
|
if (this.$el && this.$el.parentNode) {
|
||||||
|
this.$el.parentNode.removeChild(this.$el)
|
||||||
|
} else if (this.$el && this.$el.remove) {
|
||||||
|
this.$el.remove()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setCoverBg() {
|
||||||
|
if (this.$refs.coverBg) {
|
||||||
|
this.$refs.coverBg.style.backgroundImage = `url("${this.bookCoverSrc}")`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
imageLoaded() {
|
||||||
|
this.imageReady = true
|
||||||
|
|
||||||
|
if (this.$refs.cover && this.bookCoverSrc !== this.placeholderUrl) {
|
||||||
|
var { naturalWidth, naturalHeight } = this.$refs.cover
|
||||||
|
var aspectRatio = naturalHeight / naturalWidth
|
||||||
|
var arDiff = Math.abs(aspectRatio - this.bookCoverAspectRatio)
|
||||||
|
|
||||||
|
// If image aspect ratio is <= 1.45 or >= 1.75 then use cover bg, otherwise stretch to fit
|
||||||
|
if (arDiff > 0.15) {
|
||||||
|
this.showCoverBg = true
|
||||||
|
this.$nextTick(this.setCoverBg)
|
||||||
|
} else {
|
||||||
|
this.showCoverBg = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.bookMount) {
|
||||||
|
this.setEntity(this.bookMount)
|
||||||
|
|
||||||
|
if (this.bookMount.localLibraryItem) {
|
||||||
|
this.setLocalLibraryItem(this.bookMount.localLibraryItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -9,6 +9,7 @@
|
||||||
<p v-show="selectedSeriesName" class="ml-2 font-book pt-1">{{ selectedSeriesName }} ({{ totalEntities }})</p>
|
<p v-show="selectedSeriesName" class="ml-2 font-book pt-1">{{ selectedSeriesName }} ({{ totalEntities }})</p>
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<template v-if="page === 'library'">
|
<template v-if="page === 'library'">
|
||||||
|
<span class="material-icons px-2" @click="bookshelfListView = !bookshelfListView">{{ bookshelfListView ? 'view_list' : 'grid_view' }}</span>
|
||||||
<div class="relative flex items-center px-2">
|
<div class="relative flex items-center px-2">
|
||||||
<span class="material-icons" @click="showFilterModal = true">filter_alt</span>
|
<span class="material-icons" @click="showFilterModal = true">filter_alt</span>
|
||||||
<div v-show="hasFilters" class="absolute top-0 right-2 w-2 h-2 rounded-full bg-success border border-green-300 shadow-sm z-10 pointer-events-none" />
|
<div v-show="hasFilters" class="absolute top-0 right-2 w-2 h-2 rounded-full bg-success border border-green-300 shadow-sm z-10 pointer-events-none" />
|
||||||
|
@ -34,6 +35,14 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
bookshelfListView: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.globals.bookshelfListView
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$store.commit('globals/setBookshelfListView', val)
|
||||||
|
}
|
||||||
|
},
|
||||||
hasFilters() {
|
hasFilters() {
|
||||||
return this.$store.getters['user/getUserSetting']('mobileFilterBy') !== 'all'
|
return this.$store.getters['user/getUserSetting']('mobileFilterBy') !== 'all'
|
||||||
},
|
},
|
||||||
|
|
|
@ -29,35 +29,23 @@ export default {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
text: 'Title',
|
text: 'Title',
|
||||||
value: 'book.title'
|
value: 'media.metadata.title'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Author (First Last)',
|
text: 'Author (First Last)',
|
||||||
value: 'book.authorFL'
|
value: 'media.metadata.authorName'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Author (Last, First)',
|
text: 'Author (Last, First)',
|
||||||
value: 'book.authorLF'
|
value: 'media.metadata.authorNameLF'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Added At',
|
text: 'Added At',
|
||||||
value: 'addedAt'
|
value: 'addedAt'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
text: 'Volume #',
|
|
||||||
value: 'book.volumeNumber'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Duration',
|
|
||||||
value: 'duration'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
text: 'Size',
|
text: 'Size',
|
||||||
value: 'size'
|
value: 'size'
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Last Read',
|
|
||||||
value: 'recent'
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import LazyBookCard from '@/components/cards/LazyBookCard'
|
import LazyBookCard from '@/components/cards/LazyBookCard'
|
||||||
|
import LazyListBookCard from '@/components/cards/LazyListBookCard'
|
||||||
import LazySeriesCard from '@/components/cards/LazySeriesCard'
|
import LazySeriesCard from '@/components/cards/LazySeriesCard'
|
||||||
import LazyCollectionCard from '@/components/cards/LazyCollectionCard'
|
import LazyCollectionCard from '@/components/cards/LazyCollectionCard'
|
||||||
|
|
||||||
|
@ -15,6 +16,7 @@ export default {
|
||||||
getComponentClass() {
|
getComponentClass() {
|
||||||
if (this.entityName === 'series') return Vue.extend(LazySeriesCard)
|
if (this.entityName === 'series') return Vue.extend(LazySeriesCard)
|
||||||
if (this.entityName === 'collections') return Vue.extend(LazyCollectionCard)
|
if (this.entityName === 'collections') return Vue.extend(LazyCollectionCard)
|
||||||
|
if (this.showBookshelfListView) return Vue.extend(LazyListBookCard)
|
||||||
return Vue.extend(LazyBookCard)
|
return Vue.extend(LazyBookCard)
|
||||||
},
|
},
|
||||||
async mountEntityCard(index) {
|
async mountEntityCard(index) {
|
||||||
|
@ -32,10 +34,10 @@ export default {
|
||||||
bookComponent.isHovering = false
|
bookComponent.isHovering = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var shelfOffsetY = this.isBookEntity ? 24 : 16
|
var shelfOffsetY = this.showBookshelfListView ? 8 : this.isBookEntity ? 24 : 16
|
||||||
var row = index % this.entitiesPerShelf
|
var row = index % this.entitiesPerShelf
|
||||||
|
|
||||||
var marginShiftLeft = 12
|
var marginShiftLeft = this.showBookshelfListView ? 0 : 12
|
||||||
var shelfOffsetX = row * this.totalEntityCardWidth + this.bookshelfMarginLeft + marginShiftLeft
|
var shelfOffsetX = row * this.totalEntityCardWidth + this.bookshelfMarginLeft + marginShiftLeft
|
||||||
|
|
||||||
var ComponentClass = this.getComponentClass()
|
var ComponentClass = this.getComponentClass()
|
||||||
|
|
|
@ -89,8 +89,7 @@ export default {
|
||||||
|
|
||||||
return categories
|
return categories
|
||||||
},
|
},
|
||||||
async fetchCategories(from = null) {
|
async fetchCategories() {
|
||||||
console.log('[4breadcrumbs] fetchCategories', from)
|
|
||||||
if (this.loading) {
|
if (this.loading) {
|
||||||
console.log('Already loading categories')
|
console.log('Already loading categories')
|
||||||
return
|
return
|
||||||
|
@ -130,7 +129,7 @@ export default {
|
||||||
},
|
},
|
||||||
async libraryChanged(libid) {
|
async libraryChanged(libid) {
|
||||||
if (this.currentLibraryId) {
|
if (this.currentLibraryId) {
|
||||||
await this.fetchCategories('libraryChanged')
|
await this.fetchCategories()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
audiobookAdded(audiobook) {
|
audiobookAdded(audiobook) {
|
||||||
|
@ -188,7 +187,7 @@ export default {
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.initListeners()
|
this.initListeners()
|
||||||
this.fetchCategories('mounted')
|
this.fetchCategories()
|
||||||
// if (this.$server.initialized && this.currentLibraryId) {
|
// if (this.$server.initialized && this.currentLibraryId) {
|
||||||
// this.fetchCategories()
|
// this.fetchCategories()
|
||||||
// } else {
|
// } else {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export const state = () => ({
|
export const state = () => ({
|
||||||
itemDownloads: []
|
itemDownloads: [],
|
||||||
|
bookshelfListView: false
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getters = {
|
export const getters = {
|
||||||
|
@ -41,5 +42,8 @@ export const mutations = {
|
||||||
},
|
},
|
||||||
removeItemDownload(state, id) {
|
removeItemDownload(state, id) {
|
||||||
state.itemDownloads = state.itemDownloads.filter(i => i.id != id)
|
state.itemDownloads = state.itemDownloads.filter(i => i.id != id)
|
||||||
|
},
|
||||||
|
setBookshelfListView(state, val) {
|
||||||
|
state.bookshelfListView = val
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue