file uploader hooks

This commit is contained in:
StyleZhang 2024-08-09 16:48:36 +08:00
parent ade385c9c1
commit 594bf96922
4 changed files with 171 additions and 5 deletions

View File

@ -1,5 +1,162 @@
import type { ClipboardEvent } from 'react'
import {
useCallback,
useState,
} from 'react'
import produce from 'immer'
import { v4 as uuid4 } from 'uuid'
import { useTranslation } from 'react-i18next'
import type { TFile } from './types'
import { useFileStore } from './store'
import { fileUpload } from './utils'
import { useToastContext } from '@/app/components/base/toast'
export const useFile = () => {
const fileStore = useFileStore()
type UseFileParams = {
isPublicAPI?: boolean
url?: string
}
export const useFile = ({
isPublicAPI,
url,
}: UseFileParams) => {
const { t } = useTranslation()
const { notify } = useToastContext()
const fileStore = useFileStore()
const handleAddOrUpdateFiles = useCallback((newFile: TFile) => {
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
else
draft.push(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 handleLoadFileFromLink = useCallback((fileId: string, progress: number) => {
const {
files,
setFiles,
} = fileStore.getState()
const newFiles = produce(files, (draft) => {
const index = draft.findIndex(file => file._id === fileId)
if (index > -1)
draft[index]._progress = progress
})
setFiles(newFiles)
}, [fileStore])
const handleClearFiles = useCallback(() => {
const {
setFiles,
} = fileStore.getState()
setFiles([])
}, [fileStore])
const handleLocalFileUpload = useCallback((file: File) => {
const reader = new FileReader()
reader.addEventListener(
'load',
() => {
const imageFile = {
_id: uuid4(),
file,
_url: reader.result as string,
_progress: 0,
}
handleAddOrUpdateFiles(imageFile)
fileUpload({
file: imageFile.file,
onProgressCallback: (progress) => {
handleAddOrUpdateFiles({ ...imageFile, _progress: progress })
},
onSuccessCallback: (res) => {
handleAddOrUpdateFiles({ ...imageFile, _fileId: res.id, _progress: 100 })
},
onErrorCallback: () => {
notify({ type: 'error', message: t('common.imageUploader.uploadFromComputerUploadError') })
handleAddOrUpdateFiles({ ...imageFile, _progress: -1 })
},
}, isPublicAPI, url)
},
false,
)
reader.addEventListener(
'error',
() => {
notify({ type: 'error', message: t('common.imageUploader.uploadFromComputerReadError') })
},
false,
)
reader.readAsDataURL(file)
}, [notify, t, handleAddOrUpdateFiles, isPublicAPI, url])
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 {
handleAddOrUpdateFiles,
handleRemoveFile,
handleLoadFileFromLink,
handleClearFiles,
handleLocalFileUpload,
handleClipboardPasteFile,
isDragActive,
handleDragFileEnter,
handleDragFileOver,
handleDragFileLeave,
handleDropFile,
}
}

View File

@ -7,10 +7,11 @@ import {
useStore as useZustandStore,
} from 'zustand'
import { createStore } from 'zustand/vanilla'
import type { TFile } from './types'
type Shape = {
files: any[]
setFiles: (files: any[]) => void
files: TFile[]
setFiles: (files: TFile[]) => void
}
export const createFileStore = () => {

View File

@ -12,3 +12,11 @@ export enum FileTypeEnum {
GIF = 'GIF',
OTHER = 'OTHER',
}
export type TFile = {
file: File
_id: string
_fileId?: string
_progress?: number
_url?: string
}

View File

@ -7,7 +7,7 @@ type FileUploadParams = {
onErrorCallback: () => void
}
type FileUpload = (v: FileUploadParams, isPublic?: boolean, url?: string) => void
export const imageUpload: FileUpload = ({
export const fileUpload: FileUpload = ({
file,
onProgressCallback,
onSuccessCallback,