refactor: improvemets
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
* Supports individual files and entire folder uploads with path preservation
|
||||
*/
|
||||
|
||||
import { ref, computed } from 'vue'
|
||||
import { ref, computed, unref } from 'vue'
|
||||
import type { Ref, ComputedRef } from 'vue'
|
||||
import { useNodesStore, ROOT_ID } from '@DocumentsManager/stores/nodesStore'
|
||||
import { EntityObject } from '@DocumentsManager/models/entity'
|
||||
@@ -25,9 +25,19 @@ export interface FileWithPath {
|
||||
relativePath: string
|
||||
}
|
||||
|
||||
interface NormalizedUploadItem {
|
||||
file: File
|
||||
relativePath?: string
|
||||
}
|
||||
|
||||
export interface AddUploadsWithPathsOptions {
|
||||
batchSize?: number
|
||||
onProgress?: (processed: number, total: number) => void
|
||||
}
|
||||
|
||||
export interface UseFileUploadOptions {
|
||||
providerId: string
|
||||
serviceId: string
|
||||
providerId: string | Ref<string> | ComputedRef<string>
|
||||
serviceId: string | Ref<string> | ComputedRef<string>
|
||||
collectionId?: string | null
|
||||
maxFileSize?: number
|
||||
allowedTypes?: string[]
|
||||
@@ -44,6 +54,9 @@ export function useFileUpload(options: UseFileUploadOptions) {
|
||||
allowedTypes
|
||||
} = options
|
||||
|
||||
const currentProviderId = () => unref(providerId)
|
||||
const currentServiceId = () => unref(serviceId)
|
||||
|
||||
const uploads: Ref<Map<string, FileUploadProgress>> = ref(new Map())
|
||||
const isUploading = ref(false)
|
||||
|
||||
@@ -113,25 +126,69 @@ export function useFileUpload(options: UseFileUploadOptions) {
|
||||
return `${pathPart}-${file.size}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
|
||||
}
|
||||
|
||||
const createUploadProgress = (file: File, relativePath?: string): FileUploadProgress => {
|
||||
const error = validateFile(file)
|
||||
|
||||
return {
|
||||
file,
|
||||
progress: 0,
|
||||
status: error ? 'error' : 'pending',
|
||||
error: error || undefined,
|
||||
relativePath,
|
||||
}
|
||||
}
|
||||
|
||||
const yieldToBrowser = async (): Promise<void> => {
|
||||
await new Promise<void>(resolve => {
|
||||
if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function') {
|
||||
window.requestAnimationFrame(() => resolve())
|
||||
return
|
||||
}
|
||||
|
||||
setTimeout(resolve, 0)
|
||||
})
|
||||
}
|
||||
|
||||
const normalizeUploadsWithPaths = (
|
||||
filesOrList: FileList | File[] | FileWithPath[]
|
||||
): NormalizedUploadItem[] => {
|
||||
if (filesOrList instanceof FileList) {
|
||||
return Array.from(filesOrList, file => ({
|
||||
file,
|
||||
relativePath: (file as File & { webkitRelativePath?: string }).webkitRelativePath || file.name,
|
||||
}))
|
||||
}
|
||||
|
||||
if (filesOrList.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (filesOrList[0] instanceof File) {
|
||||
return (filesOrList as File[]).map(file => ({
|
||||
file,
|
||||
relativePath: (file as File & { webkitRelativePath?: string }).webkitRelativePath || file.name,
|
||||
}))
|
||||
}
|
||||
|
||||
return (filesOrList as FileWithPath[]).map(({ file, relativePath }) => ({ file, relativePath }))
|
||||
}
|
||||
|
||||
// Add files to upload queue
|
||||
const addFiles = (files: FileList | File[]): FileUploadProgress[] => {
|
||||
const added: FileUploadProgress[] = []
|
||||
|
||||
|
||||
const nextUploads = new Map(uploads.value)
|
||||
|
||||
for (const file of files) {
|
||||
const error = validateFile(file)
|
||||
const progress = createUploadProgress(file)
|
||||
const uploadId = generateUploadId(file)
|
||||
|
||||
const progress: FileUploadProgress = {
|
||||
file,
|
||||
progress: 0,
|
||||
status: error ? 'error' : 'pending',
|
||||
error: error || undefined
|
||||
}
|
||||
|
||||
uploads.value.set(uploadId, progress)
|
||||
|
||||
nextUploads.set(uploadId, progress)
|
||||
added.push(progress)
|
||||
}
|
||||
|
||||
|
||||
uploads.value = nextUploads
|
||||
|
||||
return added
|
||||
}
|
||||
|
||||
@@ -140,47 +197,55 @@ export function useFileUpload(options: UseFileUploadOptions) {
|
||||
filesOrList: FileList | File[] | FileWithPath[]
|
||||
): FileUploadProgress[] => {
|
||||
const added: FileUploadProgress[] = []
|
||||
|
||||
// Handle FileList from webkitdirectory input
|
||||
if (filesOrList instanceof FileList || (Array.isArray(filesOrList) && filesOrList[0] instanceof File && !('relativePath' in filesOrList[0]))) {
|
||||
const fileList = filesOrList as FileList | File[]
|
||||
for (const file of fileList) {
|
||||
// webkitRelativePath is set on files from folder input
|
||||
const relativePath = (file as any).webkitRelativePath || file.name
|
||||
const error = validateFile(file)
|
||||
|
||||
const nextUploads = new Map(uploads.value)
|
||||
|
||||
for (const { file, relativePath } of normalizeUploadsWithPaths(filesOrList)) {
|
||||
const progress = createUploadProgress(file, relativePath)
|
||||
const uploadId = generateUploadId(file, relativePath)
|
||||
|
||||
nextUploads.set(uploadId, progress)
|
||||
added.push(progress)
|
||||
}
|
||||
|
||||
uploads.value = nextUploads
|
||||
|
||||
return added
|
||||
}
|
||||
|
||||
const addFilesWithPathsBatched = async (
|
||||
filesOrList: FileList | File[] | FileWithPath[],
|
||||
options: AddUploadsWithPathsOptions = {}
|
||||
): Promise<FileUploadProgress[]> => {
|
||||
const items = normalizeUploadsWithPaths(filesOrList)
|
||||
const total = items.length
|
||||
const batchSize = Math.max(1, options.batchSize ?? 250)
|
||||
const added: FileUploadProgress[] = []
|
||||
|
||||
options.onProgress?.(0, total)
|
||||
|
||||
for (let start = 0; start < total; start += batchSize) {
|
||||
const batch = items.slice(start, start + batchSize)
|
||||
const nextUploads = new Map(uploads.value)
|
||||
|
||||
for (const { file, relativePath } of batch) {
|
||||
const progress = createUploadProgress(file, relativePath)
|
||||
const uploadId = generateUploadId(file, relativePath)
|
||||
|
||||
const progress: FileUploadProgress = {
|
||||
file,
|
||||
progress: 0,
|
||||
status: error ? 'error' : 'pending',
|
||||
error: error || undefined,
|
||||
relativePath
|
||||
}
|
||||
|
||||
uploads.value.set(uploadId, progress)
|
||||
|
||||
nextUploads.set(uploadId, progress)
|
||||
added.push(progress)
|
||||
}
|
||||
} else {
|
||||
// Handle FileWithPath array (from drag & drop folder processing)
|
||||
const filesWithPaths = filesOrList as FileWithPath[]
|
||||
for (const { file, relativePath } of filesWithPaths) {
|
||||
const error = validateFile(file)
|
||||
const uploadId = generateUploadId(file, relativePath)
|
||||
|
||||
const progress: FileUploadProgress = {
|
||||
file,
|
||||
progress: 0,
|
||||
status: error ? 'error' : 'pending',
|
||||
error: error || undefined,
|
||||
relativePath
|
||||
}
|
||||
|
||||
uploads.value.set(uploadId, progress)
|
||||
added.push(progress)
|
||||
|
||||
uploads.value = nextUploads
|
||||
|
||||
const processed = Math.min(start + batch.length, total)
|
||||
options.onProgress?.(processed, total)
|
||||
|
||||
if (processed < total) {
|
||||
await yieldToBrowser()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return added
|
||||
}
|
||||
|
||||
@@ -228,8 +293,8 @@ export function useFileUpload(options: UseFileUploadOptions) {
|
||||
try {
|
||||
// Create the folder
|
||||
const collection = await nodesStore.createCollection(
|
||||
providerId,
|
||||
serviceId,
|
||||
currentProviderId(),
|
||||
currentServiceId(),
|
||||
parentId,
|
||||
{ label: folderName, owner: '' }
|
||||
)
|
||||
@@ -277,8 +342,8 @@ export function useFileUpload(options: UseFileUploadOptions) {
|
||||
|
||||
// Create the entity
|
||||
const entity = await nodesStore.createEntity(
|
||||
providerId,
|
||||
serviceId,
|
||||
currentProviderId(),
|
||||
currentServiceId(),
|
||||
targetCollection || ROOT_ID,
|
||||
{
|
||||
'@type': 'documents.properties',
|
||||
@@ -295,8 +360,8 @@ export function useFileUpload(options: UseFileUploadOptions) {
|
||||
|
||||
// Write the content
|
||||
await nodesStore.writeEntity(
|
||||
providerId,
|
||||
serviceId,
|
||||
currentProviderId(),
|
||||
currentServiceId(),
|
||||
targetCollection || ROOT_ID,
|
||||
String(entity.identifier),
|
||||
content
|
||||
@@ -417,6 +482,7 @@ export function useFileUpload(options: UseFileUploadOptions) {
|
||||
validateFile,
|
||||
addFiles,
|
||||
addFilesWithPaths,
|
||||
addFilesWithPathsBatched,
|
||||
uploadFile,
|
||||
uploadAll,
|
||||
removeUpload,
|
||||
|
||||
Reference in New Issue
Block a user