diff --git a/package.json b/package.json index 84a06a4..9917e19 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,9 @@ "dependencies": { "@popperjs/core": "^2.11.6", "@vueuse/core": "^8.5.0", + "@vueuse/integrations": "^9.2.0", "axios": "^0.26.1", + "fuse.js": "^6.6.2", "pinia": "^2.0.17", "pinia-plugin-persistedstate": "^2.1.1", "sass": "^1.49.0", diff --git a/src/App.vue b/src/App.vue index df985f9..9fa6563 100644 --- a/src/App.vue +++ b/src/App.vue @@ -64,7 +64,12 @@ router.afterEach(() => { (document.getElementById("acontent") as HTMLElement).scrollTo(0, 0); }); -onStartTyping(() => { +onStartTyping((e) => { + // if control is pressed return + if (e.ctrlKey) { + console.log("ctrl pressed"); + }; + const elem = document.getElementById("globalsearch") as HTMLInputElement; elem.focus(); elem.value = ""; diff --git a/src/assets/scss/Global/basic.scss b/src/assets/scss/Global/basic.scss index 7e8a14a..f0fdfda 100644 --- a/src/assets/scss/Global/basic.scss +++ b/src/assets/scss/Global/basic.scss @@ -16,6 +16,8 @@ overflow: hidden; text-overflow: ellipsis; word-break: break-all; + width: fit-content; + max-width: 100%; } .rounded { diff --git a/src/assets/scss/Global/controls.scss b/src/assets/scss/Global/controls.scss index 2cfc74d..e69de29 100644 --- a/src/assets/scss/Global/controls.scss +++ b/src/assets/scss/Global/controls.scss @@ -1,57 +0,0 @@ -.controls { - display: grid; - grid-template-columns: repeat(3, 1fr); - - .shuffle { - width: 100%; - display: flex; - align-items: center; - - & * { - height: 2rem; - width: 2rem; - background-size: 70%; - cursor: pointer; - border-radius: 0.5rem; - - &:hover { - background-color: rgb(170, 50, 50); - } - } - - & :first-child { - background-image: url(../../assets/icons/repeat.svg); - } - - & :last-child { - background-image: url(../../assets/icons/shuffle.svg); - } - } - - .fav { - width: 100%; - display: flex; - align-items: center; - justify-content: flex-end; - - & * { - height: 2rem; - width: 2rem; - background-size: 70%; - border-radius: 0.5rem; - cursor: pointer; - - &:hover { - background-color: rgb(170, 50, 50); - } - } - - & :first-child { - background-image: url(../../assets/icons/plus.svg); - } - - & :last-child { - background-image: url(../../assets/icons/heart.svg); - } - } -} diff --git a/src/assets/scss/Global/index.scss b/src/assets/scss/Global/index.scss index 8103eb6..cc0b995 100644 --- a/src/assets/scss/Global/index.scss +++ b/src/assets/scss/Global/index.scss @@ -11,6 +11,15 @@ box-sizing: border-box; } +html { + cursor: default !important; +} + +html.loading, +html.loading * { + cursor: progress !important; +} + body { background-color: $body; color: $white; @@ -27,4 +36,8 @@ body { width: 100%; margin: 0 auto; } + + a { + cursor: default !important; + } } diff --git a/src/assets/scss/Global/search-tabheaders.scss b/src/assets/scss/Global/search-tabheaders.scss index 7efbc3d..7ee69fe 100644 --- a/src/assets/scss/Global/search-tabheaders.scss +++ b/src/assets/scss/Global/search-tabheaders.scss @@ -17,7 +17,6 @@ justify-content: center; user-select: none; - cursor: pointer; transition: all 0.3s ease; padding: 0 $small; diff --git a/src/components/AlbumView/Header.vue b/src/components/AlbumView/Header.vue index 31d1cf4..57667e5 100644 --- a/src/components/AlbumView/Header.vue +++ b/src/components/AlbumView/Header.vue @@ -3,12 +3,17 @@ class="a-header rounded" ref="albumheaderthing" :style="{ - backgroundImage: `linear-gradient( + backgroundImage: album.colors + ? `linear-gradient( 37deg, ${album.colors[0]}, ${album.colors[3]} - )`, + )` + : '', }" > -
+
- {{ album.artist }} • {{ album.date }} • {{ album.count }} Tracks • - {{ formatSeconds(album.duration, true) }} +
+ {{ album.artist }} • {{ album.date }} • {{ album.count }} + {{ album.count === 1 ? "Track" : "Tracks" }} • + {{ formatSeconds(album.duration, true) }} +
(); const emit = defineEmits<{ @@ -136,12 +143,14 @@ useVisibility(albumheaderthing, handleVisibilityState); .top { .h { + font-size: 14px; opacity: 0.5; } .title { font-size: 2.5rem; font-weight: 600; width: fit-content; + cursor: text; } .artist { @@ -157,15 +166,19 @@ useVisibility(albumheaderthing, handleVisibilityState); margin-top: $smaller; .stats { - border-radius: $small; font-weight: bold; font-size: 0.8rem; margin-bottom: 0.75rem; + cursor: text; + + + div { + width: fit-content; + } } } } - // grid-template-columns: 1fr !important; @include for-desktop-down { .art > img { height: 6rem; diff --git a/src/components/AlbumsExplorer/AlbumList.vue b/src/components/AlbumsExplorer/AlbumList.vue index aaca4eb..0fb50b9 100644 --- a/src/components/AlbumsExplorer/AlbumList.vue +++ b/src/components/AlbumsExplorer/AlbumList.vue @@ -135,7 +135,7 @@ export default { padding: $small 0.95rem $small 0.95rem; margin: $smaller; transition: all 0.2s ease-in-out; - cursor: pointer; + cursor: default; float: left; .play { @@ -146,7 +146,7 @@ export default { width: 3rem; background: url(../../assets/icons/play.svg) no-repeat center; background-size: 60%; - cursor: pointer; + cursor: default; opacity: 0; transition: all 0.5s ease-in-out; } @@ -208,7 +208,7 @@ export default { input::-webkit-search-cancel-button { position: relative; right: 20px; - cursor: pointer; + cursor: default; width: 50px; height: 50px; } diff --git a/src/components/AlbumsExplorer/TopAlbums.vue b/src/components/AlbumsExplorer/TopAlbums.vue index 9f6b567..7d2f4fc 100644 --- a/src/components/AlbumsExplorer/TopAlbums.vue +++ b/src/components/AlbumsExplorer/TopAlbums.vue @@ -74,7 +74,7 @@ export default { align-items: center; grid-template-columns: 7.5rem 1fr; padding: $small; - cursor: pointer; + cursor: default; transition: all 0.2s ease-in-out; &:hover { diff --git a/src/components/ArtistsExplorer/ArtistsList.vue b/src/components/ArtistsExplorer/ArtistsList.vue index 2d97cd8..0cb736f 100644 --- a/src/components/ArtistsExplorer/ArtistsList.vue +++ b/src/components/ArtistsExplorer/ArtistsList.vue @@ -68,7 +68,7 @@ export default { padding: $small 0.95rem $small 0.95rem; margin: $smaller; transition: all 0.2s ease-in-out; - cursor: pointer; + cursor: default; float: left; &:hover { @@ -116,7 +116,7 @@ export default { input::-webkit-search-cancel-button { position: relative; right: 20px; - cursor: pointer; + cursor: default; width: 50px; height: 50px; } diff --git a/src/components/ArtistsExplorer/TopArtists.vue b/src/components/ArtistsExplorer/TopArtists.vue index 103cb31..08b4c61 100644 --- a/src/components/ArtistsExplorer/TopArtists.vue +++ b/src/components/ArtistsExplorer/TopArtists.vue @@ -70,7 +70,7 @@ export default { align-items: center; grid-template-columns: 7.5rem 1fr; padding: $small; - cursor: pointer; + cursor: default; transition: all 0.2s ease-in-out; &:hover { diff --git a/src/components/Contextmenu/ContextItem.vue b/src/components/Contextmenu/ContextItem.vue index bdec8e6..a2b40b2 100644 --- a/src/components/Contextmenu/ContextItem.vue +++ b/src/components/Contextmenu/ContextItem.vue @@ -32,7 +32,6 @@ defineProps<{ width: 100%; display: flex; align-items: center; - cursor: default; padding: 0.4rem 1rem; position: relative; diff --git a/src/components/FolderView/SongList.vue b/src/components/FolderView/SongList.vue index 7e4128d..c40b5aa 100644 --- a/src/components/FolderView/SongList.vue +++ b/src/components/FolderView/SongList.vue @@ -16,11 +16,17 @@
@@ -37,7 +43,7 @@ diff --git a/src/components/LeftSidebar/NP/HotKeys.vue b/src/components/LeftSidebar/NP/HotKeys.vue index 0878a8a..72efce7 100644 --- a/src/components/LeftSidebar/NP/HotKeys.vue +++ b/src/components/LeftSidebar/NP/HotKeys.vue @@ -34,7 +34,6 @@ const q = useQStore(); height: 2.5rem; width: 100%; background-size: 1.5rem !important; - cursor: pointer; &:hover { background-color: $darkestblue; diff --git a/src/components/LeftSidebar/NP/SongCard.vue b/src/components/LeftSidebar/NP/SongCard.vue index f9c9dcf..c40f267 100644 --- a/src/components/LeftSidebar/NP/SongCard.vue +++ b/src/components/LeftSidebar/NP/SongCard.vue @@ -25,34 +25,26 @@
-
{{ props.track?.title }}
-
- {{ - artist - }} -
-
- {{ track.albumartist }} -
-
- Meh -
+
{{ props.track?.title }}
+
diff --git a/src/components/RightSideBar/Home/Recommendation.vue b/src/components/RightSideBar/Home/Recommendation.vue index cefc001..fedad7a 100644 --- a/src/components/RightSideBar/Home/Recommendation.vue +++ b/src/components/RightSideBar/Home/Recommendation.vue @@ -60,7 +60,6 @@ const songs = [ &:hover { background-color: #3a39393d; - cursor: pointer; } } } diff --git a/src/components/RightSideBar/Queue.vue b/src/components/RightSideBar/Queue.vue index 9d1234a..65101dd 100644 --- a/src/components/RightSideBar/Queue.vue +++ b/src/components/RightSideBar/Queue.vue @@ -10,10 +10,11 @@ v-for="(t, index) in queue.tracklist" :key="index" :track="t" - :index="index + 1" + :index="index" :isPlaying="queue.playing" :isHighlighted="false" :isCurrent="index === queue.currentindex" + :isQueueTrack="true" @PlayThis="playFromQueue(index)" />
diff --git a/src/components/RightSideBar/Queue/playingFrom.vue b/src/components/RightSideBar/Queue/playingFrom.vue deleted file mode 100644 index 34ce438..0000000 --- a/src/components/RightSideBar/Queue/playingFrom.vue +++ /dev/null @@ -1,150 +0,0 @@ - - - - - diff --git a/src/components/RightSideBar/Queue/upNext.vue b/src/components/RightSideBar/Queue/upNext.vue index d89d69f..4cc01ca 100644 --- a/src/components/RightSideBar/Queue/upNext.vue +++ b/src/components/RightSideBar/Queue/upNext.vue @@ -50,7 +50,6 @@ function showMenu(e: Event) { gap: 1rem; padding: 1rem; width: 100%; - cursor: pointer; &:hover { background-color: $gray4; diff --git a/src/components/RightSideBar/Search/TracksGrid.vue b/src/components/RightSideBar/Search/TracksGrid.vue index fc7cfad..388a4ee 100644 --- a/src/components/RightSideBar/Search/TracksGrid.vue +++ b/src/components/RightSideBar/Search/TracksGrid.vue @@ -8,7 +8,8 @@ :isHighlighted="false" :isPlaying="queue.playing" :track="track" - @PlayThis="updateQueue(index)" + @playThis="updateQueue(index)" + :index="index + 1" />
🤷
@@ -17,12 +18,13 @@ + + diff --git a/src/components/shared/ArtistCard.vue b/src/components/shared/ArtistCard.vue index c0671be..68b4d0a 100644 --- a/src/components/shared/ArtistCard.vue +++ b/src/components/shared/ArtistCard.vue @@ -32,7 +32,6 @@ defineProps<{ border-radius: 0.75rem; display: grid; justify-content: center; - cursor: pointer; .artist-image { width: 100%; diff --git a/src/components/shared/ArtistName.vue b/src/components/shared/ArtistName.vue index 9911128..4c3796d 100644 --- a/src/components/shared/ArtistName.vue +++ b/src/components/shared/ArtistName.vue @@ -1,13 +1,13 @@ diff --git a/src/components/shared/Input.vue b/src/components/shared/Input.vue new file mode 100644 index 0000000..2e68c1b --- /dev/null +++ b/src/components/shared/Input.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/src/components/shared/SongItem.vue b/src/components/shared/SongItem.vue index 463a85e..fa7eeb1 100644 --- a/src/components/shared/SongItem.vue +++ b/src/components/shared/SongItem.vue @@ -5,7 +5,9 @@ @dblclick="emitUpdate(track)" @contextmenu.prevent="showMenu" > -
{{ index }}
+
+ {{ index }} +
@@ -79,17 +82,17 @@ const artisttitle = ref(null); const props = defineProps<{ track: Track; - index?: number; + index: number; isPlaying: Boolean; isCurrent: Boolean; }>(); const emit = defineEmits<{ - (e: "updateQueue"): void; + (e: "playThis"): void; }>(); function emitUpdate(track: Track) { - emit("updateQueue"); + emit("playThis"); } function showMenu(e: Event) { @@ -118,7 +121,7 @@ function showMenu(e: Event) { .song-album { max-width: max-content; - cursor: pointer; + cursor: pointer !important; &:hover { text-decoration: underline; @@ -128,10 +131,6 @@ function showMenu(e: Event) { .song-artists { width: fit-content; max-width: 100%; - - .artist { - cursor: pointer; - } } .index { @@ -158,7 +157,6 @@ function showMenu(e: Event) { svg { transition: all 0.2s ease-in; - // transform: rotate(90deg); stroke: $track-btn-svg; circle { @@ -196,10 +194,6 @@ function showMenu(e: Event) { left: $small; top: $small; } - - .title { - cursor: pointer; - } } td { diff --git a/src/components/shared/TrackItem.vue b/src/components/shared/TrackItem.vue index 20230d6..d94982a 100644 --- a/src/components/shared/TrackItem.vue +++ b/src/components/shared/TrackItem.vue @@ -24,14 +24,7 @@

-
- {{ - artist - }} -
-
- {{ track.albumartist }} -
+
import { ref } from "vue"; -import { paths } from "@/config"; -import { putCommas } from "@/utils"; -import { Track } from "@/interfaces"; +import DelSvg from "@/assets/icons/delete.svg"; import { showTrackContextMenu as showContext } from "@/composables/context"; -import DelSvg from "@/assets/icons/plus.svg"; +import { paths } from "@/config"; +import { Track } from "@/interfaces"; import useQueueStore from "@/stores/queue"; +import ArtistName from "./ArtistName.vue"; const props = defineProps<{ track: Track; @@ -70,11 +63,11 @@ function showMenu(e: Event) { } const emit = defineEmits<{ - (e: "PlayThis"): void; + (e: "playThis"): void; }>(); const playThis = (track: Track) => { - emit("PlayThis"); + emit("playThis"); }; @@ -103,7 +96,7 @@ const playThis = (track: Track) => { .remove-track { opacity: 0; transition: all 0.25s ease; - transform: translateX(1rem) rotate(45deg); + transform: scale(0.75) translateY(1rem); &:hover { opacity: 1 !important; @@ -113,10 +106,9 @@ const playThis = (track: Track) => { &:hover { .remove-track { opacity: 0.5; - transform: translateX(0) rotate(45deg); + transform: scale(1) translateY(0); } - cursor: pointer; background: linear-gradient(37deg, $gray4, $gray3, $gray3); } @@ -146,6 +138,7 @@ const playThis = (track: Track) => { .artist { font-size: small; opacity: 0.67; + width: fit-content; } } diff --git a/src/composables/colors/album.ts b/src/composables/colors/album.ts index 9aaa188..2da394a 100644 --- a/src/composables/colors/album.ts +++ b/src/composables/colors/album.ts @@ -25,8 +25,14 @@ interface BtnColor { * @returns {BtnColor} A color to use as the play button background */ export function getButtonColor(colors: string[]): BtnColor { + const def = { + color: "#fff", + isDark: true, + }; + + if (!colors || colors.length === 0) return def; + const base_color = colors[0]; - if (colors.length === 0) return { color: "#fff", isDark: true }; for (let i = 0; i < colors.length; i++) { if (theyContrast(base_color, colors[i])) { @@ -37,10 +43,7 @@ export function getButtonColor(colors: string[]): BtnColor { } } - return { - color: "#fff", - isDark: true, - }; + return def; } /** diff --git a/src/composables/enums.ts b/src/composables/enums.ts index d2b706b..b56dc7c 100644 --- a/src/composables/enums.ts +++ b/src/composables/enums.ts @@ -37,3 +37,12 @@ export enum Routes { search = "SearchView", queue = "QueueView", } + +export const FuseTrackOptions = { + keys: [ + { name: "title", weight: 1 }, + { name: "album", weight: 0.7 }, + { name: "artists", weight: 0.5 }, + { name: "albumartist", weight: 0.25 }, + ], +}; diff --git a/src/composables/fetch/album.ts b/src/composables/fetch/album.ts index 234e7e7..8e79b37 100644 --- a/src/composables/fetch/album.ts +++ b/src/composables/fetch/album.ts @@ -21,11 +21,7 @@ const getAlbumData = async (hash: string, ToastStore: typeof useNotifStore) => { if (status == 204) { ToastStore().showNotification("Album not created yet!", NotifType.Error); return { - info: { - album: "", - artist: "", - colors: [] - }, + info: {} as AlbumInfo, tracks: [], }; } diff --git a/src/composables/useKeyboard.ts b/src/composables/useKeyboard.ts index 0a28788..d4c3cbc 100644 --- a/src/composables/useKeyboard.ts +++ b/src/composables/useKeyboard.ts @@ -2,8 +2,8 @@ import useQStore from "@/stores/queue"; let key_down_fired = false; -function focusSearchBox() { - const elem = document.getElementById("globalsearch"); +function focusPageSearchBox() { + const elem = document.getElementById("page-search") as HTMLInputElement; elem.focus(); } @@ -66,16 +66,11 @@ export default function (queue: typeof useQStore) { if (!key_down_fired) { if (!ctrlKey) return; e.preventDefault(); + focusPageSearchBox(); key_down_fired = true; } } - case "/": { - { - e.preventDefault(); - focusSearchBox(); - } - } } }); } diff --git a/src/composables/usePlayFrom.ts b/src/composables/usePlayFrom.ts index c39580b..9bf7cc4 100644 --- a/src/composables/usePlayFrom.ts +++ b/src/composables/usePlayFrom.ts @@ -24,7 +24,7 @@ export default function play( store = store as typeof folder; const f = store(); - useQueue.playFromFolder(f.path, f.tracks); + useQueue.playFromFolder(f.path, f.allTracks); useQueue.play(); break; case playSources.album: @@ -35,7 +35,7 @@ export default function play( a_store.info.title, a_store.info.artist, a_store.info.hash, - a_store.tracks + a_store.allTracks ); useQueue.play(); break; diff --git a/src/interfaces.ts b/src/interfaces.ts index 83ab1d4..a070e8b 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -13,9 +13,9 @@ export interface Track { bitrate?: number; genre?: string; image: string; - tracknumber?: number; - discnumber?: number; - index?: number; + tracknumber: number; + discnumber: number; + index: number; hash: string; copyright?: string; } @@ -110,3 +110,10 @@ export interface FetchProps { put?: boolean; headers?: {}; } + +export interface FuseResult { + item: Track; + refIndex: number; +} + + diff --git a/src/interfaces/settings.ts b/src/interfaces/settings.ts index ca6e433..76ccf0b 100644 --- a/src/interfaces/settings.ts +++ b/src/interfaces/settings.ts @@ -20,7 +20,7 @@ export interface Setting { } export interface SettingGroup { - title?: string; + name?: string; desc?: string; settings: Setting[]; } diff --git a/src/router/index.ts b/src/router/index.ts index 9ee8933..a222d53 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -81,7 +81,6 @@ const routes = [ await store .fetchTracksAndArtists(to.params.hash) - .then(() => store.fetchBio(to.params.hash)) .then(() => { state.loading.value = false; }); diff --git a/src/settings/general/index.ts b/src/settings/general/index.ts index 8a2f108..9f9459d 100644 --- a/src/settings/general/index.ts +++ b/src/settings/general/index.ts @@ -1,11 +1,12 @@ import { SettingCategory } from "@/interfaces/settings"; import nowPlaying from "./now-playing"; +import sidebarSettings from "./sidebar"; export default { title: "General", groups: [ { - settings: [...nowPlaying], + settings: [...sidebarSettings, ...nowPlaying], }, ], } as SettingCategory; diff --git a/src/settings/general/now-playing.ts b/src/settings/general/now-playing.ts index 5776ddd..32347bc 100644 --- a/src/settings/general/now-playing.ts +++ b/src/settings/general/now-playing.ts @@ -10,11 +10,5 @@ const use_alt_np: Setting = { inactive: () => settings().disable_show_alt_np, action: () => settings().toggleUseRightNP(), }; -const use_sidebar: Setting = { - title: "Use right sidebar", - type: SettingType.switch, - source: () => settings().use_sidebar, - action: () => settings().toggleDisableSidebar(), -}; -export default [use_sidebar, use_alt_np]; +export default [use_alt_np]; diff --git a/src/settings/general/sidebar.ts b/src/settings/general/sidebar.ts new file mode 100644 index 0000000..b62cd29 --- /dev/null +++ b/src/settings/general/sidebar.ts @@ -0,0 +1,13 @@ +import { Setting, SettingType } from "@/interfaces/settings"; +import useSettingsStore from "@/stores/settings"; + +const settings = useSettingsStore; + +const use_sidebar: Setting = { + title: "Use right sidebar", + type: SettingType.switch, + source: () => settings().use_sidebar, + action: () => settings().toggleDisableSidebar(), +}; + +export default [use_sidebar]; diff --git a/src/stores/loader.ts b/src/stores/loader.ts index 3366477..dfc5bbe 100644 --- a/src/stores/loader.ts +++ b/src/stores/loader.ts @@ -4,20 +4,32 @@ export default defineStore("Loader", { state: () => ({ loading: false, duration: 0, + page: null, }), actions: { startLoading() { this.loading = true; this.duration = new Date().getTime(); + + if (!this.page) { + this.page = document.getElementsByTagName("html")[0] as HTMLHtmlElement; + } + + this.page.classList.add("loading"); }, stopLoading() { const diff = new Date().getTime() - this.duration; + const resetCursor = () => { + this.page ? this.page.classList.remove("loading") : null; + }; if (diff <= 250) { setTimeout(() => { + resetCursor(); this.loading = false; }, 250 - diff); } else { + resetCursor(); this.loading = false; } }, diff --git a/src/stores/pages/album.ts b/src/stores/pages/album.ts index 665340a..6d34049 100644 --- a/src/stores/pages/album.ts +++ b/src/stores/pages/album.ts @@ -1,11 +1,12 @@ +import { useFuse } from "@/utils"; import { defineStore } from "pinia"; +import { ComputedRef } from "vue"; + +import { FuseTrackOptions } from "@/composables/enums"; + +import { getAlbumBio, getAlbumTracks } from "../../composables/fetch/album"; +import { AlbumInfo, Artist, FuseResult, Track } from "../../interfaces"; import { useNotifStore } from "../notification"; -import { Track, Artist, AlbumInfo } from "../../interfaces"; -import { - getAlbumTracks, - getAlbumArtists, - getAlbumBio, -} from "../../composables/fetch/album"; function sortTracks(tracks: Track[]) { return tracks.sort((a, b) => { @@ -32,9 +33,9 @@ function createDiscs(tracks: Track[]): Discs { export default defineStore("album", { state: () => ({ + query: "", info: {}, - tracks: [], - discs: {}, + allTracks: [], artists: [], bio: null, }), @@ -45,27 +46,38 @@ export default defineStore("album", { * @param hash title of the album */ async fetchTracksAndArtists(hash: string) { - this.tracks = []; + this.allTracks = []; const album = await getAlbumTracks(hash, useNotifStore); - const artists = await getAlbumArtists(hash); - this.discs = createDiscs(sortTracks(album.tracks)); - Object.keys(this.discs).forEach((disc) => { - this.tracks.push(...this.discs[disc]); + const discs = createDiscs(sortTracks(album.tracks)); + Object.keys(discs).forEach((disc) => { + this.allTracks.push(...discs[disc]); }); this.info = album.info; - this.artists = artists; }, - /** - * Fetches the album bio from the server - * @param {string} hash title of the album - */ - fetchBio(hash: string) { - this.bio = null; - getAlbumBio(hash).then((bio) => { - this.bio = bio; + resetQuery() { + this.query = ""; + }, + }, + getters: { + filteredTracks(): ComputedRef { + return useFuse(this.query, this.allTracks, FuseTrackOptions); + }, + tracks(): Track[] { + const tracks = this.filteredTracks.value.map((result: FuseResult) => { + const t = { + ...result.item, + index: result.refIndex, + }; + + return t; }); + + return tracks; + }, + discs(): Discs { + return createDiscs(this.tracks); }, }, }); diff --git a/src/stores/pages/folder.ts b/src/stores/pages/folder.ts index 803d2df..f093b54 100644 --- a/src/stores/pages/folder.ts +++ b/src/stores/pages/folder.ts @@ -1,19 +1,53 @@ import { defineStore } from "pinia"; -import { Folder, Track } from "../../interfaces"; +import { ComputedRef } from "vue"; -import fetchThem from "../../composables/fetch/folders"; +import { useFuse } from "@/utils"; + +import { FuseTrackOptions } from "@/composables/enums"; +import fetchThem from "@/composables/fetch/folders"; +import { Folder, FuseResult, Track } from "@/interfaces"; export default defineStore("FolderDirs&Tracks", { state: () => ({ + query: "", path: {}, - dirs: [], - tracks: [], + allDirs: [], + allTracks: [], }), actions: { async fetchAll(path: string) { const { tracks, folders } = await fetchThem(path); - [this.path, this.dirs, this.tracks] = [path, folders, tracks]; + [this.path, this.allDirs, this.allTracks] = [path, folders, tracks]; + }, + resetQuery() { + this.query = ""; + }, + }, + getters: { + filteredTracks(): ComputedRef { + return useFuse(this.query, this.allTracks, FuseTrackOptions); + }, + tracks(): Track[] { + const tracks = this.filteredTracks.value.map((result: FuseResult) => { + const t = { + ...result.item, + index: result.refIndex, + }; + + return t; + }); + + return tracks; + }, + dirs(): Folder[] { + const dirs = useFuse(this.query, this.allDirs, { + keys: ["name"], + }); + + return dirs.value.map((result) => { + return result.item; + }); }, }, }); diff --git a/src/stores/pages/playlist.ts b/src/stores/pages/playlist.ts index ead55a0..f72af7d 100644 --- a/src/stores/pages/playlist.ts +++ b/src/stores/pages/playlist.ts @@ -1,15 +1,17 @@ -import { Artist } from "./../../interfaces"; import { defineStore } from "pinia"; -import { - getPlaylist, - getPlaylistArtists, -} from "../../composables/fetch/playlists"; -import { Track, Playlist } from "../../interfaces"; +import { ComputedRef } from "vue"; + +import { useFuse } from "@/utils"; + +import { FuseTrackOptions } from "@/composables/enums"; +import { getPlaylist } from "@/composables/fetch/playlists"; +import { Artist, FuseResult, Playlist, Track } from "@/interfaces"; export default defineStore("playlist-tracks", { state: () => ({ info: {}, - tracks: [], + query: "", + allTracks: [], artists: [], }), actions: { @@ -21,12 +23,12 @@ export default defineStore("playlist-tracks", { const playlist = await getPlaylist(playlistid); this.info = playlist?.info || ({} as Playlist); - this.tracks = playlist?.tracks || []; + this.allTracks = playlist?.tracks || []; }, - async fetchArtists(playlistid: string) { - this.artists = await getPlaylistArtists(playlistid); - }, + // async fetchArtists(playlistid: string) { + // this.artists = await getPlaylistArtists(playlistid); + // }, /** * Updates the playlist header info. This is used when the playlist is * updated. @@ -40,5 +42,26 @@ export default defineStore("playlist-tracks", { resetArtists() { this.artists = []; }, + resetQuery() { + this.query = ""; + }, + }, + getters: { + filteredTracks(): ComputedRef { + return useFuse(this.query, this.allTracks, FuseTrackOptions); + }, + + tracks(): Track[] { + const tracks = this.filteredTracks.value.map((result: FuseResult) => { + const t = { + ...result.item, + index: result.refIndex, + }; + + return t; + }); + + return tracks; + }, }, }); diff --git a/src/utils/index.ts b/src/utils/index.ts index c8b7e0a..370bbb9 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -5,6 +5,7 @@ import formatSeconds from "./useFormatSeconds"; import useDebouncedRef from "./useDebouncedRef"; import createSubPaths from "./useCreateSubPaths"; import { readLocalStorage, writeLocalStorage } from "./useLocalStorage"; +import useFuse from "./useFuse"; export { readLocalStorage, @@ -15,4 +16,5 @@ export { useVisibility, formatSeconds, putCommas, + useFuse, }; diff --git a/src/utils/useFuse.ts b/src/utils/useFuse.ts new file mode 100644 index 0000000..df58d5f --- /dev/null +++ b/src/utils/useFuse.ts @@ -0,0 +1,18 @@ +import { useFuse } from "@vueuse/integrations/useFuse"; +import { Ref } from "vue"; + +/** + * Fuzzy search using Fuse.js + * @param query The query to search for + * @param data The list to search in + * @param fuseOptions Fuse.js options + * @returns A ref containing the search results + */ +export default (query: string, data: any[], fuseOptions: object) => { + const { results } = useFuse(query, data, { + matchAllWhenSearchEmpty: true, + fuseOptions: { ...fuseOptions, threshold: 0.3, ignoreLocation: true }, + }); + + return results; +}; diff --git a/src/views/FolderView.vue b/src/views/FolderView.vue index 3ec3238..57b67f8 100644 --- a/src/views/FolderView.vue +++ b/src/views/FolderView.vue @@ -24,7 +24,11 @@ diff --git a/src/views/SettingsView.vue b/src/views/SettingsView.vue index 70c5520..071584c 100644 --- a/src/views/SettingsView.vue +++ b/src/views/SettingsView.vue @@ -1,28 +1,9 @@ - - + \ No newline at end of file diff --git a/src/views/album/Content.vue b/src/views/album/Content.vue index ebb618e..a39ee1f 100644 --- a/src/views/album/Content.vue +++ b/src/views/album/Content.vue @@ -37,7 +37,7 @@ const isLastDisc = (disc: string | number) => { function playFromAlbumPage(index: number) { const { title, artist, hash } = album.info; - queue.playFromAlbum(title, artist, hash, album.tracks); + queue.playFromAlbum(title, artist, hash, album.allTracks); queue.play(index); } diff --git a/src/views/album/index.vue b/src/views/album/index.vue index e4e2960..ef23887 100644 --- a/src/views/album/index.vue +++ b/src/views/album/index.vue @@ -12,24 +12,26 @@ diff --git a/src/views/playlist/Content.vue b/src/views/playlist/Content.vue index 2fbc276..06f914a 100644 --- a/src/views/playlist/Content.vue +++ b/src/views/playlist/Content.vue @@ -27,8 +27,9 @@ import usePlaylistStore from "@/stores/pages/playlist"; import SongList from "@/components/FolderView/SongList.vue"; import { Track } from "@/interfaces"; +import { onUpdated } from "vue"; -defineProps<{ +const props = defineProps<{ tracks: Track[]; count: number; name: string; @@ -40,7 +41,7 @@ const playlist = usePlaylistStore(); function playFromPlaylistPage(index: number) { const { name, playlistid } = playlist.info; - queue.playFromPlaylist(name, playlistid, playlist.tracks); + queue.playFromPlaylist(name, playlistid, playlist.allTracks); queue.play(index); } diff --git a/src/views/playlist/index.vue b/src/views/playlist/index.vue index 7465191..011f6a6 100644 --- a/src/views/playlist/index.vue +++ b/src/views/playlist/index.vue @@ -1,37 +1,37 @@ diff --git a/yarn.lock b/yarn.lock index 32d41c4..296960b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -87,6 +87,11 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df" integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ== +"@types/web-bluetooth@^0.0.15": + version "0.0.15" + resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.15.tgz#d60330046a6ed8a13b4a53df3813c44942ebdf72" + integrity sha512-w7hEHXnPMEZ+4nGKl/KDRVpxkwYxYExuHOYXyzIzCDzEZ9ZCGMAewulr9IqJu2LR4N37fcnb1XVeuZ09qgOxhA== + "@vitejs/plugin-vue@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-3.0.1.tgz#b6af8f782485374bbb5fe09edf067a845bf4caae" @@ -187,6 +192,16 @@ resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.37.tgz#8e6adc3f2759af52f0e85863dfb0b711ecc5c702" integrity sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw== +"@vueuse/core@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-9.2.0.tgz#58e3588b9bc5a69010aa9104b00056ee9ebff738" + integrity sha512-/MZ6qpz6uSyaXrtoeBWQzAKRG3N7CvfVWvQxiM3ei3Xe5ydOjjtVbo7lGl9p8dECV93j7W8s63A8H0kFLpLyxg== + dependencies: + "@types/web-bluetooth" "^0.0.15" + "@vueuse/metadata" "9.2.0" + "@vueuse/shared" "9.2.0" + vue-demi "*" + "@vueuse/core@^8.5.0": version "8.5.0" resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-8.5.0.tgz#2b7548e52165c88e1463756c36188e105d806543" @@ -196,11 +211,25 @@ "@vueuse/shared" "8.5.0" vue-demi "*" +"@vueuse/integrations@^9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@vueuse/integrations/-/integrations-9.2.0.tgz#fdc53f120ff124e173a9400e634cf5095646eba4" + integrity sha512-0NerkCPUUWnbEb0ZZaJyrO8YKPPClR9+aLLF8yBbG/XRsoEo7pcpVq8d+uMhfHrXABoUpKD+9FZ+Tz/aRb7yFg== + dependencies: + "@vueuse/core" "9.2.0" + "@vueuse/shared" "9.2.0" + vue-demi "*" + "@vueuse/metadata@8.5.0": version "8.5.0" resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-8.5.0.tgz#1aaa3787922cfda0f38243aaa7779366a669b4db" integrity sha512-WxsD+Cd+bn+HcjpY6Dl9FJ8ywTRTT9pTwk3bCQpzEhXVYAyNczKDSahk50fCfIJKeWHhyI4B2+/ZEOxQAkUr0g== +"@vueuse/metadata@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-9.2.0.tgz#6bf7c9c44b9f5ece405837226a0e04a997994458" + integrity sha512-exN4KE6iquxDCdt72BgEhb3tlOpECtD61AUdXnUqBTIUCl70x1Ar/QXo3bYcvxmdMS2/peQyfeTzBjRTpvL5xw== + "@vueuse/shared@8.5.0": version "8.5.0" resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-8.5.0.tgz#fa01ecd3161933f521dd6428b9ef8015ded1bbd3" @@ -208,6 +237,13 @@ dependencies: vue-demi "*" +"@vueuse/shared@9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-9.2.0.tgz#7831051b2c1d01c3413c749468ee53a86024510e" + integrity sha512-NnRp/noSWuXW0dKhZK5D0YLrDi0nmZ18UeEgwXQq7Ul5TTP93lcNnKjrHtd68j2xFB/l59yPGFlCryL692bnrA== + dependencies: + vue-demi "*" + "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" @@ -1172,6 +1208,11 @@ functions-have-names@^1.2.2: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +fuse.js@^6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-6.6.2.tgz#fe463fed4b98c0226ac3da2856a415576dc9a111" + integrity sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA== + get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598"