uploader.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import type { ChangeEvent, FC } from 'react'
  2. import { useState } from 'react'
  3. import { useParams } from 'next/navigation'
  4. import { useTranslation } from 'react-i18next'
  5. import { imageUpload } from './utils'
  6. import type { ImageFile } from '@/types/app'
  7. import { TransferMethod } from '@/types/app'
  8. import { useToastContext } from '@/app/components/base/toast'
  9. type UploaderProps = {
  10. children: (hovering: boolean) => JSX.Element
  11. onUpload: (imageFile: ImageFile) => void
  12. limit?: number
  13. disabled?: boolean
  14. }
  15. const Uploader: FC<UploaderProps> = ({
  16. children,
  17. onUpload,
  18. limit,
  19. disabled,
  20. }) => {
  21. const [hovering, setHovering] = useState(false)
  22. const params = useParams()
  23. const { notify } = useToastContext()
  24. const { t } = useTranslation()
  25. const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
  26. const file = e.target.files?.[0]
  27. if (!file)
  28. return
  29. if (limit && file.size > limit * 1024 * 1024) {
  30. notify({ type: 'error', message: t('common.imageUploader.uploadFromComputerLimit', { size: limit }) })
  31. return
  32. }
  33. const reader = new FileReader()
  34. reader.addEventListener(
  35. 'load',
  36. () => {
  37. const imageFile = {
  38. type: TransferMethod.local_file,
  39. _id: `${Date.now()}`,
  40. fileId: '',
  41. file,
  42. url: reader.result as string,
  43. base64Url: reader.result as string,
  44. progress: 0,
  45. }
  46. onUpload(imageFile)
  47. imageUpload({
  48. file: imageFile.file,
  49. onProgressCallback: (progress) => {
  50. onUpload({ ...imageFile, progress })
  51. },
  52. onSuccessCallback: (res) => {
  53. onUpload({ ...imageFile, fileId: res.id, progress: 100 })
  54. },
  55. onErrorCallback: () => {
  56. notify({ type: 'error', message: t('common.imageUploader.uploadFromComputerUploadError') })
  57. onUpload({ ...imageFile, progress: -1 })
  58. },
  59. }, !!params.token)
  60. },
  61. false,
  62. )
  63. reader.addEventListener(
  64. 'error',
  65. () => {
  66. notify({ type: 'error', message: t('common.imageUploader.uploadFromComputerReadError') })
  67. },
  68. false,
  69. )
  70. reader.readAsDataURL(file)
  71. }
  72. return (
  73. <div
  74. className='relative'
  75. onMouseEnter={() => setHovering(true)}
  76. onMouseLeave={() => setHovering(false)}
  77. >
  78. {children(hovering)}
  79. <input
  80. className={`
  81. absolute block inset-0 opacity-0 text-[0]
  82. ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}
  83. `}
  84. onClick={e => (e.target as HTMLInputElement).value = ''}
  85. type='file'
  86. accept='.png, .jpg, .jpeg, .webp, .gif'
  87. onChange={handleChange}
  88. disabled={disabled}
  89. />
  90. </div>
  91. )
  92. }
  93. export default Uploader