[web] Streamline Spotify pages to match library pages
This commit is contained in:
parent
8b1c4decf5
commit
6fd4db14fb
File diff suppressed because one or more lines are too long
|
@ -1,34 +1,38 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="media is-align-items-center" @click="open">
|
<template v-for="item in items" :key="item.id">
|
||||||
<div v-if="show_artwork" class="media-left is-clickable">
|
<div class="media is-align-items-center" @click="open(item)">
|
||||||
<cover-artwork
|
<div v-if="show_artwork" class="media-left is-clickable">
|
||||||
:artwork_url="artwork_url"
|
<cover-artwork
|
||||||
:artist="item.artist"
|
:artwork_url="artwork_url(item)"
|
||||||
:album="item.name"
|
:artist="item.artist"
|
||||||
class="is-clickable fd-has-shadow fd-cover fd-cover-small-image"
|
:album="item.name"
|
||||||
/>
|
class="is-clickable fd-has-shadow fd-cover fd-cover-small-image"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="media-content is-clickable is-clipped">
|
||||||
|
<h1 class="title is-6" v-text="item.name" />
|
||||||
|
<h2
|
||||||
|
class="subtitle is-7 has-text-grey has-text-weight-bold"
|
||||||
|
v-text="item.artists[0]?.name"
|
||||||
|
/>
|
||||||
|
<h2
|
||||||
|
class="subtitle is-7 has-text-grey"
|
||||||
|
v-text="
|
||||||
|
[item.album_type, $filters.date(item.release_date)].join(', ')
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="media-right">
|
||||||
|
<a @click.prevent.stop="open_dialog(item)">
|
||||||
|
<mdicon class="icon has-text-dark" name="dots-vertical" size="16" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-content is-clickable is-clipped">
|
</template>
|
||||||
<h1 class="title is-6" v-text="item.name" />
|
|
||||||
<h2
|
|
||||||
class="subtitle is-7 has-text-grey has-text-weight-bold"
|
|
||||||
v-text="item.artists[0]?.name"
|
|
||||||
/>
|
|
||||||
<h2
|
|
||||||
class="subtitle is-7 has-text-grey"
|
|
||||||
v-text="[item.album_type, $filters.date(item.release_date)].join(', ')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="media-right">
|
|
||||||
<a @click.prevent.stop="show_details_modal = true">
|
|
||||||
<mdicon class="icon has-text-dark" name="dots-vertical" size="16" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<teleport to="#app">
|
<teleport to="#app">
|
||||||
<modal-dialog-album-spotify
|
<modal-dialog-album-spotify
|
||||||
:show="show_details_modal"
|
:show="show_details_modal"
|
||||||
:album="item"
|
:album="selected_item"
|
||||||
@close="show_details_modal = false"
|
@close="show_details_modal = false"
|
||||||
/>
|
/>
|
||||||
</teleport>
|
</teleport>
|
||||||
|
@ -41,16 +45,13 @@ import ModalDialogAlbumSpotify from '@/components/ModalDialogAlbumSpotify.vue'
|
||||||
export default {
|
export default {
|
||||||
name: 'ListItemAlbumSpotify',
|
name: 'ListItemAlbumSpotify',
|
||||||
components: { CoverArtwork, ModalDialogAlbumSpotify },
|
components: { CoverArtwork, ModalDialogAlbumSpotify },
|
||||||
props: { item: { required: true, type: Object } },
|
props: { items: { required: true, type: Object } },
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return { show_details_modal: false }
|
return { selected_item: {}, show_details_modal: false }
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
artwork_url() {
|
|
||||||
return this.item.images?.[0]?.url ?? ''
|
|
||||||
},
|
|
||||||
show_artwork() {
|
show_artwork() {
|
||||||
return this.$store.getters.settings_option(
|
return this.$store.getters.settings_option(
|
||||||
'webinterface',
|
'webinterface',
|
||||||
|
@ -60,11 +61,18 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open() {
|
artwork_url(item) {
|
||||||
|
return item.images?.[0]?.url ?? ''
|
||||||
|
},
|
||||||
|
open(item) {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'music-spotify-album',
|
name: 'music-spotify-album',
|
||||||
params: { id: this.item.id }
|
params: { id: item.id }
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
open_dialog(item) {
|
||||||
|
this.selected_item = item
|
||||||
|
this.show_details_modal = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="media is-align-items-center">
|
<template v-for="item in items" :key="item.id">
|
||||||
<div class="media-content is-clickable is-clipped" @click="open_artist">
|
<div class="media is-align-items-center">
|
||||||
<h1 class="title is-6" v-text="item.name" />
|
<div class="media-content is-clickable is-clipped" @click="open(item)">
|
||||||
|
<h1 class="title is-6" v-text="item.name" />
|
||||||
|
</div>
|
||||||
|
<div class="media-right">
|
||||||
|
<a @click.prevent.stop="open_dialog(item)">
|
||||||
|
<mdicon class="icon has-text-dark" name="dots-vertical" size="16" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
</template>
|
||||||
<a @click.prevent.stop="show_details_modal = true">
|
|
||||||
<mdicon class="icon has-text-dark" name="dots-vertical" size="16" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<teleport to="#app">
|
<teleport to="#app">
|
||||||
<modal-dialog-artist-spotify
|
<modal-dialog-artist-spotify
|
||||||
:show="show_details_modal"
|
:show="show_details_modal"
|
||||||
:artist="item"
|
:artist="selected_item"
|
||||||
@close="show_details_modal = false"
|
@close="show_details_modal = false"
|
||||||
/>
|
/>
|
||||||
</teleport>
|
</teleport>
|
||||||
|
@ -24,17 +26,21 @@ import ModalDialogArtistSpotify from '@/components/ModalDialogArtistSpotify.vue'
|
||||||
export default {
|
export default {
|
||||||
name: 'ListItemArtistSpotify',
|
name: 'ListItemArtistSpotify',
|
||||||
components: { ModalDialogArtistSpotify },
|
components: { ModalDialogArtistSpotify },
|
||||||
props: { item: { required: true, type: Object } },
|
props: { items: { required: true, type: Object } },
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return { show_details_modal: false }
|
return { selected_item: {}, show_details_modal: false }
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
open_artist() {
|
open(item) {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'music-spotify-artist',
|
name: 'music-spotify-artist',
|
||||||
params: { id: this.item.id }
|
params: { id: item.id }
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
open_dialog(item) {
|
||||||
|
this.selected_item = item
|
||||||
|
this.show_details_modal = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="media is-align-items-center">
|
<template v-for="item in items" :key="item.id">
|
||||||
<div class="media-content is-clickable is-clipped" @click="open_playlist">
|
<div class="media is-align-items-center">
|
||||||
<h1 class="title is-6" v-text="item.name" />
|
<div class="media-content is-clickable is-clipped" @click="open(item)">
|
||||||
<h2 class="subtitle is-7" v-text="item.owner.display_name" />
|
<h1 class="title is-6" v-text="item.name" />
|
||||||
|
<h2 class="subtitle is-7" v-text="item.owner.display_name" />
|
||||||
|
</div>
|
||||||
|
<div class="media-right">
|
||||||
|
<a @click.prevent.stop="open_dialog(item)">
|
||||||
|
<mdicon class="icon has-text-dark" name="dots-vertical" size="16" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
</template>
|
||||||
<a @click.prevent.stop="show_details_modal = true">
|
|
||||||
<mdicon class="icon has-text-dark" name="dots-vertical" size="16" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<teleport to="#app">
|
<teleport to="#app">
|
||||||
<modal-dialog-playlist-spotify
|
<modal-dialog-playlist-spotify
|
||||||
:show="show_details_modal"
|
:show="show_details_modal"
|
||||||
:playlist="item"
|
:playlist="selected_item"
|
||||||
@close="show_details_modal = false"
|
@close="show_details_modal = false"
|
||||||
/>
|
/>
|
||||||
</teleport>
|
</teleport>
|
||||||
|
@ -27,18 +29,19 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
ModalDialogPlaylistSpotify
|
ModalDialogPlaylistSpotify
|
||||||
},
|
},
|
||||||
props: { item: { required: true, type: Object } },
|
props: { items: { required: true, type: Object } },
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return { show_details_modal: false }
|
return { selected_item: {}, show_details_modal: false }
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open_playlist() {
|
open(item) {
|
||||||
this.$router.push({
|
this.$router.push({ name: 'playlist-spotify', params: { id: item.id } })
|
||||||
name: 'playlist-spotify',
|
},
|
||||||
params: { id: this.item.id }
|
open_dialog(item) {
|
||||||
})
|
this.selected_item = item
|
||||||
|
this.show_details_modal = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +1,51 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="media is-align-items-center">
|
<template v-for="item in items" :key="item.id">
|
||||||
<div
|
<div class="media is-align-items-center">
|
||||||
class="media-content is-clipped"
|
<div
|
||||||
:class="{
|
class="media-content is-clipped"
|
||||||
'is-clickable': item.is_playable,
|
|
||||||
'fd-is-not-allowed': !item.is_playable
|
|
||||||
}"
|
|
||||||
@click="play"
|
|
||||||
>
|
|
||||||
<h1
|
|
||||||
class="title is-6"
|
|
||||||
:class="{ 'has-text-grey-light': !item.is_playable }"
|
|
||||||
v-text="item.name"
|
|
||||||
/>
|
|
||||||
<h2
|
|
||||||
class="subtitle is-7 has-text-weight-bold"
|
|
||||||
:class="{
|
:class="{
|
||||||
'has-text-grey': item.is_playable,
|
'is-clickable': item.is_playable,
|
||||||
'has-text-grey-light': !item.is_playable
|
'fd-is-not-allowed': !item.is_playable
|
||||||
}"
|
}"
|
||||||
v-text="item.artists[0].name"
|
@click="play(item)"
|
||||||
/>
|
>
|
||||||
<h2 class="subtitle is-7 has-text-grey" v-text="item.album.name" />
|
<h1
|
||||||
<h2 v-if="!item.is_playable" class="subtitle is-7">
|
class="title is-6"
|
||||||
(<span v-text="$t('list.spotify.not-playable-track')" />
|
:class="{ 'has-text-grey-light': !item.is_playable }"
|
||||||
<span
|
v-text="item.name"
|
||||||
v-if="item.restrictions && item.restrictions.reason"
|
/>
|
||||||
v-text="
|
<h2
|
||||||
$t('list.spotify.restriction-reason', {
|
class="subtitle is-7 has-text-weight-bold"
|
||||||
reason: item.restrictions.reason
|
:class="{
|
||||||
})
|
'has-text-grey': item.is_playable,
|
||||||
"
|
'has-text-grey-light': !item.is_playable
|
||||||
/>)
|
}"
|
||||||
</h2>
|
v-text="item.artists[0].name"
|
||||||
|
/>
|
||||||
|
<h2 class="subtitle is-7 has-text-grey" v-text="item.album.name" />
|
||||||
|
<h2 v-if="!item.is_playable" class="subtitle is-7">
|
||||||
|
(<span v-text="$t('list.spotify.not-playable-track')" />
|
||||||
|
<span
|
||||||
|
v-if="item.restrictions && item.restrictions.reason"
|
||||||
|
v-text="
|
||||||
|
$t('list.spotify.restriction-reason', {
|
||||||
|
reason: item.restrictions.reason
|
||||||
|
})
|
||||||
|
"
|
||||||
|
/>)
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="media-right">
|
||||||
|
<a @click.prevent.stop="open_dialog(item)">
|
||||||
|
<mdicon class="icon has-text-dark" name="dots-vertical" size="16" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
</template>
|
||||||
<a @click.prevent.stop="show_details_modal = true">
|
|
||||||
<mdicon class="icon has-text-dark" name="dots-vertical" size="16" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<teleport to="#app">
|
<teleport to="#app">
|
||||||
<modal-dialog-track-spotify
|
<modal-dialog-track-spotify
|
||||||
:show="show_details_modal"
|
:show="show_details_modal"
|
||||||
:track="item"
|
:track="selected_item"
|
||||||
@close="show_details_modal = false"
|
@close="show_details_modal = false"
|
||||||
/>
|
/>
|
||||||
</teleport>
|
</teleport>
|
||||||
|
@ -58,19 +60,22 @@ export default {
|
||||||
components: { ModalDialogTrackSpotify },
|
components: { ModalDialogTrackSpotify },
|
||||||
props: {
|
props: {
|
||||||
context_uri: { default: '', type: String },
|
context_uri: { default: '', type: String },
|
||||||
item: { required: true, type: Object },
|
items: { required: true, type: Object }
|
||||||
position: { default: 0, type: Number }
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return { show_details_modal: false }
|
return { selected_item: {}, show_details_modal: false }
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
play() {
|
open_dialog(item) {
|
||||||
if (this.item.is_playable) {
|
this.selected_item = item
|
||||||
|
this.show_details_modal = true
|
||||||
|
},
|
||||||
|
play(item) {
|
||||||
|
if (item.is_playable) {
|
||||||
webapi.player_play_uri(
|
webapi.player_play_uri(
|
||||||
this.context_uri || this.item.uri,
|
this.context_uri || item.uri,
|
||||||
false,
|
false,
|
||||||
this.position
|
item.position || 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,13 +39,7 @@
|
||||||
$t('page.spotify.album.track-count', { count: album.tracks.total })
|
$t('page.spotify.album.track-count', { count: album.tracks.total })
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<list-item-track-spotify
|
<list-item-track-spotify :items="tracks" :context_uri="album.uri" />
|
||||||
v-for="(track, index) in tracks"
|
|
||||||
:key="track.id"
|
|
||||||
:item="track"
|
|
||||||
:position="index"
|
|
||||||
:context_uri="album.uri"
|
|
||||||
/>
|
|
||||||
<modal-dialog-album-spotify
|
<modal-dialog-album-spotify
|
||||||
:show="show_details_modal"
|
:show="show_details_modal"
|
||||||
:album="album"
|
:album="album"
|
||||||
|
|
|
@ -23,11 +23,7 @@
|
||||||
class="heading has-text-centered-mobile"
|
class="heading has-text-centered-mobile"
|
||||||
v-text="$t('page.spotify.artist.album-count', { count: total })"
|
v-text="$t('page.spotify.artist.album-count', { count: total })"
|
||||||
/>
|
/>
|
||||||
<list-item-album-spotify
|
<list-item-album-spotify :items="albums" />
|
||||||
v-for="album in albums"
|
|
||||||
:key="album.id"
|
|
||||||
:item="album"
|
|
||||||
/>
|
|
||||||
<VueEternalLoading v-if="offset < total" :load="load_next">
|
<VueEternalLoading v-if="offset < total" :load="load_next">
|
||||||
<template #loading>
|
<template #loading>
|
||||||
<div class="columns is-centered">
|
<div class="columns is-centered">
|
||||||
|
|
|
@ -7,11 +7,7 @@
|
||||||
<p class="title is-4" v-text="$t('page.spotify.music.new-releases')" />
|
<p class="title is-4" v-text="$t('page.spotify.music.new-releases')" />
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<list-item-album-spotify
|
<list-item-album-spotify :items="new_releases" />
|
||||||
v-for="album in new_releases"
|
|
||||||
:key="album.id"
|
|
||||||
:item="album"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<nav class="level">
|
<nav class="level">
|
||||||
|
@ -34,11 +30,7 @@
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<list-item-playlist-spotify
|
<list-item-playlist-spotify :items="featured_playlists" />
|
||||||
v-for="playlist in featured_playlists"
|
|
||||||
:key="playlist.id"
|
|
||||||
:item="playlist"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<nav class="level">
|
<nav class="level">
|
||||||
|
|
|
@ -9,11 +9,7 @@
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<list-item-playlist-spotify
|
<list-item-playlist-spotify :items="featured_playlists" />
|
||||||
v-for="playlist in featured_playlists"
|
|
||||||
:key="playlist.id"
|
|
||||||
:item="playlist"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,11 +6,7 @@
|
||||||
<p class="title is-4" v-text="$t('page.spotify.music.new-releases')" />
|
<p class="title is-4" v-text="$t('page.spotify.music.new-releases')" />
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<list-item-album-spotify
|
<list-item-album-spotify :items="new_releases" />
|
||||||
v-for="album in new_releases"
|
|
||||||
:key="album.id"
|
|
||||||
:item="album"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-heading>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -25,13 +25,7 @@
|
||||||
$t('page.spotify.playlist.count', { count: playlist.tracks.total })
|
$t('page.spotify.playlist.count', { count: playlist.tracks.total })
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<list-item-track-spotify
|
<list-item-track-spotify :items="tracks" :context_uri="playlist.uri" />
|
||||||
v-for="track in tracks"
|
|
||||||
:key="track.id"
|
|
||||||
:item="track"
|
|
||||||
:position="track.position"
|
|
||||||
:context_uri="playlist.uri"
|
|
||||||
/>
|
|
||||||
<VueEternalLoading v-if="offset < total" :load="load_next">
|
<VueEternalLoading v-if="offset < total" :load="load_next">
|
||||||
<template #loading>
|
<template #loading>
|
||||||
<div class="columns is-centered">
|
<div class="columns is-centered">
|
||||||
|
|
|
@ -38,12 +38,7 @@
|
||||||
<p class="title is-4" v-text="$t(`page.spotify.search.${type}s`)" />
|
<p class="title is-4" v-text="$t(`page.spotify.search.${type}s`)" />
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<component
|
<component :is="components[type]" :items="results[type].items" />
|
||||||
:is="components[type]"
|
|
||||||
v-for="item in results[type].items"
|
|
||||||
:key="item.id"
|
|
||||||
:item="item"
|
|
||||||
/>
|
|
||||||
<VueEternalLoading v-if="query.type === type" :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">
|
||||||
|
@ -62,13 +57,9 @@
|
||||||
class="button is-light is-small is-rounded"
|
class="button is-light is-small is-rounded"
|
||||||
@click="open_search(type)"
|
@click="open_search(type)"
|
||||||
v-text="
|
v-text="
|
||||||
$t(
|
$t(`page.spotify.search.show-${type}s`, results[type].total, {
|
||||||
`page.spotify.search.show-${type}s`,
|
count: $filters.number(results[type].total)
|
||||||
results[type].total,
|
})
|
||||||
{
|
|
||||||
count: $filters.number(results[type].total)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
|
@ -206,8 +197,8 @@ export default {
|
||||||
search_next({ loaded }) {
|
search_next({ loaded }) {
|
||||||
const items = this.results[this.query.type]
|
const items = this.results[this.query.type]
|
||||||
this.spotify_search([this.query.type]).then((data) => {
|
this.spotify_search([this.query.type]).then((data) => {
|
||||||
const next = Object.values(data)[0]
|
const [next] = Object.values(data)
|
||||||
items.items = items.items.concat(next.items)
|
items.items.push(...next.items)
|
||||||
items.total = next.total
|
items.total = next.total
|
||||||
this.search_param.offset += next.limit
|
this.search_param.offset += next.limit
|
||||||
loaded(next.items.length, PAGE_SIZE)
|
loaded(next.items.length, PAGE_SIZE)
|
||||||
|
|
Loading…
Reference in New Issue