2024-02-24 15:37:01 +00:00
|
|
|
<template>
|
2024-04-04 22:20:42 +00:00
|
|
|
<article
|
|
|
|
class="cover relative w-full aspect-square block rounded-md overflow-hidden bg-no-repeat bg-cover bg-center"
|
2024-02-24 15:37:01 +00:00
|
|
|
data-testid="playlist-thumbnail"
|
|
|
|
>
|
2024-07-26 14:33:14 +00:00
|
|
|
<slot />
|
|
|
|
<div
|
|
|
|
v-if="canEditPlaylist"
|
|
|
|
class="absolute inset-0 w-full h-full bg-black bg-opacity-50 opacity-0 hover:opacity-100 transition-opacity duration-200
|
|
|
|
flex items-center justify-center"
|
|
|
|
>
|
|
|
|
<div class="border border-1.5 border-white/20 rounded-md overflow-hidden">
|
|
|
|
<button class="p-2 hover:bg-black/50" title="Upload cover image" @click.prevent="uploadCover">
|
|
|
|
<Icon :icon="faUpload" class="text-white text-xl" fixed-width />
|
|
|
|
</button>
|
|
|
|
<button
|
|
|
|
v-if="playlist.cover"
|
|
|
|
class="p-2 hover:bg-black/30"
|
|
|
|
title="Remove cover image"
|
|
|
|
@click.prevent="removeCover"
|
|
|
|
>
|
|
|
|
<Icon :icon="faTrash" class="text-white text-xl" fixed-width />
|
|
|
|
</button>
|
|
|
|
</div>
|
2024-02-24 15:37:01 +00:00
|
|
|
</div>
|
2024-04-04 22:20:42 +00:00
|
|
|
</article>
|
2024-02-24 15:37:01 +00:00
|
|
|
</template>
|
|
|
|
|
2024-04-23 21:01:27 +00:00
|
|
|
<script lang="ts" setup>
|
2024-04-23 11:24:29 +00:00
|
|
|
import { computed, ref, toRefs } from 'vue'
|
|
|
|
import { defaultCover } from '@/utils'
|
|
|
|
import { playlistStore } from '@/stores'
|
2024-07-26 14:43:41 +00:00
|
|
|
import { useErrorHandler, useFileReader, useKoelPlus, useMessageToaster, usePolicies } from '@/composables'
|
2024-07-26 14:33:14 +00:00
|
|
|
import { faTrash, faUpload } from '@fortawesome/free-solid-svg-icons'
|
2024-02-24 15:37:01 +00:00
|
|
|
|
|
|
|
const props = defineProps<{ playlist: Playlist }>()
|
|
|
|
const { playlist } = toRefs(props)
|
|
|
|
|
2024-07-26 14:33:14 +00:00
|
|
|
const { currentUserCan } = usePolicies()
|
2024-02-24 15:37:01 +00:00
|
|
|
const { isPlus } = useKoelPlus()
|
2024-07-26 14:43:41 +00:00
|
|
|
const { toastSuccess } = useMessageToaster()
|
2024-02-24 15:37:01 +00:00
|
|
|
|
2024-07-26 14:33:14 +00:00
|
|
|
const canEditPlaylist = computed(() => currentUserCan.editPlaylist(playlist.value!))
|
2024-04-23 21:01:27 +00:00
|
|
|
const backgroundImage = computed(() => `url(${playlist.value.cover || defaultCover})`)
|
2024-04-04 22:20:42 +00:00
|
|
|
|
2024-07-26 14:33:14 +00:00
|
|
|
const uploadCover = () => {
|
|
|
|
const input = document.createElement('input')
|
|
|
|
input.setAttribute('type', 'file')
|
|
|
|
input.setAttribute('accept', 'image/*')
|
|
|
|
|
|
|
|
input.addEventListener('change', async () => {
|
|
|
|
const file = input.files?.[0]
|
|
|
|
|
|
|
|
if (!file) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const backupImage = playlist.value.cover
|
|
|
|
|
|
|
|
try {
|
|
|
|
useFileReader().readAsDataUrl(file, async url => {
|
|
|
|
playlist.value!.cover = url
|
|
|
|
await playlistStore.uploadCover(playlist.value, url)
|
|
|
|
toastSuccess('Playlist cover updated.')
|
|
|
|
})
|
|
|
|
} catch (error: unknown) {
|
|
|
|
// restore the backup image
|
|
|
|
playlist.value.cover = backupImage
|
|
|
|
useErrorHandler().handleHttpError(error)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
input.dispatchEvent(new MouseEvent('click'))
|
2024-02-24 15:37:01 +00:00
|
|
|
}
|
|
|
|
|
2024-07-26 14:33:14 +00:00
|
|
|
const removeCover = async () => await playlistStore.removeCover(playlist.value)
|
2024-02-24 15:37:01 +00:00
|
|
|
</script>
|
|
|
|
|
2024-04-23 21:01:27 +00:00
|
|
|
<style lang="postcss" scoped>
|
2024-04-04 22:20:42 +00:00
|
|
|
article {
|
|
|
|
background-image: v-bind(backgroundImage);
|
2024-02-24 15:37:01 +00:00
|
|
|
|
|
|
|
.thumbnail-stack {
|
2024-04-04 22:20:42 +00:00
|
|
|
@apply pointer-events-none;
|
2024-02-24 15:37:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|