refactor: improvemets

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-03-24 19:10:52 -04:00
parent b6d6bed2ee
commit da6a407445
16 changed files with 1063 additions and 254 deletions
+123 -57
View File
@@ -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,