Verified Commit f1f9f935 authored by heyarne's avatar heyarne Committed by Georg Krause
Browse files

Make playing tracks in their playlist the default

parent 5d745fea
Make "play in list" the default when interacting with individual tracks (#1274)
<template> <template>
<div class="album-entries"> <div class="album-entries">
<div :class="[{active: currentTrack && isPlaying && track.id === currentTrack.id}, 'album-entry']" v-for="track in tracks" :key="track.id"> <div :class="[{active: currentTrack && isPlaying && track.id === currentTrack.id}, 'album-entry']" @click.prevent="replacePlay(tracks, index)" v-for="(track, index) in tracks" :key="track.id">
<div class="actions"> <div class="actions">
<play-button class="basic circular icon" :button-classes="['circular inverted vibrant icon button']" :discrete="true" :icon-only="true" :track="track"></play-button> <play-button class="basic circular icon" :button-classes="['circular inverted vibrant icon button']" :discrete="true" :icon-only="true" :track="track" :tracks="tracks"></play-button>
</div> </div>
<div class="position">{{ prettyPosition(track.position) }}</div> <div class="position">{{ prettyPosition(track.position) }}</div>
<div class="content ellipsis"> <div class="content ellipsis">
<router-link :to="{name: 'library.tracks.detail', params: {id: track.id}}" class="discrete link"> <strong>{{ track.title }}</strong><br>
<strong>{{ track.title }}</strong><br>
</router-link>
</div> </div>
<div class="meta"> <div class="meta">
<template v-if="$store.state.auth.authenticated && $store.getters['favorites/isFavorite'](track.id)"> <template v-if="$store.state.auth.authenticated && $store.getters['favorites/isFavorite'](track.id)">
...@@ -17,7 +15,7 @@ ...@@ -17,7 +15,7 @@
<human-duration v-if="track.uploads[0] && track.uploads[0].duration" :duration="track.uploads[0].duration"></human-duration> <human-duration v-if="track.uploads[0] && track.uploads[0].duration" :duration="track.uploads[0].duration"></human-duration>
</div> </div>
<div class="actions"> <div class="actions">
<play-button class="play-button basic icon" :dropdown-only="true" :is-playable="track.is_playable" :dropdown-icon-classes="['ellipsis', 'vertical', 'large really discrete']" :track="track"></play-button> <play-button class="play-button basic icon" :dropdown-only="true" :is-playable="track.is_playable" :dropdown-icon-classes="['ellipsis', 'vertical', 'large really discrete']" :track="track"></play-button>
</div> </div>
</div> </div>
</div> </div>
...@@ -54,7 +52,13 @@ export default { ...@@ -54,7 +52,13 @@ export default {
var s = String(position); var s = String(position);
while (s.length < (size || 2)) {s = "0" + s;} while (s.length < (size || 2)) {s = "0" + s;}
return s; return s;
} },
replacePlay (tracks, trackIndex) {
this.$store.dispatch('queue/clean')
this.$store.dispatch('queue/appendMany', {tracks: tracks}).then(() => {
this.$store.dispatch('queue/currentIndex', trackIndex)
})
},
} }
} }
</script> </script>
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
</button> </button>
<button <button
v-if="!discrete && !iconOnly" v-if="!discrete && !iconOnly"
@click.prevent="clicked = true" @click.stop.prevent="clicked = true"
:class="['ui', {disabled: !playable && !filterableArtist}, 'floating', 'dropdown', {'icon': !dropdownOnly}, {'button': !dropdownOnly}]"> :class="['ui', {disabled: !playable && !filterableArtist}, 'floating', 'dropdown', {'icon': !dropdownOnly}, {'button': !dropdownOnly}]">
<i :class="dropdownIconClasses.concat(['icon'])" :title="title" ></i> <i :class="dropdownIconClasses.concat(['icon'])" :title="title" ></i>
<div class="menu" v-if="clicked"> <div class="menu" v-if="clicked">
...@@ -27,6 +27,9 @@ ...@@ -27,6 +27,9 @@
<button v-if="track" class="item basic" :disabled="!playable" @click.stop.prevent="$store.dispatch('radios/start', {type: 'similar', objectId: track.id})" :title="labels.startRadio"> <button v-if="track" class="item basic" :disabled="!playable" @click.stop.prevent="$store.dispatch('radios/start', {type: 'similar', objectId: track.id})" :title="labels.startRadio">
<i class="feed icon"></i><translate translate-context="*/Queue/Button.Label/Short, Verb">Play radio</translate> <i class="feed icon"></i><translate translate-context="*/Queue/Button.Label/Short, Verb">Play radio</translate>
</button> </button>
<button v-if="track" class="item basic" @click.stop.prevent="$router.push(`/library/tracks/${track.id}/`)">
<i class="info icon"></i><translate translate-context="*/Queue/Dropdown/Button/Label/Short">Track details</translate>
</button>
<div class="divider"></div> <div class="divider"></div>
<button v-if="filterableArtist" ref="filterArtist" data-ref="filterArtist" class="item basic" :disabled="!filterableArtist" @click.stop.prevent="filterArtist" :title="labels.hideArtist"> <button v-if="filterableArtist" ref="filterArtist" data-ref="filterArtist" class="item basic" :disabled="!filterableArtist" @click.stop.prevent="filterArtist" :title="labels.hideArtist">
<i class="eye slash outline icon"></i><translate translate-context="*/Queue/Dropdown/Button/Label/Short">Hide content from this artist</translate> <i class="eye slash outline icon"></i><translate translate-context="*/Queue/Dropdown/Button/Label/Short">Hide content from this artist</translate>
...@@ -35,7 +38,7 @@ ...@@ -35,7 +38,7 @@
v-for="obj in getReportableObjs({track, album, artist, playlist, account, channel})" v-for="obj in getReportableObjs({track, album, artist, playlist, account, channel})"
:key="obj.target.type + obj.target.id" :key="obj.target.type + obj.target.id"
class="item basic" class="item basic"
:ref="`report${obj.target.type}${obj.target.id}`" :data-ref="`report${obj.target.type}${obj.target.id}`" :ref="`report${obj.target.type}${obj.target.id}`" :data-ref="`report${obj.target.type}${obj.target.id}`"
@click.stop.prevent="$store.dispatch('moderation/report', obj.target)"> @click.stop.prevent="$store.dispatch('moderation/report', obj.target)">
<i class="share icon" /> {{ obj.label }} <i class="share icon" /> {{ obj.label }}
</button> </button>
...@@ -90,7 +93,7 @@ export default { ...@@ -90,7 +93,7 @@ export default {
} else { } else {
replacePlay = this.$pgettext('*/Queue/Dropdown/Button/Title', 'Play tracks') replacePlay = this.$pgettext('*/Queue/Dropdown/Button/Title', 'Play tracks')
} }
return { return {
playNow: this.$pgettext('*/Queue/Dropdown/Button/Title', 'Play now'), playNow: this.$pgettext('*/Queue/Dropdown/Button/Title', 'Play now'),
addToQueue: this.$pgettext('*/Queue/Dropdown/Button/Title', 'Add to current queue'), addToQueue: this.$pgettext('*/Queue/Dropdown/Button/Title', 'Add to current queue'),
...@@ -143,7 +146,6 @@ export default { ...@@ -143,7 +146,6 @@ export default {
}, },
}, },
methods: { methods: {
filterArtist () { filterArtist () {
this.$store.dispatch('moderation/hide', {type: 'artist', target: this.filterableArtist}) this.$store.dispatch('moderation/hide', {type: 'artist', target: this.filterableArtist})
}, },
...@@ -175,7 +177,9 @@ export default { ...@@ -175,7 +177,9 @@ export default {
let self = this let self = this
this.isLoading = true this.isLoading = true
let getTracks = new Promise((resolve, reject) => { let getTracks = new Promise((resolve, reject) => {
if (self.track) { if (self.tracks) {
resolve(self.tracks)
} else if (self.track) {
if (!self.track.uploads || self.track.uploads.length === 0) { if (!self.track.uploads || self.track.uploads.length === 0) {
// fetch uploads from api // fetch uploads from api
axios.get(`tracks/${self.track.id}/`).then((response) => { axios.get(`tracks/${self.track.id}/`).then((response) => {
...@@ -184,8 +188,6 @@ export default { ...@@ -184,8 +188,6 @@ export default {
} else { } else {
resolve([self.track]) resolve([self.track])
} }
} else if (self.tracks) {
resolve(self.tracks)
} else if (self.playlist) { } else if (self.playlist) {
let url = 'playlists/' + self.playlist.id + '/' let url = 'playlists/' + self.playlist.id + '/'
axios.get(url + 'tracks/').then((response) => { axios.get(url + 'tracks/').then((response) => {
...@@ -236,7 +238,14 @@ export default { ...@@ -236,7 +238,14 @@ export default {
let self = this let self = this
self.$store.dispatch('queue/clean') self.$store.dispatch('queue/clean')
this.getPlayableTracks().then((tracks) => { this.getPlayableTracks().then((tracks) => {
self.$store.dispatch('queue/appendMany', {tracks: tracks}).then(() => self.addMessage(tracks)) self.$store.dispatch('queue/appendMany', {tracks: tracks}).then(() => {
if (self.track) {
// set queue position to selected track
const trackIndex = self.tracks.findIndex(track => track.id === self.track.id)
self.$store.dispatch('queue/currentIndex', trackIndex)
}
self.addMessage(tracks)
})
}) })
jQuery(self.$el).find('.ui.dropdown').dropdown('hide') jQuery(self.$el).find('.ui.dropdown').dropdown('hide')
}, },
......
<template> <template>
<tr> <tr>
<td> <td>
<play-button :class="['basic', {vibrant: currentTrack && isPlaying && track.id === currentTrack.id}, 'icon']" :discrete="true" :is-playable="playable" :track="track"></play-button> <play-button :class="['basic', {vibrant: currentTrack && isPlaying && track.id === currentTrack.id}, 'icon']"
:discrete="true"
:is-playable="playable"
:track="track"
:track-index="trackIndex"
:tracks="tracks"></play-button>
</td> </td>
<td> <td>
<img alt="" class="ui mini image" v-if="track.album && track.album.cover && track.album.cover.urls.original" v-lazy="$store.getters['instance/absoluteUrl'](track.album.cover.urls.medium_square_crop)"> <img alt="" class="ui mini image" v-if="track.album && track.album.cover && track.album.cover.urls.original" v-lazy="$store.getters['instance/absoluteUrl'](track.album.cover.urls.medium_square_crop)">
<img alt="" class="ui mini image" v-else src="../../../assets/audio/default-cover.png"> <img alt="" class="ui mini image" v-else src="../../../assets/audio/default-cover.png">
</td> </td>
<td colspan="6"> <td colspan="6">
<router-link class="track" :to="{name: 'library.tracks.detail', params: {id: track.id }}"> <button class="track" @click.stop="playSong()">
<template v-if="displayPosition && track.position"> <template v-if="displayPosition && track.position">
{{ track.position }}. {{ track.position }}.
</template> </template>
{{ track.title|truncate(40) }} {{ track.title|truncate(40) }}
</router-link> </button>
</td> </td>
<td colspan="4"> <td colspan="4">
<router-link class="artist discrete link" :to="{name: 'library.artists.detail', params: {id: track.artist.id }}"> <router-link class="artist discrete link" :to="{name: 'library.artists.detail', params: {id: track.artist.id }}">
...@@ -56,6 +61,8 @@ import PlayButton from '@/components/audio/PlayButton' ...@@ -56,6 +61,8 @@ import PlayButton from '@/components/audio/PlayButton'
export default { export default {
props: { props: {
track: {type: Object, required: true}, track: {type: Object, required: true},
trackIndex: {type: Number, required: true},
tracks: {type: Array, required: false},
artist: {type: Object, required: false}, artist: {type: Object, required: false},
displayPosition: {type: Boolean, default: false}, displayPosition: {type: Boolean, default: false},
displayActions: {type: Boolean, default: true}, displayActions: {type: Boolean, default: true},
...@@ -80,6 +87,16 @@ export default { ...@@ -80,6 +87,16 @@ export default {
return this.track.album.artist return this.track.album.artist
} }
}, },
},
methods: {
playSong () {
this.$store.dispatch('queue/clean')
this.$store.dispatch('queue/appendMany', {
tracks: this.tracks
}).then(() => {
this.$store.dispatch('queue/currentIndex', this.trackIndex)
})
},
} }
} }
</script> </script>
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
:display-position="displayPosition" :display-position="displayPosition"
:display-actions="displayActions" :display-actions="displayActions"
:track="track" :track="track"
:track-index="index"
:tracks="allTracks"
:artist="artist" :artist="artist"
:key="index + '-' + track.id" :key="index + '-' + track.id"
v-for="(track, index) in allTracks"></track-row> v-for="(track, index) in allTracks"></track-row>
......
...@@ -11,4 +11,9 @@ ...@@ -11,4 +11,9 @@
visibility: hidden; visibility: hidden;
} }
} }
.track {
display: block;
line-height: 2;
}
} }
...@@ -75,6 +75,18 @@ ...@@ -75,6 +75,18 @@
} }
} }
} }
.album-entry:hover {
cursor: pointer;
// explicitly style the button as if it was hovered itself
.ui.inverted.vibrant.button {
background-color: var(--vibrant-hover-color);
color: white;
box-shadow: 0 0 0 2px var(--vibrant-color) inset;
}
}
.album-entry, .channel-entry-card { .album-entry, .channel-entry-card {
border-radius: 5px; border-radius: 5px;
padding: 0.5em; padding: 0.5em;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment