feat: Added a 'Recommended' tab in the library views for a library specific dashboard like the home page

This commit is contained in:
edwinbadillo 2021-04-03 21:53:23 -04:00
parent 3834d415ae
commit b26559dc47
6 changed files with 89 additions and 9 deletions

View File

@ -1,6 +1,5 @@
<template>
<v-bottom-navigation
v-if="collectionsCount > 0 || readListsCount > 0"
grow color="primary"
:fixed="$vuetify.breakpoint.name === 'xs'"
>
@ -9,6 +8,11 @@
<v-icon>mdi-bookshelf</v-icon>
</v-btn>
<v-btn :to="{name: 'recommended-libraries', params: {libraryId: libraryId}}">
<span>{{ $t('library_navigation.recommended') }}</span>
<v-icon>mdi-view-dashboard</v-icon>
</v-btn>
<v-btn
v-if="collectionsCount > 0"
:to="{name: 'browse-collections', params: {libraryId: libraryId}}"

View File

@ -417,7 +417,8 @@
"library_navigation": {
"browse": "Browse",
"collections": "Collections",
"readlists": "Read Lists"
"readlists": "Read Lists",
"recommended": "Recommended"
},
"login": {
"create_user_account": "Create user account",

View File

@ -90,6 +90,13 @@ const router = new Router({
component: () => import(/* webpackChunkName: "browse-libraries" */ './views/BrowseLibraries.vue'),
props: (route) => ({ libraryId: route.params.libraryId }),
},
{
path: '/libraries/:libraryId/dashboard',
name: 'recommended-libraries',
beforeEnter: noLibraryGuard,
component: () => import(/* webpackChunkName: "dashboard" */ './views/Dashboard.vue'),
props: (route) => ({ libraryId: route.params.libraryId }),
},
{
path: '/libraries/:libraryId/collections',
name: 'browse-collections',

View File

@ -1,5 +1,25 @@
<template>
<div>
<div :style="libraryId && $vuetify.breakpoint.name === 'xs' ? 'margin-bottom: 56px' : undefined">
<div v-if="libraryId">
<toolbar-sticky v-if="selectedSeries.length === 0">
<library-actions-menu
v-if="library"
:library="library"
/>
<v-toolbar-title>
<span>{{ library ? library.name : $t('common.all_libraries') }}</span>
<v-chip label class="mx-4" v-if="totalElements">
<span style="font-size: 1.1rem">{{ totalElements }}</span>
</v-chip>
</v-toolbar-title>
</toolbar-sticky>
<library-navigation :libraryId="libraryId"/>
</div>
<series-multi-select-bar
v-model="selectedSeries"
@unselect-all="selectedSeries = []"
@ -110,6 +130,9 @@ import SeriesMultiSelectBar from '@/components/bars/SeriesMultiSelectBar.vue'
import EmptyState from '@/components/EmptyState.vue'
import HorizontalScroller from '@/components/HorizontalScroller.vue'
import ItemBrowser from '@/components/ItemBrowser.vue'
import ToolbarSticky from '@/components/bars/ToolbarSticky.vue'
import LibraryActionsMenu from '@/components/menus/LibraryActionsMenu.vue'
import LibraryNavigation from '@/components/LibraryNavigation.vue'
import {ReadStatus} from '@/types/enum-books'
import {BookDto} from '@/types/komga-books'
import {BOOK_CHANGED, LIBRARY_DELETED, SERIES_CHANGED} from '@/types/events'
@ -121,12 +144,16 @@ export default Vue.extend({
components: {
HorizontalScroller,
EmptyState,
ToolbarSticky,
LibraryNavigation,
ItemBrowser,
BooksMultiSelectBar,
SeriesMultiSelectBar,
LibraryActionsMenu,
},
data: () => {
return {
library: undefined as LibraryDto | undefined,
newSeries: [] as SeriesDto[],
updatedSeries: [] as SeriesDto[],
latestBooks: [] as BookDto[],
@ -149,6 +176,11 @@ export default Vue.extend({
mounted () {
this.loadAll()
},
props: {
libraryId: {
type: String,
},
},
watch: {
selectedSeries (val: SeriesDto[]) {
val.forEach(i => this.replaceSeries(i))
@ -156,6 +188,9 @@ export default Vue.extend({
selectedBooks (val: BookDto[]) {
val.forEach(i => this.replaceBook(i))
},
'$route.path': function(val, oldVal){
this.loadAll()
},
},
computed: {
fixedCardWidth (): number {
@ -178,6 +213,9 @@ export default Vue.extend({
}
},
loadAll () {
if(this.libraryId){
this.loadLibrary(this.libraryId)
}
this.selectedSeries = []
this.selectedBooks = []
this.loadNewSeries()
@ -211,14 +249,21 @@ export default Vue.extend({
}
},
async loadNewSeries () {
this.newSeries = (await this.$komgaSeries.getNewSeries()).content
const pageRequest = {
library_id: this.libraryId,
} as PageRequest
this.newSeries = (await this.$komgaSeries.getNewSeries(pageRequest)).content
},
async loadUpdatedSeries () {
this.updatedSeries = (await this.$komgaSeries.getUpdatedSeries()).content
const pageRequest = {
library_id: this.libraryId,
} as PageRequest
this.updatedSeries = (await this.$komgaSeries.getUpdatedSeries(pageRequest)).content
},
async loadLatestBooks () {
const pageRequest = {
sort: ['createdDate,desc'],
library_id: this.libraryId,
} as PageRequest
this.latestBooks = (await this.$komgaBooks.getBooks(undefined, pageRequest)).content
@ -226,12 +271,17 @@ export default Vue.extend({
async loadInProgressBooks () {
const pageRequest = {
sort: ['readProgress.lastModified,desc'],
library_id: this.libraryId,
} as PageRequest
this.inProgressBooks = (await this.$komgaBooks.getBooks(undefined, pageRequest, undefined, undefined, [ReadStatus.IN_PROGRESS])).content
},
async loadOnDeckBooks () {
this.onDeckBooks = (await this.$komgaBooks.getBooksOnDeck()).content
const pageRequest = {
library_id: this.libraryId,
} as PageRequest
this.onDeckBooks = (await this.$komgaBooks.getBooksOnDeck(pageRequest)).content
},
singleEditSeries (series: SeriesDto) {
this.$store.dispatch('dialogUpdateSeries', series)
@ -283,6 +333,11 @@ export default Vue.extend({
this.$komgaBooks.getBook(b.id),
))
},
loadLibrary(libraryId: string) {
if (libraryId) {
this.library = this.$store.getters.getLibraryById(libraryId)
}
},
},
})
</script>

View File

@ -5972,6 +5972,14 @@
"in": "query",
"name": "size",
"description": "The size of the page to be returned"
},
{
"schema": {
"type": "string"
},
"in": "query",
"name": "library_id",
"required": false
}
],
"tags": [
@ -6188,4 +6196,4 @@
"title": "Komga API",
"version": "v1.0"
}
}
}

View File

@ -147,13 +147,18 @@ class BookController(
}
@Operation(description = "Return first unread book of series with at least one book read and no books in progress.")
@PageableWithoutSortAsQueryParam
@PageableAsQueryParam
@GetMapping("api/v1/books/ondeck")
fun getBooksOnDeck(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "library_id", required = false) libraryId: List<String>?,
@Parameter(hidden = true) page: Pageable
): Page<BookDto> {
val libraryIds = if (principal.user.sharedAllLibraries) emptySet() else principal.user.sharedLibrariesIds
val libraryIds = when {
!libraryId.isNullOrEmpty() -> libraryId
principal.user.sharedAllLibraries -> emptySet()
else -> principal.user.sharedLibrariesIds
}
return bookDtoRepository.findOnDeck(
libraryIds,