Playlists.vue 24 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  1. <template>
  2. <div class="station-playlists">
  3. <div class="tabs-container">
  4. <div class="tab-selection">
  5. <button
  6. class="button is-default"
  7. ref="search-tab"
  8. :class="{ selected: tab === 'search' }"
  9. @click="showTab('search')"
  10. >
  11. Search
  12. </button>
  13. <button
  14. v-if="station.type === 'community'"
  15. class="button is-default"
  16. ref="my-playlists-tab"
  17. :class="{ selected: tab === 'my-playlists' }"
  18. @click="showTab('my-playlists')"
  19. >
  20. My Playlists
  21. </button>
  22. <button
  23. class="button is-default"
  24. ref="party-tab"
  25. :class="{ selected: tab === 'party' }"
  26. v-if="isPartyMode()"
  27. @click="showTab('party')"
  28. >
  29. Party
  30. </button>
  31. <button
  32. class="button is-default"
  33. ref="included-tab"
  34. :class="{ selected: tab === 'included' }"
  35. v-if="isPlaylistMode()"
  36. @click="showTab('included')"
  37. >
  38. Included
  39. </button>
  40. <button
  41. class="button is-default"
  42. ref="excluded-tab"
  43. :class="{ selected: tab === 'excluded' }"
  44. @click="showTab('excluded')"
  45. >
  46. Excluded
  47. </button>
  48. </div>
  49. <div class="tab" v-show="tab === 'search'">
  50. <label class="label"> Search for a public playlist </label>
  51. <div class="control is-grouped input-with-button">
  52. <p class="control is-expanded">
  53. <input
  54. class="input"
  55. type="text"
  56. placeholder="Enter your playlist query here..."
  57. v-model="search.query"
  58. @keyup.enter="searchForPlaylists(1)"
  59. />
  60. </p>
  61. <p class="control">
  62. <a class="button is-info" @click="searchForPlaylists(1)"
  63. ><i class="material-icons icon-with-button"
  64. >search</i
  65. >Search</a
  66. >
  67. </p>
  68. </div>
  69. <div v-if="search.results.length > 0">
  70. <playlist-item
  71. v-for="playlist in search.results"
  72. :key="`searchKey-${playlist._id}`"
  73. :playlist="playlist"
  74. :show-owner="true"
  75. >
  76. <i
  77. class="material-icons"
  78. slot="item-icon"
  79. v-if="
  80. isAllowedToParty() && isSelected(playlist._id)
  81. "
  82. content="This playlist is currently selected"
  83. v-tippy
  84. >
  85. radio
  86. </i>
  87. <i
  88. class="material-icons"
  89. slot="item-icon"
  90. v-else-if="
  91. isOwnerOrAdmin() &&
  92. isPlaylistMode() &&
  93. isIncluded(playlist._id)
  94. "
  95. content="This playlist is currently included"
  96. v-tippy
  97. >
  98. play_arrow
  99. </i>
  100. <i
  101. class="material-icons excluded-icon"
  102. slot="item-icon"
  103. v-else-if="
  104. isOwnerOrAdmin() && isExcluded(playlist._id)
  105. "
  106. content="This playlist is currently excluded"
  107. v-tippy
  108. >
  109. block
  110. </i>
  111. <i
  112. class="material-icons"
  113. slot="item-icon"
  114. v-else
  115. :content="
  116. isPartyMode()
  117. ? 'This playlist is currently not selected or excluded'
  118. : 'This playlist is currently not included or excluded'
  119. "
  120. v-tippy
  121. >
  122. play_disabled
  123. </i>
  124. <div class="icons-group" slot="actions">
  125. <i
  126. v-if="isExcluded(playlist._id)"
  127. class="material-icons stop-icon"
  128. content="This playlist is blacklisted in this station"
  129. v-tippy="{ theme: 'info' }"
  130. >play_disabled</i
  131. >
  132. <confirm
  133. v-if="isPartyMode() && isSelected(playlist._id)"
  134. @confirm="deselectPartyPlaylist(playlist._id)"
  135. >
  136. <i
  137. class="material-icons stop-icon"
  138. content="Stop playing songs from this playlist"
  139. v-tippy
  140. >
  141. stop
  142. </i>
  143. </confirm>
  144. <confirm
  145. v-if="
  146. isOwnerOrAdmin() &&
  147. isPlaylistMode() &&
  148. isIncluded(playlist._id)
  149. "
  150. @confirm="removeIncludedPlaylist(playlist._id)"
  151. >
  152. <i
  153. class="material-icons stop-icon"
  154. content="Stop playing songs from this playlist"
  155. v-tippy
  156. >
  157. stop
  158. </i>
  159. </confirm>
  160. <i
  161. v-if="
  162. isPartyMode() &&
  163. !isSelected(playlist._id) &&
  164. !isExcluded(playlist._id)
  165. "
  166. @click="selectPartyPlaylist(playlist)"
  167. class="material-icons play-icon"
  168. content="Request songs from this playlist"
  169. v-tippy
  170. >play_arrow</i
  171. >
  172. <i
  173. v-if="
  174. isOwnerOrAdmin() &&
  175. isPlaylistMode() &&
  176. !isIncluded(playlist._id) &&
  177. !isExcluded(playlist._id)
  178. "
  179. @click="includePlaylist(playlist)"
  180. class="material-icons play-icon"
  181. :content="'Play songs from this playlist'"
  182. v-tippy
  183. >play_arrow</i
  184. >
  185. <confirm
  186. v-if="
  187. isOwnerOrAdmin() &&
  188. !isExcluded(playlist._id)
  189. "
  190. @confirm="blacklistPlaylist(playlist._id)"
  191. >
  192. <i
  193. class="material-icons stop-icon"
  194. content="Blacklist Playlist"
  195. v-tippy
  196. >block</i
  197. >
  198. </confirm>
  199. <confirm
  200. v-if="
  201. isOwnerOrAdmin() && isExcluded(playlist._id)
  202. "
  203. @confirm="removeExcludedPlaylist(playlist._id)"
  204. >
  205. <i
  206. class="material-icons stop-icon"
  207. content="Stop blacklisting songs from this playlist"
  208. v-tippy
  209. >
  210. stop
  211. </i>
  212. </confirm>
  213. <i
  214. v-if="playlist.createdBy === myUserId"
  215. @click="showPlaylist(playlist._id)"
  216. class="material-icons edit-icon"
  217. content="Edit Playlist"
  218. v-tippy
  219. >edit</i
  220. >
  221. <i
  222. v-if="
  223. playlist.createdBy !== myUserId &&
  224. (playlist.privacy === 'public' ||
  225. isAdmin())
  226. "
  227. @click="showPlaylist(playlist._id)"
  228. class="material-icons edit-icon"
  229. content="View Playlist"
  230. v-tippy
  231. >visibility</i
  232. >
  233. </div>
  234. </playlist-item>
  235. <button
  236. v-if="resultsLeftCount > 0"
  237. class="button is-primary load-more-button"
  238. @click="searchForPlaylists(search.page + 1)"
  239. >
  240. Load {{ nextPageResultsCount }} more results
  241. </button>
  242. </div>
  243. </div>
  244. <div
  245. v-if="station.type === 'community'"
  246. class="tab"
  247. v-show="tab === 'my-playlists'"
  248. >
  249. <button
  250. class="button is-primary"
  251. id="create-new-playlist-button"
  252. @click="openModal('createPlaylist')"
  253. >
  254. Create new playlist
  255. </button>
  256. <draggable
  257. class="menu-list scrollable-list"
  258. v-if="playlists.length > 0"
  259. v-model="playlists"
  260. v-bind="dragOptions"
  261. @start="drag = true"
  262. @end="drag = false"
  263. @change="savePlaylistOrder"
  264. >
  265. <transition-group
  266. type="transition"
  267. :name="!drag ? 'draggable-list-transition' : null"
  268. >
  269. <playlist-item
  270. class="item-draggable"
  271. v-for="playlist in playlists"
  272. :key="playlist._id"
  273. :playlist="playlist"
  274. >
  275. <i
  276. class="material-icons"
  277. slot="item-icon"
  278. v-if="
  279. isAllowedToParty() &&
  280. isSelected(playlist._id)
  281. "
  282. content="This playlist is currently selected"
  283. v-tippy
  284. >
  285. radio
  286. </i>
  287. <i
  288. class="material-icons"
  289. slot="item-icon"
  290. v-else-if="
  291. isOwnerOrAdmin() &&
  292. isPlaylistMode() &&
  293. isIncluded(playlist._id)
  294. "
  295. content="This playlist is currently included"
  296. v-tippy
  297. >
  298. play_arrow
  299. </i>
  300. <i
  301. class="material-icons excluded-icon"
  302. slot="item-icon"
  303. v-else-if="
  304. isOwnerOrAdmin() && isExcluded(playlist._id)
  305. "
  306. content="This playlist is currently excluded"
  307. v-tippy
  308. >
  309. block
  310. </i>
  311. <i
  312. class="material-icons"
  313. slot="item-icon"
  314. v-else
  315. :content="
  316. isPartyMode()
  317. ? 'This playlist is currently not selected or excluded'
  318. : 'This playlist is currently not included or excluded'
  319. "
  320. v-tippy
  321. >
  322. play_disabled
  323. </i>
  324. <div slot="actions">
  325. <!-- <i
  326. v-if="isExcluded(playlist._id)"
  327. class="material-icons stop-icon"
  328. content="This playlist is blacklisted in this station"
  329. v-tippy="{ theme: 'info' }"
  330. >play_disabled</i
  331. > -->
  332. <i
  333. v-if="
  334. isPartyMode() &&
  335. !isSelected(playlist._id)
  336. "
  337. @click="selectPartyPlaylist(playlist)"
  338. class="material-icons play-icon"
  339. content="Request songs from this playlist"
  340. v-tippy
  341. >play_arrow</i
  342. >
  343. <i
  344. v-if="
  345. isPlaylistMode() &&
  346. isOwnerOrAdmin() &&
  347. !isSelected(playlist._id)
  348. "
  349. @click="includePlaylist(playlist)"
  350. class="material-icons play-icon"
  351. content="Play songs from this playlist"
  352. v-tippy
  353. >play_arrow</i
  354. >
  355. <confirm
  356. v-if="
  357. isPartyMode() &&
  358. isSelected(playlist._id)
  359. "
  360. @confirm="
  361. deselectPartyPlaylist(playlist._id)
  362. "
  363. >
  364. <i
  365. class="material-icons stop-icon"
  366. content="Stop requesting songs from this playlist"
  367. v-tippy
  368. >stop</i
  369. >
  370. </confirm>
  371. <confirm
  372. v-if="
  373. isPlaylistMode() &&
  374. isOwnerOrAdmin() &&
  375. isIncluded(playlist._id)
  376. "
  377. @confirm="
  378. removeIncludedPlaylist(playlist._id)
  379. "
  380. >
  381. <i
  382. class="material-icons stop-icon"
  383. content="Stop playing songs from this playlist"
  384. v-tippy
  385. >stop</i
  386. >
  387. </confirm>
  388. <confirm
  389. v-if="
  390. isOwnerOrAdmin() &&
  391. !isExcluded(playlist._id)
  392. "
  393. @confirm="blacklistPlaylist(playlist._id)"
  394. >
  395. <i
  396. class="material-icons stop-icon"
  397. content="Blacklist Playlist"
  398. v-tippy
  399. >block</i
  400. >
  401. </confirm>
  402. <confirm
  403. v-if="
  404. isOwnerOrAdmin() &&
  405. isExcluded(playlist._id)
  406. "
  407. @confirm="
  408. removeExcludedPlaylist(playlist._id)
  409. "
  410. >
  411. <i
  412. class="material-icons stop-icon"
  413. content="Stop blacklisting songs from this playlist"
  414. v-tippy
  415. >
  416. stop
  417. </i>
  418. </confirm>
  419. <i
  420. @click="showPlaylist(playlist._id)"
  421. class="material-icons edit-icon"
  422. content="Edit Playlist"
  423. v-tippy
  424. >edit</i
  425. >
  426. </div>
  427. </playlist-item>
  428. </transition-group>
  429. </draggable>
  430. <p v-else class="has-text-centered scrollable-list">
  431. You don't have any playlists!
  432. </p>
  433. </div>
  434. <div class="tab" v-show="tab === 'party'" v-if="isPartyMode()">
  435. <div v-if="partyPlaylists.length > 0">
  436. <playlist-item
  437. v-for="playlist in partyPlaylists"
  438. :key="`key-${playlist._id}`"
  439. :playlist="playlist"
  440. :show-owner="true"
  441. >
  442. <i
  443. class="material-icons"
  444. slot="item-icon"
  445. content="This playlist is currently selected"
  446. v-tippy
  447. >
  448. radio
  449. </i>
  450. <div class="icons-group" slot="actions">
  451. <confirm
  452. v-if="isOwnerOrAdmin()"
  453. @confirm="deselectPartyPlaylist(playlist._id)"
  454. >
  455. <i
  456. class="material-icons stop-icon"
  457. content="Stop playing songs from this playlist"
  458. v-tippy
  459. >
  460. stop
  461. </i>
  462. </confirm>
  463. <confirm
  464. v-if="isOwnerOrAdmin()"
  465. @confirm="blacklistPlaylist(playlist._id)"
  466. >
  467. <i
  468. class="material-icons stop-icon"
  469. content="Blacklist Playlist"
  470. v-tippy
  471. >block</i
  472. >
  473. </confirm>
  474. <i
  475. v-if="playlist.createdBy === myUserId"
  476. @click="showPlaylist(playlist._id)"
  477. class="material-icons edit-icon"
  478. content="Edit Playlist"
  479. v-tippy
  480. >edit</i
  481. >
  482. <i
  483. v-if="
  484. playlist.createdBy !== myUserId &&
  485. (playlist.privacy === 'public' ||
  486. isAdmin())
  487. "
  488. @click="showPlaylist(playlist._id)"
  489. class="material-icons edit-icon"
  490. content="View Playlist"
  491. v-tippy
  492. >visibility</i
  493. >
  494. </div>
  495. </playlist-item>
  496. </div>
  497. <p v-else class="has-text-centered scrollable-list">
  498. No playlists currently being played.
  499. </p>
  500. </div>
  501. <div
  502. class="tab"
  503. v-show="tab === 'included'"
  504. v-if="isPlaylistMode()"
  505. >
  506. <div v-if="includedPlaylists.length > 0">
  507. <playlist-item
  508. v-for="playlist in includedPlaylists"
  509. :key="`key-${playlist._id}`"
  510. :playlist="playlist"
  511. :show-owner="true"
  512. >
  513. <i
  514. class="material-icons"
  515. slot="item-icon"
  516. content="This playlist is currently included"
  517. v-tippy
  518. >
  519. play_arrow
  520. </i>
  521. <div class="icons-group" slot="actions">
  522. <confirm
  523. v-if="isOwnerOrAdmin()"
  524. @confirm="removeIncludedPlaylist(playlist._id)"
  525. >
  526. <i
  527. class="material-icons stop-icon"
  528. content="Stop playing songs from this playlist"
  529. v-tippy
  530. >
  531. stop
  532. </i>
  533. </confirm>
  534. <confirm
  535. v-if="isOwnerOrAdmin()"
  536. @confirm="blacklistPlaylist(playlist._id)"
  537. >
  538. <i
  539. class="material-icons stop-icon"
  540. content="Blacklist Playlist"
  541. v-tippy
  542. >block</i
  543. >
  544. </confirm>
  545. <i
  546. v-if="playlist.createdBy === myUserId"
  547. @click="showPlaylist(playlist._id)"
  548. class="material-icons edit-icon"
  549. content="Edit Playlist"
  550. v-tippy
  551. >edit</i
  552. >
  553. <i
  554. v-if="
  555. playlist.createdBy !== myUserId &&
  556. (playlist.privacy === 'public' ||
  557. isAdmin())
  558. "
  559. @click="showPlaylist(playlist._id)"
  560. class="material-icons edit-icon"
  561. content="View Playlist"
  562. v-tippy
  563. >visibility</i
  564. >
  565. </div>
  566. </playlist-item>
  567. </div>
  568. <p v-else class="has-text-centered scrollable-list">
  569. No playlists currently included.
  570. </p>
  571. </div>
  572. <div
  573. class="tab"
  574. v-show="tab === 'excluded'"
  575. v-if="isOwnerOrAdmin()"
  576. >
  577. <div v-if="excludedPlaylists.length > 0">
  578. <playlist-item
  579. :playlist="playlist"
  580. v-for="playlist in excludedPlaylists"
  581. :key="`key-${playlist._id}`"
  582. >
  583. <i
  584. class="material-icons excluded-icon"
  585. slot="item-icon"
  586. content="This playlist is currently excluded"
  587. v-tippy
  588. >
  589. block
  590. </i>
  591. <div class="icons-group" slot="actions">
  592. <confirm
  593. @confirm="removeExcludedPlaylist(playlist._id)"
  594. >
  595. <i
  596. class="material-icons stop-icon"
  597. content="Stop blacklisting songs from this playlist
  598. "
  599. v-tippy
  600. >stop</i
  601. >
  602. </confirm>
  603. <i
  604. v-if="playlist.createdBy === userId"
  605. @click="showPlaylist(playlist._id)"
  606. class="material-icons edit-icon"
  607. content="Edit Playlist"
  608. v-tippy
  609. >edit</i
  610. >
  611. <i
  612. v-else
  613. @click="showPlaylist(playlist._id)"
  614. class="material-icons edit-icon"
  615. content="View Playlist"
  616. v-tippy
  617. >visibility</i
  618. >
  619. </div>
  620. </playlist-item>
  621. </div>
  622. <p v-else class="has-text-centered scrollable-list">
  623. No playlists currently excluded.
  624. </p>
  625. </div>
  626. </div>
  627. </div>
  628. </template>
  629. <script>
  630. import { mapActions, mapState, mapGetters } from "vuex";
  631. import Toast from "toasters";
  632. import PlaylistItem from "@/components/PlaylistItem.vue";
  633. import Confirm from "@/components/Confirm.vue";
  634. import SortablePlaylists from "@/mixins/SortablePlaylists.vue";
  635. export default {
  636. components: {
  637. PlaylistItem,
  638. Confirm
  639. },
  640. mixins: [SortablePlaylists],
  641. data() {
  642. return {
  643. tab: "included",
  644. search: {
  645. query: "",
  646. searchedQuery: "",
  647. page: 0,
  648. count: 0,
  649. resultsLeft: 0,
  650. results: []
  651. }
  652. };
  653. },
  654. computed: {
  655. resultsLeftCount() {
  656. return this.search.count - this.search.results.length;
  657. },
  658. nextPageResultsCount() {
  659. return Math.min(this.search.pageSize, this.resultsLeftCount);
  660. },
  661. ...mapState({
  662. loggedIn: state => state.user.auth.loggedIn,
  663. role: state => state.user.auth.role,
  664. userId: state => state.user.auth.userId,
  665. partyPlaylists: state => state.station.partyPlaylists
  666. }),
  667. ...mapState("modals/manageStation", {
  668. parentTab: state => state.tab,
  669. originalStation: state => state.originalStation,
  670. station: state => state.station,
  671. includedPlaylists: state => state.includedPlaylists,
  672. excludedPlaylists: state => state.excludedPlaylists,
  673. songsList: state => state.songsList
  674. }),
  675. ...mapGetters({
  676. socket: "websockets/getSocket"
  677. })
  678. },
  679. watch: {
  680. // eslint-disable-next-line func-names
  681. parentTab(value) {
  682. if (value === "playlists") {
  683. if (this.tab === "included" && this.isPartyMode()) {
  684. this.showTab("party");
  685. } else if (this.tab === "party" && this.isPlaylistMode()) {
  686. this.showTab("included");
  687. }
  688. }
  689. }
  690. },
  691. mounted() {
  692. if (this.station.type === "community" && this.station.partyMode)
  693. this.showTab("search");
  694. this.socket.dispatch("playlists.indexMyPlaylists", true, res => {
  695. if (res.status === "success") this.setPlaylists(res.data.playlists);
  696. this.orderOfPlaylists = this.calculatePlaylistOrder(); // order in regards to the database
  697. });
  698. this.socket.dispatch(
  699. `stations.getStationIncludedPlaylistsById`,
  700. this.station._id,
  701. res => {
  702. if (res.status === "success") {
  703. this.station.includedPlaylists = res.data.playlists;
  704. this.originalStation.includedPlaylists = res.data.playlists;
  705. }
  706. }
  707. );
  708. this.socket.dispatch(
  709. `stations.getStationExcludedPlaylistsById`,
  710. this.station._id,
  711. res => {
  712. if (res.status === "success") {
  713. this.station.excludedPlaylists = res.data.playlists;
  714. this.originalStation.excludedPlaylists = res.data.playlists;
  715. }
  716. }
  717. );
  718. },
  719. methods: {
  720. showTab(tab) {
  721. this.$refs[`${tab}-tab`].scrollIntoView();
  722. this.tab = tab;
  723. },
  724. isOwner() {
  725. return (
  726. this.loggedIn &&
  727. this.station &&
  728. this.userId === this.station.owner
  729. );
  730. },
  731. isAdmin() {
  732. return this.loggedIn && this.role === "admin";
  733. },
  734. isOwnerOrAdmin() {
  735. return this.isOwner() || this.isAdmin();
  736. },
  737. isPartyMode() {
  738. return (
  739. this.station &&
  740. this.station.type === "community" &&
  741. this.station.partyMode
  742. );
  743. },
  744. isAllowedToParty() {
  745. return (
  746. this.station &&
  747. this.isPartyMode() &&
  748. (!this.station.locked || this.isOwnerOrAdmin()) &&
  749. this.loggedIn
  750. );
  751. },
  752. isPlaylistMode() {
  753. return this.station && !this.isPartyMode();
  754. },
  755. showPlaylist(playlistId) {
  756. this.editPlaylist(playlistId);
  757. this.openModal("editPlaylist");
  758. },
  759. selectPartyPlaylist(playlist) {
  760. if (!this.isSelected(playlist.id)) {
  761. this.partyPlaylists.push(playlist);
  762. this.addPartyPlaylistSongToQueue();
  763. new Toast(
  764. "Successfully selected playlist to auto request songs."
  765. );
  766. } else {
  767. new Toast("Error: Playlist already selected.");
  768. }
  769. },
  770. includePlaylist(playlist) {
  771. this.socket.dispatch(
  772. "stations.includePlaylist",
  773. this.station._id,
  774. playlist._id,
  775. res => {
  776. new Toast(res.message);
  777. }
  778. );
  779. },
  780. deselectPartyPlaylist(id) {
  781. return new Promise(resolve => {
  782. let selected = false;
  783. this.partyPlaylists.forEach((playlist, index) => {
  784. if (playlist._id === id) {
  785. selected = true;
  786. this.partyPlaylists.splice(index, 1);
  787. }
  788. });
  789. if (selected) {
  790. new Toast("Successfully deselected playlist.");
  791. resolve();
  792. } else {
  793. new Toast("Playlist not selected.");
  794. resolve();
  795. }
  796. });
  797. },
  798. removeIncludedPlaylist(id) {
  799. return new Promise(resolve => {
  800. this.socket.dispatch(
  801. "stations.removeIncludedPlaylist",
  802. this.station._id,
  803. id,
  804. res => {
  805. new Toast(res.message);
  806. resolve();
  807. }
  808. );
  809. });
  810. },
  811. removeExcludedPlaylist(id) {
  812. return new Promise(resolve => {
  813. this.socket.dispatch(
  814. "stations.removeExcludedPlaylist",
  815. this.station._id,
  816. id,
  817. res => {
  818. new Toast(res.message);
  819. resolve();
  820. }
  821. );
  822. });
  823. },
  824. isSelected(id) {
  825. let selected = false;
  826. this.partyPlaylists.forEach(playlist => {
  827. if (playlist._id === id) selected = true;
  828. });
  829. return selected;
  830. },
  831. isIncluded(id) {
  832. let included = false;
  833. this.includedPlaylists.forEach(playlist => {
  834. if (playlist._id === id) included = true;
  835. });
  836. return included;
  837. },
  838. isExcluded(id) {
  839. let selected = false;
  840. this.excludedPlaylists.forEach(playlist => {
  841. if (playlist._id === id) selected = true;
  842. });
  843. return selected;
  844. },
  845. searchForPlaylists(page) {
  846. if (
  847. this.search.page >= page ||
  848. this.search.searchedQuery !== this.search.query
  849. ) {
  850. this.search.results = [];
  851. this.search.page = 0;
  852. this.search.count = 0;
  853. this.search.resultsLeft = 0;
  854. this.search.pageSize = 0;
  855. }
  856. const { query } = this.search;
  857. const action =
  858. this.station.type === "official"
  859. ? "playlists.searchOfficial"
  860. : "playlists.searchCommunity";
  861. this.search.searchedQuery = this.search.query;
  862. this.socket.dispatch(action, query, page, res => {
  863. const { data } = res;
  864. const { count, pageSize, playlists } = data;
  865. if (res.status === "success") {
  866. this.search.results = [
  867. ...this.search.results,
  868. ...playlists
  869. ];
  870. this.search.page = page;
  871. this.search.count = count;
  872. this.search.resultsLeft =
  873. count - this.search.results.length;
  874. this.search.pageSize = pageSize;
  875. } else if (res.status === "error") {
  876. this.search.results = [];
  877. this.search.page = 0;
  878. this.search.count = 0;
  879. this.search.resultsLeft = 0;
  880. this.search.pageSize = 0;
  881. new Toast(res.message);
  882. }
  883. });
  884. },
  885. async blacklistPlaylist(id) {
  886. if (this.isIncluded(id)) await this.removeIncludedPlaylist(id);
  887. this.socket.dispatch(
  888. "stations.excludePlaylist",
  889. this.station._id,
  890. id,
  891. res => {
  892. new Toast(res.message);
  893. }
  894. );
  895. },
  896. addPartyPlaylistSongToQueue() {
  897. let isInQueue = false;
  898. if (
  899. this.station.type === "community" &&
  900. this.station.partyMode === true
  901. ) {
  902. this.songsList.forEach(queueSong => {
  903. if (queueSong.requestedBy === this.userId) isInQueue = true;
  904. });
  905. if (!isInQueue && this.partyPlaylists) {
  906. const selectedPlaylist = this.partyPlaylists[
  907. Math.floor(Math.random() * this.partyPlaylists.length)
  908. ];
  909. if (
  910. selectedPlaylist._id &&
  911. selectedPlaylist.songs.length > 0
  912. ) {
  913. const selectedSong =
  914. selectedPlaylist.songs[
  915. Math.floor(
  916. Math.random() *
  917. selectedPlaylist.songs.length
  918. )
  919. ];
  920. if (selectedSong.youtubeId) {
  921. this.socket.dispatch(
  922. "stations.addToQueue",
  923. this.station._id,
  924. selectedSong.youtubeId,
  925. data => {
  926. if (data.status !== "success")
  927. new Toast("Error auto queueing song");
  928. }
  929. );
  930. }
  931. }
  932. }
  933. }
  934. },
  935. ...mapActions("station", ["updatePartyPlaylists"]),
  936. ...mapActions("modalVisibility", ["openModal"]),
  937. ...mapActions("user/playlists", ["editPlaylist", "setPlaylists"])
  938. }
  939. };
  940. </script>
  941. <style lang="scss" scoped>
  942. .excluded-icon {
  943. color: var(--red);
  944. }
  945. .included-icon {
  946. color: var(--green);
  947. }
  948. .selected-icon {
  949. color: var(--purple);
  950. }
  951. .station-playlists {
  952. .tabs-container {
  953. .tab-selection {
  954. display: flex;
  955. overflow-x: auto;
  956. .button {
  957. border-radius: 0;
  958. border: 0;
  959. text-transform: uppercase;
  960. font-size: 14px;
  961. color: var(--dark-grey-3);
  962. background-color: var(--light-grey-2);
  963. flex-grow: 1;
  964. height: 32px;
  965. &:not(:first-of-type) {
  966. margin-left: 5px;
  967. }
  968. }
  969. .selected {
  970. background-color: var(--primary-color) !important;
  971. color: var(--white) !important;
  972. font-weight: 600;
  973. }
  974. }
  975. .tab {
  976. padding: 15px 0;
  977. border-radius: 0;
  978. .playlist-item:not(:last-of-type),
  979. .item.item-draggable:not(:last-of-type) {
  980. margin-bottom: 10px;
  981. }
  982. .load-more-button {
  983. width: 100%;
  984. margin-top: 10px;
  985. }
  986. }
  987. }
  988. }
  989. .draggable-list-transition-move {
  990. transition: transform 0.5s;
  991. }
  992. .draggable-list-ghost {
  993. opacity: 0.5;
  994. filter: brightness(95%);
  995. }
  996. </style>