Queue.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <template>
  2. <div id="queue">
  3. <div
  4. :class="{
  5. 'actionable-button-hidden': !actionableButtonVisible,
  6. 'scrollable-list': true
  7. }"
  8. >
  9. <song-item
  10. v-for="(song, index) in songsList"
  11. :key="index + song.songId"
  12. :song="song"
  13. :requested-by="
  14. station.type === 'community' && station.partyMode === true
  15. "
  16. >
  17. <div
  18. v-if="isAdminOnly() || isOwnerOnly()"
  19. class="song-actions"
  20. slot="actions"
  21. >
  22. <i
  23. v-if="isOwnerOnly() || isAdminOnly()"
  24. class="material-icons delete-icon"
  25. @click="removeFromQueue(song.songId)"
  26. content="Remove Song from Queue"
  27. v-tippy
  28. >delete_forever</i
  29. >
  30. <i
  31. class="material-icons"
  32. v-if="index > 0"
  33. @click="moveSongToTop(index)"
  34. content="Move to top of Queue"
  35. v-tippy
  36. >vertical_align_top</i
  37. >
  38. <i
  39. v-if="songsList.length - 1 !== index"
  40. @click="moveSongToBottom(index)"
  41. class="material-icons"
  42. content="Move to bottom of Queue"
  43. v-tippy
  44. >vertical_align_bottom</i
  45. >
  46. </div>
  47. </song-item>
  48. <p class="nothing-here-text" v-if="songsList.length < 1">
  49. There are no songs currently queued
  50. </p>
  51. </div>
  52. <button
  53. class="button is-primary tab-actionable-button"
  54. v-if="
  55. loggedIn &&
  56. ((station.type === 'community' &&
  57. station.partyMode &&
  58. ((station.locked && isOwnerOnly()) ||
  59. !station.locked ||
  60. (station.locked &&
  61. isAdminOnly() &&
  62. dismissedWarning))) ||
  63. station.type === 'official')
  64. "
  65. @click="
  66. openModal({
  67. sector: 'station',
  68. modal: 'addSongToQueue'
  69. })
  70. "
  71. >
  72. <i class="material-icons icon-with-button">queue</i>
  73. <span class="optional-desktop-only-text"> Add Song To Queue </span>
  74. </button>
  75. <button
  76. class="button is-primary tab-actionable-button disabled"
  77. v-if="
  78. !loggedIn &&
  79. ((station.type === 'community' &&
  80. station.partyMode &&
  81. !station.locked) ||
  82. station.type === 'official')
  83. "
  84. content="Login to add songs to queue"
  85. v-tippy
  86. >
  87. <i class="material-icons icon-with-button">queue</i>
  88. <span class="optional-desktop-only-text"> Add Song To Queue </span>
  89. </button>
  90. <div
  91. id="queue-locked"
  92. v-if="station.type === 'community' && station.locked"
  93. >
  94. <button
  95. v-if="isAdminOnly() && !isOwnerOnly() && !dismissedWarning"
  96. class="button tab-actionable-button"
  97. @click="dismissedWarning = true"
  98. >
  99. THIS STATION'S QUEUE IS LOCKED.
  100. </button>
  101. <button
  102. v-if="!isAdminOnly() && !isOwnerOnly()"
  103. class="button tab-actionable-button"
  104. >
  105. THIS STATION'S QUEUE IS LOCKED.
  106. </button>
  107. </div>
  108. </div>
  109. </template>
  110. <script>
  111. import { mapActions, mapState, mapGetters } from "vuex";
  112. import Toast from "toasters";
  113. import SongItem from "@/components/SongItem.vue";
  114. export default {
  115. components: { SongItem },
  116. data() {
  117. return {
  118. dismissedWarning: false,
  119. actionableButtonVisible: false
  120. };
  121. },
  122. computed: {
  123. ...mapState({
  124. loggedIn: state => state.user.auth.loggedIn,
  125. userId: state => state.user.auth.userId,
  126. userRole: state => state.user.auth.role,
  127. station: state => state.station.station,
  128. songsList: state => state.station.songsList,
  129. noSong: state => state.station.noSong
  130. }),
  131. ...mapGetters({
  132. socket: "websockets/getSocket"
  133. })
  134. },
  135. updated() {
  136. // check if actionable button is visible, if not: set max-height of queue items to 100%
  137. if (
  138. document
  139. .getElementById("queue")
  140. .querySelectorAll(".tab-actionable-button").length > 0
  141. )
  142. this.actionableButtonVisible = true;
  143. else this.actionableButtonVisible = false;
  144. },
  145. methods: {
  146. isOwnerOnly() {
  147. return this.loggedIn && this.userId === this.station.owner;
  148. },
  149. isAdminOnly() {
  150. return this.loggedIn && this.userRole === "admin";
  151. },
  152. removeFromQueue(songId) {
  153. this.socket.dispatch(
  154. "stations.removeFromQueue",
  155. this.station._id,
  156. songId,
  157. res => {
  158. if (res.status === "success") {
  159. new Toast("Successfully removed song from the queue.");
  160. } else new Toast(res.message);
  161. }
  162. );
  163. },
  164. ...mapActions("modalVisibility", ["openModal"])
  165. }
  166. };
  167. </script>
  168. <style lang="scss" scoped>
  169. .night-mode {
  170. #queue {
  171. background-color: var(--dark-grey-3) !important;
  172. border: 0 !important;
  173. }
  174. }
  175. #queue {
  176. background-color: var(--white);
  177. border-radius: 0 0 5px 5px;
  178. .actionable-button-hidden {
  179. max-height: 100%;
  180. }
  181. .song-item:not(:last-of-type) {
  182. margin-bottom: 10px;
  183. }
  184. #queue-locked {
  185. display: flex;
  186. justify-content: center;
  187. }
  188. button.disabled {
  189. filter: grayscale(0.4);
  190. }
  191. }
  192. </style>