useSortablePlaylists.ts 4.9 KB

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