| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 | 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.remote_url,      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, params.token])  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]    const text = e.clipboardData?.getData('text/plain')    if (file && !text) {      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,  }}
 |