diff --git a/src/components/RightSideBar/Search.vue b/src/components/RightSideBar/Search.vue index ead15982..57db2d2f 100644 --- a/src/components/RightSideBar/Search.vue +++ b/src/components/RightSideBar/Search.vue @@ -1,152 +1,48 @@ - diff --git a/src/components/Search/Filters.vue b/src/components/RightSideBar/Search/Filters.vue similarity index 100% rename from src/components/Search/Filters.vue rename to src/components/RightSideBar/Search/Filters.vue diff --git a/src/components/Search/LoadMore.vue b/src/components/RightSideBar/Search/LoadMore.vue similarity index 100% rename from src/components/Search/LoadMore.vue rename to src/components/RightSideBar/Search/LoadMore.vue diff --git a/src/components/Search/Options.vue b/src/components/RightSideBar/Search/Options.vue similarity index 100% rename from src/components/Search/Options.vue rename to src/components/RightSideBar/Search/Options.vue diff --git a/src/components/Search/TracksGrid.vue b/src/components/RightSideBar/Search/TracksGrid.vue similarity index 54% rename from src/components/Search/TracksGrid.vue rename to src/components/RightSideBar/Search/TracksGrid.vue index 56354a97..f6d6e47a 100644 --- a/src/components/Search/TracksGrid.vue +++ b/src/components/RightSideBar/Search/TracksGrid.vue @@ -1,9 +1,8 @@ diff --git a/src/components/Search/ArtistGrid.vue b/src/components/Search/ArtistGrid.vue deleted file mode 100644 index e13597f4..00000000 --- a/src/components/Search/ArtistGrid.vue +++ /dev/null @@ -1,52 +0,0 @@ - - - - - diff --git a/src/components/shared/ArtistCard.vue b/src/components/shared/ArtistCard.vue index fa108040..9a332d15 100644 --- a/src/components/shared/ArtistCard.vue +++ b/src/components/shared/ArtistCard.vue @@ -17,7 +17,7 @@ const imguri = paths.images.artist; defineProps<{ artist: any; - color: string; + color?: string; }>(); diff --git a/src/components/shared/TrackItem.vue b/src/components/shared/TrackItem.vue index 94581669..b999dd5f 100644 --- a/src/components/shared/TrackItem.vue +++ b/src/components/shared/TrackItem.vue @@ -97,7 +97,6 @@ const playThis = (track: Track) => { } .track-item { - width: 26.55rem; display: flex; align-items: center; border-radius: 0.5rem; diff --git a/src/composables/loadmore.js b/src/composables/loadmore.js index 314018bd..e69de29b 100644 --- a/src/composables/loadmore.js +++ b/src/composables/loadmore.js @@ -1,42 +0,0 @@ -import axios from "axios"; - -const url = "http://127.0.0.1:9876/search/loadmore"; - -async function loadMoreTracks(start) { - const response = await axios.get(url, { - params: { - type: "tracks", - start: start, - }, - }); - - return response.data; -} - -async function loadMoreAlbums(start) { - const response = await axios.get(url, { - params: { - type: "albums", - start: start, - }, - }); - - return response.data; -} - -async function loadMoreArtists(start) { - const response = await axios.get(url, { - params: { - type: "artists", - start: start, - }, - }); - - return response.data; -} - -export default { - loadMoreTracks, - loadMoreAlbums, - loadMoreArtists, -}; diff --git a/src/composables/searchMusic.js b/src/composables/searchMusic.js deleted file mode 100644 index c696bd2f..00000000 --- a/src/composables/searchMusic.js +++ /dev/null @@ -1,27 +0,0 @@ -import state from "./state"; - -const base_url = `${state.settings.uri}/search?q=`; - -async function search(query) { - state.loading.value = true; - const url = base_url + encodeURIComponent(query.trim()); - - const res = await fetch(url); - - if (!res.ok) { - const message = `An error has occured: ${res.status}`; - throw new Error(message); - } - - const data = await res.json(); - - state.loading.value = false; - - return { - tracks: data.data[0], - albums: data.data[1], - artists: data.data[2], - }; -} - -export default search; diff --git a/src/composables/searchMusic.ts b/src/composables/searchMusic.ts new file mode 100644 index 00000000..d18d784b --- /dev/null +++ b/src/composables/searchMusic.ts @@ -0,0 +1,106 @@ +import state from "./state"; +import axios from "axios"; + +const base_url = `${state.settings.uri}/search`; + +const uris = { + tracks: `${base_url}/tracks?q=`, + albums: `${base_url}/albums?q=`, + artists: `${base_url}/artists?q=`, +}; + +async function search(query: string) { + state.loading.value = true; + + const url = base_url + encodeURIComponent(query.trim()); + + const res = await fetch(url); + + if (!res.ok) { + const message = `An error has occured: ${res.status}`; + throw new Error(message); + } + + const data = await res.json(); + + state.loading.value = false; + + return { + tracks: data.data[0], + albums: data.data[1], + artists: data.data[2], + }; +} + +async function searchTracks(query: string) { + const url = uris.tracks + encodeURIComponent(query.trim()); + + const res = await fetch(url); + + if (!res.ok) { + const message = `An error has occured: ${res.status}`; + throw new Error(message); + } + + const data = await res.json(); + + return data; +} + +async function searchAlbums(query: string) { + const url = uris.albums + encodeURIComponent(query.trim()); + + const res = await axios.get(url); + return res.data; +} + +async function searchArtists(query: string) { + const url = uris.artists + encodeURIComponent(query.trim()); + + const res = await axios.get(url); + return res.data; +} + +const url = state.settings.uri + "/search/loadmore"; + +async function loadMoreTracks(index: number) { + const response = await axios.get(url, { + params: { + type: "tracks", + index: index, + }, + }); + + return response.data; +} + +async function loadMoreAlbums(index: number) { + const response = await axios.get(url, { + params: { + type: "albums", + index: index, + }, + }); + + return response.data; +} + +async function loadMoreArtists(index: number) { + const response = await axios.get(url, { + params: { + type: "artists", + index: index, + }, + }); + + return response.data; +} + +export { + searchTracks, + searchAlbums, + searchArtists, + loadMoreTracks, + loadMoreAlbums, + loadMoreArtists, +}; diff --git a/src/composables/useDebouncedRef.js b/src/composables/useDebouncedRef.js index 8a8de05f..b31a62a3 100644 --- a/src/composables/useDebouncedRef.js +++ b/src/composables/useDebouncedRef.js @@ -1,33 +1,49 @@ -import {customRef, ref} from 'vue' +import { customRef, ref } from "vue"; +/** + * Debounces a function + * + * @param {*} fn The function to debounce + * @param {*} delay The delay in milliseconds + * @param {*} immediate whether to debounce immediately + * @returns {Function} The debounced function + */ const debounce = (fn, delay = 0, immediate = false) => { - let timeout + let timeout; return (...args) => { - if (immediate && !timeout) fn(...args) - clearTimeout(timeout) + if (immediate && !timeout) fn(...args); + clearTimeout(timeout); timeout = setTimeout(() => { - fn(...args) - }, delay) - } -} + fn(...args); + }, delay); + }; +}; -const useDebouncedRef = (initialValue, delay, immediate) => { - const state = ref(initialValue) +/** + * Emits the ref updated value after the given delay. + * + * @param {*} initialValue The default value of the ref + * @param {*} delay The delay in milliseconds + * @param {*} immediate Whether to call the function immediately + * @returns {Object} The ref and a function to call to update the ref + */ +const useDebouncedRef = (initialValue, delay, immediate = false) => { + const state = ref(initialValue); return customRef((track, trigger) => ({ get() { - track() - return state.value + track(); + return state.value; }, set: debounce( - value => { - state.value = value - trigger() - }, - delay, - immediate + (value) => { + state.value = value; + trigger(); + }, + delay, + immediate ), - })) -} + })); +}; -export default useDebouncedRef +export default useDebouncedRef; diff --git a/src/stores/queue.ts b/src/stores/queue.ts index c02ba872..fda6d73a 100644 --- a/src/stores/queue.ts +++ b/src/stores/queue.ts @@ -12,7 +12,8 @@ import { import notif from "../composables/mediaNotification"; import { FromOptions } from "../composables/enums"; -function addQToLocalStorage( + +function writeQueue( from: fromFolder | fromAlbum | fromPlaylist, tracks: Track[] ) { @@ -25,11 +26,11 @@ function addQToLocalStorage( ); } -function addCurrentToLocalStorage(track: Track) { +function writeCurrent(track: Track) { localStorage.setItem("current", JSON.stringify(track)); } -function readCurrentFromLocalStorage(): Track { +function readCurrent(): Track { const current = localStorage.getItem("current"); if (current) { return JSON.parse(current); @@ -114,7 +115,7 @@ export default defineStore("Queue", { } } }, - readQueueFromLocalStorage() { + readQueue() { const queue = localStorage.getItem("queue"); if (queue) { @@ -123,7 +124,7 @@ export default defineStore("Queue", { this.tracks = parsed.tracks; } - this.updateCurrent(readCurrentFromLocalStorage()); + this.updateCurrent(readCurrent()); }, updateCurrent(track: Track) { this.current = track; @@ -131,7 +132,7 @@ export default defineStore("Queue", { this.updateNext(this.current); this.updatePrev(this.current); - addCurrentToLocalStorage(track); + writeCurrent(track); }, updateNext(track: Track) { const index = this.tracks.findIndex( @@ -161,8 +162,9 @@ export default defineStore("Queue", { }, setNewQueue(tracklist: Track[]) { if (this.tracks !== tracklist) { - this.tracks = tracklist; - addQToLocalStorage(this.from, this.tracks); + this.tracks = []; + this.tracks.push(...tracklist); + writeQueue(this.from, this.tracks); } }, playFromFolder(fpath: string, tracks: Track[]) { @@ -201,7 +203,8 @@ export default defineStore("Queue", { }, addTrackToQueue(track: Track) { this.tracks.push(track); - addQToLocalStorage(this.from, this.tracks); + writeQueue(this.from, this.tracks); + this.updateNext(this.current); }, playTrackNext(track: Track) { const Toast = useNotifStore(); @@ -209,19 +212,24 @@ export default defineStore("Queue", { (t: Track) => t.trackid === this.current.trackid ); - const next: Track = this.tracks[currentid + 1]; + if (currentid == this.tracks.length - 1) { + this.tracks.push(track); + } else { + const next: Track = this.tracks[currentid + 1]; - if (next.trackid === track.trackid) { - Toast.showNotification("Track is already queued", NotifType.Info); - return; + if (next.trackid === track.trackid) { + Toast.showNotification("Track is already queued", NotifType.Info); + return; + } } this.tracks.splice(currentid + 1, 0, track); + this.updateNext(this.current); Toast.showNotification( `Added ${track.title} to queue`, NotifType.Success ); - addQToLocalStorage(this.from, this.tracks); + writeQueue(this.from, this.tracks); }, }, }); diff --git a/src/stores/search.ts b/src/stores/search.ts new file mode 100644 index 00000000..1b8a7292 --- /dev/null +++ b/src/stores/search.ts @@ -0,0 +1,134 @@ +import { ref, reactive } from "@vue/reactivity"; +import { defineStore } from "pinia"; +import { AlbumInfo, Artist, Track } from "../interfaces"; +import { + searchTracks, + searchAlbums, + searchArtists, + loadMoreTracks, + loadMoreAlbums, + loadMoreArtists, +} from "../composables/searchMusic"; +import { watch } from "vue"; +import useDebouncedRef from "../composables/useDebouncedRef"; + +/** + * + * @param id The id of the element of the div to scroll + * Scrolls on clicking the loadmore button + */ +function scrollOnLoad(id: string) { + const elem = document.getElementById(id); + + elem.scroll({ + top: elem.scrollHeight, + left: 0, + behavior: "smooth", + }); +} + +export default defineStore("search", () => { + const query = useDebouncedRef("", 600); + + const tracks = reactive({ + value: [], + more: false, + }); + + const albums = reactive({ + value: [], + more: false, + }); + + const artists = reactive({ + value: [], + more: false, + }); + + /** + * Searches for tracks, albums and artists + * @param query query to search for + */ + function search(query: string) { + searchTracks(query).then((res) => { + tracks.value = res.tracks; + tracks.more = res.more; + }); + + searchAlbums(query).then((res) => { + albums.value = res.albums; + albums.more = res.more; + }); + + searchArtists(query).then((res) => { + artists.value = res.artists; + artists.more = res.more; + }); + } + + /** + * Loads more search tracks results + * + * @param index The starting index of the tracks to load + */ + function loadTracks(index: number) { + loadMoreTracks(index) + .then((res) => { + tracks.value = [...tracks.value, ...res.tracks]; + tracks.more = res.more; + }) + .then(() => { + scrollOnLoad("tab-content"); + }); + } + + /** + * Loads more search albums results + * + * @param index The starting index of the albums to load + */ + function loadAlbums(index: number) { + loadMoreAlbums(index) + .then((res) => { + albums.value = [...albums.value, ...res.albums]; + albums.more = res.more; + }) + .then(() => { + scrollOnLoad("tab-content"); + }); + } + + /** + * Loads more search artists results + * + * @param index The starting index of the artists to load + */ + function loadArtists(index: number) { + loadMoreArtists(index) + .then((res) => { + artists.value = [...artists.value, ...res.artists]; + artists.more = res.more; + }) + .then(() => { + scrollOnLoad("tab-content"); + }); + } + + watch( + () => query.value, + (newQuery) => { + search(newQuery); + } + ); + + return { + tracks, + albums, + artists, + query, + search, + loadTracks, + loadAlbums, + loadArtists, + }; +});