mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-05 04:53:01 +00:00
🔷 refactor context menu to accept context src
🔷 add a getCurrentDate function to get formatted date
This commit is contained in:
+1
-28
@@ -1,63 +1,36 @@
|
|||||||
# Fixes !
|
# Fixes !
|
||||||
|
|
||||||
- [ ] Use click event to play song instead of url ⚠
|
|
||||||
- [ ] Show play/pause button correctly according to state ⚠
|
|
||||||
- [ ] Click on artist image to go to artist page ⚠
|
- [ ] Click on artist image to go to artist page ⚠
|
||||||
- [ ] Play next song if current song can't be loaded ⚠
|
- [ ] Play next song if current song can't be loaded ⚠
|
||||||
- [ ] List item song icon for long song titles ⚠
|
|
||||||
<!-- -->
|
<!-- -->
|
||||||
- [ ] Broken CSS
|
|
||||||
- [ ] Prevent scanning unchanged folders
|
|
||||||
- [ ] Handle '/' and '&' characters in song artists
|
|
||||||
- [ ] Nginx not serving all files in a folder
|
|
||||||
- [ ] Removing song duplicates from queries
|
- [ ] Removing song duplicates from queries
|
||||||
- [ ] Different songs having same link
|
|
||||||
- [ ] ConnectionError
|
|
||||||
- [ ] Move thumbnails to .config
|
|
||||||
- [ ] Write a multithreaded file server
|
|
||||||
- [ ] Add support for WAV files
|
- [ ] Add support for WAV files
|
||||||
- [ ] Support multiple folders
|
|
||||||
- [ ] Compress thumbnails
|
- [ ] Compress thumbnails
|
||||||
|
|
||||||
# Features +
|
# Features +
|
||||||
## Needed features
|
## Needed features
|
||||||
- [ ] Seeking current song
|
|
||||||
- [ ] Adding songs to queue
|
- [ ] Adding songs to queue
|
||||||
- [ ] Implement search on frontend
|
|
||||||
<!-- -->
|
<!-- -->
|
||||||
- [ ] Watching for changes in folders and updating them instantly ⚠
|
|
||||||
- [ ] Display folders and files in a tree view. ⚠ 🔵
|
|
||||||
<!-- -->
|
|
||||||
- [ ] Add favicon
|
|
||||||
- [ ] Add keyboard shortcuts
|
- [ ] Add keyboard shortcuts
|
||||||
- [ ] Right click on song to do stuff
|
|
||||||
- [ ] Adjust volume
|
- [ ] Adjust volume
|
||||||
- [ ] Add listening statistics for all songs
|
- [ ] Add listening statistics for all songs
|
||||||
- [ ] Extract color from artist image [for use with artist card gradient]
|
- [ ] Extract color from artist image [for use with artist card gradient]
|
||||||
- [ ] Adding songs to favorites
|
- [ ] Adding songs to favorites
|
||||||
- [ ] Adding songs to playlist
|
|
||||||
- [ ] Playing song radio
|
- [ ] Playing song radio
|
||||||
|
|
||||||
## Future features
|
## Future features
|
||||||
- [ ] Toggle shuffle
|
- [ ] Toggle shuffle
|
||||||
- [ ] Toggle repeat
|
- [ ] Toggle repeat
|
||||||
- [ ] Display artist albums
|
|
||||||
- [ ] Suggest similar artists
|
- [ ] Suggest similar artists
|
||||||
- [ ] Getting artist info
|
- [ ] Getting artist info
|
||||||
- [ ] Getting album info
|
|
||||||
- [ ] Create a Python script to build, bundle and serve the app
|
- [ ] Create a Python script to build, bundle and serve the app
|
||||||
- [ ] Getting extra song info (probably from genius)
|
- [ ] Getting extra song info (probably from genius)
|
||||||
- [ ] Getting lyrics
|
- [ ] Getting lyrics
|
||||||
- [ ] Notifications
|
|
||||||
- [ ] Sorting songs
|
- [ ] Sorting songs
|
||||||
- [ ] Suggest undiscorvered artists, albums and songs
|
- [ ] Suggest undiscorvered artists, albums and songs
|
||||||
- [ ] Remember last played song
|
- [ ] Remember last played song
|
||||||
- [ ] Add next and previous song transition and progress bar reset animations
|
- [ ] Add next and previous song transition and progress bar reset animations
|
||||||
- [ ] Hover animations for list items
|
- [ ] Add playlist to folder
|
||||||
- [ ] Highlight currently playing song in playlist
|
|
||||||
- [ ] Add functionality to 'Listen now' button
|
- [ ] Add functionality to 'Listen now' button
|
||||||
- [ ] Add a 'Scan' button to the sidebar
|
|
||||||
- [ ] Paginated requests for songs
|
- [ ] Paginated requests for songs
|
||||||
- [ ] Package app as installable PWA
|
- [ ] Package app as installable PWA
|
||||||
|
|
||||||
## Finished ✅
|
|
||||||
@@ -25,7 +25,6 @@
|
|||||||
<div class="text">Nothing down here 😑</div>
|
<div class="text">Nothing down here 😑</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else ref="songtitle"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<div class="info">
|
<div class="info">
|
||||||
<div class="btns">
|
<div class="btns">
|
||||||
<PlayBtnRect :source="playSources.playlist" />
|
<PlayBtnRect :source="playSources.playlist" />
|
||||||
|
<Option @showDropdown="showDropdown" :src="context.src" />
|
||||||
</div>
|
</div>
|
||||||
<div class="duration">
|
<div class="duration">
|
||||||
<span v-if="props.info.count == 0">No Tracks</span>
|
<span v-if="props.info.count == 0">No Tracks</span>
|
||||||
@@ -37,11 +38,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { playSources } from "../../composables/enums";
|
import { playSources, ContextSrc } from "../../composables/enums";
|
||||||
import { Playlist } from "../../interfaces";
|
import { Playlist } from "../../interfaces";
|
||||||
import PlayBtnRect from "../shared/PlayBtnRect.vue";
|
import PlayBtnRect from "../shared/PlayBtnRect.vue";
|
||||||
import useModalStore from "../../stores/modal";
|
import useModalStore from "../../stores/modal";
|
||||||
|
import Option from "../shared/Option.vue";
|
||||||
|
import pContext from "../../contexts/playlist";
|
||||||
|
import useContextStore from "../../stores/context";
|
||||||
|
|
||||||
|
const context = useContextStore();
|
||||||
const modal = useModalStore();
|
const modal = useModalStore();
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -51,6 +56,10 @@ const props = defineProps<{
|
|||||||
function editPlaylist() {
|
function editPlaylist() {
|
||||||
modal.showEditPlaylistModal(props.info);
|
modal.showEditPlaylistModal(props.info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showDropdown(e: any) {
|
||||||
|
context.showContextMenu(e, pContext(), ContextSrc.PHeader);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -63,6 +72,7 @@ function editPlaylist() {
|
|||||||
border-radius: 0.75rem;
|
border-radius: 0.75rem;
|
||||||
color: $white;
|
color: $white;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
z-index: 0;
|
||||||
|
|
||||||
.gradient {
|
.gradient {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -160,6 +170,8 @@ function editPlaylist() {
|
|||||||
|
|
||||||
.btns {
|
.btns {
|
||||||
margin-top: $small;
|
margin-top: $small;
|
||||||
|
display: flex;
|
||||||
|
gap: $small;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
'context-many-kids': context.hasManyChildren(),
|
'context-many-kids': context.hasManyChildren(),
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
|
id="context-menu"
|
||||||
:style="{
|
:style="{
|
||||||
left: context.x + 'px',
|
left: context.x + 'px',
|
||||||
top: context.y + 'px',
|
top: context.y + 'px',
|
||||||
@@ -56,7 +57,6 @@ const context = useContextStore();
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 12rem;
|
width: 12rem;
|
||||||
height: min-content;
|
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
transform: scale(0);
|
transform: scale(0);
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,7 @@
|
|||||||
import perks from "../../composables/perks.js";
|
import perks from "../../composables/perks.js";
|
||||||
import useContextStore from "../../stores/context";
|
import useContextStore from "../../stores/context";
|
||||||
import useModalStore from "../../stores/modal";
|
import useModalStore from "../../stores/modal";
|
||||||
|
import { ContextSrc } from "../../composables/enums";
|
||||||
|
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import trackContext from "../../contexts/track_context";
|
import trackContext from "../../contexts/track_context";
|
||||||
@@ -80,7 +81,9 @@ const showContextMenu = (e: Event) => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
contextStore.showContextMenu(e, trackContext(props.song, modalStore));
|
const menus = trackContext(props.song, modalStore);
|
||||||
|
|
||||||
|
contextStore.showContextMenu(e, menus, ContextSrc.Track);
|
||||||
context_on.value = true;
|
context_on.value = true;
|
||||||
|
|
||||||
contextStore.$subscribe((mutation, state) => {
|
contextStore.$subscribe((mutation, state) => {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import { ref } from "vue";
|
|||||||
import perks from "../../composables/perks";
|
import perks from "../../composables/perks";
|
||||||
import trackContext from "../../contexts/track_context";
|
import trackContext from "../../contexts/track_context";
|
||||||
import { Track } from "../../interfaces";
|
import { Track } from "../../interfaces";
|
||||||
|
import { ContextSrc } from "../../composables/enums";
|
||||||
|
|
||||||
import useContextStore from "../../stores/context";
|
import useContextStore from "../../stores/context";
|
||||||
import useModalStore from "../../stores/modal";
|
import useModalStore from "../../stores/modal";
|
||||||
@@ -58,7 +59,9 @@ const showContextMenu = (e: Event) => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
contextStore.showContextMenu(e, trackContext(props.track, modalStore));
|
const menus = trackContext(props.track, modalStore);
|
||||||
|
|
||||||
|
contextStore.showContextMenu(e, menus, ContextSrc.Track);
|
||||||
context_on.value = true;
|
context_on.value = true;
|
||||||
|
|
||||||
contextStore.$subscribe((mutation, state) => {
|
contextStore.$subscribe((mutation, state) => {
|
||||||
|
|||||||
@@ -17,3 +17,9 @@ export enum FromOptions {
|
|||||||
album = "album",
|
album = "album",
|
||||||
search = "search",
|
search = "search",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ContextSrc {
|
||||||
|
PHeader = "PHeader",
|
||||||
|
Track = "Track",
|
||||||
|
AHeader = "AHeader",
|
||||||
|
}
|
||||||
@@ -150,9 +150,24 @@ function formatSeconds(seconds) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCurrentDate(){
|
||||||
|
const date = new Date();
|
||||||
|
|
||||||
|
const yyyy = date.getFullYear();
|
||||||
|
const mm = date.getMonth() + 1;
|
||||||
|
const dd = date.getDate();
|
||||||
|
|
||||||
|
const hh = date.getHours();
|
||||||
|
const min = date.getMinutes();
|
||||||
|
const sec = date.getSeconds();
|
||||||
|
|
||||||
|
return `${yyyy}-${mm}-${dd} ${hh}:${min}:${sec}`;
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
putCommas,
|
putCommas,
|
||||||
focusCurrent,
|
focusCurrent,
|
||||||
formatSeconds,
|
formatSeconds,
|
||||||
getElem,
|
getElem,
|
||||||
|
getCurrentDate
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import axios from "axios";
|
|||||||
import { Playlist, Track } from "../interfaces";
|
import { Playlist, Track } from "../interfaces";
|
||||||
import { Notification, NotifType } from "../stores/notification";
|
import { Notification, NotifType } from "../stores/notification";
|
||||||
import state from "./state";
|
import state from "./state";
|
||||||
|
import { getCurrentDate } from "../composables/perks";
|
||||||
/**
|
/**
|
||||||
* Creates a new playlist on the server.
|
* Creates a new playlist on the server.
|
||||||
* @param playlist_name The name of the playlist to create.
|
* @param playlist_name The name of the playlist to create.
|
||||||
@@ -13,6 +13,7 @@ async function createNewPlaylist(playlist_name: string, track?: Track) {
|
|||||||
await axios
|
await axios
|
||||||
.post(state.settings.uri + "/playlist/new", {
|
.post(state.settings.uri + "/playlist/new", {
|
||||||
name: playlist_name,
|
name: playlist_name,
|
||||||
|
lastUpdated: getCurrentDate(),
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
new Notification("✅ Playlist created successfullly!");
|
new Notification("✅ Playlist created successfullly!");
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { Option } from "../interfaces";
|
||||||
|
|
||||||
|
export default async () => {
|
||||||
|
const deletePlaylist: Option = {
|
||||||
|
label: "Delete playlist",
|
||||||
|
critical: true,
|
||||||
|
action: () => {
|
||||||
|
console.log("delete playlist");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const playNext: Option = {
|
||||||
|
label: "Play next",
|
||||||
|
action: () => {
|
||||||
|
console.log("play next");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const addToQueue: Option = {
|
||||||
|
label: "Add to queue",
|
||||||
|
action: () => {
|
||||||
|
console.log("add to queue");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return [playNext, addToQueue, deletePlaylist];
|
||||||
|
};
|
||||||
+11
-4
@@ -1,23 +1,30 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import normalize from "../composables/normalizeContextMenu";
|
import normalize from "../composables/normalizeContextMenu";
|
||||||
import { Option } from "../interfaces";
|
import { Option } from "../interfaces";
|
||||||
|
import { ContextSrc } from "../composables/enums";
|
||||||
|
|
||||||
export default defineStore("context-menu", {
|
export default defineStore("context-menu", {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
visible: false,
|
visible: false,
|
||||||
options: Array<Option>(),
|
options: <Option[]>[],
|
||||||
x: 500,
|
x: 500,
|
||||||
y: 500,
|
y: 500,
|
||||||
normalizedX: false,
|
normalizedX: false,
|
||||||
normalizedY: false,
|
normalizedY: false,
|
||||||
|
src: "",
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
showContextMenu(e: any, context_options: Promise<Option[]>) {
|
showContextMenu(
|
||||||
|
e: any,
|
||||||
|
context_options: Promise<Option[]>,
|
||||||
|
src: ContextSrc
|
||||||
|
) {
|
||||||
if (this.visible) {
|
if (this.visible) {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.visible = true;
|
||||||
context_options.then((options) => {
|
context_options.then((options) => {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
});
|
});
|
||||||
@@ -29,11 +36,11 @@ export default defineStore("context-menu", {
|
|||||||
|
|
||||||
this.normalizedX = yo.normalizedX;
|
this.normalizedX = yo.normalizedX;
|
||||||
this.normalizedY = yo.normalizedY;
|
this.normalizedY = yo.normalizedY;
|
||||||
|
this.src = src;
|
||||||
this.visible = true;
|
|
||||||
},
|
},
|
||||||
hideContextMenu() {
|
hideContextMenu() {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
|
this.src = null;
|
||||||
},
|
},
|
||||||
hasManyChildren() {
|
hasManyChildren() {
|
||||||
let result = false;
|
let result = false;
|
||||||
|
|||||||
@@ -4,12 +4,24 @@
|
|||||||
<div class="separator no-border"></div>
|
<div class="separator no-border"></div>
|
||||||
|
|
||||||
<div class="songlist rounded">
|
<div class="songlist rounded">
|
||||||
|
<div v-if="playlist.tracks.length">
|
||||||
<SongList
|
<SongList
|
||||||
:tracks="playlist.tracks"
|
:tracks="playlist.tracks"
|
||||||
:pname="playlist.info.name"
|
:pname="playlist.info.name"
|
||||||
:playlistid="playlist.info.playlistid"
|
:playlistid="playlist.info.playlistid"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="playlist.tracks.length === 0 && playlist.info.count > 0">
|
||||||
|
<div class="no-results">
|
||||||
|
<div class="text">We can't find your music 🦋</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="playlist.tracks.length === 0 && playlist.info.count == 0">
|
||||||
|
<div class="no-results">
|
||||||
|
<div class="text">Nothing here</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="separator no-border"></div>
|
<div class="separator no-border"></div>
|
||||||
<FeaturedArtists />
|
<FeaturedArtists />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="p-view">
|
<div id="p-view">
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
|
<NewPlaylistCard />
|
||||||
<PlaylistCard
|
<PlaylistCard
|
||||||
v-for="p in pStore.playlists"
|
v-for="p in pStore.playlists"
|
||||||
:key="p.playlistid"
|
:key="p.playlistid"
|
||||||
@@ -14,6 +15,7 @@
|
|||||||
import PlaylistCard from "../components/playlists/PlaylistCard.vue";
|
import PlaylistCard from "../components/playlists/PlaylistCard.vue";
|
||||||
|
|
||||||
import usePStore from "../stores/playlists";
|
import usePStore from "../stores/playlists";
|
||||||
|
import NewPlaylistCard from "../components/playlists/NewPlaylistCard.vue";
|
||||||
const pStore = usePStore();
|
const pStore = usePStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -22,10 +24,12 @@ const pStore = usePStore();
|
|||||||
margin: $small;
|
margin: $small;
|
||||||
padding: $small;
|
padding: $small;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
scrollbar-color: $gray2 transparent;
|
||||||
|
border-top: 1px solid $gray3;
|
||||||
|
|
||||||
.grid {
|
.grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
||||||
gap: $small;
|
gap: $small;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user