2022-04-15 14:24:30 +00:00
|
|
|
<template>
|
2024-04-04 22:20:42 +00:00
|
|
|
<ScreenBase>
|
|
|
|
<template #header>
|
|
|
|
<ScreenHeader layout="collapsed">
|
|
|
|
Upload Media
|
|
|
|
|
|
|
|
<template #controls>
|
|
|
|
<BtnGroup v-if="hasUploadFailures" uppercased>
|
|
|
|
<Btn data-testid="upload-retry-all-btn" success @click="retryAll">
|
|
|
|
<Icon :icon="faRotateRight" />
|
|
|
|
Retry All
|
|
|
|
</Btn>
|
|
|
|
<Btn data-testid="upload-remove-all-btn" highlight @click="removeFailedEntries">
|
|
|
|
<Icon :icon="faTrashCan" />
|
|
|
|
Remove Failed
|
|
|
|
</Btn>
|
|
|
|
</BtnGroup>
|
|
|
|
</template>
|
|
|
|
</ScreenHeader>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<div
|
|
|
|
v-if="mediaPathSetUp"
|
|
|
|
:class="{ droppable }"
|
|
|
|
class="relative flex-1 flex flex-col"
|
|
|
|
@dragenter.prevent="onDragEnter"
|
|
|
|
@dragleave.prevent="onDragLeave"
|
|
|
|
@drop.prevent="onDrop"
|
|
|
|
@dragover.prevent
|
|
|
|
>
|
|
|
|
<div v-if="files.length" class="pb-4 space-y-3">
|
|
|
|
<UploadItem v-for="file in files" :key="file.id" :file="file" data-testid="upload-item" />
|
2022-04-15 14:24:30 +00:00
|
|
|
</div>
|
|
|
|
|
2022-04-21 18:12:11 +00:00
|
|
|
<ScreenEmptyState v-else>
|
2022-12-02 16:17:37 +00:00
|
|
|
<template #icon>
|
2024-04-04 22:20:42 +00:00
|
|
|
<Icon :icon="faUpload" />
|
2022-04-15 14:24:30 +00:00
|
|
|
</template>
|
2024-04-04 22:20:42 +00:00
|
|
|
|
|
|
|
{{ canDropFolders ? 'Drop files or folders to upload' : 'Drop files to upload' }}
|
|
|
|
|
|
|
|
<span class="secondary block">
|
|
|
|
<a class="block relative" role="button">
|
|
|
|
or click here to select songs
|
|
|
|
<input
|
|
|
|
:accept="acceptAttribute"
|
|
|
|
multiple
|
|
|
|
name="file[]"
|
|
|
|
type="file"
|
|
|
|
class="absolute opacity-0 w-full h-full z-[2] cursor-pointer left-0 top-0"
|
|
|
|
@change="onFileInputChange"
|
|
|
|
>
|
|
|
|
</a>
|
|
|
|
</span>
|
2022-04-21 18:12:11 +00:00
|
|
|
</ScreenEmptyState>
|
2022-04-15 14:24:30 +00:00
|
|
|
</div>
|
2024-04-04 22:20:42 +00:00
|
|
|
|
|
|
|
<ScreenEmptyState v-else>
|
|
|
|
<template #icon>
|
|
|
|
<Icon :icon="faWarning" />
|
|
|
|
</template>
|
|
|
|
No media path set.
|
|
|
|
</ScreenEmptyState>
|
|
|
|
</ScreenBase>
|
2022-04-15 14:24:30 +00:00
|
|
|
</template>
|
|
|
|
|
2022-04-15 17:00:08 +00:00
|
|
|
<script lang="ts" setup>
|
2022-11-08 19:35:18 +00:00
|
|
|
import { faRotateRight, faTrashCan, faUpload, faWarning } from '@fortawesome/free-solid-svg-icons'
|
2022-04-21 18:39:18 +00:00
|
|
|
import { computed, defineAsyncComponent, ref, toRef } from 'vue'
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-09-12 11:11:56 +00:00
|
|
|
import { isDirectoryReadingSupported as canDropFolders } from '@/utils'
|
|
|
|
import { acceptedMediaTypes } from '@/config'
|
2022-04-24 08:50:45 +00:00
|
|
|
import { uploadService } from '@/services'
|
2022-09-12 11:11:56 +00:00
|
|
|
import { useUpload } from '@/composables'
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-07-07 18:05:46 +00:00
|
|
|
import ScreenHeader from '@/components/ui/ScreenHeader.vue'
|
|
|
|
import ScreenEmptyState from '@/components/ui/ScreenEmptyState.vue'
|
2024-04-04 22:20:42 +00:00
|
|
|
import BtnGroup from '@/components/ui/form/BtnGroup.vue'
|
|
|
|
import ScreenBase from '@/components/screens/ScreenBase.vue'
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2024-04-04 22:20:42 +00:00
|
|
|
const Btn = defineAsyncComponent(() => import('@/components/ui/form/Btn.vue'))
|
2022-07-07 18:05:46 +00:00
|
|
|
const UploadItem = defineAsyncComponent(() => import('@/components/ui/upload/UploadItem.vue'))
|
2022-04-15 17:00:08 +00:00
|
|
|
|
2022-04-24 08:29:14 +00:00
|
|
|
const acceptAttribute = acceptedMediaTypes.join(',')
|
2022-04-21 18:39:18 +00:00
|
|
|
|
2022-09-12 11:11:56 +00:00
|
|
|
const { allowsUpload, mediaPathSetUp, queueFilesForUpload, handleDropEvent } = useUpload()
|
|
|
|
|
2022-04-24 08:50:45 +00:00
|
|
|
const files = toRef(uploadService.state, 'files')
|
2022-04-15 17:00:08 +00:00
|
|
|
const droppable = ref(false)
|
|
|
|
|
2024-01-24 22:39:47 +00:00
|
|
|
const hasUploadFailures = computed(() => files.value.filter(({ status }) => status === 'Errored').length > 0)
|
2022-04-15 17:00:08 +00:00
|
|
|
|
|
|
|
const onDragEnter = () => (droppable.value = allowsUpload.value)
|
|
|
|
const onDragLeave = () => (droppable.value = false)
|
|
|
|
|
2022-12-02 16:17:37 +00:00
|
|
|
const onFileInputChange = (event: Event) => {
|
2022-04-15 17:00:08 +00:00
|
|
|
const selectedFileList = (event.target as HTMLInputElement).files
|
|
|
|
|
2022-09-12 11:11:56 +00:00
|
|
|
if (selectedFileList?.length) {
|
|
|
|
queueFilesForUpload(Array.from(selectedFileList))
|
2022-04-15 17:00:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-12 11:11:56 +00:00
|
|
|
const onDrop = async (event: DragEvent) => {
|
|
|
|
droppable.value = false
|
|
|
|
await handleDropEvent(event)
|
2022-04-15 17:00:08 +00:00
|
|
|
}
|
|
|
|
|
2022-09-12 11:11:56 +00:00
|
|
|
const retryAll = () => uploadService.retryAll()
|
|
|
|
const removeFailedEntries = () => uploadService.removeFailed()
|
2022-04-15 14:24:30 +00:00
|
|
|
</script>
|