[client] minor refactors

This commit is contained in:
geoffrey45 2022-03-27 18:22:35 +03:00
parent 9ada6c9058
commit e4640d9985
10 changed files with 128 additions and 57 deletions

View File

@ -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 {

View File

@ -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>

View File

@ -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;
} }
} }

View File

@ -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>

View File

@ -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);

View File

@ -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 };

View File

@ -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,

View File

@ -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
View File

@ -0,0 +1,5 @@
export enum NotifType {
Success,
Info,
Error
}

View File

@ -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 };