mirror of
https://github.com/gotson/komga.git
synced 2025-12-20 15:34:17 +01:00
feat(webui): multi-select collections and read lists
This commit is contained in:
parent
217bc493a1
commit
19e3f18cad
9 changed files with 199 additions and 76 deletions
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
<collection-delete-dialog
|
||||
v-model="deleteCollectionDialog"
|
||||
:collection="deleteCollection"
|
||||
:collections="deleteCollections"
|
||||
/>
|
||||
|
||||
<read-list-add-to-dialog
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
<read-list-delete-dialog
|
||||
v-model="deleteReadListDialog"
|
||||
:read-list="deleteReadList"
|
||||
:read-lists="deleteReadLists"
|
||||
/>
|
||||
|
||||
<library-edit-dialog
|
||||
|
|
@ -114,8 +114,8 @@ export default Vue.extend({
|
|||
this.$store.dispatch('dialogDeleteCollectionDisplay', val)
|
||||
},
|
||||
},
|
||||
deleteCollection (): CollectionDto {
|
||||
return this.$store.state.deleteCollection
|
||||
deleteCollections (): CollectionDto | CollectionDto[] {
|
||||
return this.$store.state.deleteCollections
|
||||
},
|
||||
// read lists
|
||||
addToReadListDialog: {
|
||||
|
|
@ -148,8 +148,8 @@ export default Vue.extend({
|
|||
this.$store.dispatch('dialogDeleteReadListDisplay', val)
|
||||
},
|
||||
},
|
||||
deleteReadList (): ReadListDto {
|
||||
return this.$store.state.deleteReadList
|
||||
deleteReadLists (): ReadListDto | ReadListDto[] {
|
||||
return this.$store.state.deleteReadLists
|
||||
},
|
||||
// libraries
|
||||
editLibraryDialog: {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
<v-spacer/>
|
||||
|
||||
<v-btn icon @click="markRead">
|
||||
<v-btn icon @click="markRead" v-if="kind === 'books' || kind === 'series'">
|
||||
<v-tooltip bottom>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-icon v-on="on">mdi-bookmark-check</v-icon>
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
</v-tooltip>
|
||||
</v-btn>
|
||||
|
||||
<v-btn icon @click="markUnread">
|
||||
<v-btn icon @click="markUnread" v-if="kind === 'books' || kind === 'series'">
|
||||
<v-tooltip bottom>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-icon v-on="on">mdi-bookmark-remove</v-icon>
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
</v-tooltip>
|
||||
</v-btn>
|
||||
|
||||
<v-btn icon @click="edit" v-if="isAdmin">
|
||||
<v-btn icon @click="edit" v-if="isAdmin && (kind === 'books' || kind === 'series')">
|
||||
<v-tooltip bottom>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-icon v-on="on">mdi-pencil</v-icon>
|
||||
|
|
@ -69,6 +69,15 @@
|
|||
<span>{{ $t('menu.edit_metadata') }}</span>
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
|
||||
<v-btn icon @click="doDelete" v-if="isAdmin && (kind === 'collections' || kind === 'readlists')">
|
||||
<v-tooltip bottom>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-icon v-on="on">mdi-delete</v-icon>
|
||||
</template>
|
||||
<span>{{ $t('menu.delete') }}</span>
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
</toolbar-sticky>
|
||||
</v-scroll-y-transition>
|
||||
</template>
|
||||
|
|
@ -88,7 +97,10 @@ export default Vue.extend({
|
|||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
// books or series
|
||||
/**
|
||||
* The kind of items this toolbar acts on.
|
||||
* @values books, series, collections, readlists
|
||||
*/
|
||||
kind: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
|
@ -125,6 +137,9 @@ export default Vue.extend({
|
|||
edit () {
|
||||
this.$emit('edit')
|
||||
},
|
||||
doDelete () {
|
||||
this.$emit('delete')
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -4,19 +4,30 @@
|
|||
max-width="450"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title>{{ $t('dialog.delete_collection.dialog_title') }}</v-card-title>
|
||||
<v-card-title v-if="single">{{ $t('dialog.delete_collection.dialog_title') }}</v-card-title>
|
||||
<v-card-title v-else>{{ $t('dialog.delete_collection.dialog_title_multiple') }}</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<v-container fluid>
|
||||
<v-row>
|
||||
<v-col v-html="$t('dialog.delete_collection.warning_html', { name: collection.name})"></v-col>
|
||||
<v-col
|
||||
v-if="single"
|
||||
v-html="$t('dialog.delete_collection.warning_html', { name: collections.name})"
|
||||
/>
|
||||
<v-col
|
||||
v-else
|
||||
v-html="$t('dialog.delete_collection.warning_multiple_html', { count: collections.length})"
|
||||
/>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-checkbox v-model="confirmDelete" color="red">
|
||||
<template v-slot:label>
|
||||
{{ $t('dialog.delete_collection.confirm_delete', {name: collection.name}) }}
|
||||
<template v-slot:label v-if="single">
|
||||
{{ $t('dialog.delete_collection.confirm_delete', {name: collections.name}) }}
|
||||
</template>
|
||||
<template v-slot:label v-else>
|
||||
{{ $t('dialog.delete_collection.confirm_delete_multiple', {count: collections.length}) }}
|
||||
</template>
|
||||
</v-checkbox>
|
||||
</v-col>
|
||||
|
|
@ -66,8 +77,8 @@ export default Vue.extend({
|
|||
},
|
||||
props: {
|
||||
value: Boolean,
|
||||
collection: {
|
||||
type: Object as () => CollectionDto,
|
||||
collections: {
|
||||
type: [Object as () => CollectionDto, Array as () => CollectionDto[]],
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
|
@ -79,24 +90,32 @@ export default Vue.extend({
|
|||
!val && this.dialogCancel()
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
single(): boolean {
|
||||
return !Array.isArray(this.collections)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
dialogCancel() {
|
||||
this.$emit('input', false)
|
||||
this.confirmDelete = false
|
||||
},
|
||||
dialogConfirm() {
|
||||
this.deleteCollection()
|
||||
this.deleteCollections()
|
||||
this.$emit('input', false)
|
||||
},
|
||||
showSnack(message: string) {
|
||||
this.snackText = message
|
||||
this.snackbar = true
|
||||
},
|
||||
async deleteCollection() {
|
||||
try {
|
||||
await this.$komgaCollections.deleteCollection(this.collection.id)
|
||||
} catch (e) {
|
||||
this.showSnack(e.message)
|
||||
async deleteCollections() {
|
||||
const toUpdate = (this.single ? [this.collections] : this.collections) as CollectionDto[]
|
||||
for (const b of toUpdate) {
|
||||
try {
|
||||
await this.$komgaCollections.deleteCollection(b.id)
|
||||
} catch (e) {
|
||||
this.showSnack(e.message)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,19 +4,30 @@
|
|||
max-width="450"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title>{{ $t('dialog.delete_readlist.dialog_title') }}</v-card-title>
|
||||
<v-card-title v-if="single">{{ $t('dialog.delete_readlist.dialog_title') }}</v-card-title>
|
||||
<v-card-title v-else>{{ $t('dialog.delete_readlist.dialog_title_multiple') }}</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<v-container fluid>
|
||||
<v-row>
|
||||
<v-col v-html="$t('dialog.delete_readlist.warning_html', {name: readList.name})"></v-col>
|
||||
<v-col
|
||||
v-if="single"
|
||||
v-html="$t('dialog.delete_readlist.warning_html', {name: readLists.name})"
|
||||
/>
|
||||
<v-col
|
||||
v-else
|
||||
v-html="$t('dialog.delete_readlist.warning_multiple_html', {count: readLists.length})"
|
||||
/>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-checkbox v-model="confirmDelete" color="red">
|
||||
<template v-slot:label>
|
||||
{{ $t('dialog.delete_readlist.confirm_delete', { name: readList.name}) }}
|
||||
<template v-slot:label v-if="single">
|
||||
{{ $t('dialog.delete_readlist.confirm_delete', { name: readLists.name}) }}
|
||||
</template>
|
||||
<template v-slot:label v-else>
|
||||
{{ $t('dialog.delete_readlist.confirm_delete_multiple', { count: readLists.length}) }}
|
||||
</template>
|
||||
</v-checkbox>
|
||||
</v-col>
|
||||
|
|
@ -64,8 +75,8 @@ export default Vue.extend({
|
|||
},
|
||||
props: {
|
||||
value: Boolean,
|
||||
readList: {
|
||||
type: Object as () => ReadListDto,
|
||||
readLists: {
|
||||
type: [Object as () => ReadListDto, Array as () => ReadListDto[]],
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
|
@ -77,6 +88,11 @@ export default Vue.extend({
|
|||
!val && this.dialogCancel()
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
single(): boolean {
|
||||
return !Array.isArray(this.readLists)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
dialogCancel() {
|
||||
this.$emit('input', false)
|
||||
|
|
@ -91,10 +107,13 @@ export default Vue.extend({
|
|||
this.snackbar = true
|
||||
},
|
||||
async delete() {
|
||||
try {
|
||||
await this.$komgaReadLists.deleteReadList(this.readList.id)
|
||||
} catch (e) {
|
||||
this.showSnack(e.message)
|
||||
const toUpdate = (this.single ? [this.readLists] : this.readLists) as ReadListDto[]
|
||||
for (const b of toUpdate) {
|
||||
try {
|
||||
await this.$komgaReadLists.deleteReadList(b.id)
|
||||
} catch (e) {
|
||||
this.showSnack(e.message)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -255,8 +255,11 @@
|
|||
"button_cancel": "Cancel",
|
||||
"button_confirm": "Delete",
|
||||
"confirm_delete": "Yes, delete the collection \"{name}\"",
|
||||
"confirm_delete_multiple": "Yes, delete {count} collections",
|
||||
"dialog_title": "Delete Collection",
|
||||
"warning_html": "The collection <b>{name}</b> will be removed from this server. Your media files will not be affected. This <b>cannot</b> be undone. Continue?"
|
||||
"dialog_title_multiple": "Delete Collections",
|
||||
"warning_html": "The collection <b>{name}</b> will be removed from this server. Your media files will not be affected. This <b>cannot</b> be undone. Continue?",
|
||||
"warning_multiple_html": "{count} collections will be removed from this server. Your media files will not be affected. This <b>cannot</b> be undone. Continue?"
|
||||
},
|
||||
"delete_library": {
|
||||
"button_cancel": "Cancel",
|
||||
|
|
@ -269,8 +272,11 @@
|
|||
"button_cancel": "Cancel",
|
||||
"button_confirm": "Delete",
|
||||
"confirm_delete": "Yes, delete the read list \"{name}\"",
|
||||
"confirm_delete_multiple": "Yes, delete {count} read lists",
|
||||
"dialog_title": "Delete Read List",
|
||||
"warning_html": "The read list <b>{name}</b> will be removed from this server. Your media files will not be affected. This <b>cannot</b> be undone. Continue?"
|
||||
"dialog_title_multiple": "Delete Read Lists",
|
||||
"warning_html": "The read list <b>{name}</b> will be removed from this server. Your media files will not be affected. This <b>cannot</b> be undone. Continue?",
|
||||
"warning_multiple_html": "{count} read lists will be removed from this server. Your media files will not be affected. This <b>cannot</b> be undone. Continue?"
|
||||
},
|
||||
"delete_user": {
|
||||
"button_cancel": "Cancel",
|
||||
|
|
@ -540,6 +546,7 @@
|
|||
"add_to_readlist": "Add to read list",
|
||||
"analyze": "Analyze",
|
||||
"delete": "Delete",
|
||||
"deselect_all": "Deselect all",
|
||||
"download_series": "Download series",
|
||||
"edit": "Edit",
|
||||
"edit_metadata": "Edit metadata",
|
||||
|
|
@ -547,7 +554,6 @@
|
|||
"mark_unread": "Mark as unread",
|
||||
"refresh_metadata": "Refresh metadata",
|
||||
"scan_library_files": "Scan library files",
|
||||
"deselect_all": "Deselect all",
|
||||
"select_all": "Select all"
|
||||
},
|
||||
"navigation": {
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ export default new Vuex.Store({
|
|||
addToCollectionDialog: false,
|
||||
editCollection: {} as CollectionDto,
|
||||
editCollectionDialog: false,
|
||||
deleteCollection: {} as CollectionDto,
|
||||
deleteCollections: {} as CollectionDto | CollectionDto[],
|
||||
deleteCollectionDialog: false,
|
||||
// read lists
|
||||
addToReadListBooks: {} as BookDto | BookDto[],
|
||||
addToReadListDialog: false,
|
||||
editReadList: {} as ReadListDto,
|
||||
editReadListDialog: false,
|
||||
deleteReadList: {} as ReadListDto,
|
||||
deleteReadLists: {} as ReadListDto | ReadListDto[],
|
||||
deleteReadListDialog: false,
|
||||
// libraries
|
||||
editLibrary: {} as LibraryDto | undefined,
|
||||
|
|
@ -53,27 +53,27 @@ export default new Vuex.Store({
|
|||
setEditCollectionDialog (state, dialog) {
|
||||
state.editCollectionDialog = dialog
|
||||
},
|
||||
setDeleteCollection (state, collection) {
|
||||
state.deleteCollection = collection
|
||||
setDeleteCollections (state, collections) {
|
||||
state.deleteCollections = collections
|
||||
},
|
||||
setDeleteCollectionDialog (state, dialog) {
|
||||
state.deleteCollectionDialog = dialog
|
||||
},
|
||||
// Read Lists
|
||||
setAddToReadListBooks (state, Book) {
|
||||
state.addToReadListBooks = Book
|
||||
setAddToReadListBooks (state, book) {
|
||||
state.addToReadListBooks = book
|
||||
},
|
||||
setAddToReadListDialog (state, dialog) {
|
||||
state.addToReadListDialog = dialog
|
||||
},
|
||||
setEditReadList (state, ReadList) {
|
||||
state.editReadList = ReadList
|
||||
setEditReadList (state, readList) {
|
||||
state.editReadList = readList
|
||||
},
|
||||
setEditReadListDialog (state, dialog) {
|
||||
state.editReadListDialog = dialog
|
||||
},
|
||||
setDeleteReadList (state, ReadList) {
|
||||
state.deleteReadList = ReadList
|
||||
setDeleteReadLists (state, readLists) {
|
||||
state.deleteReadLists = readLists
|
||||
},
|
||||
setDeleteReadListDialog (state, dialog) {
|
||||
state.deleteReadListDialog = dialog
|
||||
|
|
@ -122,8 +122,8 @@ export default new Vuex.Store({
|
|||
dialogEditCollectionDisplay ({ commit }, value) {
|
||||
commit('setEditCollectionDialog', value)
|
||||
},
|
||||
dialogDeleteCollection ({ commit }, collection) {
|
||||
commit('setDeleteCollection', collection)
|
||||
dialogDeleteCollection ({ commit }, collections) {
|
||||
commit('setDeleteCollections', collections)
|
||||
commit('setDeleteCollectionDialog', true)
|
||||
},
|
||||
dialogDeleteCollectionDisplay ({ commit }, value) {
|
||||
|
|
@ -144,8 +144,8 @@ export default new Vuex.Store({
|
|||
dialogEditReadListDisplay ({ commit }, value) {
|
||||
commit('setEditReadListDialog', value)
|
||||
},
|
||||
dialogDeleteReadList ({ commit }, readList) {
|
||||
commit('setDeleteReadList', readList)
|
||||
dialogDeleteReadList ({ commit }, readLists) {
|
||||
commit('setDeleteReadLists', readLists)
|
||||
commit('setDeleteReadListDialog', true)
|
||||
},
|
||||
dialogDeleteReadListDisplay ({ commit }, value) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div :style="$vuetify.breakpoint.name === 'xs' ? 'margin-bottom: 56px' : undefined">
|
||||
<toolbar-sticky>
|
||||
<toolbar-sticky v-if="selectedCollections.length === 0">
|
||||
<!-- Action menu -->
|
||||
<library-actions-menu v-if="library"
|
||||
:library="library"/>
|
||||
|
|
@ -21,6 +21,15 @@
|
|||
<page-size-select v-model="pageSize"/>
|
||||
</toolbar-sticky>
|
||||
|
||||
<multi-select-bar
|
||||
v-model="selectedCollections"
|
||||
kind="collections"
|
||||
show-select-all
|
||||
@unselect-all="selectedCollections = []"
|
||||
@select-all="selectedCollections = collections"
|
||||
@delete="deleteCollections"
|
||||
/>
|
||||
|
||||
<library-navigation v-if="$vuetify.breakpoint.name === 'xs'" :libraryId="libraryId" bottom-navigation/>
|
||||
|
||||
<v-container fluid>
|
||||
|
|
@ -33,7 +42,8 @@
|
|||
|
||||
<item-browser
|
||||
:items="collections"
|
||||
:selectable="false"
|
||||
selectable
|
||||
:selected.sync="selectedCollections"
|
||||
:edit-function="editSingleCollection"
|
||||
/>
|
||||
|
||||
|
|
@ -59,6 +69,7 @@ import Vue from 'vue'
|
|||
import {Location} from 'vue-router'
|
||||
import {LIBRARIES_ALL, LIBRARY_ROUTE} from '@/types/library'
|
||||
import {LibrarySseDto} from "@/types/komga-sse";
|
||||
import MultiSelectBar from "@/components/bars/MultiSelectBar.vue";
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'BrowseCollections',
|
||||
|
|
@ -68,11 +79,13 @@ export default Vue.extend({
|
|||
LibraryNavigation,
|
||||
ItemBrowser,
|
||||
PageSizeSelect,
|
||||
MultiSelectBar,
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
library: undefined as LibraryDto | undefined,
|
||||
collections: [] as CollectionDto[],
|
||||
selectedCollections: [] as CollectionDto[],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
totalPages: 1,
|
||||
|
|
@ -196,6 +209,8 @@ export default Vue.extend({
|
|||
}
|
||||
},
|
||||
async loadPage(libraryId: string, page: number) {
|
||||
this.selectedCollections = []
|
||||
|
||||
const pageRequest = {
|
||||
page: page - 1,
|
||||
size: this.pageSize,
|
||||
|
|
@ -218,6 +233,9 @@ export default Vue.extend({
|
|||
editSingleCollection(collection: CollectionDto) {
|
||||
this.$store.dispatch('dialogEditCollection', collection)
|
||||
},
|
||||
deleteCollections() {
|
||||
this.$store.dispatch('dialogDeleteCollection', this.selectedCollections)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div :style="$vuetify.breakpoint.name === 'xs' ? 'margin-bottom: 56px' : undefined">
|
||||
<toolbar-sticky>
|
||||
<toolbar-sticky v-if="selectedReadLists.length === 0">
|
||||
<!-- Action menu -->
|
||||
<library-actions-menu v-if="library"
|
||||
:library="library"/>
|
||||
|
|
@ -21,6 +21,15 @@
|
|||
<page-size-select v-model="pageSize"/>
|
||||
</toolbar-sticky>
|
||||
|
||||
<multi-select-bar
|
||||
v-model="selectedReadLists"
|
||||
kind="readlists"
|
||||
show-select-all
|
||||
@unselect-all="selectedReadLists = []"
|
||||
@select-all="selectedReadLists = readLists"
|
||||
@delete="deleteReadLists"
|
||||
/>
|
||||
|
||||
<library-navigation v-if="$vuetify.breakpoint.name === 'xs'" :libraryId="libraryId" bottom-navigation/>
|
||||
|
||||
<v-container fluid>
|
||||
|
|
@ -33,7 +42,8 @@
|
|||
|
||||
<item-browser
|
||||
:items="readLists"
|
||||
:selectable="false"
|
||||
selectable
|
||||
:selected.sync="selectedReadLists"
|
||||
:edit-function="editSingle"
|
||||
/>
|
||||
|
||||
|
|
@ -59,6 +69,7 @@ import Vue from 'vue'
|
|||
import {Location} from 'vue-router'
|
||||
import {LIBRARIES_ALL, LIBRARY_ROUTE} from '@/types/library'
|
||||
import {LibrarySseDto} from "@/types/komga-sse";
|
||||
import MultiSelectBar from "@/components/bars/MultiSelectBar.vue";
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'BrowseReadLists',
|
||||
|
|
@ -68,11 +79,13 @@ export default Vue.extend({
|
|||
LibraryNavigation,
|
||||
ItemBrowser,
|
||||
PageSizeSelect,
|
||||
MultiSelectBar,
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
library: undefined as LibraryDto | undefined,
|
||||
readLists: [] as ReadListDto[],
|
||||
selectedReadLists: [] as ReadListDto[],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
totalPages: 1,
|
||||
|
|
@ -87,19 +100,19 @@ export default Vue.extend({
|
|||
default: LIBRARIES_ALL,
|
||||
},
|
||||
},
|
||||
created () {
|
||||
created() {
|
||||
this.$eventHub.$on(READLIST_ADDED, this.reloadElements)
|
||||
this.$eventHub.$on(READLIST_CHANGED, this.reloadElements)
|
||||
this.$eventHub.$on(READLIST_DELETED, this.reloadElements)
|
||||
this.$eventHub.$on(LIBRARY_CHANGED, this.reloadLibrary)
|
||||
},
|
||||
beforeDestroy () {
|
||||
beforeDestroy() {
|
||||
this.$eventHub.$off(READLIST_ADDED, this.reloadElements)
|
||||
this.$eventHub.$off(READLIST_CHANGED, this.reloadElements)
|
||||
this.$eventHub.$off(READLIST_DELETED, this.reloadElements)
|
||||
this.$eventHub.$off(LIBRARY_CHANGED, this.reloadLibrary)
|
||||
},
|
||||
mounted () {
|
||||
mounted() {
|
||||
this.$store.commit('setLibraryRoute', {id: this.libraryId, route: LIBRARY_ROUTE.READLISTS})
|
||||
this.pageSize = this.$store.state.persistedState.browsingPageSize || this.pageSize
|
||||
|
||||
|
|
@ -111,7 +124,7 @@ export default Vue.extend({
|
|||
|
||||
this.setWatches()
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
beforeRouteUpdate(to, from, next) {
|
||||
if (to.params.libraryId !== from.params.libraryId) {
|
||||
// reset
|
||||
this.page = 1
|
||||
|
|
@ -125,10 +138,10 @@ export default Vue.extend({
|
|||
next()
|
||||
},
|
||||
computed: {
|
||||
isAdmin (): boolean {
|
||||
isAdmin(): boolean {
|
||||
return this.$store.getters.meAdmin
|
||||
},
|
||||
paginationVisible (): number {
|
||||
paginationVisible(): number {
|
||||
switch (this.$vuetify.breakpoint.name) {
|
||||
case 'xs':
|
||||
return 5
|
||||
|
|
@ -143,7 +156,7 @@ export default Vue.extend({
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
setWatches () {
|
||||
setWatches() {
|
||||
this.pageSizeUnwatch = this.$watch('pageSize', (val) => {
|
||||
this.$store.commit('setBrowsingPageSize', val)
|
||||
this.updateRouteAndReload()
|
||||
|
|
@ -154,11 +167,11 @@ export default Vue.extend({
|
|||
this.loadPage(this.libraryId, val)
|
||||
})
|
||||
},
|
||||
unsetWatches () {
|
||||
unsetWatches() {
|
||||
this.pageUnwatch()
|
||||
this.pageSizeUnwatch()
|
||||
},
|
||||
updateRouteAndReload () {
|
||||
updateRouteAndReload() {
|
||||
this.unsetWatches()
|
||||
|
||||
this.page = 1
|
||||
|
|
@ -168,10 +181,10 @@ export default Vue.extend({
|
|||
|
||||
this.setWatches()
|
||||
},
|
||||
updateRoute () {
|
||||
updateRoute() {
|
||||
this.$router.replace({
|
||||
name: this.$route.name,
|
||||
params: { libraryId: this.$route.params.libraryId },
|
||||
params: {libraryId: this.$route.params.libraryId},
|
||||
query: {
|
||||
page: `${this.page}`,
|
||||
pageSize: `${this.pageSize}`,
|
||||
|
|
@ -179,23 +192,25 @@ export default Vue.extend({
|
|||
} as Location).catch((_: any) => {
|
||||
})
|
||||
},
|
||||
reloadElements () {
|
||||
reloadElements() {
|
||||
this.loadLibrary(this.libraryId)
|
||||
},
|
||||
reloadLibrary (event: LibrarySseDto) {
|
||||
reloadLibrary(event: LibrarySseDto) {
|
||||
if (event.libraryId === this.libraryId) {
|
||||
this.loadLibrary(this.libraryId)
|
||||
}
|
||||
},
|
||||
async loadLibrary (libraryId: string) {
|
||||
async loadLibrary(libraryId: string) {
|
||||
this.library = this.getLibraryLazy(libraryId)
|
||||
await this.loadPage(libraryId, this.page)
|
||||
|
||||
if (this.totalElements === 0) {
|
||||
await this.$router.push({ name: 'browse-libraries', params: { libraryId: libraryId.toString() } })
|
||||
await this.$router.push({name: 'browse-libraries', params: {libraryId: libraryId.toString()}})
|
||||
}
|
||||
},
|
||||
async loadPage (libraryId: string, page: number) {
|
||||
async loadPage(libraryId: string, page: number) {
|
||||
this.selectedReadLists = []
|
||||
|
||||
const pageRequest = {
|
||||
page: page - 1,
|
||||
size: this.pageSize,
|
||||
|
|
@ -208,16 +223,19 @@ export default Vue.extend({
|
|||
this.totalElements = elementsPage.totalElements
|
||||
this.readLists = elementsPage.content
|
||||
},
|
||||
getLibraryLazy (libraryId: string): LibraryDto | undefined {
|
||||
getLibraryLazy(libraryId: string): LibraryDto | undefined {
|
||||
if (libraryId !== LIBRARIES_ALL) {
|
||||
return this.$store.getters.getLibraryById(libraryId)
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
editSingle (element: ReadListDto) {
|
||||
editSingle(element: ReadListDto) {
|
||||
this.$store.dispatch('dialogEditReadList', element)
|
||||
},
|
||||
deleteReadLists () {
|
||||
this.$store.dispatch('dialogDeleteReadList', this.selectedReadLists)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,20 @@
|
|||
@edit="editMultipleBooks"
|
||||
/>
|
||||
|
||||
<multi-select-bar
|
||||
v-model="selectedCollections"
|
||||
kind="collections"
|
||||
@unselect-all="selectedCollections = []"
|
||||
@delete="deleteCollections"
|
||||
/>
|
||||
|
||||
<multi-select-bar
|
||||
v-model="selectedReadLists"
|
||||
kind="readlists"
|
||||
@unselect-all="selectedReadLists = []"
|
||||
@delete="deleteReadLists"
|
||||
/>
|
||||
|
||||
<v-container fluid>
|
||||
<empty-state
|
||||
v-if="emptyResults"
|
||||
|
|
@ -47,7 +61,7 @@
|
|||
nowrap
|
||||
:edit-function="singleEditSeries"
|
||||
:selected.sync="selectedSeries"
|
||||
:selectable="selectedBooks.length === 0"
|
||||
:selectable="selectedBooks.length === 0 && selectedCollections.length === 0 && selectedReadLists.length === 0"
|
||||
:fixed-item-width="fixedCardWidth"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -62,7 +76,7 @@
|
|||
nowrap
|
||||
:edit-function="singleEditBook"
|
||||
:selected.sync="selectedBooks"
|
||||
:selectable="selectedSeries.length === 0"
|
||||
:selectable="selectedSeries.length === 0 && selectedCollections.length === 0 && selectedReadLists.length === 0"
|
||||
:fixed-item-width="fixedCardWidth"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -76,7 +90,8 @@
|
|||
<item-browser :items="collections"
|
||||
nowrap
|
||||
:edit-function="singleEditCollection"
|
||||
:selectable="false"
|
||||
:selected.sync="selectedCollections"
|
||||
:selectable="selectedSeries.length === 0 && selectedBooks.length === 0 && selectedReadLists.length === 0"
|
||||
:fixed-item-width="fixedCardWidth"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -90,7 +105,8 @@
|
|||
<item-browser :items="readLists"
|
||||
nowrap
|
||||
:edit-function="singleEditReadList"
|
||||
:selectable="false"
|
||||
:selected.sync="selectedReadLists"
|
||||
:selectable="selectedSeries.length === 0 && selectedBooks.length === 0 && selectedCollections.length === 0"
|
||||
:fixed-item-width="fixedCardWidth"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -145,6 +161,8 @@ export default Vue.extend({
|
|||
loading: false,
|
||||
selectedSeries: [] as SeriesDto[],
|
||||
selectedBooks: [] as BookDto[],
|
||||
selectedCollections: [] as CollectionDto[],
|
||||
selectedReadLists: [] as ReadListDto[],
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
|
@ -179,6 +197,8 @@ export default Vue.extend({
|
|||
this.loadResults(val)
|
||||
this.selectedBooks = []
|
||||
this.selectedSeries = []
|
||||
this.selectedCollections = []
|
||||
this.selectedReadLists = []
|
||||
},
|
||||
deep: true,
|
||||
immediate: true,
|
||||
|
|
@ -189,7 +209,7 @@ export default Vue.extend({
|
|||
return this.$vuetify.breakpoint.name === 'xs' ? 120 : 150
|
||||
},
|
||||
showToolbar (): boolean {
|
||||
return this.selectedSeries.length === 0 && this.selectedBooks.length === 0
|
||||
return this.selectedSeries.length === 0 && this.selectedBooks.length === 0 && this.selectedCollections.length === 0 && this.selectedReadLists.length === 0
|
||||
},
|
||||
emptyResults (): boolean {
|
||||
return !this.loading && this.series.length === 0 && this.books.length === 0 && this.collections.length === 0 && this.readLists.length === 0
|
||||
|
|
@ -261,6 +281,12 @@ export default Vue.extend({
|
|||
editMultipleBooks () {
|
||||
this.$store.dispatch('dialogUpdateBooks', this.selectedBooks)
|
||||
},
|
||||
deleteCollections () {
|
||||
this.$store.dispatch('dialogDeleteCollection', this.selectedCollections)
|
||||
},
|
||||
deleteReadLists () {
|
||||
this.$store.dispatch('dialogDeleteReadList', this.selectedReadLists)
|
||||
},
|
||||
async markSelectedBooksRead () {
|
||||
await Promise.all(this.selectedBooks.map(b =>
|
||||
this.$komgaBooks.updateReadProgress(b.id, { completed: true }),
|
||||
|
|
@ -277,6 +303,8 @@ export default Vue.extend({
|
|||
async loadResults (search: string) {
|
||||
this.selectedBooks = []
|
||||
this.selectedSeries = []
|
||||
this.selectedCollections = []
|
||||
this.selectedReadLists = []
|
||||
if (search) {
|
||||
this.loading = true
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue