QueueSongs.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. <template>
  2. <div>
  3. <div class="container">
  4. <input
  5. v-model="searchQuery"
  6. type="text"
  7. class="input"
  8. placeholder="Search for Songs"
  9. />
  10. <br />
  11. <br />
  12. <table class="table is-striped">
  13. <thead>
  14. <tr>
  15. <td>Thumbnail</td>
  16. <td>Title</td>
  17. <td>Artists</td>
  18. <td>Genres</td>
  19. <td>ID / YouTube ID</td>
  20. <td>Requested By</td>
  21. <td>Options</td>
  22. </tr>
  23. </thead>
  24. <tbody>
  25. <tr v-for="(song, index) in filteredSongs" :key="index">
  26. <td>
  27. <img
  28. class="song-thumbnail"
  29. :src="song.thumbnail"
  30. onerror="this.src='/assets/notes-transparent.png'"
  31. />
  32. </td>
  33. <td>
  34. <strong>{{ song.title }}</strong>
  35. </td>
  36. <td>{{ song.artists.join(", ") }}</td>
  37. <td>{{ song.genres.join(", ") }}</td>
  38. <td>
  39. {{ song._id }}
  40. <br />
  41. <a
  42. :href="
  43. 'https://www.youtube.com/watch?v=' +
  44. `${song.songId}`
  45. "
  46. target="_blank"
  47. >
  48. {{ song.songId }}</a
  49. >
  50. </td>
  51. <td>
  52. <user-id-to-username
  53. :userId="song.requestedBy"
  54. :link="true"
  55. />
  56. </td>
  57. <td class="optionsColumn">
  58. <button
  59. class="button is-primary"
  60. @click="edit(song, index)"
  61. >
  62. <i class="material-icons">edit</i>
  63. </button>
  64. <button
  65. class="button is-success"
  66. @click="add(song)"
  67. >
  68. <i class="material-icons">add</i>
  69. </button>
  70. <button
  71. class="button is-danger"
  72. @click="remove(song._id, index)"
  73. >
  74. <i class="material-icons">cancel</i>
  75. </button>
  76. </td>
  77. </tr>
  78. </tbody>
  79. </table>
  80. </div>
  81. <nav class="pagination">
  82. <a
  83. v-if="position > 1"
  84. class="button"
  85. href="#"
  86. @click="getSet(position - 1)"
  87. >
  88. <i class="material-icons">navigate_before</i>
  89. </a>
  90. <a
  91. v-if="maxPosition > position"
  92. class="button"
  93. href="#"
  94. @click="getSet(position + 1)"
  95. >
  96. <i class="material-icons">navigate_next</i>
  97. </a>
  98. </nav>
  99. <edit-song v-if="modals.editSong" />
  100. </div>
  101. </template>
  102. <script>
  103. import { mapState, mapActions } from "vuex";
  104. import { Toast } from "vue-roaster";
  105. import EditSong from "../Modals/EditSong.vue";
  106. import UserIdToUsername from "../UserIdToUsername.vue";
  107. import io from "../../io";
  108. export default {
  109. components: { EditSong, UserIdToUsername },
  110. data() {
  111. return {
  112. position: 1,
  113. maxPosition: 1,
  114. searchQuery: "",
  115. songs: []
  116. };
  117. },
  118. computed: {
  119. filteredSongs() {
  120. return this.songs.filter(
  121. song =>
  122. JSON.stringify(Object.values(song)).indexOf(
  123. this.searchQuery
  124. ) !== -1
  125. );
  126. },
  127. ...mapState("modals", {
  128. modals: state => state.modals.admin
  129. })
  130. },
  131. // watch: {
  132. // "modals.editSong": function(value) {
  133. // console.log(value);
  134. // if (value === false) this.stopVideo();
  135. // }
  136. // },
  137. methods: {
  138. getSet(position) {
  139. this.socket.emit("queueSongs.getSet", position, data => {
  140. this.songs = data;
  141. this.position = position;
  142. });
  143. },
  144. edit(song, index) {
  145. const newSong = {};
  146. Object.keys(song).forEach(n => {
  147. newSong[n] = song[n];
  148. });
  149. this.editSong({ index, song: newSong, type: "queueSongs" });
  150. this.openModal({ sector: "admin", modal: "editSong" });
  151. },
  152. add(song) {
  153. this.socket.emit("songs.add", song, res => {
  154. if (res.status === "success")
  155. Toast.methods.addToast(res.message, 2000);
  156. else Toast.methods.addToast(res.message, 4000);
  157. });
  158. },
  159. remove(id) {
  160. this.socket.emit("queueSongs.remove", id, res => {
  161. if (res.status === "success")
  162. Toast.methods.addToast(res.message, 2000);
  163. else Toast.methods.addToast(res.message, 4000);
  164. });
  165. },
  166. init() {
  167. this.socket.emit("queueSongs.index", data => {
  168. this.songs = data.songs;
  169. this.maxPosition = Math.round(data.maxLength / 50);
  170. });
  171. this.socket.emit("apis.joinAdminRoom", "queue", () => {});
  172. },
  173. ...mapActions("admin/songs", ["stopVideo", "editSong"]),
  174. ...mapActions("modals", ["openModal"])
  175. },
  176. mounted() {
  177. io.getSocket(socket => {
  178. this.socket = socket;
  179. this.socket.on("event:admin.queueSong.added", queueSong => {
  180. this.songs.push(queueSong);
  181. });
  182. this.socket.on("event:admin.queueSong.removed", songId => {
  183. this.songs = this.songs.filter(song => {
  184. return song._id !== songId;
  185. });
  186. });
  187. this.socket.on("event:admin.queueSong.updated", updatedSong => {
  188. for (let i = 0; i < this.songs.length; i += 1) {
  189. const song = this.songs[i];
  190. if (song._id === updatedSong._id) {
  191. this.songs.$set(i, updatedSong);
  192. }
  193. }
  194. });
  195. if (this.socket.connected) {
  196. this.init();
  197. }
  198. io.onConnect(() => {
  199. this.init();
  200. });
  201. });
  202. }
  203. };
  204. </script>
  205. <style lang="scss" scoped>
  206. @import "styles/global.scss";
  207. .optionsColumn {
  208. width: 140px;
  209. button {
  210. width: 35px;
  211. }
  212. }
  213. .song-thumbnail {
  214. display: block;
  215. max-width: 50px;
  216. margin: 0 auto;
  217. }
  218. td {
  219. vertical-align: middle;
  220. }
  221. .is-primary:focus {
  222. background-color: #029ce3 !important;
  223. }
  224. </style>