mirror of
https://github.com/tcsenpai/swingmusic.git
synced 2025-07-24 18:40:05 +00:00
🔷 refactor context menu to accept context src
🔷 add a getCurrentDate function to get formatted date
This commit is contained in:
parent
24db32ae09
commit
83330a7fad
@ -1,63 +1,36 @@
|
||||
# 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 ⚠
|
||||
- [ ] 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
|
||||
- [ ] Different songs having same link
|
||||
- [ ] ConnectionError
|
||||
- [ ] Move thumbnails to .config
|
||||
- [ ] Write a multithreaded file server
|
||||
- [ ] Add support for WAV files
|
||||
- [ ] Support multiple folders
|
||||
- [ ] Compress thumbnails
|
||||
|
||||
# Features +
|
||||
## Needed features
|
||||
- [ ] Seeking current song
|
||||
- [ ] 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
|
||||
- [ ] Right click on song to do stuff
|
||||
- [ ] Adjust volume
|
||||
- [ ] Add listening statistics for all songs
|
||||
- [ ] Extract color from artist image [for use with artist card gradient]
|
||||
- [ ] Adding songs to favorites
|
||||
- [ ] Adding songs to playlist
|
||||
- [ ] Playing song radio
|
||||
|
||||
## Future features
|
||||
- [ ] Toggle shuffle
|
||||
- [ ] Toggle repeat
|
||||
- [ ] Display artist albums
|
||||
- [ ] Suggest similar artists
|
||||
- [ ] Getting artist info
|
||||
- [ ] Getting album info
|
||||
- [ ] Create a Python script to build, bundle and serve the app
|
||||
- [ ] Getting extra song info (probably from genius)
|
||||
- [ ] Getting lyrics
|
||||
- [ ] Notifications
|
||||
- [ ] Sorting songs
|
||||
- [ ] Suggest undiscorvered artists, albums and songs
|
||||
- [ ] Remember last played song
|
||||
- [ ] Add next and previous song transition and progress bar reset animations
|
||||
- [ ] Hover animations for list items
|
||||
- [ ] Highlight currently playing song in playlist
|
||||
- [ ] Add playlist to folder
|
||||
- [ ] Add functionality to 'Listen now' button
|
||||
- [ ] Add a 'Scan' button to the sidebar
|
||||
- [ ] Paginated requests for songs
|
||||
- [ ] Package app as installable PWA
|
||||
|
||||
## Finished ✅
|
@ -25,7 +25,6 @@
|
||||
<div class="text">Nothing down here 😑</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else ref="songtitle"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
<div class="info">
|
||||
<div class="btns">
|
||||
<PlayBtnRect :source="playSources.playlist" />
|
||||
<Option @showDropdown="showDropdown" :src="context.src" />
|
||||
</div>
|
||||
<div class="duration">
|
||||
<span v-if="props.info.count == 0">No Tracks</span>
|
||||
@ -37,11 +38,15 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { playSources } from "../../composables/enums";
|
||||
import { playSources, ContextSrc } from "../../composables/enums";
|
||||
import { Playlist } from "../../interfaces";
|
||||
import PlayBtnRect from "../shared/PlayBtnRect.vue";
|
||||
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 props = defineProps<{
|
||||
@ -51,6 +56,10 @@ const props = defineProps<{
|
||||
function editPlaylist() {
|
||||
modal.showEditPlaylistModal(props.info);
|
||||
}
|
||||
|
||||
function showDropdown(e: any) {
|
||||
context.showContextMenu(e, pContext(), ContextSrc.PHeader);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@ -63,6 +72,7 @@ function editPlaylist() {
|
||||
border-radius: 0.75rem;
|
||||
color: $white;
|
||||
background-color: transparent;
|
||||
z-index: 0;
|
||||
|
||||
.gradient {
|
||||
position: absolute;
|
||||
@ -160,6 +170,8 @@ function editPlaylist() {
|
||||
|
||||
.btns {
|
||||
margin-top: $small;
|
||||
display: flex;
|
||||
gap: $small;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
'context-many-kids': context.hasManyChildren(),
|
||||
},
|
||||
]"
|
||||
id="context-menu"
|
||||
:style="{
|
||||
left: context.x + 'px',
|
||||
top: context.y + 'px',
|
||||
@ -56,7 +57,6 @@ const context = useContextStore();
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 12rem;
|
||||
height: min-content;
|
||||
z-index: 10;
|
||||
transform: scale(0);
|
||||
|
||||
|
@ -67,6 +67,7 @@
|
||||
import perks from "../../composables/perks.js";
|
||||
import useContextStore from "../../stores/context";
|
||||
import useModalStore from "../../stores/modal";
|
||||
import { ContextSrc } from "../../composables/enums";
|
||||
|
||||
import { ref } from "vue";
|
||||
import trackContext from "../../contexts/track_context";
|
||||
@ -80,7 +81,9 @@ const showContextMenu = (e: Event) => {
|
||||
e.preventDefault();
|
||||
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;
|
||||
|
||||
contextStore.$subscribe((mutation, state) => {
|
||||
|
@ -39,6 +39,7 @@ import { ref } from "vue";
|
||||
import perks from "../../composables/perks";
|
||||
import trackContext from "../../contexts/track_context";
|
||||
import { Track } from "../../interfaces";
|
||||
import { ContextSrc } from "../../composables/enums";
|
||||
|
||||
import useContextStore from "../../stores/context";
|
||||
import useModalStore from "../../stores/modal";
|
||||
@ -58,7 +59,9 @@ const showContextMenu = (e: Event) => {
|
||||
e.preventDefault();
|
||||
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;
|
||||
|
||||
contextStore.$subscribe((mutation, state) => {
|
||||
|
@ -17,3 +17,9 @@ export enum FromOptions {
|
||||
album = "album",
|
||||
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 {
|
||||
putCommas,
|
||||
focusCurrent,
|
||||
formatSeconds,
|
||||
getElem,
|
||||
getCurrentDate
|
||||
};
|
||||
|
@ -2,7 +2,7 @@ import axios from "axios";
|
||||
import { Playlist, Track } from "../interfaces";
|
||||
import { Notification, NotifType } from "../stores/notification";
|
||||
import state from "./state";
|
||||
|
||||
import { getCurrentDate } from "../composables/perks";
|
||||
/**
|
||||
* Creates a new playlist on the server.
|
||||
* @param playlist_name The name of the playlist to create.
|
||||
@ -13,6 +13,7 @@ async function createNewPlaylist(playlist_name: string, track?: Track) {
|
||||
await axios
|
||||
.post(state.settings.uri + "/playlist/new", {
|
||||
name: playlist_name,
|
||||
lastUpdated: getCurrentDate(),
|
||||
})
|
||||
.then((res) => {
|
||||
new Notification("✅ Playlist created successfullly!");
|
||||
|
28
src/contexts/playlist.ts
Normal file
28
src/contexts/playlist.ts
Normal file
@ -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];
|
||||
};
|
@ -1,23 +1,30 @@
|
||||
import { defineStore } from "pinia";
|
||||
import normalize from "../composables/normalizeContextMenu";
|
||||
import { Option } from "../interfaces";
|
||||
import { ContextSrc } from "../composables/enums";
|
||||
|
||||
export default defineStore("context-menu", {
|
||||
state: () => ({
|
||||
visible: false,
|
||||
options: Array<Option>(),
|
||||
options: <Option[]>[],
|
||||
x: 500,
|
||||
y: 500,
|
||||
normalizedX: false,
|
||||
normalizedY: false,
|
||||
src: "",
|
||||
}),
|
||||
actions: {
|
||||
showContextMenu(e: any, context_options: Promise<Option[]>) {
|
||||
showContextMenu(
|
||||
e: any,
|
||||
context_options: Promise<Option[]>,
|
||||
src: ContextSrc
|
||||
) {
|
||||
if (this.visible) {
|
||||
this.visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.visible = true;
|
||||
context_options.then((options) => {
|
||||
this.options = options;
|
||||
});
|
||||
@ -29,11 +36,11 @@ export default defineStore("context-menu", {
|
||||
|
||||
this.normalizedX = yo.normalizedX;
|
||||
this.normalizedY = yo.normalizedY;
|
||||
|
||||
this.visible = true;
|
||||
this.src = src;
|
||||
},
|
||||
hideContextMenu() {
|
||||
this.visible = false;
|
||||
this.src = null;
|
||||
},
|
||||
hasManyChildren() {
|
||||
let result = false;
|
||||
|
@ -4,11 +4,23 @@
|
||||
<div class="separator no-border"></div>
|
||||
|
||||
<div class="songlist rounded">
|
||||
<SongList
|
||||
:tracks="playlist.tracks"
|
||||
:pname="playlist.info.name"
|
||||
:playlistid="playlist.info.playlistid"
|
||||
/>
|
||||
<div v-if="playlist.tracks.length">
|
||||
<SongList
|
||||
:tracks="playlist.tracks"
|
||||
:pname="playlist.info.name"
|
||||
:playlistid="playlist.info.playlistid"
|
||||
/>
|
||||
</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>
|
||||
<FeaturedArtists />
|
||||
|
@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div id="p-view">
|
||||
<div class="grid">
|
||||
<NewPlaylistCard />
|
||||
<PlaylistCard
|
||||
v-for="p in pStore.playlists"
|
||||
:key="p.playlistid"
|
||||
@ -14,6 +15,7 @@
|
||||
import PlaylistCard from "../components/playlists/PlaylistCard.vue";
|
||||
|
||||
import usePStore from "../stores/playlists";
|
||||
import NewPlaylistCard from "../components/playlists/NewPlaylistCard.vue";
|
||||
const pStore = usePStore();
|
||||
</script>
|
||||
|
||||
@ -22,10 +24,12 @@ const pStore = usePStore();
|
||||
margin: $small;
|
||||
padding: $small;
|
||||
overflow: auto;
|
||||
scrollbar-color: $gray2 transparent;
|
||||
border-top: 1px solid $gray3;
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
||||
gap: $small;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user