mirror of
https://github.com/tcsenpai/swingmusic.git
synced 2025-06-08 12:15:39 +00:00
[client] minor refactors
This commit is contained in:
parent
9ada6c9058
commit
e4640d9985
@ -41,7 +41,7 @@ $accent: $indigo;
|
|||||||
$cta: $blue;
|
$cta: $blue;
|
||||||
$danger: $red;
|
$danger: $red;
|
||||||
$track-hover: $gray4;
|
$track-hover: $gray4;
|
||||||
$context: $gray4;
|
$context: $gray5;
|
||||||
|
|
||||||
// media query mixins
|
// media query mixins
|
||||||
@mixin phone-only {
|
@mixin phone-only {
|
||||||
|
@ -1,30 +1,47 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div class="toasts" v-if="notifStore.notifs">
|
||||||
class="new-notif rounded"
|
<div
|
||||||
:class="{ 'notif-error': notif.type == NotificationType.Error }"
|
class="new-notif rounded"
|
||||||
v-if="notif.visible"
|
:class="[
|
||||||
>
|
{ 'notif-error': notif.type == NotifType.Error },
|
||||||
<div>{{ notif.text }}</div>
|
{
|
||||||
|
'notif-info': notif.type == NotifType.Info,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
v-for="notif in notifStore.notifs"
|
||||||
|
>
|
||||||
|
<div>{{ notif.text }}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useNotificationStore, NotificationType } from "../stores/notification";
|
import { useNotifStore, NotifType } from "../stores/notification";
|
||||||
|
|
||||||
const notif = useNotificationStore();
|
const notifStore = useNotifStore();
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.new-notif {
|
.toasts {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 2000;
|
|
||||||
width: 25rem;
|
|
||||||
bottom: 2rem;
|
bottom: 2rem;
|
||||||
padding: $small;
|
|
||||||
left: 50%;
|
left: 50%;
|
||||||
translate: -50%;
|
translate: -50%;
|
||||||
background-color: rgb(5, 62, 168);
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
gap: $small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-notif {
|
||||||
|
width: 20rem;
|
||||||
|
height: 3.5rem;
|
||||||
|
bottom: 2rem;
|
||||||
|
padding: $small;
|
||||||
|
background-color: $green;
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: 0px 0px 2rem rgb(0, 0, 0);
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@ -35,4 +52,8 @@ const notif = useNotificationStore();
|
|||||||
.notif-error {
|
.notif-error {
|
||||||
background-color: $red;
|
background-color: $red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notif-info {
|
||||||
|
background-color: $gray2;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
class="context-item"
|
class="context-item"
|
||||||
v-for="child in option.children"
|
v-for="child in option.children"
|
||||||
:key="child"
|
:key="child"
|
||||||
|
:class="[{ critical: child.critical }, child.type]"
|
||||||
@click="child.action()"
|
@click="child.action()"
|
||||||
>
|
>
|
||||||
<div class="label ellip">
|
<div class="label ellip">
|
||||||
@ -56,11 +57,11 @@ const context = useContextStore();
|
|||||||
left: 0;
|
left: 0;
|
||||||
width: 12rem;
|
width: 12rem;
|
||||||
height: min-content;
|
height: min-content;
|
||||||
z-index: 100000 !important;
|
z-index: 10;
|
||||||
transform: scale(0);
|
transform: scale(0);
|
||||||
|
|
||||||
padding: $small;
|
padding: $small;
|
||||||
background: $gray3;
|
background: $context;
|
||||||
transform-origin: top left;
|
transform-origin: top left;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
|
|
||||||
@ -74,6 +75,7 @@ const context = useContextStore();
|
|||||||
border-radius: $small;
|
border-radius: $small;
|
||||||
color: rgb(255, 255, 255);
|
color: rgb(255, 255, 255);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
text-transform: capitalize;
|
||||||
|
|
||||||
.more {
|
.more {
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
@ -90,7 +92,7 @@ const context = useContextStore();
|
|||||||
max-height: 21.25rem;
|
max-height: 21.25rem;
|
||||||
|
|
||||||
padding: $small !important;
|
padding: $small !important;
|
||||||
background: $gray3;
|
background-color: $context;
|
||||||
transform: scale(0);
|
transform: scale(0);
|
||||||
transform-origin: left;
|
transform-origin: left;
|
||||||
}
|
}
|
||||||
@ -173,7 +175,6 @@ const context = useContextStore();
|
|||||||
|
|
||||||
.context-normalizedY {
|
.context-normalizedY {
|
||||||
.context-item > .children {
|
.context-item > .children {
|
||||||
bottom: -0.5rem;
|
|
||||||
transform-origin: bottom right;
|
transform-origin: bottom right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
import { createNewPlaylist } from "../../composables/playlists";
|
import { createNewPlaylist } from "../../composables/playlists";
|
||||||
|
import { Track } from "../../interfaces";
|
||||||
|
import { Notification, NotifType } from "../../stores/notification";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
track: Track;
|
||||||
|
}>();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
document.getElementById("modal-playlist-name-input").focus();
|
document.getElementById("modal-playlist-name-input").focus();
|
||||||
@ -33,7 +39,16 @@ function create(e: Event) {
|
|||||||
const name = (e.target as HTMLFormElement).elements["name"].value;
|
const name = (e.target as HTMLFormElement).elements["name"].value;
|
||||||
|
|
||||||
if (name.trim()) {
|
if (name.trim()) {
|
||||||
createNewPlaylist(name).then(() => emit("hideModal"));
|
createNewPlaylist(name, props.track).then((status: boolean) => {
|
||||||
|
if (status) {
|
||||||
|
emit("hideModal");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
new Notification(
|
||||||
|
"A Playlist name can not be empty",
|
||||||
|
NotifType.Error
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -65,7 +65,6 @@ import perks from "../../composables/perks.js";
|
|||||||
import state from "../../composables/state";
|
import state from "../../composables/state";
|
||||||
import useContextStore from "../../stores/context";
|
import useContextStore from "../../stores/context";
|
||||||
import useModalStore from "../../stores/modal";
|
import useModalStore from "../../stores/modal";
|
||||||
import usePlaylistStore from "../../stores/playlists";
|
|
||||||
|
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import trackContext from "../../contexts/track_context";
|
import trackContext from "../../contexts/track_context";
|
||||||
@ -73,7 +72,6 @@ import { Track } from "../../interfaces.js";
|
|||||||
|
|
||||||
const contextStore = useContextStore();
|
const contextStore = useContextStore();
|
||||||
const modalStore = useModalStore();
|
const modalStore = useModalStore();
|
||||||
const playlistStore = usePlaylistStore();
|
|
||||||
|
|
||||||
const context_on = ref(false);
|
const context_on = ref(false);
|
||||||
|
|
||||||
|
@ -1,25 +1,40 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { Playlist } from "../interfaces";
|
import { Playlist, Track } from "../interfaces";
|
||||||
import { Notification, NotificationType } from "../stores/notification";
|
import { Notification, NotifType } from "../stores/notification";
|
||||||
import state from "./state";
|
import state from "./state";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
async function createNewPlaylist(playlist_name: string) {
|
async function createNewPlaylist(playlist_name: string, track?: Track) {
|
||||||
|
let status = false;
|
||||||
|
|
||||||
await axios
|
await axios
|
||||||
.post(state.settings.uri + "/playlist/new", {
|
.post(state.settings.uri + "/playlist/new", {
|
||||||
name: playlist_name,
|
name: playlist_name,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
new Notification("✅ Playlist created successfullly!");
|
new Notification("✅ Playlist created successfullly!");
|
||||||
|
|
||||||
|
if (track) {
|
||||||
|
setTimeout(() => {
|
||||||
|
addTrackToPlaylist(res.data.playlist, track);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
status = true;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (err.response.status == 409) {
|
if (err.response.status == 409) {
|
||||||
new Notification("❌ Playlist already exists!", NotificationType.Error);
|
new Notification(
|
||||||
|
"That playlist already exists ... you might want to try another name!",
|
||||||
|
NotifType.Error
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,4 +58,20 @@ async function getAllPlaylists(): Promise<Playlist[]> {
|
|||||||
return playlists;
|
return playlists;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { createNewPlaylist, getAllPlaylists };
|
async function addTrackToPlaylist(playlist: Playlist, track: Track) {
|
||||||
|
const uri = `${state.settings.uri}/playlist/${playlist.playlistid}/add`;
|
||||||
|
console.log(track.trackid, playlist.playlistid);
|
||||||
|
|
||||||
|
await axios
|
||||||
|
.post(uri, { track: track.trackid })
|
||||||
|
.then(() => {
|
||||||
|
new Notification(track.title + " added to " + playlist.name);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (error.response.status == 409) {
|
||||||
|
new Notification("Track already exists in playlist", NotifType.Info);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { createNewPlaylist, getAllPlaylists, addTrackToPlaylist };
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Playlist, Track } from "../interfaces";
|
import { Playlist, Track } from "../interfaces";
|
||||||
import Router from "../router";
|
import Router from "../router";
|
||||||
import { Option } from "../interfaces";
|
import { Option } from "../interfaces";
|
||||||
import { getAllPlaylists } from "../composables/playlists";
|
import { getAllPlaylists, addTrackToPlaylist } from "../composables/playlists";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of context menu items for a track.
|
* Returns a list of context menu items for a track.
|
||||||
@ -11,17 +11,20 @@ import { getAllPlaylists } from "../composables/playlists";
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export default async (track: Track, modalStore: any): Promise<Option[]> => {
|
export default async (track: Track, modalStore: any): Promise<Option[]> => {
|
||||||
|
const separator: Option = {
|
||||||
|
type: "separator",
|
||||||
|
};
|
||||||
|
|
||||||
const single_artist = track.artists.length === 1;
|
const single_artist = track.artists.length === 1;
|
||||||
|
|
||||||
let playlists = <Option[]>[];
|
let playlists = <Option[]>[];
|
||||||
|
|
||||||
const p = await getAllPlaylists();
|
const p = await getAllPlaylists();
|
||||||
|
|
||||||
playlists = p.map((playlist: Playlist) => {
|
playlists = p.map((playlist: Playlist) => {
|
||||||
return <Option>{
|
return <Option>{
|
||||||
label: playlist.name,
|
label: playlist.name,
|
||||||
action: () => {
|
action: () => {
|
||||||
console.log(playlist.name);
|
addTrackToPlaylist(playlist, track);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -43,12 +46,11 @@ export default async (track: Track, modalStore: any): Promise<Option[]> => {
|
|||||||
const new_playlist = <Option>{
|
const new_playlist = <Option>{
|
||||||
label: "New playlist",
|
label: "New playlist",
|
||||||
action: () => {
|
action: () => {
|
||||||
modalStore.showModal(modalStore.options.newPlaylist);
|
modalStore.showNewPlaylistModal(track);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log([new_playlist, ...playlists]);
|
return [new_playlist, separator, ...playlists];
|
||||||
return [new_playlist, ...playlists];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const add_to_playlist: Option = {
|
const add_to_playlist: Option = {
|
||||||
@ -115,10 +117,6 @@ export default async (track: Track, modalStore: any): Promise<Option[]> => {
|
|||||||
icon: "heart",
|
icon: "heart",
|
||||||
};
|
};
|
||||||
|
|
||||||
const separator: Option = {
|
|
||||||
type: "separator",
|
|
||||||
};
|
|
||||||
|
|
||||||
const options: Option[] = [
|
const options: Option[] = [
|
||||||
add_to_playlist,
|
add_to_playlist,
|
||||||
add_to_q,
|
add_to_q,
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { NotifType } from "./stores/enums";
|
||||||
|
|
||||||
interface Track {
|
interface Track {
|
||||||
trackid: string;
|
trackid: string;
|
||||||
title: string;
|
title: string;
|
||||||
@ -52,4 +54,9 @@ interface Playlist {
|
|||||||
image?: string;
|
image?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Track, Folder, AlbumInfo, Artist, Option, Playlist };
|
interface Notif {
|
||||||
|
text: string;
|
||||||
|
type: NotifType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Track, Folder, AlbumInfo, Artist, Option, Playlist, Notif };
|
||||||
|
5
src/stores/enums.ts
Normal file
5
src/stores/enums.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export enum NotifType {
|
||||||
|
Success,
|
||||||
|
Info,
|
||||||
|
Error
|
||||||
|
}
|
@ -1,34 +1,29 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
|
import { Notif } from "../interfaces";
|
||||||
|
import { NotifType } from "./enums";
|
||||||
|
|
||||||
enum NotificationType {
|
const useNotifStore = defineStore("notification", {
|
||||||
Success,
|
|
||||||
Error,
|
|
||||||
}
|
|
||||||
|
|
||||||
const useNotificationStore = defineStore("notification", {
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
text: "",
|
notifs: <Notif[]>[],
|
||||||
type: NotificationType.Success,
|
|
||||||
visible: false,
|
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
showNotification(new_text: string, new_type?: NotificationType) {
|
showNotification(new_text: string, new_type?: NotifType) {
|
||||||
console.log(arguments);
|
this.notifs.push(<Notif>{
|
||||||
this.text = new_text;
|
text: new_text,
|
||||||
this.type = new_type;
|
type: new_type,
|
||||||
this.visible = true;
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.visible = false;
|
this.notifs.shift();
|
||||||
}, 2000);
|
}, 3000);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class Notification {
|
class Notification {
|
||||||
constructor(text: string, type?: NotificationType) {
|
constructor(text: string, type?: NotifType) {
|
||||||
useNotificationStore().showNotification(text, type);
|
useNotifStore().showNotification(text, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { useNotificationStore, Notification, NotificationType };
|
export { useNotifStore, Notification, NotifType };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user