Update search page for new data model and adding podcasts

This commit is contained in:
advplyr 2022-04-12 19:10:11 -05:00
parent 021538820a
commit 24d124e2e8
7 changed files with 80 additions and 51 deletions

View file

@ -1,5 +1,5 @@
<template> <template>
<div @mouseover="mouseover" @mouseout="mouseout"> <div>
<div :style="{ width: width + 'px', height: height + 'px' }" class="bg-primary box-shadow-book rounded-md relative overflow-hidden"> <div :style="{ width: width + 'px', height: height + 'px' }" class="bg-primary box-shadow-book rounded-md relative overflow-hidden">
<!-- Image or placeholder --> <!-- Image or placeholder -->
<covers-author-image :author="author" /> <covers-author-image :author="author" />
@ -10,14 +10,6 @@
<p class="text-center text-gray-200" :style="{ fontSize: sizeMultiplier * 0.65 + 'rem' }">{{ numBooks }} Book{{ numBooks === 1 ? '' : 's' }}</p> <p class="text-center text-gray-200" :style="{ fontSize: sizeMultiplier * 0.65 + 'rem' }">{{ numBooks }} Book{{ numBooks === 1 ? '' : 's' }}</p>
</div> </div>
<!-- Search icon btn -->
<div v-show="!searching && isHovering" class="absolute top-0 left-0 p-2 cursor-pointer hover:text-white text-gray-200 transform transition-transform hover:scale-125" @click.prevent.stop="searchAuthor">
<span class="material-icons text-lg">search</span>
</div>
<div v-show="isHovering && !searching" class="absolute top-0 right-0 p-2 cursor-pointer hover:text-white text-gray-200 transform transition-transform hover:scale-125" @click.prevent.stop="$emit('edit', author)">
<span class="material-icons text-lg">edit</span>
</div>
<!-- Loading spinner --> <!-- Loading spinner -->
<div v-show="searching" class="absolute top-0 left-0 z-10 w-full h-full bg-black bg-opacity-50 flex items-center justify-center"> <div v-show="searching" class="absolute top-0 left-0 z-10 w-full h-full bg-black bg-opacity-50 flex items-center justify-center">
<widgets-loading-spinner size="" /> <widgets-loading-spinner size="" />
@ -46,8 +38,7 @@ export default {
}, },
data() { data() {
return { return {
searching: false, searching: false
isHovering: false
} }
}, },
computed: { computed: {
@ -68,12 +59,6 @@ export default {
} }
}, },
methods: { methods: {
mouseover() {
this.isHovering = true
},
mouseout() {
this.isHovering = false
},
async searchAuthor() { async searchAuthor() {
this.searching = true this.searching = true
var response = await this.$axios.$post(`/api/authors/${this.authorId}/match`, { q: this.name }).catch((error) => { var response = await this.$axios.$post(`/api/authors/${this.authorId}/match`, { q: this.name }).catch((error) => {

View file

@ -1,6 +1,13 @@
<template> <template>
<div class="flex h-full px-1 overflow-hidden shadow-sm"> <div class="flex h-full px-1 overflow-hidden">
<!-- <img src="/icons/NoUserPhoto.png" class="w-40 h-40 max-h-40 object-contain" style="max-height: 40px; max-width: 40px" /> --> <div class="overflow-hidden bg-primary rounded" style="height: 50px; width: 40px">
<covers-author-image :author="author" />
</div>
<div class="flex-grow px-2 authorSearchCardContent h-full">
<p class="truncate text-sm">{{ name }}</p>
</div>
</div>
<!-- <div class="flex h-full px-1 overflow-hidden shadow-sm">
<div style="max-height: 48px; max-width: 48px" class="w-12 h-12 bg-primary overflow-hidden rounded"> <div style="max-height: 48px; max-width: 48px" class="w-12 h-12 bg-primary overflow-hidden rounded">
<svg width="140%" height="140%" style="margin-left: -20%; margin-top: -20%; opacity: 0.6" viewBox="0 0 177 266" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="140%" height="140%" style="margin-left: -20%; margin-top: -20%; opacity: 0.6" viewBox="0 0 177 266" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill="white" d="M40.7156 165.47C10.2694 150.865 -31.5407 148.629 -38.0532 155.529L63.3191 204.159L76.9443 190.899C66.828 181.394 54.006 171.846 40.7156 165.47Z" stroke="white" stroke-width="4" transform="translate(-2 -1)" /> <path fill="white" d="M40.7156 165.47C10.2694 150.865 -31.5407 148.629 -38.0532 155.529L63.3191 204.159L76.9443 190.899C66.828 181.394 54.006 171.846 40.7156 165.47Z" stroke="white" stroke-width="4" transform="translate(-2 -1)" />
@ -19,18 +26,25 @@
<div class="flex-grow px-2 authorSearchCardContent h-full"> <div class="flex-grow px-2 authorSearchCardContent h-full">
<p class="truncate text-sm">{{ author }}</p> <p class="truncate text-sm">{{ author }}</p>
</div> </div>
</div> </div> -->
</template> </template>
<script> <script>
export default { export default {
props: { props: {
author: String author: {
type: Object,
default: () => {}
}
}, },
data() { data() {
return {} return {}
}, },
computed: {}, computed: {
name() {
return this.author.name
}
},
methods: {}, methods: {},
mounted() {} mounted() {}
} }

View file

@ -1,16 +1,16 @@
<template> <template>
<div class="flex h-full px-1 overflow-hidden"> <div class="flex h-full px-1 overflow-hidden">
<covers-book-cover :audiobook="audiobook" :width="coverWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" /> <covers-book-cover :library-item="libraryItem" :width="coverWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" />
<div class="flex-grow px-2 h-full audiobookSearchCardContent"> <div class="flex-grow px-2 audiobookSearchCardContent">
<p v-if="matchKey !== 'title'" class="truncate text-sm">{{ title }}</p> <p v-if="matchKey !== 'title'" class="truncate text-sm">{{ title }}</p>
<p v-else class="truncate text-sm" v-html="matchHtml" /> <p v-else class="truncate text-sm" v-html="matchHtml" />
<p v-if="matchKey === 'subtitle'" class="truncate text-xs text-gray-300">{{ matchHtml }}</p> <p v-if="matchKey === 'subtitle'" class="truncate text-xs text-gray-300">{{ matchHtml }}</p>
<p v-if="matchKey !== 'authorFL'" class="text-xs text-gray-200 truncate">by {{ authorFL }}</p> <p v-if="matchKey !== 'authors'" class="text-xs text-gray-200 truncate">by {{ authorName }}</p>
<p v-else class="truncate text-xs text-gray-200" v-html="matchHtml" /> <p v-else class="truncate text-xs text-gray-200" v-html="matchHtml" />
<div v-if="matchKey === 'series' || matchKey === 'tags'" class="m-0 p-0 truncate" v-html="matchHtml" /> <div v-if="matchKey === 'series' || matchKey === 'tags' || matchKey === 'isbn' || matchKey === 'asin'" class="m-0 p-0 truncate text-xs" v-html="matchHtml" />
</div> </div>
</div> </div>
</template> </template>
@ -18,7 +18,7 @@
<script> <script>
export default { export default {
props: { props: {
audiobook: { libraryItem: {
type: Object, type: Object,
default: () => {} default: () => {}
}, },
@ -37,17 +37,27 @@ export default {
if (this.bookCoverAspectRatio === 1) return 50 * 1.2 if (this.bookCoverAspectRatio === 1) return 50 * 1.2
return 50 return 50
}, },
book() { media() {
return this.audiobook ? this.audiobook.book || {} : {} return this.libraryItem ? this.libraryItem.media || {} : {}
},
mediaMetadata() {
return this.media.metadata || {}
},
mediaType() {
return this.libraryItem ? this.libraryItem.mediaType : null
},
isPodcast() {
return this.mediaType === 'podcast'
}, },
title() { title() {
return this.book ? this.book.title : 'No Title' return this.mediaMetadata.title || 'No Title'
}, },
subtitle() { subtitle() {
return this.book ? this.book.subtitle : '' return this.mediaMetadata.subtitle
}, },
authorFL() { authorName() {
return this.book ? this.book.authorFL : 'Unknown' if (this.isPodcast) return this.mediaMetadata.author
return this.mediaMetadata.authorName
}, },
matchHtml() { matchHtml() {
if (!this.matchText || !this.search) return '' if (!this.matchText || !this.search) return ''
@ -69,7 +79,9 @@ export default {
html += lastPart html += lastPart
if (this.matchKey === 'tags') return `<p class="truncate">Tags: ${html}</p>` if (this.matchKey === 'tags') return `<p class="truncate">Tags: ${html}</p>`
if (this.matchKey === 'authorFL') return `by ${html}` if (this.matchKey === 'authors') return `by ${html}`
if (this.matchKey === 'isbn') return `<p class="truncate">ISBN: ${html}</p>`
if (this.matchKey === 'asin') return `<p class="truncate">ASIN: ${html}</p>`
if (this.matchKey === 'series') return `<p class="truncate">Series: ${html}</p>` if (this.matchKey === 'series') return `<p class="truncate">Series: ${html}</p>`
return `${html}` return `${html}`
} }

View file

@ -7,7 +7,7 @@
<div class="absolute cover-bg" ref="coverBg" /> <div class="absolute cover-bg" ref="coverBg" />
</div> </div>
<div class="w-full h-full absolute top-0 left-0"> <div class="w-full h-full absolute top-0 left-0 bg-primary">
<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 }" /> <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> </div>

View file

@ -1,8 +1,8 @@
<template> <template>
<div class="flex h-full px-1 overflow-hidden"> <div class="flex h-full px-1 overflow-hidden">
<covers-group-cover :name="series" :book-items="bookItems" :width="80" :height="60" :book-cover-aspect-ratio="bookCoverAspectRatio" /> <covers-group-cover :name="name" :book-items="bookItems" :width="80" :height="60" :book-cover-aspect-ratio="bookCoverAspectRatio" />
<div class="flex-grow px-2 seriesSearchCardContent h-full"> <div class="flex-grow px-2 seriesSearchCardContent h-full">
<p class="truncate text-sm">{{ series }}</p> <p class="truncate text-sm">{{ name }}</p>
</div> </div>
</div> </div>
</template> </template>
@ -10,7 +10,10 @@
<script> <script>
export default { export default {
props: { props: {
series: String, series: {
type: Object,
default: () => {}
},
bookItems: { bookItems: {
type: Array, type: Array,
default: () => [] default: () => []
@ -22,6 +25,9 @@ export default {
computed: { computed: {
bookCoverAspectRatio() { bookCoverAspectRatio() {
return this.$store.getters['getBookCoverAspectRatio'] return this.$store.getters['getBookCoverAspectRatio']
},
name() {
return this.series.name
} }
}, },
methods: {}, methods: {},

View file

@ -4,7 +4,7 @@
<script> <script>
export default { export default {
async asyncData({ params, app, store }) { async asyncData({ params, app, store, redirect }) {
var series = await app.$axios.$get(`/api/series/${params.id}`).catch((error) => { var series = await app.$axios.$get(`/api/series/${params.id}`).catch((error) => {
console.error('Failed', error) console.error('Failed', error)
return false return false

View file

@ -11,28 +11,37 @@
<p class="text-lg text-gray-400">Nothing found</p> <p class="text-lg text-gray-400">Nothing found</p>
</div> </div>
<p v-if="bookResults.length" class="font-semibold text-sm mb-1">Books</p> <p v-if="bookResults.length" class="font-semibold text-sm mb-1">Books</p>
<template v-for="bookResult in bookResults"> <template v-for="item in bookResults">
<div :key="bookResult.audiobook.id" class="w-full h-16 py-1"> <div :key="item.id" class="w-full h-16 py-1">
<nuxt-link :to="`/item/${bookResult.audiobook.id}`"> <nuxt-link :to="`/item/${item.id}`">
<cards-book-search-card :audiobook="bookResult.audiobook" :search="lastSearch" :match-key="bookResult.matchKey" :match-text="bookResult.matchText" /> <cards-item-search-card :library-item="item.libraryItem" :match-key="item.matchKey" :match-text="item.matchText" :search="lastSearch" />
</nuxt-link>
</div>
</template>
<p v-if="podcastResults.length" class="uppercase text-xs text-gray-400 my-1 px-1 font-semibold">Podcasts</p>
<template v-for="item in podcastResults">
<div :key="item.id" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1">
<nuxt-link :to="`/item/${item.id}`">
<cards-item-search-card :library-item="item.libraryItem" :match-key="item.matchKey" :match-text="item.matchText" :search="lastSearch" />
</nuxt-link> </nuxt-link>
</div> </div>
</template> </template>
<p v-if="seriesResults.length" class="font-semibold text-sm mb-1 mt-2">Series</p> <p v-if="seriesResults.length" class="font-semibold text-sm mb-1 mt-2">Series</p>
<template v-for="seriesResult in seriesResults"> <template v-for="seriesResult in seriesResults">
<div :key="seriesResult.series" class="w-full h-16 py-1"> <div :key="seriesResult.series.id" class="w-full h-16 py-1">
<nuxt-link :to="`/bookshelf/series/${$encode(seriesResult.series)}`"> <nuxt-link :to="`/bookshelf/series/${seriesResult.series.id}`">
<cards-series-search-card :series="seriesResult.series" :book-items="seriesResult.audiobooks" /> <cards-series-search-card :series="seriesResult.series" :book-items="seriesResult.books" />
</nuxt-link> </nuxt-link>
</div> </div>
</template> </template>
<p v-if="authorResults.length" class="font-semibold text-sm mb-1 mt-2">Authors</p> <p v-if="authorResults.length" class="font-semibold text-sm mb-1 mt-2">Authors</p>
<template v-for="authorResult in authorResults"> <template v-for="authorResult in authorResults">
<div :key="authorResult.author" class="w-full h-14 py-1"> <div :key="authorResult.id" class="w-full h-14 py-1">
<nuxt-link :to="`/bookshelf/library?filter=authors.${$encode(authorResult.author)}`"> <nuxt-link :to="`/bookshelf/library?filter=authors.${$encode(authorResult.id)}`">
<cards-author-search-card :key="authorResult.author" :author="authorResult.author" /> <cards-author-search-card :key="authorResult.id" :author="authorResult" />
</nuxt-link> </nuxt-link>
</div> </div>
</template> </template>
@ -49,6 +58,7 @@ export default {
lastSearch: null, lastSearch: null,
isFetching: false, isFetching: false,
bookResults: [], bookResults: [],
podcastResults: [],
seriesResults: [], seriesResults: [],
authorResults: [] authorResults: []
} }
@ -61,7 +71,7 @@ export default {
return this.$store.getters['getBookCoverAspectRatio'] return this.$store.getters['getBookCoverAspectRatio']
}, },
totalResults() { totalResults() {
return this.bookResults.length + this.seriesResults.length + this.authorResults.length return this.bookResults.length + this.seriesResults.length + this.authorResults.length + this.podcastResults.length
} }
}, },
methods: { methods: {
@ -69,6 +79,7 @@ export default {
this.lastSearch = value this.lastSearch = value
if (!this.lastSearch) { if (!this.lastSearch) {
this.bookResults = [] this.bookResults = []
this.podcastResults = []
this.seriesResults = [] this.seriesResults = []
this.authorResults = [] this.authorResults = []
return return
@ -82,7 +93,8 @@ export default {
this.isFetching = false this.isFetching = false
this.bookResults = results ? results.audiobooks || [] : [] this.bookResults = results ? results.book || [] : []
this.podcastResults = results ? results.podcast || [] : []
this.seriesResults = results ? results.series || [] : [] this.seriesResults = results ? results.series || [] : []
this.authorResults = results ? results.authors || [] : [] this.authorResults = results ? results.authors || [] : []
}, },