AddToPlaylistDropdown.vue 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. <template>
  2. <tippy
  3. class="addToPlaylistDropdown"
  4. interactive="true"
  5. :placement="placement"
  6. theme="addToPlaylist"
  7. trigger="click"
  8. append-to="parent"
  9. @show="
  10. () => {
  11. $parent.showPlaylistDropdown = true;
  12. }
  13. "
  14. @hide="
  15. () => {
  16. $parent.showPlaylistDropdown = false;
  17. }
  18. "
  19. >
  20. <template #trigger>
  21. <slot name="button" />
  22. </template>
  23. <div class="nav-dropdown-items" v-if="playlists.length > 0">
  24. <button
  25. class="nav-item"
  26. href="#"
  27. v-for="(playlist, index) in playlists"
  28. :key="playlist._id"
  29. @click.prevent="toggleSongInPlaylist(index)"
  30. :title="playlist.displayName"
  31. >
  32. <p class="control is-expanded checkbox-control">
  33. <input
  34. type="checkbox"
  35. :id="index"
  36. :checked="hasSong(playlist)"
  37. @click="toggleSongInPlaylist(index)"
  38. />
  39. <label :for="index">
  40. <span></span>
  41. <p>{{ playlist.displayName }}</p>
  42. </label>
  43. </p>
  44. </button>
  45. </div>
  46. <p v-else>You haven't created any playlists.</p>
  47. </tippy>
  48. </template>
  49. <script>
  50. import { mapGetters, mapState, mapActions } from "vuex";
  51. import Toast from "toasters";
  52. export default {
  53. props: {
  54. song: {
  55. type: Object,
  56. default: () => {}
  57. },
  58. placement: {
  59. type: String,
  60. default: "left"
  61. }
  62. },
  63. computed: {
  64. ...mapGetters({
  65. socket: "websockets/getSocket"
  66. }),
  67. ...mapState({
  68. fetchedPlaylists: state => state.user.playlists.fetchedPlaylists
  69. }),
  70. playlists: {
  71. get() {
  72. return this.$store.state.user.playlists.playlists.filter(
  73. playlist => playlist.isUserModifiable
  74. );
  75. },
  76. set(playlists) {
  77. this.$store.commit("user/playlists/setPlaylists", playlists);
  78. }
  79. }
  80. },
  81. mounted() {
  82. if (!this.fetchedPlaylists)
  83. this.socket.dispatch("playlists.indexMyPlaylists", false, res => {
  84. if (res.status === "success")
  85. this.setPlaylists(res.data.playlists);
  86. });
  87. this.socket.on(
  88. "event:playlist.created",
  89. res => this.playlists.push(res.data.playlist),
  90. { replaceable: true }
  91. );
  92. this.socket.on(
  93. "event:playlist.deleted",
  94. res => {
  95. this.playlists.forEach((playlist, index) => {
  96. if (playlist._id === res.data.playlistId) {
  97. this.playlists.splice(index, 1);
  98. }
  99. });
  100. },
  101. { replaceable: true }
  102. );
  103. this.socket.on(
  104. "event:playlist.displayName.updated",
  105. res => {
  106. this.playlists.forEach((playlist, index) => {
  107. if (playlist._id === res.data.playlistId) {
  108. this.playlists[index].displayName =
  109. res.data.displayName;
  110. }
  111. });
  112. },
  113. { replaceable: true }
  114. );
  115. },
  116. methods: {
  117. toggleSongInPlaylist(playlistIndex) {
  118. const playlist = this.playlists[playlistIndex];
  119. if (!this.hasSong(playlist)) {
  120. this.socket.dispatch(
  121. "playlists.addSongToPlaylist",
  122. false,
  123. this.song.youtubeId,
  124. playlist._id,
  125. res => new Toast(res.message)
  126. );
  127. } else {
  128. this.socket.dispatch(
  129. "playlists.removeSongFromPlaylist",
  130. this.song.youtubeId,
  131. playlist._id,
  132. res => new Toast(res.message)
  133. );
  134. }
  135. },
  136. hasSong(playlist) {
  137. return (
  138. playlist.songs.map(song => song._id).indexOf(this.song._id) !==
  139. -1
  140. );
  141. },
  142. ...mapActions("user/playlists", ["setPlaylists"])
  143. }
  144. };
  145. </script>