Skip to content

Commit

Permalink
feat(webui): read lists
Browse files Browse the repository at this point in the history
closes gotson#106
  • Loading branch information
gotson committed Aug 20, 2020
1 parent f0c864f commit 27edf17
Show file tree
Hide file tree
Showing 38 changed files with 1,539 additions and 52 deletions.
83 changes: 83 additions & 0 deletions komga-webui/src/components/Dialogs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@
@deleted="collectionDeleted"
/>

<read-list-add-to-dialog
v-model="addToReadListDialog"
:books="addToReadListBooks"
@added="readListAdded"
@created="readListAdded"
/>

<read-list-edit-dialog
v-model="editReadListDialog"
:read-list="editReadList"
@updated="readListUpdated"
/>

<read-list-delete-dialog
v-model="deleteReadListDialog"
:read-list="deleteReadList"
@deleted="readListDeleted"
/>

<library-edit-dialog
v-model="editLibraryDialog"
:library="editLibrary"
Expand Down Expand Up @@ -62,23 +81,34 @@ import {
collectionToEventCollectionDeleted,
LIBRARY_DELETED,
libraryToEventLibraryDeleted,
READLIST_CHANGED,
READLIST_DELETED,
readListToEventReadListChanged,
readListToEventReadListDeleted,
SERIES_CHANGED,
seriesToEventSeriesChanged,
} from '@/types/events'
import Vue from 'vue'
import ReadListAddToDialog from '@/components/dialogs/ReadListAddToDialog.vue'
import ReadListDeleteDialog from '@/components/dialogs/ReadListDeleteDialog.vue'
import ReadListEditDialog from '@/components/dialogs/ReadListEditDialog.vue'
export default Vue.extend({
name: 'Dialogs',
components: {
CollectionAddToDialog,
CollectionEditDialog,
CollectionDeleteDialog,
ReadListAddToDialog,
ReadListEditDialog,
ReadListDeleteDialog,
LibraryEditDialog,
LibraryDeleteDialog,
EditBooksDialog,
EditSeriesDialog,
},
computed: {
// collections
addToCollectionDialog: {
get (): boolean {
return this.$store.state.addToCollectionDialog
Expand Down Expand Up @@ -112,6 +142,41 @@ export default Vue.extend({
deleteCollection (): CollectionDto {
return this.$store.state.deleteCollection
},
// read lists
addToReadListDialog: {
get (): boolean {
return this.$store.state.addToReadListDialog
},
set (val) {
this.$store.dispatch('dialogAddBooksToReadListDisplay', val)
},
},
addToReadListBooks (): BookDto | BookDto[] {
return this.$store.state.addToReadListBooks
},
editReadListDialog: {
get (): boolean {
return this.$store.state.editReadListDialog
},
set (val) {
this.$store.dispatch('dialogEditReadListDisplay', val)
},
},
editReadList (): ReadListDto {
return this.$store.state.editReadList
},
deleteReadListDialog: {
get (): boolean {
return this.$store.state.deleteReadListDialog
},
set (val) {
this.$store.dispatch('dialogDeleteReadListDisplay', val)
},
},
deleteReadList (): ReadListDto {
return this.$store.state.deleteReadList
},
// libraries
editLibraryDialog: {
get (): boolean {
return this.$store.state.editLibraryDialog
Expand All @@ -134,6 +199,7 @@ export default Vue.extend({
deleteLibrary (): LibraryDto {
return this.$store.state.deleteLibrary
},
// books
updateBooksDialog: {
get (): boolean {
return this.$store.state.updateBooksDialog
Expand All @@ -145,6 +211,7 @@ export default Vue.extend({
updateBooks (): BookDto | BookDto[] {
return this.$store.state.updateBooks
},
// series
updateSeriesDialog: {
get (): boolean {
return this.$store.state.updateSeriesDialog
Expand Down Expand Up @@ -174,6 +241,22 @@ export default Vue.extend({
collectionDeleted () {
this.$eventHub.$emit(COLLECTION_DELETED, collectionToEventCollectionDeleted(this.deleteCollection))
},
readListAdded (readList: ReadListDto) {
if (Array.isArray(this.addToReadListBooks)) {
this.addToReadListBooks.forEach(b => {
this.$eventHub.$emit(BOOK_CHANGED, bookToEventBookChanged(b))
})
} else {
this.$eventHub.$emit(BOOK_CHANGED, bookToEventBookChanged(this.addToReadListBooks))
}
this.$eventHub.$emit(READLIST_CHANGED, readListToEventReadListChanged(readList))
},
readListUpdated () {
this.$eventHub.$emit(READLIST_CHANGED, readListToEventReadListChanged(this.editReadList))
},
readListDeleted () {
this.$eventHub.$emit(READLIST_DELETED, readListToEventReadListDeleted(this.deleteReadList))
},
libraryDeleted () {
this.$eventHub.$emit(LIBRARY_DELETED, libraryToEventLibraryDeleted(this.deleteLibrary))
},
Expand Down
11 changes: 8 additions & 3 deletions komga-webui/src/components/ItemCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@
:collection="item"
:menu.sync="actionMenuState"
/>
<read-list-actions-menu v-if="computedItem.type() === ItemTypes.READLIST"
:read-list="item"
:menu.sync="actionMenuState"
/>
</div>
</v-overlay>
</v-fade-transition>
Expand Down Expand Up @@ -119,13 +123,14 @@ import { ReadStatus } from '@/types/enum-books'
import { createItem, Item, ItemTypes } from '@/types/items'
import Vue from 'vue'
import { RawLocation } from 'vue-router'
import ReadListActionsMenu from '@/components/menus/ReadListActionsMenu.vue'
export default Vue.extend({
name: 'ItemCard',
components: { BookActionsMenu, SeriesActionsMenu, CollectionActionsMenu },
components: { BookActionsMenu, SeriesActionsMenu, CollectionActionsMenu, ReadListActionsMenu },
props: {
item: {
type: Object as () => BookDto | SeriesDto | CollectionDto,
type: Object as () => BookDto | SeriesDto | CollectionDto | ReadListDto,
required: true,
},
// hide the bottom part of the card
Expand Down Expand Up @@ -184,7 +189,7 @@ export default Vue.extend({
overlay (): boolean {
return this.onEdit !== undefined || this.onSelected !== undefined || this.bookReady || this.canReadPages || this.actionMenu
},
computedItem (): Item<BookDto | SeriesDto | CollectionDto> {
computedItem (): Item<BookDto | SeriesDto | CollectionDto | ReadListDto> {
return createItem(this.item)
},
disableHover (): boolean {
Expand Down
53 changes: 50 additions & 3 deletions komga-webui/src/components/LibraryNavigation.vue
Original file line number Diff line number Diff line change
@@ -1,31 +1,78 @@
<template>
<v-bottom-navigation grow color="primary"
:fixed="$vuetify.breakpoint.name === 'xs'"
<v-bottom-navigation
v-if="collectionsCount > 0 || readListsCount > 0"
grow color="primary"
:fixed="$vuetify.breakpoint.name === 'xs'"
>
<v-btn :to="{name: 'browse-libraries', params: {libraryId: libraryId}}">
<span>Browse</span>
<v-icon>mdi-bookshelf</v-icon>
</v-btn>

<v-btn :to="{name: 'browse-collections', params: {libraryId: libraryId}}">
<v-btn
v-if="collectionsCount > 0"
:to="{name: 'browse-collections', params: {libraryId: libraryId}}"
>
<span>Collections</span>
<v-icon>mdi-layers-triple</v-icon>
</v-btn>

<v-btn
v-if="readListsCount > 0"
:to="{name: 'browse-readlists', params: {libraryId: libraryId}}"
>
<span>Read Lists</span>
<v-icon>mdi-book-multiple</v-icon>
</v-btn>

</v-bottom-navigation>
</template>

<script lang="ts">
import Vue from 'vue'
import { COLLECTION_CHANGED, READLIST_CHANGED } from '@/types/events'
import { LIBRARIES_ALL } from '@/types/library'
export default Vue.extend({
name: 'LibraryNavigation',
data: () => {
return {
collectionsCount: 0,
readListsCount: 0,
}
},
props: {
libraryId: {
type: String,
required: true,
},
},
watch: {
libraryId: {
handler (val) {
this.loadCounts(val)
},
immediate: true,
},
},
created () {
this.$eventHub.$on(COLLECTION_CHANGED, this.reloadCounts)
this.$eventHub.$on(READLIST_CHANGED, this.reloadCounts)
},
beforeDestroy () {
this.$eventHub.$off(COLLECTION_CHANGED, this.reloadCounts)
this.$eventHub.$off(READLIST_CHANGED, this.reloadCounts)
},
methods: {
reloadCounts () {
this.loadCounts(this.libraryId)
},
async loadCounts (libraryId: string) {
const lib = libraryId !== LIBRARIES_ALL ? [libraryId] : undefined
this.collectionsCount = (await this.$komgaCollections.getCollections(lib, { size: 1 })).totalElements
this.readListsCount = (await this.$komgaReadLists.getReadLists(lib, { size: 1 })).totalElements
},
},
})
</script>

Expand Down
71 changes: 71 additions & 0 deletions komga-webui/src/components/ReadListsExpansionPanels.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<template>
<v-expansion-panels v-model="readListPanel">
<v-expansion-panel v-for="(r, index) in readLists"
:key="index"
>
<v-expansion-panel-header>{{ r.name }} read list</v-expansion-panel-header>
<v-expansion-panel-content>
<horizontal-scroller>
<template v-slot:prepend>
<router-link class="text-overline"
:to="{name: 'browse-readlist', params: {readListId: r.id}}"
>Manage read list
</router-link>
</template>
<template v-slot:content>
<item-browser :items="readListsContent[index]"
nowrap
:selectable="false"
:action-menu="false"
:fixed-item-width="100"
/>
</template>
</horizontal-scroller>
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
</template>

<script lang="ts">
import HorizontalScroller from '@/components/HorizontalScroller.vue'
import ItemBrowser from '@/components/ItemBrowser.vue'
import Vue from 'vue'
export default Vue.extend({
name: 'ReadListsExpansionPanels',
components: {
HorizontalScroller,
ItemBrowser,
},
props: {
readLists: {
type: Array as () => ReadListDto[],
required: true,
},
},
data: () => {
return {
readListPanel: undefined as number | undefined,
readListsContent: [[]] as any[],
}
},
watch: {
readLists: {
handler (val) {
this.readListPanel = undefined
this.readListsContent = [...Array(val.length)].map(elem => new Array(0))
},
immediate: true,
},
async readListPanel (val) {
if (val !== undefined) {
const rlId = this.readLists[val].id
if (this.$_.isEmpty(this.readListsContent[val])) {
const content = (await this.$komgaReadLists.getBooks(rlId, { unpaged: true } as PageRequest)).content
this.readListsContent.splice(val, 1, content)
}
}
},
},
})
</script>

0 comments on commit 27edf17

Please sign in to comment.