mirror of
https://github.com/owntone/owntone-server.git
synced 2025-04-14 08:16:17 -04:00
[web] Refactor Spotify search page
This commit is contained in:
parent
7adde0340e
commit
a6b2f93f41
@ -1,51 +1,50 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<section class="section pb-0">
|
||||||
<!-- Search field + recent searches -->
|
<div class="container">
|
||||||
<section class="section pb-0">
|
<div class="columns is-centered">
|
||||||
<div class="container">
|
<div class="column is-four-fifths">
|
||||||
<div class="columns is-centered">
|
<form @submit.prevent="new_search">
|
||||||
<div class="column is-four-fifths">
|
<div class="field">
|
||||||
<form @submit.prevent="new_search">
|
<p class="control has-icons-left">
|
||||||
<div class="field">
|
<input
|
||||||
<p class="control has-icons-left">
|
ref="search_field"
|
||||||
<input
|
v-model="search_query"
|
||||||
ref="search_field"
|
class="input is-rounded is-shadowless"
|
||||||
v-model="search_query"
|
type="text"
|
||||||
class="input is-rounded is-shadowless"
|
:placeholder="$t('page.spotify.search.placeholder')"
|
||||||
type="text"
|
autocomplete="off"
|
||||||
:placeholder="$t('page.spotify.search.placeholder')"
|
/>
|
||||||
autocomplete="off"
|
<mdicon class="icon is-left" name="magnify" size="16" />
|
||||||
/>
|
</p>
|
||||||
<mdicon class="icon is-left" name="magnify" size="16" />
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<div class="tags mt-4">
|
|
||||||
<a
|
|
||||||
v-for="recent_search in recent_searches"
|
|
||||||
:key="recent_search"
|
|
||||||
class="tag"
|
|
||||||
@click="open_recent_search(recent_search)"
|
|
||||||
v-text="recent_search"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="tags mt-4">
|
||||||
|
<a
|
||||||
|
v-for="recent_search in recent_searches"
|
||||||
|
:key="recent_search"
|
||||||
|
class="tag"
|
||||||
|
@click="open_recent_search(recent_search)"
|
||||||
|
v-text="recent_search"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
<tabs-search :query="search_query" />
|
</section>
|
||||||
<!-- Tracks -->
|
<tabs-search :query="search_query" />
|
||||||
<content-with-heading v-if="show('track') && tracks.total" class="pt-0">
|
<template v-for="type in validSearchTypes" :key="type">
|
||||||
|
<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.spotify.search.tracks')" />
|
<p class="title is-4" v-text="$t(`page.spotify.search.${type}s`)" />
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<list-item-track-spotify
|
<component
|
||||||
v-for="track in tracks.items"
|
:is="components[type]"
|
||||||
:key="track.id"
|
v-for="item in results[type].items"
|
||||||
:item="track"
|
:key="item.id"
|
||||||
|
:item="item"
|
||||||
/>
|
/>
|
||||||
<VueEternalLoading v-if="query.type === 'track'" :load="search_next">
|
<VueEternalLoading v-if="query.type === type" :load="search_next">
|
||||||
<template #loading>
|
<template #loading>
|
||||||
<div class="columns is-centered">
|
<div class="columns is-centered">
|
||||||
<div class="column has-text-centered">
|
<div class="column has-text-centered">
|
||||||
@ -57,160 +56,32 @@
|
|||||||
</VueEternalLoading>
|
</VueEternalLoading>
|
||||||
</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.spotify.search.show-all-tracks', tracks.total, {
|
$t(
|
||||||
count: $filters.number(tracks.total)
|
`page.spotify.search.show-all-${type}s`,
|
||||||
})
|
results[type].total,
|
||||||
|
{
|
||||||
|
count: $filters.number(results[type].total)
|
||||||
|
}
|
||||||
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
</nav>
|
</nav>
|
||||||
|
<p v-if="!results[type].total" class="has-text-centered-mobile">
|
||||||
|
<i v-text="$t(`page.spotify.search.no-${type}s`)" />
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
<content-text v-if="show('track') && !tracks.total" class="pt-0">
|
</template>
|
||||||
<template #content>
|
|
||||||
<p><i v-text="$t('page.spotify.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.spotify.search.artists')" />
|
|
||||||
</template>
|
|
||||||
<template #content>
|
|
||||||
<list-item-artist-spotify
|
|
||||||
v-for="artist in artists.items"
|
|
||||||
:key="artist.id"
|
|
||||||
:item="artist"
|
|
||||||
/>
|
|
||||||
<VueEternalLoading v-if="query.type === 'artist'" :load="search_next">
|
|
||||||
<template #loading>
|
|
||||||
<div class="columns is-centered">
|
|
||||||
<div class="column has-text-centered">
|
|
||||||
<mdicon class="icon mdi-spin" name="loading" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #no-more> </template>
|
|
||||||
</VueEternalLoading>
|
|
||||||
</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.spotify.search.show-all-artists', artists.total, {
|
|
||||||
count: $filters.number(artists.total)
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</nav>
|
|
||||||
</template>
|
|
||||||
</content-with-heading>
|
|
||||||
<content-text v-if="show('artist') && !artists.total">
|
|
||||||
<template #content>
|
|
||||||
<p><i v-text="$t('page.spotify.search.no-artists')" /></p>
|
|
||||||
</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.spotify.search.albums')" />
|
|
||||||
</template>
|
|
||||||
<template #content>
|
|
||||||
<list-item-album-spotify
|
|
||||||
v-for="album in albums.items"
|
|
||||||
:key="album.id"
|
|
||||||
:item="album"
|
|
||||||
/>
|
|
||||||
<VueEternalLoading v-if="query.type === 'album'" :load="search_next">
|
|
||||||
<template #loading>
|
|
||||||
<div class="columns is-centered">
|
|
||||||
<div class="column has-text-centered">
|
|
||||||
<mdicon class="icon mdi-spin" name="loading" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #no-more> </template>
|
|
||||||
</VueEternalLoading>
|
|
||||||
</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.spotify.search.show-all-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.spotify.search.no-albums')" /></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.spotify.search.playlists')" />
|
|
||||||
</template>
|
|
||||||
<template #content>
|
|
||||||
<list-item-playlist-spotify
|
|
||||||
v-for="playlist in playlists.items"
|
|
||||||
:key="playlist.id"
|
|
||||||
:item="playlist"
|
|
||||||
/>
|
|
||||||
<VueEternalLoading v-if="query.type === 'playlist'" :load="search_next">
|
|
||||||
<template #loading>
|
|
||||||
<div class="columns is-centered">
|
|
||||||
<div class="column has-text-centered">
|
|
||||||
<mdicon class="icon mdi-spin" name="loading" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #no-more> </template>
|
|
||||||
</VueEternalLoading>
|
|
||||||
</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.spotify.search.show-all-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.spotify.search.no-playlists')" /></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 ListItemAlbumSpotify from '@/components/ListItemAlbumSpotify.vue'
|
import ListItemAlbumSpotify from '@/components/ListItemAlbumSpotify.vue'
|
||||||
import ListItemArtistSpotify from '@/components/ListItemArtistSpotify.vue'
|
import ListItemArtistSpotify from '@/components/ListItemArtistSpotify.vue'
|
||||||
@ -226,7 +97,6 @@ const PAGE_SIZE = 50
|
|||||||
export default {
|
export default {
|
||||||
name: 'PageSearchSpotify',
|
name: 'PageSearchSpotify',
|
||||||
components: {
|
components: {
|
||||||
ContentText,
|
|
||||||
ContentWithHeading,
|
ContentWithHeading,
|
||||||
ListItemAlbumSpotify,
|
ListItemAlbumSpotify,
|
||||||
ListItemArtistSpotify,
|
ListItemArtistSpotify,
|
||||||
@ -238,13 +108,21 @@ export default {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
albums: { items: [], total: 0 },
|
components: {
|
||||||
artists: { items: [], total: 0 },
|
album: ListItemAlbumSpotify.name,
|
||||||
playlists: { items: [], total: 0 },
|
artist: ListItemArtistSpotify.name,
|
||||||
|
playlist: ListItemPlaylistSpotify.name,
|
||||||
|
track: ListItemTrackSpotify.name
|
||||||
|
},
|
||||||
query: {},
|
query: {},
|
||||||
|
results: {
|
||||||
|
album: { items: [], total: 0 },
|
||||||
|
artist: { items: [], total: 0 },
|
||||||
|
playlist: { items: [], total: 0 },
|
||||||
|
track: { items: [], total: 0 }
|
||||||
|
},
|
||||||
search_param: {},
|
search_param: {},
|
||||||
search_query: '',
|
search_query: '',
|
||||||
tracks: { items: [], total: 0 },
|
|
||||||
validSearchTypes: ['track', 'artist', 'album', 'playlist']
|
validSearchTypes: ['track', 'artist', 'album', 'playlist']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -299,10 +177,9 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
reset() {
|
reset() {
|
||||||
this.tracks = { items: [], total: 0 }
|
Object.entries(this.results).forEach(
|
||||||
this.artists = { items: [], total: 0 }
|
(key) => (this.results[key] = { items: [], total: 0 })
|
||||||
this.albums = { items: [], total: 0 }
|
)
|
||||||
this.playlists = { items: [], total: 0 }
|
|
||||||
},
|
},
|
||||||
search() {
|
search() {
|
||||||
this.reset()
|
this.reset()
|
||||||
@ -319,38 +196,39 @@ export default {
|
|||||||
this.search_all()
|
this.search_all()
|
||||||
},
|
},
|
||||||
search_all() {
|
search_all() {
|
||||||
this.spotify_search().then((data) => {
|
const types = this.query.type
|
||||||
this.tracks = data.tracks ?? { items: [], total: 0 }
|
.split(',')
|
||||||
this.artists = data.artists ?? { items: [], total: 0 }
|
.filter((type) => this.validSearchTypes.includes(type))
|
||||||
this.albums = data.albums ?? { items: [], total: 0 }
|
this.spotify_search(types).then((data) => {
|
||||||
this.playlists = data.playlists ?? { items: [], total: 0 }
|
this.results.track = data.tracks ?? { items: [], total: 0 }
|
||||||
|
this.results.artist = data.artists ?? { items: [], total: 0 }
|
||||||
|
this.results.album = data.albums ?? { items: [], total: 0 }
|
||||||
|
this.results.playlist = data.playlists ?? { items: [], total: 0 }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
search_next(obj) {
|
search_next({ loaded }) {
|
||||||
const items = this[`${this.query.type}s`]
|
const items = this.results[this.query.type]
|
||||||
this.spotify_search().then((data) => {
|
this.spotify_search([this.query.type]).then((data) => {
|
||||||
const newItems = data[`${this.query.type}s`]
|
const next = Object.values(data)[0]
|
||||||
items.items = items.items.concat(newItems.items)
|
items.items = items.items.concat(next.items)
|
||||||
items.total = newItems.total
|
items.total = next.total
|
||||||
this.search_param.offset += newItems.limit
|
this.search_param.offset += next.limit
|
||||||
obj.loaded(newItems.items.length, PAGE_SIZE)
|
loaded(next.items.length, PAGE_SIZE)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
show(type) {
|
show(type) {
|
||||||
return this.$route.query.type?.includes(type) ?? false
|
return this.$route.query.type?.includes(type) ?? false
|
||||||
},
|
},
|
||||||
show_all_button(items) {
|
show_all_button(type) {
|
||||||
|
const items = this.results[type]
|
||||||
return items.total > items.items.length
|
return items.total > items.items.length
|
||||||
},
|
},
|
||||||
spotify_search() {
|
spotify_search(types) {
|
||||||
return webapi.spotify().then(({ data }) => {
|
return webapi.spotify().then(({ data }) => {
|
||||||
this.search_param.market = data.webapi_country
|
this.search_param.market = data.webapi_country
|
||||||
const spotifyApi = new SpotifyWebApi()
|
const api = new SpotifyWebApi()
|
||||||
spotifyApi.setAccessToken(data.webapi_token)
|
api.setAccessToken(data.webapi_token)
|
||||||
const types = this.query.type
|
return api.search(this.query.query, types, this.search_param)
|
||||||
.split(',')
|
|
||||||
.filter((type) => this.validSearchTypes.includes(type))
|
|
||||||
return spotifyApi.search(this.query.query, types, this.search_param)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user