mirror of
https://github.com/tcsenpai/swingmusic.git
synced 2025-07-28 13:41:42 +00:00
highlight the selected when you go to folder
This commit is contained in:
parent
09c588c856
commit
4688665156
25
src/App.vue
25
src/App.vue
@ -21,8 +21,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Navigation from "@/components/LeftSidebar/Navigation.vue";
|
import { useRouter, useRoute, RouteLocationNormalized } from "vue-router";
|
||||||
|
import { onStartTyping } from "@vueuse/core";
|
||||||
|
|
||||||
|
import Navigation from "@/components/LeftSidebar/Navigation.vue";
|
||||||
import RightSideBar from "@/components/RightSideBar/Main.vue";
|
import RightSideBar from "@/components/RightSideBar/Main.vue";
|
||||||
import nowPlaying from "@/components/LeftSidebar/nowPlaying.vue";
|
import nowPlaying from "@/components/LeftSidebar/nowPlaying.vue";
|
||||||
import NavBar from "@/components/nav/NavBar.vue";
|
import NavBar from "@/components/nav/NavBar.vue";
|
||||||
@ -33,14 +35,16 @@ import ContextMenu from "@/components/contextMenu.vue";
|
|||||||
import Modal from "@/components/modal.vue";
|
import Modal from "@/components/modal.vue";
|
||||||
import Notification from "@/components/Notification.vue";
|
import Notification from "@/components/Notification.vue";
|
||||||
import useQStore from "@/stores/queue";
|
import useQStore from "@/stores/queue";
|
||||||
import useShortcuts from "@/composables/useKeyboard";
|
|
||||||
import Logo from "@/components/Logo.vue";
|
import Logo from "@/components/Logo.vue";
|
||||||
import { useRouter } from "vue-router";
|
|
||||||
import { onStartTyping } from "@vueuse/core";
|
import useShortcuts from "@/composables/useKeyboard";
|
||||||
|
import { isSameRoute } from "@/composables/perks";
|
||||||
|
|
||||||
const context_store = useContextStore();
|
const context_store = useContextStore();
|
||||||
const queue = useQStore();
|
const queue = useQStore();
|
||||||
const app_dom = document.getElementById("app");
|
const app_dom = document.getElementById("app");
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
queue.readQueue();
|
queue.readQueue();
|
||||||
useShortcuts(useQStore);
|
useShortcuts(useQStore);
|
||||||
@ -51,7 +55,18 @@ app_dom.addEventListener("click", (e) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
useRouter().afterEach(() => {
|
function removeHighlight(route: RouteLocationNormalized) {
|
||||||
|
setTimeout(() => {
|
||||||
|
router.push({ name: route.name, params: route.params });
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
router.afterEach((to, from) => {
|
||||||
|
const hid = to.query.highlight as string;
|
||||||
|
|
||||||
|
if (hid) removeHighlight(to);
|
||||||
|
if (isSameRoute(to, from)) return;
|
||||||
|
|
||||||
document.getElementById("acontent")?.scrollTo(0, 0);
|
document.getElementById("acontent")?.scrollTo(0, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5,11 +5,12 @@
|
|||||||
<SongItem
|
<SongItem
|
||||||
v-for="track in getTracks()"
|
v-for="track in getTracks()"
|
||||||
:key="track.trackid"
|
:key="track.trackid"
|
||||||
:song="track"
|
:track="track"
|
||||||
:index="track.index"
|
:index="track.index"
|
||||||
@updateQueue="updateQueue"
|
@updateQueue="updateQueue"
|
||||||
:isPlaying="queue.playing"
|
:isPlaying="queue.playing"
|
||||||
:isCurrent="queue.currentid == track.trackid"
|
:isCurrent="queue.currentid == track.trackid"
|
||||||
|
:isHighlighted="highlightid == track.trackid"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -22,12 +23,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRoute } from "vue-router";
|
import { onBeforeRouteUpdate, useRoute } from "vue-router";
|
||||||
|
|
||||||
import SongItem from "../shared/SongItem.vue";
|
import SongItem from "../shared/SongItem.vue";
|
||||||
|
|
||||||
import useQStore from "../../stores/queue";
|
import { focusElem } from "@/composables/perks";
|
||||||
import { Track } from "../../interfaces";
|
import { onMounted, onUpdated, ref } from "vue";
|
||||||
|
import { Track } from "@/interfaces";
|
||||||
|
import useQStore from "@/stores/queue";
|
||||||
|
|
||||||
const queue = useQStore();
|
const queue = useQStore();
|
||||||
|
|
||||||
@ -39,8 +42,34 @@ const props = defineProps<{
|
|||||||
on_album_page?: boolean;
|
on_album_page?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let route = useRoute().name;
|
const route = useRoute();
|
||||||
|
const routename = route.name as string;
|
||||||
|
const highlightid = ref(route.query.highlight as string);
|
||||||
|
|
||||||
|
function highlightTrack(trackid: string) {
|
||||||
|
focusElem(`track-${trackid}`, 400, "center");
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeRouteUpdate(async (to, from) => {
|
||||||
|
const hid = to.query.highlight as string;
|
||||||
|
highlightid.value = hid as string;
|
||||||
|
|
||||||
|
if (hid) {
|
||||||
|
highlightTrack(hid as string);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onUpdated(() => {
|
||||||
|
if (highlightid.value) {
|
||||||
|
highlightTrack(highlightid.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (highlightid.value) {
|
||||||
|
highlightTrack(highlightid.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
/**
|
/**
|
||||||
* Plays a clicked track and updates the queue
|
* Plays a clicked track and updates the queue
|
||||||
*
|
*
|
||||||
@ -51,7 +80,7 @@ function updateQueue(track: Track) {
|
|||||||
(t: Track) => t.trackid === track.trackid
|
(t: Track) => t.trackid === track.trackid
|
||||||
);
|
);
|
||||||
|
|
||||||
switch (route) {
|
switch (routename) {
|
||||||
case "FolderView":
|
case "FolderView":
|
||||||
queue.playFromFolder(props.path, props.tracks);
|
queue.playFromFolder(props.path, props.tracks);
|
||||||
queue.play(index);
|
queue.play(index);
|
||||||
@ -118,10 +147,30 @@ function getTracks() {
|
|||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-on {
|
.context-on {
|
||||||
background-color: $gray4;
|
background-color: $gray4;
|
||||||
color: $white !important;
|
color: $white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.highlighted {
|
||||||
|
color: $white !important;
|
||||||
|
animation: blinker 1.5s ease 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blinker {
|
||||||
|
25% {
|
||||||
|
background-color: $gray4;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
background-color: $gray4;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,39 +1,46 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="songlist-item rounded"
|
class="songlist-item rounded"
|
||||||
:class="[{ current: props.isCurrent }, { 'context-on': context_on }]"
|
:class="[
|
||||||
@dblclick="emitUpdate(props.song)"
|
{ current: isCurrent },
|
||||||
|
{ 'context-on': context_on },
|
||||||
|
{
|
||||||
|
highlighted: isHighlighted,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
v-bind:class="`track-${track.trackid}`"
|
||||||
|
@dblclick="emitUpdate(track)"
|
||||||
@contextmenu="showContextMenu"
|
@contextmenu="showContextMenu"
|
||||||
>
|
>
|
||||||
<div class="index">{{ props.index }}</div>
|
<div class="index">{{ index }}</div>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div @click="emitUpdate(props.song)" class="thumbnail">
|
<div @click="emitUpdate(track)" class="thumbnail">
|
||||||
<img
|
<img
|
||||||
:src="imguri + props.song.image"
|
:src="imguri + track.image"
|
||||||
alt=""
|
alt=""
|
||||||
class="album-art image rounded"
|
class="album-art image rounded"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="now-playing-track image"
|
class="now-playing-track image"
|
||||||
v-if="props.isPlaying && props.isCurrent"
|
v-if="isPlaying && isCurrent"
|
||||||
:class="{ active: isPlaying, not_active: !isPlaying }"
|
:class="{ active: isPlaying, not_active: !isPlaying }"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<div @click="emitUpdate(props.song)">
|
<div @click="emitUpdate(track)">
|
||||||
<span class="ellip title">{{ props.song.title }}</span>
|
<span class="ellip title">{{ track.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="song-artists">
|
<div class="song-artists">
|
||||||
<div class="ellip" v-if="props.song.artists[0] !== ''">
|
<div class="ellip" v-if="track.artists[0] !== ''">
|
||||||
<span
|
<span
|
||||||
class="artist"
|
class="artist"
|
||||||
v-for="artist in putCommas(props.song.artists)"
|
v-for="artist in putCommas(track.artists)"
|
||||||
:key="artist"
|
:key="artist"
|
||||||
>{{ artist }}</span
|
>{{ artist }}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="ellip" v-else>
|
<div class="ellip" v-else>
|
||||||
<span class="artist">{{ props.song.albumartist }}</span>
|
<span class="artist">{{ track.albumartist }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<router-link
|
<router-link
|
||||||
@ -41,14 +48,14 @@
|
|||||||
:to="{
|
:to="{
|
||||||
name: 'AlbumView',
|
name: 'AlbumView',
|
||||||
params: {
|
params: {
|
||||||
hash: props.song.albumhash,
|
hash: track.albumhash,
|
||||||
},
|
},
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ props.song.album }}
|
{{ track.album }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="song-duration">
|
<div class="song-duration">
|
||||||
<div class="text">{{ formatSeconds(props.song.length) }}</div>
|
<div class="text">{{ formatSeconds(track.length) }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="options-icon circular"
|
class="options-icon circular"
|
||||||
@ -66,17 +73,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { putCommas, formatSeconds } from "@/composables/perks";
|
import OptionSvg from "@/assets/icons/more.svg";
|
||||||
|
import { ContextSrc } from "@/composables/enums";
|
||||||
|
import { formatSeconds, putCommas } from "@/composables/perks";
|
||||||
import useContextStore from "@/stores/context";
|
import useContextStore from "@/stores/context";
|
||||||
import useModalStore from "@/stores/modal";
|
import useModalStore from "@/stores/modal";
|
||||||
import useQueueStore from "@/stores/queue";
|
import useQueueStore from "@/stores/queue";
|
||||||
import { ContextSrc } from "@/composables/enums";
|
|
||||||
import OptionSvg from "@/assets/icons/more.svg";
|
|
||||||
|
|
||||||
import { ref } from "vue";
|
import { paths } from "@/config";
|
||||||
import trackContext from "@/contexts/track_context";
|
import trackContext from "@/contexts/track_context";
|
||||||
import { Track } from "@/interfaces";
|
import { Track } from "@/interfaces";
|
||||||
import { paths } from "@/config";
|
import { ref } from "vue";
|
||||||
|
|
||||||
const contextStore = useContextStore();
|
const contextStore = useContextStore();
|
||||||
|
|
||||||
@ -88,7 +95,7 @@ const showContextMenu = (e: Event) => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
const menus = trackContext(props.song, useModalStore, useQueueStore);
|
const menus = trackContext(props.track, useModalStore, useQueueStore);
|
||||||
|
|
||||||
contextStore.showContextMenu(e, menus, ContextSrc.Track);
|
contextStore.showContextMenu(e, menus, ContextSrc.Track);
|
||||||
context_on.value = true;
|
context_on.value = true;
|
||||||
@ -102,10 +109,11 @@ const showContextMenu = (e: Event) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
song: Track;
|
track: Track;
|
||||||
index: Number;
|
index: Number;
|
||||||
isPlaying: Boolean;
|
isPlaying: Boolean;
|
||||||
isCurrent: Boolean;
|
isCurrent: Boolean;
|
||||||
|
isHighlighted: Boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { RouteLocationNormalized } from "vue-router";
|
||||||
|
|
||||||
const putCommas = (artists: string[]) => {
|
const putCommas = (artists: string[]) => {
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
@ -12,14 +14,14 @@ const putCommas = (artists: string[]) => {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
function focusElem(className: string, delay?: number) {
|
function focusElem(className: string, delay?: number, pos?: any) {
|
||||||
const dom = document.getElementsByClassName(className)[0];
|
const dom = document.getElementsByClassName(className)[0];
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (dom) {
|
if (dom) {
|
||||||
dom.scrollIntoView({
|
dom.scrollIntoView({
|
||||||
behavior: "smooth",
|
behavior: "smooth",
|
||||||
block: "start",
|
block: `${pos ?? "start"}` as any,
|
||||||
inline: "center",
|
inline: "center",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -37,6 +39,16 @@ function getElem(id: string, type: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type r = RouteLocationNormalized;
|
||||||
|
|
||||||
|
function isSameRoute(to: r, from: r) {
|
||||||
|
if (to.params.path == from.params.path) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts seconds into minutes and hours.
|
* Converts seconds into minutes and hours.
|
||||||
* @param seconds The seconds to convert
|
* @param seconds The seconds to convert
|
||||||
@ -80,4 +92,4 @@ function formatSeconds(seconds: number, long?: boolean) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { putCommas, focusElem, formatSeconds, getElem };
|
export { putCommas, focusElem, formatSeconds, getElem, isSameRoute };
|
||||||
|
@ -90,6 +90,7 @@ export default async (
|
|||||||
Router.push({
|
Router.push({
|
||||||
name: "FolderView",
|
name: "FolderView",
|
||||||
params: { path: track.folder },
|
params: { path: track.folder },
|
||||||
|
query: { highlight: track.trackid },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: "folder",
|
icon: "folder",
|
||||||
|
@ -38,7 +38,7 @@ const props = defineProps<{
|
|||||||
/**
|
/**
|
||||||
* Called when the bottom container is raised.
|
* Called when the bottom container is raised.
|
||||||
*/
|
*/
|
||||||
onBottomRaised?: (routeparams?: RouteParams) => void;
|
bottomRaisedCallback?: (routeparams?: RouteParams) => void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let elem: HTMLElement = null;
|
let elem: HTMLElement = null;
|
||||||
@ -56,7 +56,8 @@ onMounted(() => {
|
|||||||
|
|
||||||
onBeforeRouteUpdate((to) => {
|
onBeforeRouteUpdate((to) => {
|
||||||
if (bottomContainerRaised.value) {
|
if (bottomContainerRaised.value) {
|
||||||
props.onBottomRaised(to.params);
|
if (!props.bottomRaisedCallback) return;
|
||||||
|
props.bottomRaisedCallback(to.params);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -83,7 +84,8 @@ function toggleBottom() {
|
|||||||
classlist.add("addbottompadding");
|
classlist.add("addbottompadding");
|
||||||
if (!bottomRaisedCallbackExecuted) {
|
if (!bottomRaisedCallbackExecuted) {
|
||||||
bottomRaisedCallbackExecuted = true;
|
bottomRaisedCallbackExecuted = true;
|
||||||
props.onBottomRaised(route.params);
|
if (!props.bottomRaisedCallback) return;
|
||||||
|
props.bottomRaisedCallback(route.params);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -16,16 +16,21 @@ import FolderList from "@/components/FolderView/FolderList.vue";
|
|||||||
|
|
||||||
import useFStore from "../stores/pages/folder";
|
import useFStore from "../stores/pages/folder";
|
||||||
import useLoaderStore from "../stores/loader";
|
import useLoaderStore from "../stores/loader";
|
||||||
|
import { isSameRoute } from "@/composables/perks";
|
||||||
|
|
||||||
const loader = useLoaderStore();
|
const loader = useLoaderStore();
|
||||||
const FStore = useFStore();
|
const FStore = useFStore();
|
||||||
|
|
||||||
const scrollable = ref(null);
|
const scrollable = ref(null);
|
||||||
|
|
||||||
onBeforeRouteUpdate((to) => {
|
onBeforeRouteUpdate((to, from) => {
|
||||||
|
if (isSameRoute(to, from)) return;
|
||||||
|
|
||||||
loader.startLoading();
|
loader.startLoading();
|
||||||
FStore.fetchAll(to.params.path as string)
|
FStore.fetchAll(to.params.path as string)
|
||||||
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
console.log("fetched");
|
||||||
scrollable.value.scrollTop = 0;
|
scrollable.value.scrollTop = 0;
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<Page :onBottomRaised="fetchAlbumBio">
|
<Page :bottomRaisedCallback="fetchAlbumBio">
|
||||||
<template #header>
|
<template #header>
|
||||||
<Header :album="album.info" />
|
<Header :album="album.info" />
|
||||||
</template>
|
</template>
|
||||||
@ -13,14 +13,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onBeforeRouteUpdate, RouteLocationNormalized, RouteParams } from "vue-router";
|
|
||||||
import useAStore from "@/stores/pages/album";
|
import useAStore from "@/stores/pages/album";
|
||||||
|
import { onBeforeRouteUpdate, RouteLocationNormalized, RouteParams } from "vue-router";
|
||||||
|
|
||||||
import Page from "@/layouts/HeaderContentBottom.vue";
|
import Page from "@/layouts/HeaderContentBottom.vue";
|
||||||
import Header from "./Header.vue";
|
|
||||||
import Content from "./Content.vue";
|
|
||||||
import Bottom from "./Bottom.vue";
|
import Bottom from "./Bottom.vue";
|
||||||
import { onBeforeUnmount } from "vue";
|
import Content from "./Content.vue";
|
||||||
|
import Header from "./Header.vue";
|
||||||
|
|
||||||
const album = useAStore();
|
const album = useAStore();
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ const playlist = usePTrackStore();
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
playlist.fetchArtists(route.params.pid as string);
|
playlist.fetchArtists(route.params.pid as string);
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss"></style>
|
<style lang="scss"></style>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user