Spaces:
Build error
Build error
import type { ClipboardEvent } from 'react' | |
import { | |
useCallback, | |
useState, | |
} from 'react' | |
import { useParams } from 'next/navigation' | |
import produce from 'immer' | |
import { v4 as uuid4 } from 'uuid' | |
import { useTranslation } from 'react-i18next' | |
import type { FileEntity } from './types' | |
import { useFileStore } from './store' | |
import { | |
fileUpload, | |
getSupportFileType, | |
isAllowedFileExtension, | |
} from './utils' | |
import { | |
AUDIO_SIZE_LIMIT, | |
FILE_SIZE_LIMIT, | |
IMG_SIZE_LIMIT, | |
MAX_FILE_UPLOAD_LIMIT, | |
VIDEO_SIZE_LIMIT, | |
} from '@/app/components/base/file-uploader/constants' | |
import { useToastContext } from '@/app/components/base/toast' | |
import { TransferMethod } from '@/types/app' | |
import { SupportUploadFileTypes } from '@/app/components/workflow/types' | |
import type { FileUpload } from '@/app/components/base/features/types' | |
import { formatFileSize } from '@/utils/format' | |
import { uploadRemoteFileInfo } from '@/service/common' | |
import type { FileUploadConfigResponse } from '@/models/common' | |
export const useFileSizeLimit = (fileUploadConfig?: FileUploadConfigResponse) => { | |
const imgSizeLimit = Number(fileUploadConfig?.image_file_size_limit) * 1024 * 1024 || IMG_SIZE_LIMIT | |
const docSizeLimit = Number(fileUploadConfig?.file_size_limit) * 1024 * 1024 || FILE_SIZE_LIMIT | |
const audioSizeLimit = Number(fileUploadConfig?.audio_file_size_limit) * 1024 * 1024 || AUDIO_SIZE_LIMIT | |
const videoSizeLimit = Number(fileUploadConfig?.video_file_size_limit) * 1024 * 1024 || VIDEO_SIZE_LIMIT | |
const maxFileUploadLimit = Number(fileUploadConfig?.workflow_file_upload_limit) || MAX_FILE_UPLOAD_LIMIT | |
return { | |
imgSizeLimit, | |
docSizeLimit, | |
audioSizeLimit, | |
videoSizeLimit, | |
maxFileUploadLimit, | |
} | |
} | |
export const useFile = (fileConfig: FileUpload) => { | |
const { t } = useTranslation() | |
const { notify } = useToastContext() | |
const fileStore = useFileStore() | |
const params = useParams() | |
const { imgSizeLimit, docSizeLimit, audioSizeLimit, videoSizeLimit } = useFileSizeLimit(fileConfig.fileUploadConfig) | |
const checkSizeLimit = useCallback((fileType: string, fileSize: number) => { | |
switch (fileType) { | |
case SupportUploadFileTypes.image: { | |
if (fileSize > imgSizeLimit) { | |
notify({ | |
type: 'error', | |
message: t('common.fileUploader.uploadFromComputerLimit', { | |
type: SupportUploadFileTypes.image, | |
size: formatFileSize(imgSizeLimit), | |
}), | |
}) | |
return false | |
} | |
return true | |
} | |
case SupportUploadFileTypes.document: { | |
if (fileSize > docSizeLimit) { | |
notify({ | |
type: 'error', | |
message: t('common.fileUploader.uploadFromComputerLimit', { | |
type: SupportUploadFileTypes.document, | |
size: formatFileSize(docSizeLimit), | |
}), | |
}) | |
return false | |
} | |
return true | |
} | |
case SupportUploadFileTypes.audio: { | |
if (fileSize > audioSizeLimit) { | |
notify({ | |
type: 'error', | |
message: t('common.fileUploader.uploadFromComputerLimit', { | |
type: SupportUploadFileTypes.audio, | |
size: formatFileSize(audioSizeLimit), | |
}), | |
}) | |
return false | |
} | |
return true | |
} | |
case SupportUploadFileTypes.video: { | |
if (fileSize > videoSizeLimit) { | |
notify({ | |
type: 'error', | |
message: t('common.fileUploader.uploadFromComputerLimit', { | |
type: SupportUploadFileTypes.video, | |
size: formatFileSize(videoSizeLimit), | |
}), | |
}) | |
return false | |
} | |
return true | |
} | |
case SupportUploadFileTypes.custom: { | |
if (fileSize > docSizeLimit) { | |
notify({ | |
type: 'error', | |
message: t('common.fileUploader.uploadFromComputerLimit', { | |
type: SupportUploadFileTypes.document, | |
size: formatFileSize(docSizeLimit), | |
}), | |
}) | |
return false | |
} | |
return true | |
} | |
default: { | |
return true | |
} | |
} | |
}, [audioSizeLimit, docSizeLimit, imgSizeLimit, notify, t, videoSizeLimit]) | |
const handleAddFile = useCallback((newFile: FileEntity) => { | |
const { | |
files, | |
setFiles, | |
} = fileStore.getState() | |
const newFiles = produce(files, (draft) => { | |
draft.push(newFile) | |
}) | |
setFiles(newFiles) | |
}, [fileStore]) | |
const handleUpdateFile = useCallback((newFile: FileEntity) => { | |
const { | |
files, | |
setFiles, | |
} = fileStore.getState() | |
const newFiles = produce(files, (draft) => { | |
const index = draft.findIndex(file => file.id === newFile.id) | |
if (index > -1) | |
draft[index] = newFile | |
}) | |
setFiles(newFiles) | |
}, [fileStore]) | |
const handleRemoveFile = useCallback((fileId: string) => { | |
const { | |
files, | |
setFiles, | |
} = fileStore.getState() | |
const newFiles = files.filter(file => file.id !== fileId) | |
setFiles(newFiles) | |
}, [fileStore]) | |
const handleReUploadFile = useCallback((fileId: string) => { | |
const { | |
files, | |
setFiles, | |
} = fileStore.getState() | |
const index = files.findIndex(file => file.id === fileId) | |
if (index > -1) { | |
const uploadingFile = files[index] | |
const newFiles = produce(files, (draft) => { | |
draft[index].progress = 0 | |
}) | |
setFiles(newFiles) | |
fileUpload({ | |
file: uploadingFile.originalFile!, | |
onProgressCallback: (progress) => { | |
handleUpdateFile({ ...uploadingFile, progress }) | |
}, | |
onSuccessCallback: (res) => { | |
handleUpdateFile({ ...uploadingFile, uploadedId: res.id, progress: 100 }) | |
}, | |
onErrorCallback: () => { | |
notify({ type: 'error', message: t('common.fileUploader.uploadFromComputerUploadError') }) | |
handleUpdateFile({ ...uploadingFile, progress: -1 }) | |
}, | |
}, !!params.token) | |
} | |
}, [fileStore, notify, t, handleUpdateFile, params]) | |
const startProgressTimer = useCallback((fileId: string) => { | |
const timer = setInterval(() => { | |
const files = fileStore.getState().files | |
const file = files.find(file => file.id === fileId) | |
if (file && file.progress < 80 && file.progress >= 0) | |
handleUpdateFile({ ...file, progress: file.progress + 20 }) | |
else | |
clearTimeout(timer) | |
}, 200) | |
}, [fileStore, handleUpdateFile]) | |
const handleLoadFileFromLink = useCallback((url: string) => { | |
const allowedFileTypes = fileConfig.allowed_file_types | |
const uploadingFile = { | |
id: uuid4(), | |
name: url, | |
type: '', | |
size: 0, | |
progress: 0, | |
transferMethod: TransferMethod.local_file, | |
supportFileType: '', | |
url, | |
isRemote: true, | |
} | |
handleAddFile(uploadingFile) | |
startProgressTimer(uploadingFile.id) | |
uploadRemoteFileInfo(url, !!params.token).then((res) => { | |
const newFile = { | |
...uploadingFile, | |
type: res.mime_type, | |
size: res.size, | |
progress: 100, | |
supportFileType: getSupportFileType(res.name, res.mime_type, allowedFileTypes?.includes(SupportUploadFileTypes.custom)), | |
uploadedId: res.id, | |
url: res.url, | |
} | |
if (!isAllowedFileExtension(res.name, res.mime_type, fileConfig.allowed_file_types || [], fileConfig.allowed_file_extensions || [])) { | |
notify({ type: 'error', message: t('common.fileUploader.fileExtensionNotSupport') }) | |
handleRemoveFile(uploadingFile.id) | |
} | |
if (!checkSizeLimit(newFile.supportFileType, newFile.size)) | |
handleRemoveFile(uploadingFile.id) | |
else | |
handleUpdateFile(newFile) | |
}).catch(() => { | |
notify({ type: 'error', message: t('common.fileUploader.pasteFileLinkInvalid') }) | |
handleRemoveFile(uploadingFile.id) | |
}) | |
}, [checkSizeLimit, handleAddFile, handleUpdateFile, notify, t, handleRemoveFile, fileConfig?.allowed_file_types, fileConfig.allowed_file_extensions, startProgressTimer]) | |
const handleLoadFileFromLinkSuccess = useCallback(() => { }, []) | |
const handleLoadFileFromLinkError = useCallback(() => { }, []) | |
const handleClearFiles = useCallback(() => { | |
const { | |
setFiles, | |
} = fileStore.getState() | |
setFiles([]) | |
}, [fileStore]) | |
const handleLocalFileUpload = useCallback((file: File) => { | |
if (!isAllowedFileExtension(file.name, file.type, fileConfig.allowed_file_types || [], fileConfig.allowed_file_extensions || [])) { | |
notify({ type: 'error', message: t('common.fileUploader.fileExtensionNotSupport') }) | |
return | |
} | |
const allowedFileTypes = fileConfig.allowed_file_types | |
const fileType = getSupportFileType(file.name, file.type, allowedFileTypes?.includes(SupportUploadFileTypes.custom)) | |
if (!checkSizeLimit(fileType, file.size)) | |
return | |
const reader = new FileReader() | |
const isImage = file.type.startsWith('image') | |
reader.addEventListener( | |
'load', | |
() => { | |
const uploadingFile = { | |
id: uuid4(), | |
name: file.name, | |
type: file.type, | |
size: file.size, | |
progress: 0, | |
transferMethod: TransferMethod.local_file, | |
supportFileType: getSupportFileType(file.name, file.type, allowedFileTypes?.includes(SupportUploadFileTypes.custom)), | |
originalFile: file, | |
base64Url: isImage ? reader.result as string : '', | |
} | |
handleAddFile(uploadingFile) | |
fileUpload({ | |
file: uploadingFile.originalFile, | |
onProgressCallback: (progress) => { | |
handleUpdateFile({ ...uploadingFile, progress }) | |
}, | |
onSuccessCallback: (res) => { | |
handleUpdateFile({ ...uploadingFile, uploadedId: res.id, progress: 100 }) | |
}, | |
onErrorCallback: () => { | |
notify({ type: 'error', message: t('common.fileUploader.uploadFromComputerUploadError') }) | |
handleUpdateFile({ ...uploadingFile, progress: -1 }) | |
}, | |
}, !!params.token) | |
}, | |
false, | |
) | |
reader.addEventListener( | |
'error', | |
() => { | |
notify({ type: 'error', message: t('common.fileUploader.uploadFromComputerReadError') }) | |
}, | |
false, | |
) | |
reader.readAsDataURL(file) | |
}, [checkSizeLimit, notify, t, handleAddFile, handleUpdateFile, params.token, fileConfig?.allowed_file_types, fileConfig?.allowed_file_extensions]) | |
const handleClipboardPasteFile = useCallback((e: ClipboardEvent<HTMLTextAreaElement>) => { | |
const file = e.clipboardData?.files[0] | |
if (file) { | |
e.preventDefault() | |
handleLocalFileUpload(file) | |
} | |
}, [handleLocalFileUpload]) | |
const [isDragActive, setIsDragActive] = useState(false) | |
const handleDragFileEnter = useCallback((e: React.DragEvent<HTMLElement>) => { | |
e.preventDefault() | |
e.stopPropagation() | |
setIsDragActive(true) | |
}, []) | |
const handleDragFileOver = useCallback((e: React.DragEvent<HTMLElement>) => { | |
e.preventDefault() | |
e.stopPropagation() | |
}, []) | |
const handleDragFileLeave = useCallback((e: React.DragEvent<HTMLElement>) => { | |
e.preventDefault() | |
e.stopPropagation() | |
setIsDragActive(false) | |
}, []) | |
const handleDropFile = useCallback((e: React.DragEvent<HTMLElement>) => { | |
e.preventDefault() | |
e.stopPropagation() | |
setIsDragActive(false) | |
const file = e.dataTransfer.files[0] | |
if (file) | |
handleLocalFileUpload(file) | |
}, [handleLocalFileUpload]) | |
return { | |
handleAddFile, | |
handleUpdateFile, | |
handleRemoveFile, | |
handleReUploadFile, | |
handleLoadFileFromLink, | |
handleLoadFileFromLinkSuccess, | |
handleLoadFileFromLinkError, | |
handleClearFiles, | |
handleLocalFileUpload, | |
handleClipboardPasteFile, | |
isDragActive, | |
handleDragFileEnter, | |
handleDragFileOver, | |
handleDragFileLeave, | |
handleDropFile, | |
} | |
} | |