import type { MaybeRefOrGetter } from 'vue' import type { BglFile, QueueFile } from './upload.types' import { ref, computed, onMounted, toValue, } from 'vue' import { files } from './upload' interface UseFileUploadProps { multiple?: boolean dirPath?: MaybeRefOrGetter accept?: string capture?: boolean | 'user' | 'environment' disabled?: boolean namespace?: string onFileQueued?: (files: QueueFile[]) => void onChange?: () => void } export function useFileUpload(props: UseFileUploadProps = {}) { const namespace = props.namespace || 'default' const fileQueue = ref([]) const storageFiles = ref([]) const pk = ref([]) // Computed const pathKeys = computed(() => { const storagePathKeys = storageFiles.value.map(file => file.path_key) return [...pk.value, ...storagePathKeys] }) // File handling methods const fileToUrl = (file: File) => URL.createObjectURL(file) function addFile(file?: File | File[] | FileList | null) { if (!file) { return } let filesToAdd: File[] = [] if (file instanceof File) { filesToAdd = [file] } else if (file instanceof FileList) { filesToAdd = Array.from(file) } else if (Array.isArray(file)) { filesToAdd = file } const newQueueFiles = filesToAdd.map(f => ({ url: fileToUrl(f), name: f.name, file: f, progress: 0, namespace })) if (props.multiple) { fileQueue.value.push(...newQueueFiles) } else { fileQueue.value = newQueueFiles } // Call the onFilesQueued callback if provided if (props.onFileQueued && newQueueFiles.length > 0) { props.onFileQueued(newQueueFiles) } } async function removeFile(pathKeyOrFile: string | File | QueueFile) { if (typeof pathKeyOrFile === 'string') { // Remove from both lists storageFiles.value = storageFiles.value.filter(file => file.path_key !== pathKeyOrFile) const pathKeyIndex = pk.value.indexOf(pathKeyOrFile) if (pathKeyIndex !== -1) { pk.value.splice(pathKeyIndex, 1) } try { await files.delete(pathKeyOrFile) } catch (error) { console.error('Error deleting file:', error) } } else if (pathKeyOrFile) { // For File or QueueFile, check if it's in our queue for this namespace const fileToRemove = pathKeyOrFile instanceof File ? { name: pathKeyOrFile.name } : pathKeyOrFile const index = fileQueue.value.findIndex(queueFile => queueFile.file.name === fileToRemove.name && queueFile.namespace === namespace ) if (index !== -1) { fileQueue.value.splice(index, 1) } } // Notify parent of change props.onChange?.() } async function flushQueue() { // Only process files in the current namespace const namespaceQueue = fileQueue.value.filter(file => file.namespace === namespace) for (const file of namespaceQueue) { file.uploading = true // If not multiple, replace the existing file if (!props.multiple) { pk.value.splice(0, 1) } try { const { data } = await files.upload(file.file, { onUploadProgress: (e: ProgressEvent) => { file.progress = (e.loaded / e.total) * 100 - 1 }, dirPath: toValue(props.dirPath) }) pk.value.push(data.path_key) } catch (error) { console.error('Error uploading file:', error) } } // Clear only files in the current namespace from the queue fileQueue.value = fileQueue.value.filter(file => file.namespace !== namespace) } // UI interaction function browse(autoFlush = false) { if (props.disabled) { return } const input = document.createElement('input') input.type = 'file' input.multiple = !!props.multiple input.accept = props.accept || '' if (props.capture !== undefined && props.capture !== false) { input.capture = props.capture === true ? 'environment' : props.capture } input.onchange = (e: Event) => { addFile((e.target as HTMLInputElement).files) if (autoFlush) { flushQueue() } } input.click() } // Load initial files onMounted(() => { const dirPath = toValue(props.dirPath) if (dirPath) { files.list(dirPath) .then((response) => { const responseData = Array.isArray(response.data) ? response.data : [response.data] storageFiles.value.push(...responseData) }) .catch((error) => { console.error('Error loading files:', error) }) } }) return { fileQueue, storageFiles, pk, pathKeys, fileToUrl, removeFile, flushQueue, addFile, browse, namespace } }