funkwhale/front/src/views/Search.vue

332 lines
8.7 KiB
Vue

<template>
<main
v-title="labels.title"
class="main pusher"
>
<section class="ui vertical stripe segment">
<div
v-if="initialId"
class="ui small text container"
>
<h2>{{ labels.title }}</h2>
<remote-search-form
:initial-id="initialId"
:type="initialType"
/>
</div>
<div
v-else
class="ui container"
>
<h2>
<label for="query">
<translate translate-context="Content/Search/Input.Label/Noun">Search</translate>
</label>
</h2>
<form
class="ui form"
@submit.prevent="page = 1; search()"
>
<div class="ui field">
<div class="ui action input">
<input
id="query"
v-model="query"
class="ui input"
name="query"
type="text"
>
<button
:aria-label="labels.submitSearch"
type="submit"
class="ui icon button"
>
<i class="search icon" />
</button>
</div>
</div>
</form>
<div class="ui secondary pointing menu">
<a
v-for="t in types"
:key="t.id"
:class="['item', {active: type === t.id}]"
href=""
@click.prevent="type = t.id"
>
{{ t.label }}
<span
v-if="results[t.id]"
class="ui circular mini right floated label"
>
{{ results[t.id].count }}</span>
</a>
</div>
<div v-if="isLoading">
<div
v-if="isLoading"
class="ui inverted active dimmer"
>
<div class="ui loader" />
</div>
</div>
<empty-state
v-else-if="!currentResults || currentResults.count === 0"
:refresh="true"
@refresh="search"
/>
<div
v-else-if="type === 'artists' || type === 'podcasts'"
class="ui five app-cards cards"
>
<artist-card
v-for="artist in currentResults.results"
:key="artist.id"
:artist="artist"
/>
</div>
<div
v-else-if="type === 'albums' || type === 'series'"
class="ui five app-cards cards"
>
<album-card
v-for="album in currentResults.results"
:key="album.id"
:album="album"
/>
</div>
<track-table
v-else-if="type === 'tracks'"
:tracks="currentResults.results"
/>
<playlist-card-list
v-else-if="type === 'playlists'"
:playlists="currentResults.results"
/>
<div
v-else-if="type === 'radios'"
class="ui cards"
>
<radio-card
v-for="radio in currentResults.results"
:key="radio.id"
type="custom"
:custom-radio="radio"
/>
</div>
<tags-list
v-else-if="type === 'tags'"
:truncate-size="200"
:limit="paginateBy"
:tags="currentResults.results.map(t => {return t.name })"
/>
<pagination
v-if="currentResults && currentResults.count > paginateBy"
:current="page"
:paginate-by="paginateBy"
:total="currentResults.count"
@page-changed="page = $event"
/>
</div>
</section>
</main>
</template>
<script>
import RemoteSearchForm from '@/components/RemoteSearchForm'
import ArtistCard from '@/components/audio/artist/Card'
import AlbumCard from '@/components/audio/album/Card'
import TrackTable from '@/components/audio/track/Table'
import Pagination from '@/components/Pagination'
import PlaylistCardList from '@/components/playlists/CardList'
import RadioCard from '@/components/radios/Card'
import TagsList from '@/components/tags/List'
import axios from 'axios'
export default {
components: {
RemoteSearchForm,
ArtistCard,
AlbumCard,
TrackTable,
Pagination,
PlaylistCardList,
RadioCard,
TagsList
},
props: {
initialId: { type: String, required: false, default: '' },
initialType: { type: String, required: false, default: '' },
initialQuery: { type: String, required: false, default: '' },
initialPage: { type: Number, required: false, default: 0 }
},
data () {
return {
query: this.initialQuery,
type: this.initialType,
page: this.initialPage,
results: {
artists: null,
albums: null,
tracks: null,
playlists: null,
radios: null,
tags: null,
podcasts: null,
series: null
},
isLoading: false,
paginateBy: 25
}
},
computed: {
labels () {
const submitSearch = this.$pgettext('Content/Search/Button.Label/Verb', 'Submit Search Query')
let title = this.$pgettext('Content/Search/Input.Label/Noun', 'Search')
if (this.initialId) {
title = this.$pgettext('Head/Fetch/Title', 'Search a remote object')
if (this.type === 'rss') {
title = this.$pgettext('Head/Fetch/Title', 'Subscribe to a podcast RSS feed')
}
}
return {
title,
submitSearch
}
},
axiosParams () {
const params = new URLSearchParams()
params.append('q', this.query)
params.append('page', this.page)
params.append('page_size', this.paginateBy)
if (this.currentType.contentCategory !== undefined) { params.append('content_category', this.currentType.contentCategory) };
if (this.currentType.includeChannels !== undefined) { params.append('include_channels', this.currentType.includeChannels) };
return params
},
types () {
return [
{
id: 'artists',
label: this.$pgettext('*/*/*/Noun', 'Artists'),
includeChannels: true,
contentCategory: 'music'
},
{
id: 'albums',
label: this.$pgettext('*/*/*', 'Albums'),
includeChannels: true,
contentCategory: 'music'
},
{
id: 'tracks',
label: this.$pgettext('*/*/*', 'Tracks')
},
{
id: 'playlists',
label: this.$pgettext('*/*/*', 'Playlists')
},
{
id: 'radios',
label: this.$pgettext('*/*/*', 'Radios'),
endpoint: 'radios/radios'
},
{
id: 'tags',
label: this.$pgettext('*/*/*', 'Tags')
},
{
id: 'podcasts',
label: this.$pgettext('*/*/*', 'Podcasts'),
endpoint: '/artists',
contentCategory: 'podcast',
includeChannels: true
},
{
id: 'series',
label: this.$pgettext('*/*/*', 'Series'),
endpoint: '/albums',
includeChannels: true,
contentCategory: 'podcast'
}
]
},
currentType () {
return this.types.filter(t => {
return t.id === this.type
})[0]
},
currentResults () {
return this.results[this.currentType.id]
}
},
watch: {
async type () {
this.page = 1
this.updateQueryString()
await this.search()
},
async page () {
this.updateQueryString()
await this.search()
},
'$route.query.q': async function (v) {
this.query = v
this.updateQueryString()
await this.search()
}
},
created () {
this.search()
},
methods: {
async search () {
this.updateQueryString()
if (!this.query) {
this.types.forEach(t => {
this.results[t.id] = null
})
return
}
this.isLoading = true
const response = await axios.get(
this.currentType.endpoint || this.currentType.id,
{ params: this.axiosParams }
)
this.results[this.currentType.id] = response.data
this.isLoading = false
this.types.forEach(t => {
if (t.id !== this.currentType.id) {
axios.get(t.endpoint || t.id, {
params: {
q: this.query,
page_size: 1,
content_category: t.contentCategory,
include_channels: t.includeChannels
}
}).then(response => {
this.results[t.id] = response.data
})
}
})
},
updateQueryString: function () {
history.pushState(
{},
null,
this.$route.path + '?' + new URLSearchParams(
{
q: this.query,
page: this.page,
type: this.type
}).toString()
)
}
}
}
</script>