|
@@ -1,54 +1,69 @@
|
|
|
<template>
|
|
|
<div id="queue">
|
|
|
- <div
|
|
|
+ <draggable
|
|
|
:class="{
|
|
|
'actionable-button-hidden': !actionableButtonVisible,
|
|
|
'scrollable-list': true
|
|
|
}"
|
|
|
+ v-if="queue.length > 0"
|
|
|
+ v-model="queue"
|
|
|
+ v-bind="dragOptions"
|
|
|
+ @start="drag = true"
|
|
|
+ @end="drag = false"
|
|
|
+ @change="repositionSongInQueue"
|
|
|
>
|
|
|
- <song-item
|
|
|
- v-for="(song, index) in songsList"
|
|
|
- :key="index + song.songId"
|
|
|
- :song="song"
|
|
|
- :requested-by="
|
|
|
- station.type === 'community' && station.partyMode === true
|
|
|
- "
|
|
|
+ <transition-group
|
|
|
+ type="transition"
|
|
|
+ :name="!drag ? 'draggable-list-transition' : null"
|
|
|
>
|
|
|
- <div
|
|
|
- v-if="isAdminOnly() || isOwnerOnly()"
|
|
|
- class="song-actions"
|
|
|
- slot="actions"
|
|
|
+ <song-item
|
|
|
+ v-for="(song, index) in queue"
|
|
|
+ :key="index + song.songId"
|
|
|
+ :song="song"
|
|
|
+ :requested-by="
|
|
|
+ station.type === 'community' &&
|
|
|
+ station.partyMode === true
|
|
|
+ "
|
|
|
+ :class="{
|
|
|
+ 'item-draggable': isAdminOnly() || isOwnerOnly()
|
|
|
+ }"
|
|
|
>
|
|
|
- <i
|
|
|
- v-if="isOwnerOnly() || isAdminOnly()"
|
|
|
- class="material-icons delete-icon"
|
|
|
- @click="removeFromQueue(song.songId)"
|
|
|
- content="Remove Song from Queue"
|
|
|
- v-tippy
|
|
|
- >delete_forever</i
|
|
|
- >
|
|
|
- <i
|
|
|
- class="material-icons"
|
|
|
- v-if="index > 0"
|
|
|
- @click="moveSongToTop(index)"
|
|
|
- content="Move to top of Queue"
|
|
|
- v-tippy
|
|
|
- >vertical_align_top</i
|
|
|
- >
|
|
|
- <i
|
|
|
- v-if="songsList.length - 1 !== index"
|
|
|
- @click="moveSongToBottom(index)"
|
|
|
- class="material-icons"
|
|
|
- content="Move to bottom of Queue"
|
|
|
- v-tippy
|
|
|
- >vertical_align_bottom</i
|
|
|
+ <div
|
|
|
+ v-if="isAdminOnly() || isOwnerOnly()"
|
|
|
+ class="song-actions"
|
|
|
+ slot="actions"
|
|
|
>
|
|
|
- </div>
|
|
|
- </song-item>
|
|
|
- <p class="nothing-here-text" v-if="songsList.length < 1">
|
|
|
- There are no songs currently queued
|
|
|
- </p>
|
|
|
- </div>
|
|
|
+ <i
|
|
|
+ v-if="isOwnerOnly() || isAdminOnly()"
|
|
|
+ class="material-icons delete-icon"
|
|
|
+ @click="removeFromQueue(song.songId)"
|
|
|
+ content="Remove Song from Queue"
|
|
|
+ v-tippy
|
|
|
+ >delete_forever</i
|
|
|
+ >
|
|
|
+ <i
|
|
|
+ class="material-icons"
|
|
|
+ v-if="index > 0"
|
|
|
+ @click="moveSongToTop(song, index)"
|
|
|
+ content="Move to top of Queue"
|
|
|
+ v-tippy
|
|
|
+ >vertical_align_top</i
|
|
|
+ >
|
|
|
+ <i
|
|
|
+ v-if="queue.length - 1 !== index"
|
|
|
+ @click="moveSongToBottom(song, index)"
|
|
|
+ class="material-icons"
|
|
|
+ content="Move to bottom of Queue"
|
|
|
+ v-tippy
|
|
|
+ >vertical_align_bottom</i
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </song-item>
|
|
|
+ </transition-group>
|
|
|
+ </draggable>
|
|
|
+ <p class="nothing-here-text" v-else>
|
|
|
+ There are no songs currently queued
|
|
|
+ </p>
|
|
|
<button
|
|
|
class="button is-primary tab-actionable-button"
|
|
|
v-if="
|
|
@@ -110,19 +125,37 @@
|
|
|
|
|
|
<script>
|
|
|
import { mapActions, mapState, mapGetters } from "vuex";
|
|
|
+import draggable from "vuedraggable";
|
|
|
import Toast from "toasters";
|
|
|
|
|
|
import SongItem from "@/components/SongItem.vue";
|
|
|
|
|
|
export default {
|
|
|
- components: { SongItem },
|
|
|
+ components: { draggable, SongItem },
|
|
|
data() {
|
|
|
return {
|
|
|
dismissedWarning: false,
|
|
|
- actionableButtonVisible: false
|
|
|
+ actionableButtonVisible: false,
|
|
|
+ drag: false
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
|
+ queue: {
|
|
|
+ get() {
|
|
|
+ return this.$store.state.station.songsList;
|
|
|
+ },
|
|
|
+ set(queue) {
|
|
|
+ this.$store.commit("station/updateSongsList", queue);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ dragOptions() {
|
|
|
+ return {
|
|
|
+ animation: 200,
|
|
|
+ group: "queue",
|
|
|
+ disabled: !(this.isAdminOnly() || this.isOwnerOnly()),
|
|
|
+ ghostClass: "draggable-list-ghost"
|
|
|
+ };
|
|
|
+ },
|
|
|
...mapState({
|
|
|
loggedIn: state => state.user.auth.loggedIn,
|
|
|
userId: state => state.user.auth.userId,
|
|
@@ -164,6 +197,40 @@ export default {
|
|
|
}
|
|
|
);
|
|
|
},
|
|
|
+ repositionSongInQueue({ moved }) {
|
|
|
+ if (!moved) return; // we only need to update when song is moved
|
|
|
+
|
|
|
+ this.socket.dispatch(
|
|
|
+ "stations.repositionSongInQueue",
|
|
|
+ {
|
|
|
+ ...moved.element,
|
|
|
+ oldIndex: moved.oldIndex,
|
|
|
+ newIndex: moved.newIndex
|
|
|
+ },
|
|
|
+ this.station._id,
|
|
|
+ res => {
|
|
|
+ new Toast({ content: res.message, timeout: 4000 });
|
|
|
+ }
|
|
|
+ );
|
|
|
+ },
|
|
|
+ moveSongToTop(song, index) {
|
|
|
+ this.repositionSongInQueue({
|
|
|
+ moved: {
|
|
|
+ element: song,
|
|
|
+ oldIndex: index,
|
|
|
+ newIndex: 0
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ moveSongToBottom(song, index) {
|
|
|
+ this.repositionSongInQueue({
|
|
|
+ moved: {
|
|
|
+ element: song,
|
|
|
+ oldIndex: index,
|
|
|
+ newIndex: this.songsList.length
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
...mapActions("modalVisibility", ["openModal"])
|
|
|
}
|
|
|
};
|