add functionality to adjust image position

This commit is contained in:
geoffrey45 2023-01-13 15:53:36 +03:00 committed by Mungai Njoroge
parent 4d4a276ed8
commit ceedc22843
9 changed files with 146 additions and 65 deletions

View File

@ -5,6 +5,7 @@
:style="[ :style="[
{ {
backgroundImage: info.image ? `url(${imguri + info.image})` : undefined, backgroundImage: info.image ? `url(${imguri + info.image})` : undefined,
backgroundPosition: `center ${bannerPos}%`,
}, },
]" ]"
:class="{ border: !info.image }" :class="{ border: !info.image }"
@ -28,7 +29,7 @@
<span class="status" <span class="status"
>Last updated {{ info.last_updated }} &#160;|&#160;&#160;</span >Last updated {{ info.last_updated }} &#160;|&#160;&#160;</span
> >
<span class="edit" @click="editPlaylist">Edit&#160;&#160;|</span> <div class="edit" @click="editPlaylist">Edit&#160;&#160;|</div>
<DeleteSvg class="edit" @click="deletePlaylist" /> <DeleteSvg class="edit" @click="deletePlaylist" />
</div> </div>
</div> </div>
@ -56,7 +57,7 @@ const playlist = usePStore();
const imguri = paths.images.playlist; const imguri = paths.images.playlist;
const playlistheader = ref<HTMLElement | null>(null); const playlistheader = ref<HTMLElement | null>(null);
const { info } = storeToRefs(playlist); const { info, bannerPos } = storeToRefs(playlist);
useVisibility(playlistheader, nav.toggleShowPlay); useVisibility(playlistheader, nav.toggleShowPlay);
@ -76,6 +77,7 @@ function deletePlaylist() {
height: $banner-height; height: $banner-height;
position: relative; position: relative;
background-color: $gray5; background-color: $gray5;
background-position: center 50%;
.gradient { .gradient {
position: absolute; position: absolute;

View File

@ -23,7 +23,7 @@
@change="handleUpload" @change="handleUpload"
ref="dropZoneRef" ref="dropZoneRef"
/> />
<div id="upload" class="rounded-sm" @click="selectFiles"> <div id="upload" class="boxed rounded-sm" @click="selectFiles">
<div>Click to upload cover image</div> <div>Click to upload cover image</div>
<div <div
id="update-pl-img-preview" id="update-pl-img-preview"
@ -35,6 +35,18 @@
}" }"
/> />
</div> </div>
<div class="boxed banner-position-adjust rounded-sm">
<div class="t-center">Adjust image position</div>
<div class="buttons">
<button @click.prevent="pStore.minusBannerPos">
<ExpandSvg />
</button>
<button @click.prevent="pStore.plusBannerPos">
<ExpandSvg />
</button>
</div>
</div>
<button class="circular btn-active"> <button class="circular btn-active">
{{ clicked ? "Updating" : "Update" }} {{ clicked ? "Updating" : "Update" }}
</button> </button>
@ -45,12 +57,14 @@
import { onMounted, ref } from "vue"; import { onMounted, ref } from "vue";
// import { useDropZone } from "@vueuse/core"; // import { useDropZone } from "@vueuse/core";
import { updatePlaylist } from "@/composables/fetch/playlists";
import { paths } from "@/config"; import { paths } from "@/config";
import { Playlist } from "@/interfaces"; import { Playlist } from "@/interfaces";
import usePStore from "@/stores/pages/playlist"; import usePStore from "@/stores/pages/playlist";
import { updatePlaylist } from "@/composables/fetch/playlists";
import ExpandSvg from "@/assets/icons/expand.svg";
const pStore = usePStore(); const pStore = usePStore();
const bannerPos = ref(0);
const props = defineProps<{ const props = defineProps<{
playlist: Playlist; playlist: Playlist;
@ -140,15 +154,18 @@ function update_playlist(e: Event) {
<style lang="scss"> <style lang="scss">
.new-p-form { .new-p-form {
.boxed {
border: solid 2px $gray3;
color: $gray1;
place-items: center;
display: grid;
grid-template-columns: 1fr max-content;
margin-bottom: $small;
}
#upload { #upload {
width: 100%; width: 100%;
padding: $small; padding: $small;
border: solid 2px $gray3;
display: grid;
grid-template-columns: 1fr max-content;
place-items: center;
color: $gray1;
margin: $small 0;
cursor: pointer; cursor: pointer;
#update-pl-img-preview { #update-pl-img-preview {
@ -159,5 +176,31 @@ function update_playlist(e: Event) {
background-color: $gray4; background-color: $gray4;
} }
} }
.banner-position-adjust {
gap: 1rem;
padding: $small 1rem;
margin-bottom: 1rem;
.t-center {
position: relative;
}
button {
aspect-ratio: 1;
height: 2rem;
width: 2rem;
padding: 0;
background: transparent;
}
button:last-child {
transform: rotate(90deg);
}
button:first-child {
transform: rotate(-90deg);
}
}
} }
</style> </style>

View File

@ -32,7 +32,7 @@
/> />
</div> </div>
</div> </div>
<div class="float-buttons flex" v-if="isQueueTrack"> <div class="float-buttons flex">
<div <div
:title="is_fav ? 'Add to favorites' : 'Remove from favorites'" :title="is_fav ? 'Add to favorites' : 'Remove from favorites'"
@click.stop="() => addToFav(track.trackhash)" @click.stop="() => addToFav(track.trackhash)"
@ -40,6 +40,7 @@
<HeartSvg :state="is_fav" :no_emit="true" /> <HeartSvg :state="is_fav" :no_emit="true" />
</div> </div>
<div <div
v-if="isQueueTrack"
class="remove-track" class="remove-track"
title="remove from queue" title="remove from queue"
@click.stop="queue.removeFromQueue(index)" @click.stop="queue.removeFromQueue(index)"

View File

@ -14,7 +14,7 @@ const {
* 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, track?: Track) { export async function createNewPlaylist(playlist_name: string, track?: Track) {
const { data, status } = await useAxios({ const { data, status } = await useAxios({
url: newPlaylistUrl, url: newPlaylistUrl,
props: { props: {
@ -37,7 +37,13 @@ async function createNewPlaylist(playlist_name: string, track?: Track) {
}; };
} }
new Notification("That playlist already exists", NotifType.Error); let message = "Something went wrong";
if (status == 409) {
message = "That playlist already exists";
}
new Notification(message, NotifType.Error);
return { return {
success: false, success: false,
@ -49,7 +55,7 @@ async function createNewPlaylist(playlist_name: string, track?: Track) {
* Fetches all playlists from the server. * Fetches all playlists from the server.
* @returns {Promise<Playlist[]>} A promise that resolves to an array of playlists. * @returns {Promise<Playlist[]>} A promise that resolves to an array of playlists.
*/ */
async function getAllPlaylists(): Promise<Playlist[]> { export async function getAllPlaylists(): Promise<Playlist[]> {
const { data, error } = await useAxios({ const { data, error } = await useAxios({
url: allPlaylistsUrl, url: allPlaylistsUrl,
get: true, get: true,
@ -64,7 +70,7 @@ async function getAllPlaylists(): Promise<Playlist[]> {
return []; return [];
} }
async function addTrackToPlaylist(playlist: Playlist, track: Track) { export async function addTrackToPlaylist(playlist: Playlist, track: Track) {
const uri = `${basePlaylistUrl}/${playlist.id}/add`; const uri = `${basePlaylistUrl}/${playlist.id}/add`;
const { status } = await useAxios({ const { status } = await useAxios({
@ -85,7 +91,7 @@ async function addTrackToPlaylist(playlist: Playlist, track: Track) {
); );
} }
async function getPlaylist(pid: string) { export async function getPlaylist(pid: string) {
const uri = `${basePlaylistUrl}/${pid}`; const uri = `${basePlaylistUrl}/${pid}`;
interface PlaylistData { interface PlaylistData {
@ -109,7 +115,11 @@ async function getPlaylist(pid: string) {
return null; return null;
} }
async function updatePlaylist(pid: string, playlist: FormData, pStore: any) { export async function updatePlaylist(
pid: string,
playlist: FormData,
pStore: any
) {
const uri = `${basePlaylistUrl}/${pid}/update`; const uri = `${basePlaylistUrl}/${pid}/update`;
const { data, status } = await useAxios({ const { data, status } = await useAxios({
@ -157,7 +167,6 @@ export async function getPlaylistArtists(pid: string): Promise<Artist[]> {
} }
export async function deletePlaylist(pid: string) { export async function deletePlaylist(pid: string) {
console.log(pid);
const { status } = await useAxios({ const { status } = await useAxios({
url: paths.api.playlist.base + "/delete", url: paths.api.playlist.base + "/delete",
props: { props: {
@ -170,10 +179,18 @@ export async function deletePlaylist(pid: string) {
} }
} }
export { export async function updateBannerPos(pid: number, pos: number) {
createNewPlaylist, const { status } = await useAxios({
getAllPlaylists, url: paths.api.playlist.base + `/${pid}/set-image-pos`,
addTrackToPlaylist, props: {
getPlaylist, pos,
updatePlaylist, },
}; });
if (status === 200) {
new Notification("Image position saved", NotifType.Info);
return;
}
new Notification("Unable to save image position", NotifType.Error);
}

View File

@ -87,6 +87,8 @@ export interface Playlist {
last_updated: string; last_updated: string;
thumb: string; thumb: string;
duration: number; duration: number;
has_gif: boolean;
banner_pos: number;
} }
export interface Notif { export interface Notif {

View File

@ -11,6 +11,7 @@ export default defineStore("playlist-tracks", {
state: () => ({ state: () => ({
info: <Playlist>{}, info: <Playlist>{},
query: "", query: "",
bannerPos: 0,
allTracks: <Track[]>[], allTracks: <Track[]>[],
artists: <Artist[]>[], artists: <Artist[]>[],
}), }),
@ -23,6 +24,7 @@ export default defineStore("playlist-tracks", {
const playlist = await getPlaylist(id); const playlist = await getPlaylist(id);
this.info = playlist?.info || ({} as Playlist); this.info = playlist?.info || ({} as Playlist);
this.bannerPos = this.info.banner_pos;
this.allTracks = playlist?.tracks || []; this.allTracks = playlist?.tracks || [];
}, },
@ -42,6 +44,12 @@ export default defineStore("playlist-tracks", {
this.info = { ...this.info, duration, count }; this.info = { ...this.info, duration, count };
}, },
plusBannerPos() {
this.bannerPos !== 100 ? (this.bannerPos += 10) : null;
},
minusBannerPos() {
this.bannerPos !== 0 ? (this.bannerPos -= 10) : null;
},
resetArtists() { resetArtists() {
this.artists = []; this.artists = [];
}, },
@ -66,5 +74,8 @@ export default defineStore("playlist-tracks", {
return tracks; return tracks;
}, },
bannerPosUpdated(): boolean {
return this.info.banner_pos - this.bannerPos !== 0;
},
}, },
}); });

View File

@ -1,11 +1,7 @@
<template> <template>
<div id="p-view" class="content-page"> <div id="p-view" class="content-page">
<div class="grid"> <div class="grid">
<PlaylistCard <PlaylistCard v-for="p in pStore.playlists" :key="p.id" :playlist="p" />
v-for="p in pStore.playlists"
:key="p.id"
:playlist="p"
/>
</div> </div>
</div> </div>
</template> </template>
@ -24,6 +20,7 @@ const pStore = usePStore();
padding-bottom: $content-padding-bottom; padding-bottom: $content-padding-bottom;
height: 100%; height: 100%;
overflow: auto; overflow: auto;
padding-right: 1rem;
.grid { .grid {
grid-template-columns: repeat(auto-fill, minmax(9.25rem, 1fr)); grid-template-columns: repeat(auto-fill, minmax(9.25rem, 1fr));

View File

@ -23,7 +23,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from "@vue/reactivity"; import { computed } from "@vue/reactivity";
import { onBeforeRouteLeave } from "vue-router"; import { onBeforeRouteLeave, onBeforeRouteUpdate } from "vue-router";
import { isMedium, isSmall } from "@/stores/content-width"; import { isMedium, isSmall } from "@/stores/content-width";
import usePlaylistStore from "@/stores/pages/playlist"; import usePlaylistStore from "@/stores/pages/playlist";
@ -31,6 +31,7 @@ import useQueueStore from "@/stores/queue";
import Header from "@/components/PlaylistView/Header.vue"; import Header from "@/components/PlaylistView/Header.vue";
import SongItem from "@/components/shared/SongItem.vue"; import SongItem from "@/components/shared/SongItem.vue";
import { updateBannerPos } from "@/composables/fetch/playlists";
const queue = useQueueStore(); const queue = useQueueStore();
const playlist = usePlaylistStore(); const playlist = usePlaylistStore();
@ -70,9 +71,16 @@ function playFromPlaylistPage(index: number) {
queue.play(index); queue.play(index);
} }
onBeforeRouteLeave(() => { [onBeforeRouteLeave, onBeforeRouteUpdate].forEach((guard) => {
setTimeout(() => { guard(() => {
playlist.resetQuery(); if (playlist.bannerPosUpdated) {
}, 500); console.log("Update banner pos in server");
updateBannerPos(parseInt(playlist.info.id), playlist.bannerPos);
}
setTimeout(() => {
playlist.resetQuery();
}, 500);
});
}); });
</script> </script>

View File

@ -2804,36 +2804,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"musicx-v@workspace:.":
version: 0.0.0-use.local
resolution: "musicx-v@workspace:."
dependencies:
"@formkit/auto-animate": ^1.0.0-beta.3
"@popperjs/core": ^2.11.6
"@vitejs/plugin-vue": ^3.2.0
"@vueuse/components": ^9.2.0
"@vueuse/core": ^8.5.0
"@vueuse/integrations": ^9.2.0
axios: ^0.26.1
eslint: ^8.7.0
eslint-plugin-vue: ^8.3.0
fuse.js: ^6.6.2
pinia: ^2.0.17
pinia-plugin-persistedstate: ^2.1.1
sass: ^1.56.1
sass-loader: ^13.2.0
vite: ^3.0.4
vite-svg-loader: ^3.4.0
vue: ^3.2.37
vue-debounce: ^3.0.2
vue-router: ^4.1.3
vue-svg-loader: ^0.16.0
vue-template-compiler: ^2.0.0
vue-virtual-scroller: ^2.0.0-alpha.1
webpack: ^5.74.0
languageName: unknown
linkType: soft
"nanoid@npm:^3.2.0": "nanoid@npm:^3.2.0":
version: 3.2.0 version: 3.2.0
resolution: "nanoid@npm:3.2.0" resolution: "nanoid@npm:3.2.0"
@ -3639,6 +3609,36 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"swing_music_client@workspace:.":
version: 0.0.0-use.local
resolution: "swing_music_client@workspace:."
dependencies:
"@formkit/auto-animate": ^1.0.0-beta.3
"@popperjs/core": ^2.11.6
"@vitejs/plugin-vue": ^3.2.0
"@vueuse/components": ^9.2.0
"@vueuse/core": ^8.5.0
"@vueuse/integrations": ^9.2.0
axios: ^0.26.1
eslint: ^8.7.0
eslint-plugin-vue: ^8.3.0
fuse.js: ^6.6.2
pinia: ^2.0.17
pinia-plugin-persistedstate: ^2.1.1
sass: ^1.56.1
sass-loader: ^13.2.0
vite: ^3.0.4
vite-svg-loader: ^3.4.0
vue: ^3.2.37
vue-debounce: ^3.0.2
vue-router: ^4.1.3
vue-svg-loader: ^0.16.0
vue-template-compiler: ^2.0.0
vue-virtual-scroller: ^2.0.0-alpha.1
webpack: ^5.74.0
languageName: unknown
linkType: soft
"tapable@npm:^2.1.1, tapable@npm:^2.2.0": "tapable@npm:^2.1.1, tapable@npm:^2.2.0":
version: 2.2.1 version: 2.2.1
resolution: "tapable@npm:2.2.1" resolution: "tapable@npm:2.2.1"