Skip to content
Snippets Groups Projects
Verified Commit 1e64f3db authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Playlist detail page and editor

parent bed66db5
No related branches found
No related tags found
No related merge requests found
<template>
<div class="ui text container">
<h2 class="ui header">Playlist editor</h2>
<p>Drag and drop rows to reorder tracks in the playlist</p>
<div class="ui buttons">
<div
@click="insertMany(queueTracks)"
:disabled="queueTracks.length === 0"
:class="['ui', {disabled: queueTracks.length === 0}, 'button']"
title="Copy tracks from current queue to playlist">Insert from queue ({{ queueTracks.length }} tracks)</div>
</div>
<h5 class="ui header">Status</h5>
<div>
<template v-if="status === 'loading'">
<div class="ui active tiny inline loader"></div>
Syncing changes to server...
</template>
<template v-else-if="status === 'errored'">
<i class="red x icon"></i>
An error occured while saving your changes
<div v-if="errors.length > 0" class="ui negative message">
<ul class="list">
<li v-for="error in errors">{{ error }}</li>
</ul>
</div>
</template>
<template v-else-if="status === 'saved'">
<i class="green check icon"></i> Changes synced with server
</template>
</div>
<table class="ui compact very basic fixed single line unstackable table">
<draggable v-model="plts" element="tbody" @update="reorder">
<tr v-for="(plt, index) in plts" :key="plt.id">
<td class="left aligned">{{ plt.index + 1}}</td>
<td class="center aligned">
<img class="ui mini image" v-if="plt.track.album.cover" :src="plt.track.album.cover">
<img class="ui mini image" v-else src="../../assets/audio/default-cover.png">
</td>
<td colspan="4">
<strong>{{ plt.track.title }}</strong><br />
{{ plt.track.artist.name }}
</td>
<td class="right aligned">
<i @click.stop="removePlt(index)" class="circular red trash icon"></i>
</td>
</tr>
</draggable>
</table>
</div>
</template>
<script>
import {mapState} from 'vuex'
import axios from 'axios'
import draggable from 'vuedraggable'
export default {
components: {
draggable
},
props: ['playlist', 'playlistTracks'],
data () {
return {
plts: this.playlistTracks,
isLoading: false,
errors: []
}
},
methods: {
success () {
this.isLoading = false
this.errors = []
},
errored (errors) {
this.isLoading = false
this.errors = errors
},
reorder ({oldIndex, newIndex}) {
let self = this
self.isLoading = true
let plt = this.plts[newIndex]
let url = 'playlist-tracks/' + plt.id + '/'
axios.patch(url, {index: newIndex}).then((response) => {
self.success()
}, error => {
self.errored(error.backendErrors)
})
},
removePlt (index) {
let plt = this.plts[index]
this.plts.splice(index, 1)
let self = this
self.isLoading = true
let url = 'playlist-tracks/' + plt.id + '/'
axios.delete(url).then((response) => {
self.success()
}, error => {
self.errored(error.backendErrors)
})
},
insertMany (tracks) {
let self = this
let ids = tracks.map(t => {
return t.id
})
self.isLoading = true
let url = 'playlists/' + this.playlist.id + '/add/'
axios.post(url, {tracks: ids}).then((response) => {
response.data.results.forEach(r => {
self.plts.push(r)
})
self.success()
}, error => {
self.errored(error.backendErrors)
})
}
},
computed: {
...mapState({
queueTracks: state => state.queue.tracks
}),
status () {
if (this.isLoading) {
return 'loading'
}
if (this.errors.length > 0) {
return 'errored'
}
return 'saved'
}
},
watch: {
plts: {
handler (newValue) {
newValue.forEach((e, i) => {
e.index = i
})
this.$emit('tracks-updated', newValue)
},
deep: true
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
......@@ -21,7 +21,7 @@ import RadioBuilder from '@/components/library/radios/Builder'
import BatchList from '@/components/library/import/BatchList'
import BatchDetail from '@/components/library/import/BatchDetail'
import RequestsList from '@/components/requests/RequestsList'
import PlaylistDetail from '@/views/playlists/Detail'
import Favorites from '@/components/favorites/List'
Vue.use(Router)
......@@ -110,6 +110,7 @@ export default new Router({
},
{ path: 'radios/build', name: 'library.radios.build', component: RadioBuilder, props: true },
{ path: 'radios/build/:id', name: 'library.radios.edit', component: RadioBuilder, props: true },
{ path: 'playlists/:id', name: 'library.playlists.detail', component: PlaylistDetail, props: true },
{ path: 'artists/:id', name: 'library.artists.detail', component: LibraryArtist, props: true },
{ path: 'albums/:id', name: 'library.albums.detail', component: LibraryAlbum, props: true },
{ path: 'tracks/:id', name: 'library.tracks.detail', component: LibraryTrack, props: true },
......
<template>
<div v-if="playlist">
<div class="ui head vertical center aligned stripe segment">
<div class="segment-content">
<h2 class="ui center aligned icon header">
<i class="circular inverted list yellow icon"></i>
<div class="content">
{{ playlist.name }}
<div class="sub header">
Playlist containing {{ playlistTracks.length }} tracks,
by <username :username="playlist.user.username"></username>
</div>
</div>
</h2>
<div class="ui hidden divider"></div>
</button>
<play-button class="orange" :tracks="tracks">Play all</play-button>
<button
class="ui icon button"
v-if="playlist.user.id === $store.state.auth.profile.id"
@click="edit = !edit">
<i class="pencil icon"></i>
<template v-if="edit">End edition</template>
<template v-else>Edit...</template>
</button>
</div>
</div>
<div v-if="tracks.length > 0" class="ui vertical stripe segment">
<template v-if="edit">
<playlist-editor @tracks-updated="updatePlts" :playlist="playlist" :playlist-tracks="playlistTracks"></playlist-editor>
</template>
<template v-else>
<h2>Tracks</h2>
<track-table :display-position="true" :tracks="tracks"></track-table>
</template>
</div>
</div>
</template>
<script>
import axios from 'axios'
import TrackTable from '@/components/audio/track/Table'
import RadioButton from '@/components/radios/Button'
import PlayButton from '@/components/audio/PlayButton'
import PlaylistEditor from '@/components/playlists/Editor'
export default {
props: {
id: {required: true}
},
components: {
PlaylistEditor,
TrackTable,
PlayButton,
RadioButton
},
data: function () {
return {
edit: false,
playlist: null,
tracks: [],
playlistTracks: []
}
},
created: function () {
this.fetch()
},
methods: {
updatePlts (v) {
this.playlistTracks = v
this.tracks = v.map((e, i) => {
let track = e.track
track.position = i + 1
return track
})
},
fetch: function () {
let self = this
let url = 'playlists/' + this.id + '/'
axios.get(url).then((response) => {
self.playlist = response.data
axios.get(url + 'tracks').then((response) => {
self.updatePlts(response.data.results)
})
})
}
}
}
</script>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment