[web] Refactor library search page

This commit is contained in:
Alain Nussbaumer 2024-03-25 18:03:52 +01:00
parent 17c6454afa
commit 36842dfc04

View File

@ -1,6 +1,4 @@
<template> <template>
<div>
<!-- Search field + recent searches -->
<section class="section pb-0"> <section class="section pb-0">
<div class="container"> <div class="container">
<div class="columns is-centered"> <div class="columns is-centered">
@ -48,215 +46,37 @@
</div> </div>
</section> </section>
<tabs-search :query="search_query" /> <tabs-search :query="search_query" />
<!-- Tracks --> <template v-for="type in search_types" :key="type">
<content-with-heading v-if="show('track') && tracks.total" class="pt-0"> <content-with-heading v-if="show(type)" class="pt-0">
<template #heading-left> <template #heading-left>
<p class="title is-4" v-text="$t('page.search.tracks')" /> <p class="title is-4" v-text="$t(`page.search.${type}s`)" />
</template> </template>
<template #content> <template #content>
<list-tracks :items="tracks" /> <component :is="components[type]" :items="results[type]" />
</template> </template>
<template #footer> <template #footer>
<nav v-if="show_all_button(tracks)" class="level"> <nav v-if="show_all_button(type)" class="level">
<p class="level-item"> <p class="level-item">
<a <a
class="button is-light is-small is-rounded" class="button is-light is-small is-rounded"
@click="open_search('track')" @click="open_search(type)"
v-text=" v-text="
$t('page.search.show-tracks', tracks.total, { $t(`page.search.show-${type}s`, results[type].total, {
count: $filters.number(tracks.total) count: $filters.number(results[type].total)
}) })
" "
/> />
</p> </p>
</nav> </nav>
</template> <p v-if="!results[type].total" class="has-text-centered-mobile">
</content-with-heading> <i v-text="$t('page.search.no-tracks')" />
<content-text v-if="show('track') && !tracks.total" class="pt-0">
<template #content>
<p><i v-text="$t('page.search.no-tracks')" /></p>
</template>
</content-text>
<!-- Artists -->
<content-with-heading v-if="show('artist') && artists.total">
<template #heading-left>
<p class="title is-4" v-text="$t('page.search.artists')" />
</template>
<template #content>
<list-artists :items="artists" />
</template>
<template #footer>
<nav v-if="show_all_button(artists)" class="level">
<p class="level-item">
<a
class="button is-light is-small is-rounded"
@click="open_search('artist')"
v-text="
$t('page.search.show-artists', artists.total, {
count: $filters.number(artists.total)
})
"
/>
</p> </p>
</nav>
</template> </template>
</content-with-heading> </content-with-heading>
<content-text v-if="show('artist') && !artists.total">
<template #content>
<p><i v-text="$t('page.search.no-artists')" /></p>
</template> </template>
</content-text>
<!-- Albums -->
<content-with-heading v-if="show('album') && albums.total">
<template #heading-left>
<p class="title is-4" v-text="$t('page.search.albums')" />
</template>
<template #content>
<list-albums :items="albums" />
</template>
<template #footer>
<nav v-if="show_all_button(albums)" class="level">
<p class="level-item">
<a
class="button is-light is-small is-rounded"
@click="open_search('album')"
v-text="
$t('page.search.show-albums', albums.total, {
count: $filters.number(albums.total)
})
"
/>
</p>
</nav>
</template>
</content-with-heading>
<content-text v-if="show('album') && !albums.total">
<template #content>
<p><i v-text="$t('page.search.no-albums')" /></p>
</template>
</content-text>
<!-- Composers -->
<content-with-heading v-if="show('composer') && composers.total">
<template #heading-left>
<p class="title is-4" v-text="$t('page.search.composers')" />
</template>
<template #content>
<list-composers :items="composers" />
</template>
<template #footer>
<nav v-if="show_all_button(composers)" class="level">
<p class="level-item">
<a
class="button is-light is-small is-rounded"
@click="open_search('composer')"
v-text="
$t('page.search.show-composers', composers.total, {
count: $filters.number(composers.total)
})
"
/>
</p>
</nav>
</template>
</content-with-heading>
<content-text v-if="show('composer') && !composers.total">
<template #content>
<p><i v-text="$t('page.search.no-composers')" /></p>
</template>
</content-text>
<!-- Playlists -->
<content-with-heading v-if="show('playlist') && playlists.total">
<template #heading-left>
<p class="title is-4" v-text="$t('page.search.playlists')" />
</template>
<template #content>
<list-playlists :items="playlists" />
</template>
<template #footer>
<nav v-if="show_all_button(playlists)" class="level">
<p class="level-item">
<a
class="button is-light is-small is-rounded"
@click="open_search('playlist')"
v-text="
$t('page.search.show-playlists', playlists.total, {
count: $filters.number(playlists.total)
})
"
/>
</p>
</nav>
</template>
</content-with-heading>
<content-text v-if="show('playlist') && !playlists.total">
<template #content>
<p><i v-text="$t('page.search.no-playlists')" /></p>
</template>
</content-text>
<!-- Podcasts -->
<content-with-heading v-if="show('podcast') && podcasts.total">
<template #heading-left>
<p class="title is-4" v-text="$t('page.search.podcasts')" />
</template>
<template #content>
<list-albums :items="podcasts" />
</template>
<template #footer>
<nav v-if="show_all_button(podcasts)" class="level">
<p class="level-item">
<a
class="button is-light is-small is-rounded"
@click="open_search('podcast')"
v-text="
$t('page.search.show-podcasts', podcasts.total, {
count: $filters.number(podcasts.total)
})
"
/>
</p>
</nav>
</template>
</content-with-heading>
<content-text v-if="show('podcast') && !podcasts.total">
<template #content>
<p><i v-text="$t('page.search.no-podcasts')" /></p>
</template>
</content-text>
<!-- Audiobooks -->
<content-with-heading v-if="show('audiobook') && audiobooks.total">
<template #heading-left>
<p class="title is-4" v-text="$t('page.search.audiobooks')" />
</template>
<template #content>
<list-albums :items="audiobooks" />
</template>
<template #footer>
<nav v-if="show_all_button(audiobooks)" class="level">
<p class="level-item">
<a
class="button is-light is-small is-rounded"
@click="open_search('audiobook')"
v-text="
$t('page.search.show-audiobooks', audiobooks.total, {
count: $filters.number(audiobooks.total)
})
"
/>
</p>
</nav>
</template>
</content-with-heading>
<content-text v-if="show('audiobook') && !audiobooks.total">
<template #content>
<p><i v-text="$t('page.search.no-audiobooks')" /></p>
</template>
</content-text>
</div>
</template> </template>
<script> <script>
import ContentText from '@/templates/ContentText.vue'
import ContentWithHeading from '@/templates/ContentWithHeading.vue' import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import { GroupedList } from '@/lib/GroupedList' import { GroupedList } from '@/lib/GroupedList'
import ListAlbums from '@/components/ListAlbums.vue' import ListAlbums from '@/components/ListAlbums.vue'
@ -270,7 +90,6 @@ import webapi from '@/webapi'
export default { export default {
name: 'PageSearchLibrary', name: 'PageSearchLibrary',
components: { components: {
ContentText,
ContentWithHeading, ContentWithHeading,
ListAlbums, ListAlbums,
ListArtists, ListArtists,
@ -282,13 +101,34 @@ export default {
data() { data() {
return { return {
albums: new GroupedList(), components: {
artists: new GroupedList(), album: ListAlbums.name,
audiobooks: new GroupedList(), artist: ListArtists.name,
composers: new GroupedList(), audiobook: ListAlbums.name,
playlists: new GroupedList(), composer: ListComposers.name,
podcasts: new GroupedList(), playlist: ListPlaylists.name,
podcast: ListAlbums.name,
track: ListTracks.name
},
results: {
album: new GroupedList(),
artist: new GroupedList(),
audiobook: new GroupedList(),
composer: new GroupedList(),
playlist: new GroupedList(),
podcast: new GroupedList(),
track: new GroupedList()
},
search_query: '', search_query: '',
search_types: [
'track',
'artist',
'album',
'composer',
'playlist',
'audiobook',
'podcast'
],
tracks: new GroupedList() tracks: new GroupedList()
} }
}, },
@ -310,6 +150,31 @@ export default {
}, },
methods: { methods: {
new_search() {
if (!this.search_query) {
return
}
this.$router.push({
name: 'search-library',
query: {
limit: 3,
offset: 0,
query: this.search_query,
type: this.search_types.join()
}
})
this.$refs.search_field.blur()
},
open_recent_search(query) {
this.search_query = query
this.new_search()
},
open_search(type) {
this.$router.push({
name: 'search-library',
query: { query: this.$route.query.query, type }
})
},
search(route) { search(route) {
this.search_query = route.query.query?.trim() this.search_query = route.query.query?.trim()
if (!this.search_query || !this.search_query.replace(/^query:/u, '')) { if (!this.search_query || !this.search_query.replace(/^query:/u, '')) {
@ -318,11 +183,10 @@ export default {
} }
route.query.query = this.search_query route.query.query = this.search_query
this.searchMusic(route.query) this.searchMusic(route.query)
this.searchAudiobooks(route.query) this.searchType(route.query, 'audiobook')
this.searchPodcasts(route.query) this.searchType(route.query, 'podcast')
this.$store.dispatch('add_recent_search', this.search_query) this.$store.dispatch('add_recent_search', this.search_query)
}, },
searchMusic(query) { searchMusic(query) {
if ( if (
!query.type.includes('track') && !query.type.includes('track') &&
@ -333,30 +197,29 @@ export default {
) { ) {
return return
} }
const searchParams = { const parameters = {
type: query.type type: query.type
} }
if (query.query.startsWith('query:')) { if (query.query.startsWith('query:')) {
searchParams.expression = `(${query.query.replace(/^query:/u, '').trim()}) and media_kind is music` parameters.expression = `(${query.query.replace(/^query:/u, '').trim()}) and media_kind is music`
} else { } else {
searchParams.query = query.query parameters.query = query.query
searchParams.media_kind = 'music' parameters.media_kind = 'music'
} }
if (query.limit) { if (query.limit) {
searchParams.limit = query.limit parameters.limit = query.limit
searchParams.offset = query.offset parameters.offset = query.offset
} }
webapi.search(searchParams).then(({ data }) => { webapi.search(parameters).then(({ data }) => {
this.tracks = new GroupedList(data.tracks) this.results.track = new GroupedList(data.tracks)
this.artists = new GroupedList(data.artists) this.results.artist = new GroupedList(data.artists)
this.albums = new GroupedList(data.albums) this.results.album = new GroupedList(data.albums)
this.composers = new GroupedList(data.composers) this.results.composer = new GroupedList(data.composers)
this.playlists = new GroupedList(data.playlists) this.results.playlist = new GroupedList(data.playlists)
}) })
}, },
searchType(query, type) {
searchAudiobooks(query) { if (!query.type.includes(type)) {
if (!query.type.includes('audiobook')) {
return return
} }
const parameters = { const parameters = {
@ -367,76 +230,21 @@ export default {
} else { } else {
parameters.expression = `album includes "${query.query}" or artist includes "${query.query}"` parameters.expression = `album includes "${query.query}" or artist includes "${query.query}"`
} }
parameters.expression = `(${parameters.expression}) and media_kind is audiobook` parameters.expression = `(${parameters.expression}) and media_kind is ${type}`
if (query.limit) { if (query.limit) {
parameters.limit = query.limit parameters.limit = query.limit
parameters.offset = query.offset parameters.offset = query.offset
} }
webapi.search(parameters).then(({ data }) => { webapi.search(parameters).then(({ data }) => {
this.audiobooks = new GroupedList(data.albums) this.results[type] = new GroupedList(data.albums)
}) })
}, },
searchPodcasts(query) {
if (!query.type.includes('podcast')) {
return
}
const parameters = {
type: 'album'
}
if (query.query.startsWith('query:')) {
parameters.expression = query.query.replace(/^query:/u, '').trim()
} else {
parameters.expression = `album includes "${query.query}" or artist includes "${query.query}"`
}
parameters.expression = `(${parameters.expression}) and media_kind is podcast`
if (query.limit) {
parameters.limit = query.limit
parameters.offset = query.offset
}
webapi.search(parameters).then(({ data }) => {
this.podcasts = new GroupedList(data.albums)
})
},
new_search() {
if (!this.search_query) {
return
}
this.$router.push({
name: 'search-library',
query: {
limit: 3,
offset: 0,
query: this.search_query,
type: 'track,artist,album,playlist,audiobook,podcast,composer'
}
})
this.$refs.search_field.blur()
},
show(type) { show(type) {
return this.$route.query.type?.includes(type) ?? false return this.$route.query.type?.includes(type) ?? false
}, },
show_all_button(type) {
show_all_button(items) { const items = this.results[type]
return items.total > items.items.length return items.total > items.items.length
},
open_search(type) {
this.$router.push({
name: 'search-library',
query: {
query: this.$route.query.query,
type: type
}
})
},
open_recent_search(query) {
this.search_query = query
this.new_search()
} }
} }
} }