AddToPlaylistDropdown.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. <template>
  2. <div id="nav-dropdown">
  3. <div class="nav-dropdown-items" v-if="playlists.length > 0">
  4. <!-- <a class="nav-item" id="nightmode-toggle">
  5. <span>Nightmode</span>
  6. <label class="switch">
  7. <input type="checkbox" checked />
  8. <span class="slider round"></span>
  9. </label>
  10. </a> -->
  11. <button
  12. class="nav-item"
  13. href="#"
  14. v-for="(playlist, index) in playlists"
  15. :key="index"
  16. @click.prevent="toggleSongInPlaylist(index, playlist._id)"
  17. :title="playlist.displayName"
  18. >
  19. <p class="control is-expanded checkbox-control">
  20. <input
  21. type="checkbox"
  22. :id="index"
  23. v-model="playlist.hasSong"
  24. />
  25. <label :for="index">
  26. <span></span>
  27. <p>{{ playlist.displayName }}</p>
  28. </label>
  29. </p>
  30. </button>
  31. </div>
  32. <p class="nav-dropdown-items" id="no-playlists" v-else>
  33. You haven't created any playlists.
  34. </p>
  35. </div>
  36. </template>
  37. <script>
  38. import { mapState } from "vuex";
  39. import Toast from "toasters";
  40. import io from "../../../io";
  41. export default {
  42. data() {
  43. return {
  44. playlists: []
  45. };
  46. },
  47. computed: {
  48. ...mapState("station", {
  49. currentSong: state => state.currentSong
  50. })
  51. },
  52. mounted() {
  53. io.getSocket(socket => {
  54. this.socket = socket;
  55. this.socket.emit("playlists.indexMyPlaylists", false, res => {
  56. if (res.status === "success") {
  57. this.playlists = res.data;
  58. this.checkIfPlaylistsHaveSong();
  59. }
  60. });
  61. this.socket.on("event:songs.next", () => {
  62. this.checkIfPlaylistsHaveSong();
  63. });
  64. this.socket.on("event:playlist.create", playlist => {
  65. this.playlists.push(playlist);
  66. });
  67. this.socket.on("event:playlist.delete", playlistId => {
  68. this.playlists.forEach((playlist, index) => {
  69. if (playlist._id === playlistId) {
  70. this.playlists.splice(index, 1);
  71. }
  72. });
  73. });
  74. this.socket.on("event:playlist.updateDisplayName", data => {
  75. this.playlists.forEach((playlist, index) => {
  76. if (playlist._id === data.playlistId) {
  77. this.playlists[index].displayName = data.displayName;
  78. }
  79. });
  80. });
  81. });
  82. },
  83. methods: {
  84. toggleSongInPlaylist(index, playlistId) {
  85. if (!this.playlists[index].hasSong) {
  86. this.socket.emit(
  87. "playlists.addSongToPlaylist",
  88. false,
  89. this.currentSong.songId,
  90. playlistId,
  91. res => {
  92. new Toast({ content: res.message, timeout: 4000 });
  93. if (res.status === "success") {
  94. this.playlists[index].songs.push(this.currentSong);
  95. this.playlists[index].hasSong = true;
  96. }
  97. }
  98. );
  99. } else {
  100. this.socket.emit(
  101. "playlists.removeSongFromPlaylist",
  102. this.currentSong.songId,
  103. playlistId,
  104. res => {
  105. new Toast({ content: res.message, timeout: 4000 });
  106. if (res.status === "success") {
  107. this.playlists[index].songs.forEach(
  108. (song, index) => {
  109. if (song.songId === this.currentSong.songId)
  110. this.playlists[index].songs.splice(
  111. index,
  112. 1
  113. );
  114. }
  115. );
  116. this.playlists[index].hasSong = false;
  117. }
  118. }
  119. );
  120. }
  121. },
  122. checkIfPlaylistsHaveSong() {
  123. this.playlists.forEach((playlist, index) => {
  124. let hasSong = false;
  125. for (let song = 0; song < playlist.songs.length; song += 1) {
  126. if (playlist.songs[song].songId === this.currentSong.songId)
  127. hasSong = true;
  128. }
  129. this.$set(this.playlists[index], "hasSong", hasSong);
  130. });
  131. }
  132. }
  133. };
  134. </script>
  135. <style lang="scss" scoped>
  136. .night-mode {
  137. .nav-dropdown-items {
  138. background-color: var(--dark-grey-2);
  139. border: 0 !important;
  140. .nav-item {
  141. background-color: var(--dark-grey);
  142. &:focus {
  143. outline-color: var(--dark-grey);
  144. }
  145. p {
  146. color: var(--white);
  147. }
  148. .checkbox-control label span {
  149. background-color: var(--dark-grey-2);
  150. }
  151. }
  152. }
  153. }
  154. #nav-dropdown {
  155. position: absolute;
  156. margin-left: 4px;
  157. z-index: 1;
  158. margin-bottom: 36px;
  159. }
  160. #nav-dropdown-triangle {
  161. border-style: solid;
  162. border-width: 15px 15px 0 15px;
  163. border-color: var(--dark-grey-2) transparent transparent transparent;
  164. }
  165. .nav-dropdown-items {
  166. border: 1px solid var(--light-grey-3);
  167. box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
  168. background-color: var(--white);
  169. padding: 5px;
  170. border-radius: 5px;
  171. position: relative;
  172. right: calc(100% - 110px);
  173. .nav-item {
  174. width: 100%;
  175. justify-content: flex-start;
  176. border: 0;
  177. padding: 10px;
  178. font-size: 15.5px;
  179. height: 36px;
  180. background: var(--light-grey);
  181. border-radius: 5px;
  182. cursor: pointer;
  183. .checkbox-control {
  184. display: flex;
  185. align-items: center;
  186. margin-bottom: 0 !important;
  187. input {
  188. margin-right: 5px;
  189. }
  190. input[type="checkbox"] {
  191. opacity: 0;
  192. position: absolute;
  193. }
  194. label {
  195. display: flex;
  196. flex-direction: row;
  197. align-items: center;
  198. span {
  199. cursor: pointer;
  200. width: 24px;
  201. height: 24px;
  202. background-color: var(--white);
  203. display: inline-block;
  204. border: 1px solid var(--dark-grey-2);
  205. position: relative;
  206. border-radius: 3px;
  207. }
  208. p {
  209. margin-left: 10px;
  210. cursor: pointer;
  211. color: var(--black);
  212. }
  213. }
  214. input[type="checkbox"]:checked + label span::after {
  215. content: "";
  216. width: 18px;
  217. height: 18px;
  218. left: 2px;
  219. top: 2px;
  220. border-radius: 3px;
  221. background-color: var(--primary-color);
  222. position: absolute;
  223. }
  224. }
  225. &:focus {
  226. outline-color: var(--light-grey-3);
  227. }
  228. &:not(:last-of-type) {
  229. margin-bottom: 5px;
  230. }
  231. }
  232. }
  233. #nightmode-toggle {
  234. display: flex;
  235. justify-content: space-evenly;
  236. }
  237. /* CSS - Toggle Switch */
  238. .switch {
  239. position: relative;
  240. display: inline-block;
  241. width: 50px;
  242. height: 24px;
  243. input {
  244. opacity: 0;
  245. width: 0;
  246. height: 0;
  247. }
  248. }
  249. .slider {
  250. position: absolute;
  251. cursor: pointer;
  252. top: 0;
  253. left: 0;
  254. right: 0;
  255. bottom: 0;
  256. background-color: var(--light-grey-3);
  257. transition: 0.4s;
  258. border-radius: 34px;
  259. &:before {
  260. position: absolute;
  261. content: "";
  262. height: 16px;
  263. width: 16px;
  264. left: 4px;
  265. bottom: 4px;
  266. background-color: var(--white);
  267. transition: 0.4s;
  268. border-radius: 50%;
  269. }
  270. }
  271. input:checked + .slider {
  272. background-color: var(--primary-color);
  273. }
  274. input:focus + .slider {
  275. box-shadow: 0 0 1px var(--primary-color);
  276. }
  277. input:checked + .slider:before {
  278. transform: translateX(26px);
  279. }
  280. #no-playlists {
  281. padding: 10px;
  282. }
  283. </style>