QueueSongs.vue 5.0 KB

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