useSortablePlaylists.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import { ref, computed, onMounted, onBeforeUnmount, nextTick } from "vue";
  2. import { useStore } from "vuex";
  3. import { Sortable } from "sortablejs-vue3";
  4. import Toast from "toasters";
  5. import ws from "@/ws";
  6. export default function useSortablePlaylists() {
  7. const orderOfPlaylists = ref([]);
  8. const drag = ref(false);
  9. const userId = ref();
  10. const store = useStore();
  11. const playlists = computed({
  12. get: () => store.state.user.playlists.playlists,
  13. set: playlists => {
  14. store.commit("user/playlists/updatePlaylists", playlists);
  15. }
  16. });
  17. const myUserId = computed(() => store.state.user.auth.userId);
  18. const isCurrentUser = computed(() => userId.value === myUserId.value);
  19. const dragOptions = computed(() => ({
  20. animation: 200,
  21. group: "playlists",
  22. disabled: !isCurrentUser.value,
  23. ghostClass: "draggable-list-ghost"
  24. }));
  25. const { socket } = store.state.websockets;
  26. const setPlaylists = playlists =>
  27. store.dispatch("user/playlists/setPlaylists", playlists);
  28. const addPlaylist = playlist =>
  29. store.dispatch("user/playlists/addPlaylist", playlist);
  30. const removePlaylist = playlist =>
  31. store.dispatch("user/playlists/removePlaylist", playlist);
  32. const calculatePlaylistOrder = () => {
  33. const calculatedOrder = [];
  34. playlists.value.forEach(playlist => calculatedOrder.push(playlist._id));
  35. return calculatedOrder;
  36. };
  37. const savePlaylistOrder = ({ oldIndex, newIndex }) => {
  38. if (oldIndex === newIndex) return;
  39. const oldPlaylists = playlists.value;
  40. oldPlaylists.splice(newIndex, 0, oldPlaylists.splice(oldIndex, 1)[0]);
  41. setPlaylists(oldPlaylists).then(() => {
  42. const recalculatedOrder = calculatePlaylistOrder();
  43. socket.dispatch(
  44. "users.updateOrderOfPlaylists",
  45. recalculatedOrder,
  46. res => {
  47. if (res.status === "error") return new Toast(res.message);
  48. orderOfPlaylists.value = calculatePlaylistOrder(); // new order in regards to the database
  49. return new Toast(res.message);
  50. }
  51. );
  52. });
  53. };
  54. onMounted(async () => {
  55. await nextTick();
  56. if (!userId.value) userId.value = myUserId.value;
  57. ws.onConnect(() => {
  58. if (!isCurrentUser.value)
  59. socket.dispatch(
  60. "apis.joinRoom",
  61. `profile.${userId.value}.playlists`,
  62. () => {}
  63. );
  64. socket.dispatch("playlists.indexForUser", userId.value, res => {
  65. if (res.status === "success") setPlaylists(res.data.playlists);
  66. orderOfPlaylists.value = calculatePlaylistOrder(); // order in regards to the database
  67. });
  68. });
  69. socket.on(
  70. "event:playlist.created",
  71. res => addPlaylist(res.data.playlist),
  72. { replaceable: true }
  73. );
  74. socket.on(
  75. "event:playlist.deleted",
  76. res => removePlaylist(res.data.playlistId),
  77. { replaceable: true }
  78. );
  79. socket.on(
  80. "event:playlist.song.added",
  81. res => {
  82. playlists.value.forEach((playlist, index) => {
  83. if (playlist._id === res.data.playlistId) {
  84. playlists.value[index].songs.push(res.data.song);
  85. }
  86. });
  87. },
  88. { replaceable: true }
  89. );
  90. socket.on(
  91. "event:playlist.song.removed",
  92. res => {
  93. playlists.value.forEach((playlist, playlistIndex) => {
  94. if (playlist._id === res.data.playlistId) {
  95. playlists.value[playlistIndex].songs.forEach(
  96. (song, songIndex) => {
  97. if (song.youtubeId === res.data.youtubeId) {
  98. playlists.value[playlistIndex].songs.splice(
  99. songIndex,
  100. 1
  101. );
  102. }
  103. }
  104. );
  105. }
  106. });
  107. },
  108. { replaceable: true }
  109. );
  110. socket.on(
  111. "event:playlist.displayName.updated",
  112. res => {
  113. playlists.value.forEach((playlist, index) => {
  114. if (playlist._id === res.data.playlistId) {
  115. playlists.value[index].displayName =
  116. res.data.displayName;
  117. }
  118. });
  119. },
  120. { replaceable: true }
  121. );
  122. socket.on(
  123. "event:playlist.privacy.updated",
  124. res => {
  125. playlists.value.forEach((playlist, index) => {
  126. if (playlist._id === res.data.playlist._id) {
  127. playlists.value[index].privacy =
  128. res.data.playlist.privacy;
  129. }
  130. });
  131. },
  132. { replaceable: true }
  133. );
  134. socket.on(
  135. "event:user.orderOfPlaylists.updated",
  136. res => {
  137. const order = res.data.order.filter(playlistId =>
  138. playlists.value.find(
  139. playlist =>
  140. playlist._id === playlistId &&
  141. (isCurrentUser.value ||
  142. playlist.privacy === "public")
  143. )
  144. );
  145. const sortedPlaylists = [];
  146. playlists.value.forEach(playlist => {
  147. const playlistOrder = order.indexOf(playlist._id);
  148. if (playlistOrder >= 0)
  149. sortedPlaylists[playlistOrder] = playlist;
  150. });
  151. playlists.value = sortedPlaylists;
  152. orderOfPlaylists.value = calculatePlaylistOrder();
  153. },
  154. { replaceable: true }
  155. );
  156. });
  157. onBeforeUnmount(() => {
  158. if (!isCurrentUser.value)
  159. socket.dispatch(
  160. "apis.leaveRoom",
  161. `profile.${userId.value}.playlists`,
  162. () => {}
  163. );
  164. });
  165. return {
  166. Sortable,
  167. drag,
  168. userId,
  169. isCurrentUser,
  170. playlists,
  171. dragOptions,
  172. orderOfPlaylists,
  173. myUserId,
  174. savePlaylistOrder,
  175. calculatePlaylistOrder
  176. };
  177. }