Search.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. <template>
  2. <div class="search">
  3. <div class="musare-search">
  4. <label class="label"> Search for a song on Musare </label>
  5. <div class="control is-grouped input-with-button">
  6. <p class="control is-expanded">
  7. <input
  8. class="input"
  9. type="text"
  10. placeholder="Enter your song query here..."
  11. v-model="musareSearch.query"
  12. @keyup.enter="searchForMusareSongs(1)"
  13. />
  14. </p>
  15. <p class="control">
  16. <a class="button is-info" @click="searchForMusareSongs(1)"
  17. ><i class="material-icons icon-with-button">search</i
  18. >Search</a
  19. >
  20. </p>
  21. </div>
  22. <div v-if="musareSearch.results.length > 0">
  23. <song-item
  24. v-for="song in musareSearch.results"
  25. :key="song._id"
  26. :song="song"
  27. >
  28. <div class="song-actions" slot="actions">
  29. <i
  30. class="material-icons add-to-queue-icon"
  31. v-if="station.partyMode && !station.locked"
  32. @click="addSongToQueue(song.youtubeId)"
  33. content="Add Song to Queue"
  34. v-tippy
  35. >queue</i
  36. >
  37. </div>
  38. </song-item>
  39. <button
  40. v-if="resultsLeftCount > 0"
  41. class="button is-primary load-more-button"
  42. @click="searchForMusareSongs(musareSearch.page + 1)"
  43. >
  44. Load {{ nextPageResultsCount }} more results
  45. </button>
  46. </div>
  47. </div>
  48. <div class="youtube-search">
  49. <label class="label"> Search for a song on YouTube </label>
  50. <div class="control is-grouped input-with-button">
  51. <p class="control is-expanded">
  52. <input
  53. class="input"
  54. type="text"
  55. placeholder="Enter your YouTube query here..."
  56. v-model="search.songs.query"
  57. autofocus
  58. @keyup.enter="searchForSongs()"
  59. />
  60. </p>
  61. <p class="control">
  62. <a class="button is-info" @click.prevent="searchForSongs()"
  63. ><i class="material-icons icon-with-button">search</i
  64. >Search</a
  65. >
  66. </p>
  67. </div>
  68. <div v-if="search.songs.results.length > 0" id="song-query-results">
  69. <search-query-item
  70. v-for="(result, index) in search.songs.results"
  71. :key="result.id"
  72. :result="result"
  73. >
  74. <div slot="actions">
  75. <transition name="search-query-actions" mode="out-in">
  76. <a
  77. class="button is-success"
  78. v-if="result.isAddedToQueue"
  79. key="added-to-queue"
  80. >
  81. <i class="material-icons icon-with-button"
  82. >done</i
  83. >
  84. Added to queue
  85. </a>
  86. <a
  87. class="button is-dark"
  88. v-else
  89. @click.prevent="
  90. addSongToQueue(result.id, index)
  91. "
  92. key="add-to-queue"
  93. >
  94. <i class="material-icons icon-with-button"
  95. >add</i
  96. >
  97. Add to queue
  98. </a>
  99. </transition>
  100. </div>
  101. </search-query-item>
  102. <a
  103. class="button is-primary load-more-button"
  104. @click.prevent="loadMoreSongs()"
  105. >
  106. Load more...
  107. </a>
  108. </div>
  109. </div>
  110. </div>
  111. </template>
  112. <script>
  113. import { mapState, mapGetters } from "vuex";
  114. import Toast from "toasters";
  115. import SearchYoutube from "@/mixins/SearchYoutube.vue";
  116. import SongItem from "@/components/SongItem.vue";
  117. import SearchQueryItem from "../../../SearchQueryItem.vue";
  118. export default {
  119. components: {
  120. SongItem,
  121. SearchQueryItem
  122. },
  123. mixins: [SearchYoutube],
  124. data() {
  125. return {
  126. musareSearch: {
  127. query: "",
  128. searchedQuery: "",
  129. page: 0,
  130. count: 0,
  131. resultsLeft: 0,
  132. results: []
  133. }
  134. };
  135. },
  136. computed: {
  137. resultsLeftCount() {
  138. return this.musareSearch.count - this.musareSearch.results.length;
  139. },
  140. nextPageResultsCount() {
  141. return Math.min(this.musareSearch.pageSize, this.resultsLeftCount);
  142. },
  143. ...mapState("modals/manageStation", {
  144. station: state => state.station,
  145. originalStation: state => state.originalStation
  146. }),
  147. ...mapGetters({
  148. socket: "websockets/getSocket"
  149. })
  150. },
  151. methods: {
  152. addSongToQueue(youtubeId, index) {
  153. if (this.station.type === "community") {
  154. this.socket.dispatch(
  155. "stations.addToQueue",
  156. this.station._id,
  157. youtubeId,
  158. res => {
  159. if (res.status !== "success")
  160. new Toast(`Error: ${res.message}`);
  161. else {
  162. if (index)
  163. this.search.songs.results[
  164. index
  165. ].isAddedToQueue = true;
  166. new Toast(res.message);
  167. }
  168. }
  169. );
  170. } else {
  171. this.socket.dispatch("songs.request", youtubeId, res => {
  172. if (res.status !== "success")
  173. new Toast(`Error: ${res.message}`);
  174. else {
  175. this.search.songs.results[index].isAddedToQueue = true;
  176. new Toast(res.message);
  177. }
  178. });
  179. }
  180. },
  181. searchForMusareSongs(page) {
  182. if (
  183. this.musareSearch.page >= page ||
  184. this.musareSearch.searchedQuery !== this.musareSearch.query
  185. ) {
  186. this.musareSearch.results = [];
  187. this.musareSearch.page = 0;
  188. this.musareSearch.count = 0;
  189. this.musareSearch.resultsLeft = 0;
  190. this.musareSearch.pageSize = 0;
  191. }
  192. this.musareSearch.searchedQuery = this.musareSearch.query;
  193. this.socket.dispatch(
  194. "songs.searchOfficial",
  195. this.musareSearch.query,
  196. page,
  197. res => {
  198. const { data } = res;
  199. const { count, pageSize, songs } = data;
  200. if (res.status === "success") {
  201. this.musareSearch.results = [
  202. ...this.musareSearch.results,
  203. ...songs
  204. ];
  205. this.musareSearch.page = page;
  206. this.musareSearch.count = count;
  207. this.musareSearch.resultsLeft =
  208. count - this.musareSearch.results.length;
  209. this.musareSearch.pageSize = pageSize;
  210. } else if (res.status === "error") {
  211. this.musareSearch.results = [];
  212. this.musareSearch.page = 0;
  213. this.musareSearch.count = 0;
  214. this.musareSearch.resultsLeft = 0;
  215. this.musareSearch.pageSize = 0;
  216. new Toast(res.message);
  217. }
  218. }
  219. );
  220. }
  221. }
  222. };
  223. </script>
  224. <style lang="scss">
  225. .search {
  226. .musare-search,
  227. .universal-item:not(:last-of-type) {
  228. margin-bottom: 10px;
  229. }
  230. .load-more-button {
  231. width: 100%;
  232. margin-top: 10px;
  233. }
  234. }
  235. </style>