add experimental remove from queue action

+ show albumartist on TrackItem if artists == ""
+ add action to reset playlist page artists to prevent content flashes
+ remove use of defaultTrackItem
This commit is contained in:
geoffrey45
2022-08-15 10:25:34 +03:00
parent 1f374eeda1
commit e1c9cfa99b
17 changed files with 122 additions and 79 deletions
+2 -2
View File
@@ -47,7 +47,7 @@ const queue = useQStore();
const router = useRouter(); const router = useRouter();
const modal = useModalStore(); const modal = useModalStore();
const context_store = useContextStore(); const context_store = useContextStore();
const app_dom = document.getElementById("app"); const app_dom = document.getElementById("app") as HTMLElement;
queue.readQueue(); queue.readQueue();
handleShortcuts(useQStore); handleShortcuts(useQStore);
@@ -59,7 +59,7 @@ app_dom.addEventListener("click", (e) => {
}); });
router.afterEach(() => { router.afterEach(() => {
document.getElementById("acontent")?.scrollTo(0, 0); (document.getElementById("acontent") as HTMLElement).scrollTo(0, 0);
}); });
onStartTyping(() => { onStartTyping(() => {
+5 -5
View File
@@ -19,7 +19,7 @@
@updateQueue="updateQueue" @updateQueue="updateQueue"
:isPlaying="queue.playing" :isPlaying="queue.playing"
:isCurrent="queue.currentid == track.trackid" :isCurrent="queue.currentid == track.trackid"
:isHighlighted="($route.query.highlight as string) == track.uniq_hash" :isHighlighted="($route.query.highlight as string) == track.hash"
/> />
</div> </div>
</div> </div>
@@ -39,12 +39,12 @@ import { onBeforeRouteUpdate, useRoute } from "vue-router";
import SongItem from "../shared/SongItem.vue"; import SongItem from "../shared/SongItem.vue";
import { Routes } from "@/composables/enums";
import { Track } from "@/interfaces";
import useAlbumStore from "@/stores/pages/album";
import useQStore from "@/stores/queue";
import { focusElem } from "@/utils"; import { focusElem } from "@/utils";
import { onMounted, onUpdated, ref } from "vue"; import { onMounted, onUpdated, ref } from "vue";
import { Track } from "@/interfaces";
import useQStore from "@/stores/queue";
import useAlbumStore from "@/stores/pages/album";
import { Routes } from "@/composables/enums";
const queue = useQStore(); const queue = useQStore();
const album = useAlbumStore(); const album = useAlbumStore();
+15 -11
View File
@@ -6,13 +6,13 @@
:to="{ :to="{
name: 'AlbumView', name: 'AlbumView',
params: { params: {
hash: track.albumhash, hash: track?.albumhash ? track.albumhash : ' ',
}, },
}" }"
> >
<div class="art"> <div class="art">
<img <img
:src="imguri + track.image" :src="imguri + track?.image"
alt="" alt=""
class="l-image rounded force-lm" class="l-image rounded force-lm"
loading="lazy" loading="lazy"
@@ -20,23 +20,27 @@
</div> </div>
</router-link> </router-link>
<div id="bitrate" v-if="track.bitrate"> <div id="bitrate" v-if="track?.bitrate">
<span v-if="track.bitrate > 1500">MASTER</span> <span v-if="track.bitrate > 1500">MASTER</span>
<span v-else-if="track.bitrate > 330">FLAC</span> <span v-else-if="track.bitrate > 330">FLAC</span>
<span v-else>MP3</span> <span v-else>MP3</span>
{{ track.bitrate }} {{ track.bitrate }}
</div> </div>
<div class="title ellip">{{ props.track.title }}</div> <div class="title ellip">{{ props.track?.title }}</div>
<div class="separator no-border"></div> <div class="separator no-border"></div>
<div class="artists ellip" v-if="props.track.artists[0] !== ''"> <div
<span class="artists ellip"
v-for="artist in putCommas(props.track.artists)" v-if="track?.artists && track?.artists[0] !== ''"
:key="artist"
>{{ artist }}</span
> >
<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>
<div class="artists" v-else> <div class="artists" v-else>
<span>{{ props.track.albumartist }}</span> <span>Meh</span>
</div> </div>
</div> </div>
</div> </div>
@@ -50,6 +54,6 @@ import { Track } from "../../../interfaces";
const imguri = paths.images.thumb; const imguri = paths.images.thumb;
const props = defineProps<{ const props = defineProps<{
track: Track; track: Track | null;
}>(); }>();
</script> </script>
+2 -1
View File
@@ -17,6 +17,8 @@
@playThis="queue.play(index)" @playThis="queue.play(index)"
:isCurrent="index === queue.current" :isCurrent="index === queue.current"
:isPlaying="queue.playing" :isPlaying="queue.playing"
:isQueueTrack="true"
:index="index"
/> />
</TransitionGroup> </TransitionGroup>
</div> </div>
@@ -88,7 +90,6 @@ onUpdated(() => {
grid-template-rows: max-content 1fr max-content; grid-template-rows: max-content 1fr max-content;
gap: $small; gap: $small;
.scrollable-r { .scrollable-r {
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
+7 -4
View File
@@ -6,14 +6,15 @@
@contextmenu.prevent="showMenu" @contextmenu.prevent="showMenu"
> >
<div class="nextup abs">next up</div> <div class="nextup abs">next up</div>
<img :src="paths.images.thumb + track.image" class="rounded" /> <img :src="paths.images.thumb + track?.image" class="rounded" />
<div class="tags"> <div class="tags">
<div class="title ellip">{{ track.title }}</div> <div class="title ellip">{{ track?.title || "Don't click here" }}</div>
<div class="artist ellip"> <div class="artist ellip" v-if="track">
<span v-for="artist in putCommas(track.artists)" :key="artist">{{ <span v-for="artist in putCommas(track.artists)" :key="artist">{{
artist artist
}}</span> }}</span>
</div> </div>
<span v-else class="artist">nothing will happen</span>
</div> </div>
</div> </div>
</template> </template>
@@ -27,14 +28,16 @@ import { showTrackContextMenu as showContext } from "@/composables/context";
import { ref } from "vue"; import { ref } from "vue";
const props = defineProps<{ const props = defineProps<{
track: Track; track: Track | null;
playNext: () => void; playNext: () => void;
}>(); }>();
const context_on = ref(false); const context_on = ref(false);
function showMenu(e: Event) { function showMenu(e: Event) {
if (props.track) {
showContext(e, props.track, context_on); showContext(e, props.track, context_on);
}
} }
</script> </script>
+2 -2
View File
@@ -8,7 +8,7 @@
highlighted: isHighlighted, highlighted: isHighlighted,
}, },
]" ]"
v-bind:class="`track-${track.uniq_hash}`" v-bind:class="`track-${track.hash}`"
@dblclick="emitUpdate(track)" @dblclick="emitUpdate(track)"
@contextmenu.prevent="showMenu" @contextmenu.prevent="showMenu"
> >
@@ -75,10 +75,10 @@ import { ref } from "vue";
import OptionSvg from "@/assets/icons/more.svg"; import OptionSvg from "@/assets/icons/more.svg";
import { showTrackContextMenu as showContext } from "@/composables/context";
import { paths } from "@/config"; import { paths } from "@/config";
import { Track } from "@/interfaces"; import { Track } from "@/interfaces";
import { formatSeconds, putCommas } from "@/utils"; import { formatSeconds, putCommas } from "@/utils";
import { showTrackContextMenu as showContext } from "@/composables/context";
const context_on = ref(false); const context_on = ref(false);
const imguri = paths.images.thumb; const imguri = paths.images.thumb;
+40 -12
View File
@@ -1,10 +1,10 @@
<template> <template>
<div <div
class="track-item" class="track-item"
@click="playThis(props.track)" @click="playThis(track)"
:class="[ :class="[
{ {
currentInQueue: props.isCurrent, currentInQueue: isCurrent,
}, },
{ contexton: context_on }, { contexton: context_on },
]" ]"
@@ -14,20 +14,32 @@
<img :src="paths.images.thumb + track.image" alt="" class="rounded" /> <img :src="paths.images.thumb + track.image" alt="" class="rounded" />
<div <div
class="now-playing-track-indicator image" class="now-playing-track-indicator image"
v-if="props.isCurrent" v-if="isCurrent"
:class="{ last_played: !props.isPlaying }" :class="{ last_played: !isPlaying }"
></div> ></div>
</div> </div>
<div class="tags"> <div class="tags">
<div class="title ellip"> <div class="title ellip">
{{ props.track.title }} {{ track.title }}
</div> </div>
<hr /> <hr />
<div class="artist ellip"> <div class="artist">
<span v-for="artist in putCommas(props.track.artists)" :key="artist">{{ <div class="ellip" v-if="track.artists[0] !== ''">
<span v-for="artist in putCommas(track.artists)" :key="artist">{{
artist artist
}}</span> }}</span>
</div> </div>
<div class="ellip" v-else>
<span>{{ track.albumartist }}</span>
</div>
</div>
</div>
<div
class="remove-track flex"
@click.stop="queue.removeFromQueue(index)"
v-if="isQueueTrack"
>
<DelSvg />
</div> </div>
</div> </div>
</template> </template>
@@ -39,13 +51,18 @@ import { paths } from "@/config";
import { putCommas } from "@/utils"; import { putCommas } from "@/utils";
import { Track } from "@/interfaces"; import { Track } from "@/interfaces";
import { showTrackContextMenu as showContext } from "@/composables/context"; import { showTrackContextMenu as showContext } from "@/composables/context";
import DelSvg from "@/assets/icons/plus.svg";
import useQueueStore from "@/stores/queue";
const props = defineProps<{ const props = defineProps<{
track: Track; track: Track;
isCurrent: boolean; isCurrent: boolean;
isPlaying: boolean; isPlaying: boolean;
isQueueTrack?: boolean;
index?: number;
}>(); }>();
const queue = useQueueStore();
const context_on = ref(false); const context_on = ref(false);
function showMenu(e: Event) { function showMenu(e: Event) {
@@ -73,11 +90,26 @@ const playThis = (track: Track) => {
.track-item { .track-item {
display: grid; display: grid;
grid-template-columns: min-content 1fr; grid-template-columns: min-content 1fr max-content;
align-items: center; align-items: center;
padding: $small 1rem; padding: $small 1rem;
.remove-track {
opacity: 0;
transition: all 0.25s ease;
transform: translateX(1rem) rotate(45deg);
&:hover { &:hover {
opacity: 1 !important;
}
}
&:hover {
.remove-track {
opacity: 0.5;
transform: translateX(0) rotate(45deg);
}
cursor: pointer; cursor: pointer;
background: linear-gradient(37deg, $gray4, $gray3, $gray3); background: linear-gradient(37deg, $gray4, $gray3, $gray3);
} }
@@ -87,10 +119,6 @@ const playThis = (track: Track) => {
margin: 0.1rem; margin: 0.1rem;
} }
// .tags {
// border: solid 1px;
// }
.album-art { .album-art {
display: flex; display: flex;
align-items: center; align-items: center;
+7 -5
View File
@@ -1,13 +1,15 @@
import { Playlist, Track } from "../interfaces"; import { Playlist, Track } from "../interfaces";
import Router from "../router";
// @ts-ignore
import { Option } from "../interfaces"; import { Option } from "../interfaces";
import Router from "../router";
import { import {
getAllPlaylists, addTrackToPlaylist, getAllPlaylists
addTrackToPlaylist,
} from "../composables/fetch/playlists"; } from "../composables/fetch/playlists";
import useQueueStore from "../stores/queue";
import useModalStore from "../stores/modal"; import useModalStore from "../stores/modal";
import useQueueStore from "../stores/queue";
/** /**
* Returns a list of context menu items for a track. * Returns a list of context menu items for a track.
* @param {any} track a track object. * @param {any} track a track object.
@@ -90,7 +92,7 @@ export default async (
Router.push({ Router.push({
name: "FolderView", name: "FolderView",
params: { path: track.folder }, params: { path: track.folder },
query: { highlight: track.uniq_hash }, query: { highlight: track.hash },
}); });
}, },
icon: "folder", icon: "folder",
+7 -7
View File
@@ -16,7 +16,7 @@ export interface Track {
tracknumber?: number; tracknumber?: number;
discnumber?: number; discnumber?: number;
index?: number; index?: number;
uniq_hash: string; hash: string;
copyright?: string; copyright?: string;
} }
@@ -62,12 +62,12 @@ export interface Playlist {
playlistid: string; playlistid: string;
name: string; name: string;
description?: string; description?: string;
image?: string | FormData; image: string | FormData;
tracks?: Track[]; tracks: Track[];
count?: number; count: number;
lastUpdated?: string; lastUpdated: string;
thumb?: string; thumb: string;
duration?: number; duration: number;
} }
export interface Notif { export interface Notif {
+1 -1
View File
@@ -40,7 +40,7 @@ const props = defineProps<{
/** /**
* Called when the bottom container is raised. * Called when the bottom container is raised.
*/ */
bottomRaisedCallback?: (routeparams?: RouteParams) => void; bottomRaisedCallback?: (routeparams: RouteParams) => void;
}>(); }>();
let elem: HTMLElement; let elem: HTMLElement;
+1
View File
@@ -53,6 +53,7 @@ export default defineStore("context-menu", {
hideContextMenu() { hideContextMenu() {
this.visible = false; this.visible = false;
this.src = null; this.src = null;
this.options = [];
}, },
hasManyChildren() { hasManyChildren() {
let result = false; let result = false;
+5 -2
View File
@@ -20,8 +20,8 @@ export default defineStore("playlist-tracks", {
async fetchAll(playlistid: string) { async fetchAll(playlistid: string) {
const playlist = await getPlaylist(playlistid); const playlist = await getPlaylist(playlistid);
this.info = playlist.info; this.info = playlist?.info || ({} as Playlist);
this.tracks = playlist.tracks; this.tracks = playlist?.tracks || [];
}, },
async fetchArtists(playlistid: string) { async fetchArtists(playlistid: string) {
@@ -35,5 +35,8 @@ export default defineStore("playlist-tracks", {
updatePInfo(info: Playlist) { updatePInfo(info: Playlist) {
this.info = info; this.info = info;
}, },
resetArtists() {
this.artists = [];
},
}, },
}); });
+12 -18
View File
@@ -35,14 +35,6 @@ function readCurrent(): number {
return 0; return 0;
} }
const defaultTrack = <Track>{
title: "Nothing played yet",
albumhash: " ",
artists: ["Alice"],
trackid: "",
image: "meh",
};
function shuffle(tracks: Track[]) { function shuffle(tracks: Track[]) {
const shuffled = tracks.slice(); const shuffled = tracks.slice();
for (let i = shuffled.length - 1; i > 0; i--) { for (let i = shuffled.length - 1; i > 0; i--) {
@@ -70,18 +62,19 @@ export default defineStore("Queue", {
current: 0, current: 0,
next: 0, next: 0,
prev: 0, prev: 0,
currentid: "", currentid: <string | null>"",
playing: false, playing: false,
from: {} as From, from: {} as From,
currenttrack: {} as Track, currenttrack: {} as Track,
tracklist: [defaultTrack] as Track[], tracklist: [] as Track[],
}), }),
actions: { actions: {
play(index: number = 0) { play(index: number = 0) {
if (this.tracklist.length === 0) return;
this.current = index; this.current = index;
const track = this.tracklist[index]; const track = this.tracklist[index];
this.currentid = track.trackid; this.currentid = track.trackid;
const uri = state.settings.uri + "/file/" + track.trackid; const uri = state.settings.uri + "/file/" + track.hash;
const elem = document.getElementById("progress") as HTMLElement; const elem = document.getElementById("progress") as HTMLElement;
this.updateCurrent(index); this.updateCurrent(index);
@@ -178,8 +171,8 @@ export default defineStore("Queue", {
this.currenttrack = track; this.currenttrack = track;
this.current = index; this.current = index;
this.currentid = track.trackid; this.currentid = track?.trackid || null;
this.duration.full = track.length || 0; this.duration.full = track?.length || 0;
}, },
setNewQueue(tracklist: Track[]) { setNewQueue(tracklist: Track[]) {
if (this.tracklist !== tracklist) { if (this.tracklist !== tracklist) {
@@ -256,15 +249,13 @@ export default defineStore("Queue", {
writeQueue(this.from, this.tracklist); writeQueue(this.from, this.tracklist);
}, },
clearQueue() { clearQueue() {
this.tracklist = [defaultTrack] as Track[]; this.tracklist = [] as Track[];
this.current = 0;
this.currentid = ""; this.currentid = "";
this.next = 0; this.current, this.next, (this.prev = 0);
this.prev = 0;
this.from = <From>{}; this.from = <From>{};
writeQueue(this.from, [defaultTrack] as Track[]);
writeCurrent(0); writeCurrent(0);
writeQueue(this.from, [] as Track[]);
}, },
shuffleQueue() { shuffleQueue() {
const Toast = useNotifStore(); const Toast = useNotifStore();
@@ -286,5 +277,8 @@ export default defineStore("Queue", {
writeQueue(this.from, shuffled); writeQueue(this.from, shuffled);
writeCurrent(0); writeCurrent(0);
}, },
removeFromQueue(index: number = 0) {
this.tracklist.splice(index, 1);
},
}, },
}); });
+4 -1
View File
@@ -2,7 +2,10 @@
<div class="bottom-content"> <div class="bottom-content">
<FeaturedArtists :artists="artists" /> <FeaturedArtists :artists="artists" />
<AlbumBio :bio="bio" :images="{ album: image, artist: artists[0].image }" /> <AlbumBio
:bio="bio"
:images="{ album: image, artist: artists[0]?.image }"
/>
</div> </div>
</template> </template>
+1 -1
View File
@@ -10,7 +10,7 @@
<Bottom <Bottom
:artists="album.artists" :artists="album.artists"
:bio="album.bio" :bio="album.bio"
:image="album.info.image" :image="album.info?.image"
/> />
</template> </template>
</Page> </Page>
+4 -2
View File
@@ -6,7 +6,7 @@
<template #content> <template #content>
<Content <Content
:tracks="playlist.tracks" :tracks="playlist.tracks"
:count="playlist.info.count" :count="playlist.info?.count"
:name="playlist.info.name" :name="playlist.info.name"
:playlistid="playlist.info.playlistid" :playlistid="playlist.info.playlistid"
/> />
@@ -25,7 +25,7 @@ import Content from "./Content.vue";
import FeaturedArtists from "@/components/PlaylistView/FeaturedArtists.vue"; import FeaturedArtists from "@/components/PlaylistView/FeaturedArtists.vue";
import usePTrackStore from "@/stores/pages/playlist"; import usePTrackStore from "@/stores/pages/playlist";
import { onBeforeUnmount, onMounted } from "vue"; import { onMounted, onUnmounted } from "vue";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
const route = useRoute(); const route = useRoute();
@@ -34,6 +34,8 @@ const playlist = usePTrackStore();
onMounted(() => { onMounted(() => {
playlist.fetchArtists(route.params.pid as string); playlist.fetchArtists(route.params.pid as string);
}); });
onUnmounted(() => playlist.reset());
</script> </script>
<style lang="scss"></style> <style lang="scss"></style>
+2
View File
@@ -16,3 +16,5 @@
- using square image on playlist - using square image on playlist
- Use bottom bar - Use bottom bar
- Fix search album and artist grid on 720p - Fix search album and artist grid on 720p
- Use persisted pinia for queue store
- Introduce stores (pinia-ish) for the server