mirror of
https://github.com/tcsenpai/swingmusic.git
synced 2025-06-11 05:27:21 +00:00
Implement fuzzy page search using fuse.js (#86)
This commit is contained in:
parent
befdf383b6
commit
5770a66d67
@ -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",
|
||||
|
@ -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 = "";
|
||||
|
@ -16,6 +16,8 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-all;
|
||||
width: fit-content;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.rounded {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
padding: 0 $small;
|
||||
|
||||
|
@ -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]}
|
||||
)`,
|
||||
)`
|
||||
: '',
|
||||
}"
|
||||
>
|
||||
<div class="info" :class="{ nocontrast: isLight(album.colors[0]) }">
|
||||
<div
|
||||
class="info"
|
||||
:class="{ nocontrast: album.colors ? isLight(album.colors[0]) : false }"
|
||||
>
|
||||
<div class="art">
|
||||
<img
|
||||
:src="imguri.artist + album.artistimg"
|
||||
@ -31,8 +36,11 @@
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="stats">
|
||||
{{ album.artist }} • {{ album.date }} • {{ album.count }} Tracks •
|
||||
{{ formatSeconds(album.duration, true) }}
|
||||
<div class="border rounded-sm pad-sm">
|
||||
{{ album.artist }} • {{ album.date }} • {{ album.count }}
|
||||
{{ album.count === 1 ? "Track" : "Tracks" }} •
|
||||
{{ formatSeconds(album.duration, true) }}
|
||||
</div>
|
||||
</div>
|
||||
<PlayBtnRect
|
||||
:source="playSources.album"
|
||||
@ -62,9 +70,8 @@ import { getButtonColor, isLight } from "../../composables/colors/album";
|
||||
|
||||
import PlayBtnRect from "../shared/PlayBtnRect.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
defineProps<{
|
||||
album: AlbumInfo;
|
||||
bio: string | null;
|
||||
}>();
|
||||
|
||||
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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -32,7 +32,6 @@ defineProps<{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: default;
|
||||
padding: 0.4rem 1rem;
|
||||
position: relative;
|
||||
|
||||
|
@ -16,11 +16,17 @@
|
||||
</div>
|
||||
<div class="songlist">
|
||||
<SongItem
|
||||
v-for="(track, index) in getTrackList()"
|
||||
v-for="(track, index) in tracks"
|
||||
:key="track.trackid"
|
||||
:track="track"
|
||||
:index="track.index"
|
||||
@updateQueue="updateQueue(index)"
|
||||
:index="
|
||||
on_album_page
|
||||
? track.tracknumber
|
||||
: track.index !== undefined
|
||||
? track.index + 1
|
||||
: index + 1
|
||||
"
|
||||
@playThis="updateQueue(track.index !== undefined ? track.index : index)"
|
||||
:isPlaying="queue.playing"
|
||||
:isCurrent="queue.currentid == track.trackid"
|
||||
/>
|
||||
@ -37,7 +43,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { onUpdated, ref } from "vue";
|
||||
import { useElementSize } from "@vueuse/core";
|
||||
import { computed } from "@vue/reactivity";
|
||||
|
||||
@ -59,6 +65,10 @@ const props = defineProps<{
|
||||
copyright?: string | null;
|
||||
}>();
|
||||
|
||||
// onUpdated(() => {
|
||||
// console.log(props.tracks[1].index);
|
||||
// });
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "playFromPage", index: number): void;
|
||||
}>();
|
||||
@ -79,11 +89,11 @@ function updateQueue(index: number) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to show track numbers as indexes in the album page.
|
||||
* Used to show handle track indexes.
|
||||
*/
|
||||
function getTrackList() {
|
||||
if (props.on_album_page) {
|
||||
let tracks = props.tracks.map((track) => {
|
||||
const tracks = props.tracks.map((track) => {
|
||||
track.index = track.tracknumber;
|
||||
return track;
|
||||
});
|
||||
@ -91,12 +101,7 @@ function getTrackList() {
|
||||
return tracks;
|
||||
}
|
||||
|
||||
const tracks = props.tracks.map((track, index) => {
|
||||
track.index = index + 1;
|
||||
return track;
|
||||
});
|
||||
|
||||
return tracks;
|
||||
return props.tracks;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -34,7 +34,6 @@ const q = useQStore();
|
||||
height: 2.5rem;
|
||||
width: 100%;
|
||||
background-size: 1.5rem !important;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: $darkestblue;
|
||||
|
@ -25,34 +25,26 @@
|
||||
</router-link>
|
||||
|
||||
<div class="bottom">
|
||||
<div class="title ellip">{{ props.track?.title }}</div>
|
||||
<div
|
||||
class="artists ellip"
|
||||
v-if="track?.artists && track?.artists[0] !== ''"
|
||||
>
|
||||
<span v-for="artist in putCommas(track.artists)" :key="artist">{{
|
||||
artist
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="artists" v-else-if="track?.artists">
|
||||
<span>{{ track.albumartist }}</span>
|
||||
</div>
|
||||
<div class="artists" v-else>
|
||||
<span>Meh</span>
|
||||
</div>
|
||||
<div class="title ellip t-center">{{ props.track?.title }}</div>
|
||||
<ArtistName
|
||||
:artists="track?.artists || ['Artist']"
|
||||
:albumartist="track?.albumartist"
|
||||
class="artists"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { putCommas } from "@/utils";
|
||||
import { paths } from "../../../config";
|
||||
import { Track } from "../../../interfaces";
|
||||
const imguri = paths.images.thumb;
|
||||
import ArtistName from "@/components/shared/ArtistName.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
track: Track | null;
|
||||
}>();
|
||||
|
||||
const imguri = paths.images.thumb;
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@ -91,12 +83,14 @@ const props = defineProps<{
|
||||
|
||||
.title {
|
||||
font-weight: 900;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.artists {
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.75;
|
||||
|
||||
margin: 0 auto;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline 1px !important;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="l-sidebar">
|
||||
<div class="l-sidebar noscroll">
|
||||
<div class="withlogo">
|
||||
<Logo />
|
||||
<Navigation />
|
||||
|
@ -19,9 +19,9 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from "@vue/reactivity";
|
||||
import ArtistCard from "@/components/shared/ArtistCard.vue";
|
||||
import { Artist } from "@/interfaces";
|
||||
import { ref } from "@vue/reactivity";
|
||||
import ArrowSvg from "../../assets/icons/right-arrow.svg";
|
||||
|
||||
defineProps<{
|
||||
@ -82,7 +82,6 @@ const scrollRight = () => {
|
||||
|
||||
.icon {
|
||||
border-radius: $small;
|
||||
cursor: pointer;
|
||||
transition: all 0.5s ease;
|
||||
background-color: rgb(51, 51, 51);
|
||||
padding: $smaller;
|
||||
|
@ -37,20 +37,17 @@
|
||||
import { ref } from "vue";
|
||||
|
||||
import useNavStore from "@/stores/nav";
|
||||
import useModalStore from "../../stores/modal";
|
||||
import pContext from "../../contexts/playlist";
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import useContextStore from "../../stores/context";
|
||||
import useModalStore from "../../stores/modal";
|
||||
|
||||
import { playSources } from "@/composables/enums";
|
||||
import { formatSeconds, useVisibility } from "@/utils";
|
||||
import { paths } from "../../config";
|
||||
import { Playlist } from "../../interfaces";
|
||||
import { useVisibility, formatSeconds } from "@/utils";
|
||||
import { ContextSrc, playSources } from "@/composables/enums";
|
||||
|
||||
import PlayBtnRect from "../shared/PlayBtnRect.vue";
|
||||
|
||||
const imguri = paths.images.playlist;
|
||||
const context = useContextStore();
|
||||
const modal = useModalStore();
|
||||
const nav = useNavStore();
|
||||
const playlistheader = ref<HTMLElement | null>(null);
|
||||
@ -64,10 +61,6 @@ const props = defineProps<{
|
||||
function editPlaylist() {
|
||||
modal.showEditPlaylistModal(props.info);
|
||||
}
|
||||
|
||||
function showDropdown(e: any) {
|
||||
context.showContextMenu(e, pContext(), ContextSrc.PHeader);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@ -155,16 +148,7 @@ function showDropdown(e: any) {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 900;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.desc {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: initial;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
max-width: 50%;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.duration {
|
||||
@ -173,6 +157,7 @@ function showDropdown(e: any) {
|
||||
padding: $smaller;
|
||||
padding-left: 0;
|
||||
font-weight: 900;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.btns {
|
||||
|
@ -1,28 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="p-card new-playlist-card rounded"
|
||||
@click="Modal.showNewPlaylistModal()"
|
||||
>
|
||||
<PlusSvg />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import PlusSvg from "../../assets/icons/plus.svg";
|
||||
import useModalStore from "../../stores/modal";
|
||||
|
||||
const Modal = useModalStore();
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.new-playlist-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
transform: scale(3);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -60,7 +60,6 @@ const songs = [
|
||||
|
||||
&:hover {
|
||||
background-color: #3a39393d;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)"
|
||||
/>
|
||||
</div>
|
||||
|
@ -1,150 +0,0 @@
|
||||
<template>
|
||||
<div id="playing-from" class="bg-primary rounded" @click="goTo">
|
||||
<div class="h">
|
||||
<div class="icon image" :class="from.icon"></div>
|
||||
Playing from
|
||||
</div>
|
||||
<div class="name">
|
||||
<div id="to">
|
||||
{{ from.text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FromOptions } from "@/composables/enums";
|
||||
import {
|
||||
fromAlbum, fromFolder, fromPlaylist,
|
||||
fromSearch
|
||||
} from "@/interfaces";
|
||||
import { computed } from "@vue/reactivity";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const props = defineProps<{
|
||||
from: fromFolder | fromAlbum | fromPlaylist | fromSearch;
|
||||
}>();
|
||||
|
||||
interface from {
|
||||
icon: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
const from = computed((): from => {
|
||||
switch (props.from.type) {
|
||||
case undefined:
|
||||
return {
|
||||
icon: "album",
|
||||
text: "Welcome to Alice",
|
||||
};
|
||||
case FromOptions.folder:
|
||||
return {
|
||||
icon: "folder",
|
||||
text: props.from.name,
|
||||
};
|
||||
case FromOptions.album:
|
||||
return {
|
||||
icon: "album",
|
||||
text: `${props.from.name} - ${props.from.albumartist}`,
|
||||
};
|
||||
case FromOptions.playlist:
|
||||
return {
|
||||
icon: "playlist",
|
||||
text: props.from.name,
|
||||
};
|
||||
case FromOptions.search:
|
||||
return {
|
||||
icon: "search",
|
||||
text: `Search results for: "${props.from.query}"`,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
function goToAlbum(from: fromAlbum) {
|
||||
router.push({
|
||||
name: "AlbumView",
|
||||
params: {
|
||||
hash: from.hash,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function goToFolder(from: fromFolder) {
|
||||
router.push({
|
||||
name: "FolderView",
|
||||
params: {
|
||||
path: from.path,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function goToPlaylist(from: fromPlaylist) {
|
||||
router.push({
|
||||
name: "PlaylistView",
|
||||
params: {
|
||||
pid: from.playlistid,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function goTo() {
|
||||
switch (props.from.type) {
|
||||
case FromOptions.folder:
|
||||
goToFolder(props.from);
|
||||
break;
|
||||
case FromOptions.album:
|
||||
goToAlbum(props.from);
|
||||
break;
|
||||
case FromOptions.playlist:
|
||||
goToPlaylist(props.from);
|
||||
break;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#playing-from {
|
||||
background-size: 120%;
|
||||
padding: 0.75rem;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.2s ease;
|
||||
background-color: $black;
|
||||
|
||||
&:hover {
|
||||
background-position: -4rem;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.h {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: $small;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $small;
|
||||
color: rgba(255, 255, 255, 0.849);
|
||||
|
||||
.icon {
|
||||
height: 1.25rem;
|
||||
width: 1.25rem;
|
||||
}
|
||||
|
||||
.folder {
|
||||
background-image: url("../../../assets/icons/folder.fill.svg") !important;
|
||||
}
|
||||
|
||||
.album {
|
||||
background-image: url("../../../assets/icons/album.svg") !important;
|
||||
}
|
||||
|
||||
.playlist {
|
||||
background-image: url("../../../assets/icons/playlist.svg") !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -50,7 +50,6 @@ function showMenu(e: Event) {
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: $gray4;
|
||||
|
@ -8,7 +8,8 @@
|
||||
:isHighlighted="false"
|
||||
:isPlaying="queue.playing"
|
||||
:track="track"
|
||||
@PlayThis="updateQueue(index)"
|
||||
@playThis="updateQueue(index)"
|
||||
:index="index + 1"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="t-center"><h5>🤷</h5></div>
|
||||
@ -17,12 +18,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
|
||||
import LoadMore from "./LoadMore.vue";
|
||||
import TrackItem from "@/components/shared/TrackItem.vue";
|
||||
import SongItem from "@/components/shared/SongItem.vue";
|
||||
import useQStore from "../../../stores/queue";
|
||||
import useSearchStore from "../../../stores/search";
|
||||
import { computed } from "vue";
|
||||
import useQStore from "@/stores/queue";
|
||||
import useSearchStore from "@/stores/search";
|
||||
|
||||
const queue = useQStore();
|
||||
const search = useSearchStore();
|
||||
|
@ -5,8 +5,6 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
|
||||
defineProps<{
|
||||
state: boolean;
|
||||
}>();
|
||||
@ -14,16 +12,16 @@ defineProps<{
|
||||
|
||||
<style lang="scss">
|
||||
.switch {
|
||||
height: 2rem;
|
||||
height: 1.5rem;
|
||||
background-color: $gray;
|
||||
width: 3.75rem;
|
||||
width: 2.5rem;
|
||||
padding: $smaller;
|
||||
position: relative;
|
||||
transition: all 0.25s ease;
|
||||
|
||||
.circle {
|
||||
transition: all 0.25s ease;
|
||||
height: 1.5rem;
|
||||
height: 1rem;
|
||||
aspect-ratio: 1;
|
||||
background-color: $gray1;
|
||||
position: absolute;
|
||||
@ -37,7 +35,7 @@ defineProps<{
|
||||
|
||||
.circle {
|
||||
background-color: $white;
|
||||
left: calc((100% - ($smaller + 1.5rem)));
|
||||
left: calc((100% - ($smaller + 1rem)));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -20,7 +20,7 @@ defineProps<{
|
||||
<style lang="scss">
|
||||
.settingscontent {
|
||||
width: 100%;
|
||||
max-width: 40rem;
|
||||
// max-width: 40rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<div class="settingsgroup">
|
||||
<div>
|
||||
<h4 v-if="group.title">{{ group.title }}</h4>
|
||||
<div v-if="group.name || group.desc">
|
||||
<h4 v-if="group.name">{{ group.name }}</h4>
|
||||
<div class="desc" v-if="group.desc">{{ group.desc }}</div>
|
||||
</div>
|
||||
<div class="setting rounded bg-primary pad-lg">
|
||||
<div class="setting rounded border pad-lg">
|
||||
<div
|
||||
v-for="(setting, index) in group.settings"
|
||||
:key="index"
|
||||
:class="{ inactive: setting.inactive && setting.inactive() }"
|
||||
>
|
||||
<div class="title">
|
||||
<div class="title ellip" @click="setting.action()">
|
||||
{{ setting.title }}
|
||||
</div>
|
||||
<div class="options">
|
||||
@ -69,6 +69,7 @@ defineProps<{
|
||||
|
||||
.title {
|
||||
margin: auto 0;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="settingsnav">
|
||||
<div class="buttongroup rounded bg-primary">
|
||||
<div class="buttongroup rounded-sm bg-primary">
|
||||
<button v-for="(group, index) in settingGroups" :key="index">
|
||||
{{ group.title }}
|
||||
</button>
|
||||
|
@ -108,7 +108,6 @@
|
||||
|
||||
button {
|
||||
padding: 0 $medium;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,6 @@ function hideModal() {
|
||||
transform: rotate(45deg);
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
}
|
||||
|
@ -37,10 +37,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import { onMounted } from "vue";
|
||||
import { updatePlaylist } from "@/composables/fetch/playlists";
|
||||
import { Playlist } from "@/interfaces";
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import { onMounted } from "vue";
|
||||
|
||||
const pStore = usePStore();
|
||||
|
||||
@ -132,7 +132,6 @@ function update_playlist(e: Event) {
|
||||
place-items: center;
|
||||
color: $gray1;
|
||||
margin: $small 0;
|
||||
cursor: pointer;
|
||||
|
||||
#update-pl-img-preview {
|
||||
width: 4.5rem;
|
||||
|
@ -2,15 +2,19 @@
|
||||
<div class="topnav">
|
||||
<div class="left">
|
||||
<NavButtons />
|
||||
|
||||
<div
|
||||
class="info"
|
||||
:style="{
|
||||
overflow: $route.name === Routes.search ? 'visible' : 'hidden',
|
||||
overflowY: hideOverflow() ? 'visible' : 'hidden',
|
||||
}"
|
||||
class="info"
|
||||
>
|
||||
<APTitle v-if="showAPTitle" />
|
||||
<SimpleTitle v-if="$route.name == Routes.settings" :text="'Settings'" />
|
||||
<Folder v-if="$route.name == Routes.folder" :subPaths="subPaths" />
|
||||
<APTitle
|
||||
v-if="$route.name == Routes.album || $route.name == Routes.playlist"
|
||||
:header_shown="nav.h_visible"
|
||||
/>
|
||||
<SettingsTitle v-if="$route.name == Routes.settings" :text="'Settings'" />
|
||||
<FolderTitle v-if="$route.name == Routes.folder" :subPaths="subPaths" />
|
||||
<SearchTitle v-if="$route.name == Routes.search" />
|
||||
<PlaylistsTitle v-if="$route.name == Routes.playlists" />
|
||||
<QueueTitle v-if="$route.name == Routes.queue" />
|
||||
@ -26,7 +30,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { computed } from "@vue/reactivity";
|
||||
|
||||
import { subPath } from "@/interfaces";
|
||||
import useNavStore from "@/stores/nav";
|
||||
@ -34,26 +37,26 @@ import { createSubPaths } from "@/utils";
|
||||
import { Routes } from "@/composables/enums";
|
||||
|
||||
import NavButtons from "./NavButtons.vue";
|
||||
// import Loader from "../shared/Loader.vue";
|
||||
|
||||
import Folder from "./Titles/Folder.vue";
|
||||
import FolderTitle from "./Titles/Folder.vue";
|
||||
import SimpleTitle from "./Titles/SimpleTitle.vue";
|
||||
import APTitle from "./Titles/APTitle.vue";
|
||||
import SearchTitle from "./Titles/SearchTitle.vue";
|
||||
import PlaylistsTitle from "./Titles/PlaylistsTitle.vue";
|
||||
import QueueTitle from "./Titles/QueueTitle.vue";
|
||||
import SettingsTitle from "./Titles/SettingsTitle.vue";
|
||||
|
||||
const route = useRoute();
|
||||
const nav = useNavStore();
|
||||
|
||||
const subPaths = ref<subPath[]>([]);
|
||||
|
||||
const showAPTitle = computed(() => {
|
||||
return (
|
||||
(route.name == Routes.album || route.name == Routes.playlist) &&
|
||||
!nav.h_visible
|
||||
);
|
||||
});
|
||||
function hideOverflow() {
|
||||
const { name } = route;
|
||||
const { album, playlist, search, folder } = Routes;
|
||||
|
||||
return (album + playlist + search + folder).includes(name as string);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.name,
|
||||
@ -88,7 +91,6 @@ watch(
|
||||
display: grid;
|
||||
grid-template-columns: 1fr min-content;
|
||||
width: 100%;
|
||||
// gap: $small;
|
||||
|
||||
.left {
|
||||
display: grid;
|
||||
|
@ -1,19 +1,33 @@
|
||||
<template>
|
||||
<div class="title albumnavtitle">
|
||||
<PlayBtn :source="things.source" :store="things.store" />
|
||||
<div class="ellip">
|
||||
{{ things.text }}
|
||||
<div
|
||||
class="title grid albumnavtitle"
|
||||
:class="{
|
||||
hide_play: header_shown,
|
||||
}"
|
||||
>
|
||||
<div class="first grid">
|
||||
<PlayBtn :source="things.source" :store="things.store" />
|
||||
<div class="ellip">
|
||||
{{ things.text }}
|
||||
</div>
|
||||
</div>
|
||||
<Input :page="($route.name as string)" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from "vue-router";
|
||||
import { computed } from "@vue/reactivity";
|
||||
|
||||
import PlayBtn from "@/components/shared/PlayBtn.vue";
|
||||
import { playSources, Routes } from "@/composables/enums";
|
||||
import useAlbumStore from "@/stores/pages/album";
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import { computed } from "@vue/reactivity";
|
||||
import { useRoute } from "vue-router";
|
||||
import Input from "@/components/shared/Input.vue";
|
||||
|
||||
defineProps<{
|
||||
header_shown: boolean;
|
||||
}>();
|
||||
|
||||
const things = computed(() => {
|
||||
const route = useRoute();
|
||||
@ -46,10 +60,22 @@ const things = computed(() => {
|
||||
|
||||
<style lang="scss">
|
||||
.albumnavtitle {
|
||||
display: flex;
|
||||
grid-template-columns: max-content 1fr;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: $small;
|
||||
outline: solid 1px $gray3;
|
||||
height: 100%;
|
||||
|
||||
.first {
|
||||
grid-template-columns: max-content 1fr;
|
||||
gap: $small;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.albumnavtitle.hide_play {
|
||||
.first {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,37 +1,43 @@
|
||||
<template>
|
||||
<div id="folder-nav-title">
|
||||
<div class="folder">
|
||||
<div class="fname">
|
||||
<div
|
||||
class="icon image"
|
||||
@click="
|
||||
$router.push({ name: Routes.folder, params: { path: '$home' } })
|
||||
"
|
||||
></div>
|
||||
<div class="paths">
|
||||
<div class="fname-wrapper">
|
||||
<div class="fname">
|
||||
<div
|
||||
class="path"
|
||||
v-for="path in subPaths.slice(1)"
|
||||
:key="path.path"
|
||||
:class="{ inthisfolder: path.active }"
|
||||
>
|
||||
<router-link
|
||||
class="text"
|
||||
:to="{ name: Routes.folder, params: { path: path.path } }"
|
||||
>{{ path.name }}</router-link
|
||||
class="icon image"
|
||||
@click="
|
||||
$router.push({ name: Routes.folder, params: { path: '$home' } })
|
||||
"
|
||||
></div>
|
||||
<div class="paths">
|
||||
<div
|
||||
class="path"
|
||||
v-for="path in subPaths.slice(1)"
|
||||
:key="path.path"
|
||||
:class="{ inthisfolder: path.active }"
|
||||
>
|
||||
<router-link
|
||||
class="text"
|
||||
:to="{ name: Routes.folder, params: { path: path.path } }"
|
||||
>{{ path.name }}</router-link
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Input :page="Routes.folder" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { focusElem } from "@/utils";
|
||||
import { subPath } from "@/interfaces";
|
||||
import { onUpdated } from "vue";
|
||||
import Input from "@/components/shared/Input.vue";
|
||||
import { Routes } from "@/composables/enums";
|
||||
import { subPath } from "@/interfaces";
|
||||
import { focusElem } from "@/utils";
|
||||
import { onUpdated } from "vue";
|
||||
|
||||
defineProps<{
|
||||
subPaths: subPath[];
|
||||
@ -44,24 +50,16 @@ onUpdated(() => {
|
||||
|
||||
<style lang="scss">
|
||||
#folder-nav-title {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
|
||||
.folder {
|
||||
display: flex;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr max-content;
|
||||
gap: $small;
|
||||
|
||||
.playbtnrect {
|
||||
height: 2.25rem;
|
||||
}
|
||||
|
||||
.drop-btn {
|
||||
width: 2.25rem;
|
||||
|
||||
.drop-icon {
|
||||
height: 2.25rem;
|
||||
width: 2.25rem;
|
||||
}
|
||||
.fname-wrapper {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.fname {
|
||||
@ -70,8 +68,9 @@ onUpdated(() => {
|
||||
height: 2.25rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: auto;
|
||||
padding-right: $smaller;
|
||||
width: fit-content;
|
||||
max-width: 100%;
|
||||
|
||||
.icon {
|
||||
height: 2rem;
|
||||
@ -79,7 +78,6 @@ onUpdated(() => {
|
||||
background-image: url("../../../assets/icons/folder.fill.svg");
|
||||
background-size: 1.5rem;
|
||||
margin-left: $smaller;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.paths {
|
||||
@ -97,10 +95,6 @@ onUpdated(() => {
|
||||
white-space: nowrap;
|
||||
margin: auto 0;
|
||||
|
||||
a {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.text {
|
||||
padding: $smaller;
|
||||
border-radius: $smaller;
|
||||
|
@ -20,10 +20,10 @@ import QueueActions from "@/components/RightSideBar/Queue/QueueActions.vue";
|
||||
import { FromOptions, Routes } from "@/composables/enums";
|
||||
import useQueueStore from "@/stores/queue";
|
||||
|
||||
import FolderSvg from "@/assets/icons/folder.svg";
|
||||
import SearchSvg from "@/assets/icons/search.svg";
|
||||
import AlbumSvg from "@/assets/icons/album.svg";
|
||||
import FolderSvg from "@/assets/icons/folder.svg";
|
||||
import PlaylistSvg from "@/assets/icons/playlist.svg";
|
||||
import SearchSvg from "@/assets/icons/search.svg";
|
||||
|
||||
import { RouteLocationRaw } from "vue-router";
|
||||
|
||||
@ -120,9 +120,6 @@ const { name, icon: SourceIcon, location } = getSource();
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.queue-actions {
|
||||
|
@ -23,6 +23,7 @@ import SearchInput from "@/components/RightSideBar/SearchInput.vue";
|
||||
#ginner {
|
||||
max-width: 30rem;
|
||||
margin: 0 auto;
|
||||
border-radius: $small;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
|
18
src/components/nav/Titles/SettingsTitle.vue
Normal file
18
src/components/nav/Titles/SettingsTitle.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div class="settings-nav">
|
||||
<SimpleTitle :text="'Settings'" />
|
||||
<Nav />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SimpleTitle from "./SimpleTitle.vue";
|
||||
import Nav from "@/components/SettingsView/Nav.vue";
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.settings-nav {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr max-content;
|
||||
}
|
||||
</style>
|
@ -32,7 +32,6 @@ defineProps<{
|
||||
border-radius: 0.75rem;
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
|
||||
.artist-image {
|
||||
width: 100%;
|
||||
|
@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div v-tooltip="returnArtists()" style="width: auto;">
|
||||
<div class="ellip" v-if="artists[0] !== '' && artists.length > 1">
|
||||
<div v-tooltip="returnArtists()" style="width: auto">
|
||||
<div class="ellip" v-if="artists[0] === '' && artists.length === 1">
|
||||
<span>{{ albumartist }}</span>
|
||||
</div>
|
||||
<div class="ellip" v-else>
|
||||
<span v-for="artist in putCommas(artists)" :key="artist">{{
|
||||
artist
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="ellip" v-else>
|
||||
<span>{{ albumartist }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
59
src/components/shared/Input.vue
Normal file
59
src/components/shared/Input.vue
Normal file
@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<input
|
||||
type="search"
|
||||
class="header-input rounded-sm pad-sm"
|
||||
placeholder="search here"
|
||||
v-model.trim="source"
|
||||
id="page-search"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import useFolderStore from "@/stores/pages/folder";
|
||||
import useAlbumStore from "@/stores/pages/album";
|
||||
|
||||
import { storeToRefs } from "pinia";
|
||||
import { Routes } from "@/composables/enums";
|
||||
|
||||
const { query: playlistQuery } = storeToRefs(usePStore());
|
||||
const { query: folderQuery } = storeToRefs(useFolderStore());
|
||||
const { query: albumQuery } = storeToRefs(useAlbumStore());
|
||||
|
||||
const props = defineProps<{
|
||||
page: Routes | string;
|
||||
}>();
|
||||
|
||||
function getRef() {
|
||||
switch (props.page) {
|
||||
case Routes.playlist:
|
||||
return playlistQuery;
|
||||
|
||||
case Routes.folder:
|
||||
return folderQuery;
|
||||
|
||||
case Routes.album:
|
||||
return albumQuery;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const source = getRef();
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.header-input {
|
||||
background-color: $gray3;
|
||||
outline: none;
|
||||
border: none;
|
||||
color: inherit;
|
||||
font-size: 1rem;
|
||||
z-index: 200;
|
||||
|
||||
&:focus {
|
||||
outline: solid;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -5,7 +5,9 @@
|
||||
@dblclick="emitUpdate(track)"
|
||||
@contextmenu.prevent="showMenu"
|
||||
>
|
||||
<div class="index t-center ellip">{{ index }}</div>
|
||||
<div class="index t-center ellip">
|
||||
{{ index }}
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div @click="emitUpdate(track)" class="thumbnail">
|
||||
<img
|
||||
@ -54,6 +56,7 @@
|
||||
class="options-icon circular"
|
||||
:class="{ options_button_clicked }"
|
||||
@click.stop="showMenu"
|
||||
@dblclick.stop="() => {}"
|
||||
>
|
||||
<OptionSvg />
|
||||
</div>
|
||||
@ -79,17 +82,17 @@ const artisttitle = ref<HTMLElement | null>(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 {
|
||||
|
@ -24,14 +24,7 @@
|
||||
</div>
|
||||
<hr />
|
||||
<div class="artist">
|
||||
<div class="ellip" v-if="track.artists[0] !== ''">
|
||||
<span v-for="artist in putCommas(track.artists)" :key="artist">{{
|
||||
artist
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="ellip" v-else>
|
||||
<span>{{ track.albumartist }}</span>
|
||||
</div>
|
||||
<ArtistName :artists="track.artists" :albumartist="track.albumartist" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@ -47,12 +40,12 @@
|
||||
<script setup lang="ts">
|
||||
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");
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 },
|
||||
],
|
||||
};
|
||||
|
@ -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: [],
|
||||
};
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -20,7 +20,7 @@ export interface Setting {
|
||||
}
|
||||
|
||||
export interface SettingGroup {
|
||||
title?: string;
|
||||
name?: string;
|
||||
desc?: string;
|
||||
settings: Setting[];
|
||||
}
|
||||
|
@ -81,7 +81,6 @@ const routes = [
|
||||
|
||||
await store
|
||||
.fetchTracksAndArtists(to.params.hash)
|
||||
.then(() => store.fetchBio(to.params.hash))
|
||||
.then(() => {
|
||||
state.loading.value = false;
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
13
src/settings/general/sidebar.ts
Normal file
13
src/settings/general/sidebar.ts
Normal file
@ -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];
|
@ -4,20 +4,32 @@ export default defineStore("Loader", {
|
||||
state: () => ({
|
||||
loading: false,
|
||||
duration: 0,
|
||||
page: <HTMLHtmlElement | null>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;
|
||||
}
|
||||
},
|
||||
|
@ -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: <AlbumInfo>{},
|
||||
tracks: <Track[]>[],
|
||||
discs: <Discs>{},
|
||||
allTracks: <Track[]>[],
|
||||
artists: <Artist[]>[],
|
||||
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<FuseResult[]> {
|
||||
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);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -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: <string>{},
|
||||
dirs: <Folder[]>[],
|
||||
tracks: <Track[]>[],
|
||||
allDirs: <Folder[]>[],
|
||||
allTracks: <Track[]>[],
|
||||
}),
|
||||
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<FuseResult[]> {
|
||||
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;
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -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: <Playlist>{},
|
||||
tracks: <Track[]>[],
|
||||
query: "",
|
||||
allTracks: <Track[]>[],
|
||||
artists: <Artist[]>[],
|
||||
}),
|
||||
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<FuseResult[]> {
|
||||
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;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -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,
|
||||
};
|
||||
|
18
src/utils/useFuse.ts
Normal file
18
src/utils/useFuse.ts
Normal file
@ -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;
|
||||
};
|
@ -24,7 +24,11 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "@vue/reactivity";
|
||||
import { onBeforeRouteUpdate, RouteLocationNormalized } from "vue-router";
|
||||
import {
|
||||
onBeforeRouteLeave,
|
||||
onBeforeRouteUpdate,
|
||||
RouteLocationNormalized,
|
||||
} from "vue-router";
|
||||
|
||||
import SongList from "@/components/FolderView/SongList.vue";
|
||||
import FolderList from "@/components/FolderView/FolderList.vue";
|
||||
@ -47,24 +51,27 @@ function getFolderName(route: RouteLocationNormalized) {
|
||||
}
|
||||
|
||||
function playFromPage(index: number) {
|
||||
queue.playFromFolder(folder.path, folder.tracks);
|
||||
queue.playFromFolder(folder.path, folder.allTracks);
|
||||
queue.play(index);
|
||||
}
|
||||
|
||||
onBeforeRouteUpdate((to, from) => {
|
||||
if (isSameRoute(to, from)) return;
|
||||
|
||||
loader.startLoading();
|
||||
folder
|
||||
.fetchAll(to.params.path as string)
|
||||
|
||||
.then(() => {
|
||||
scrollable.value.scrollTop = 0;
|
||||
folder.resetQuery();
|
||||
})
|
||||
.then(() => {
|
||||
loader.stopLoading();
|
||||
});
|
||||
});
|
||||
|
||||
onBeforeRouteLeave(() => {
|
||||
setTimeout(() => folder.resetQuery(), 500);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -36,7 +36,7 @@ import Main from "@/components/RightSideBar/Search/Main.vue";
|
||||
|
||||
#tracks-results {
|
||||
margin-right: 1rem;
|
||||
margin-left: -2.25rem;
|
||||
// margin-left: -2.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,28 +1,9 @@
|
||||
<template>
|
||||
<div class="settingspage">
|
||||
<div class="scrollable">
|
||||
<Content :current="0" />
|
||||
</div>
|
||||
<Nav />
|
||||
<Content :current="0" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Nav from "@/components/SettingsView/Nav.vue";
|
||||
import Content from "../components/SettingsView/Content.vue";
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.settingspage {
|
||||
height: 100%;
|
||||
padding: 1rem;
|
||||
display: grid;
|
||||
grid-template-rows: 1fr max-content;
|
||||
gap: 1rem;
|
||||
background-color: $black;
|
||||
|
||||
.scrollable {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</script>
|
@ -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);
|
||||
}
|
||||
</script>
|
||||
|
@ -12,24 +12,26 @@
|
||||
<script setup lang="ts">
|
||||
import useAStore from "@/stores/pages/album";
|
||||
import {
|
||||
onBeforeRouteLeave,
|
||||
onBeforeRouteUpdate,
|
||||
RouteLocationNormalized,
|
||||
RouteParams,
|
||||
} from "vue-router";
|
||||
|
||||
import Page from "@/layouts/HeaderContentBottom.vue";
|
||||
import Content from "./Content.vue";
|
||||
import Header from "./Header.vue";
|
||||
import Content from "./Content.vue";
|
||||
import Page from "@/layouts/HeaderContentBottom.vue";
|
||||
|
||||
const album = useAStore();
|
||||
|
||||
function fetchAlbumBio(params: RouteParams) {
|
||||
album.fetchBio(params.hash.toString());
|
||||
}
|
||||
|
||||
onBeforeRouteUpdate(async (to: RouteLocationNormalized) => {
|
||||
await album
|
||||
.fetchTracksAndArtists(to.params.hash.toString())
|
||||
.then(() => album.fetchBio(to.params.hash.toString()));
|
||||
.then(() => album.resetQuery());
|
||||
});
|
||||
|
||||
onBeforeRouteLeave(() => {
|
||||
setTimeout(() => {
|
||||
album.resetQuery();
|
||||
}, 500);
|
||||
});
|
||||
</script>
|
||||
|
@ -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);
|
||||
}
|
||||
</script>
|
||||
|
@ -1,37 +1,37 @@
|
||||
<template>
|
||||
<Page>
|
||||
<template #header>
|
||||
<Header :info="playlist.info" />
|
||||
<Header :info="playlist" />
|
||||
</template>
|
||||
<template #content>
|
||||
<Content
|
||||
:tracks="playlist.tracks"
|
||||
:count="playlist.info?.count"
|
||||
:name="playlist.info.name"
|
||||
:playlistid="playlist.info.playlistid"
|
||||
:tracks="tracks"
|
||||
:count="playlist.count"
|
||||
:name="playlist.name"
|
||||
:playlistid="playlist.playlistid"
|
||||
/>
|
||||
</template>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from "pinia";
|
||||
import Page from "@/layouts/HeaderContentBottom.vue";
|
||||
|
||||
import Header from "@/components/PlaylistView/Header.vue";
|
||||
import Content from "./Content.vue";
|
||||
|
||||
import usePTrackStore from "@/stores/pages/playlist";
|
||||
import { onMounted, onUnmounted } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import usePlaylistStore from "@/stores/pages/playlist";
|
||||
import { onBeforeRouteLeave } from "vue-router";
|
||||
|
||||
const route = useRoute();
|
||||
const playlist = usePTrackStore();
|
||||
const store = usePlaylistStore();
|
||||
const { info: playlist, tracks } = storeToRefs(store);
|
||||
|
||||
onMounted(() => {
|
||||
playlist.fetchArtists(route.params.pid as string);
|
||||
onBeforeRouteLeave(() => {
|
||||
setTimeout(() => {
|
||||
store.resetQuery();
|
||||
}, 500);
|
||||
});
|
||||
|
||||
onUnmounted(() => playlist.resetArtists());
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
|
41
yarn.lock
41
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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user