All files / app/src/shared/lib/upload upload.tsx

58.82% Statements 50/85
66.66% Branches 2/3
40% Functions 2/5
58.82% Lines 50/85

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 1151x                                             1x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x   11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x                               11x 11x                                     11x           11x   11x 11x 11x 11x 11x 11x 11x 11x   11x   11x  
import RCUpload, { UploadProps as RCUploadProps } from 'rc-upload'
import { cloneElement, ReactElement } from 'react'
import { Controller, RegisterOptions, useFormContext } from 'react-hook-form'
import { UploadHookProps, useUpload } from '@/shared/hooks'
import { RcFile } from 'rc-upload/lib/interface'
import { AxiosRequestConfig, AxiosResponse } from 'axios'
import { FCWithClassName, FileModel } from '@/shared/@types'
import cn from 'classnames'
import { notify } from '../notification'
import { useTranslate } from '../change-language'
import { extension } from 'mime-types'
 
export interface UploadProps extends Omit<RCUploadProps, 'customRequest' | 'onSuccess'> {
  optimistic?: boolean
  withOffline?: boolean
  rules?: RegisterOptions
  error?: boolean
  children?: ReactElement
  onSuccess?: (file: FileModel) => void
  customRequest?: (file: RcFile, config?: AxiosRequestConfig<RcFile>) => Promise<AxiosResponse<FileModel>>
}
 
// Использовать только внутри компонента Form
export const Upload: FCWithClassName<UploadProps> = ({
  children,
  name = 'files',
  optimistic,
  withOffline,
  rules,
  max,
  error,
  className = '',
  onSuccess,
  customRequest,
  ...rest
}) => {
  const { t } = useTranslate(['common'])
  const { control, getValues, setValue } = useFormContext()
  const { upload } = useUpload({
    multiple: rest.multiple,
    optimistic,
    withOffline,
    customRequest,
  } as UploadHookProps)
 
  return (
    <Controller
      name={name}
      control={control}
      rules={rules}
      render={({ field }) => (
        <RCUpload
          max={max}
          disabled={rest.disabled}
          className={cn('inline-flex input-focus focus-visible:ring-primary', {
            'focus-visible:ring-red': error,
            [className]: className,
          })}
          beforeUpload={(file, list) => {
            const hasMaxNotReached = !max || list.length + (getValues(name) || []).length <= max
            const fileExtension = extension(file.type)
            const hasAcceptedExtension = !rest.accept || !fileExtension || rest.accept.includes(fileExtension)
            const hasMaxSizeNotReached = file.size / 1048576 < 100
            if (!hasMaxNotReached) {
              notify(t('Maximum number of files exceeded'), { status: 'error' })
            }
            if (!hasAcceptedExtension) {
              notify(t('Unsupported file format'), { status: 'error' })
            }
            if (!hasMaxSizeNotReached) {
              notify(t('The maximum file size is 100 megabytes'), { status: 'error' })
            }
            return hasMaxNotReached && hasAcceptedExtension && hasMaxSizeNotReached
          }}
          customRequest={upload}
          onSuccess={res => {
            if (rest.multiple) {
              const currentValue = getValues(name) || []
              const existItemIndex = currentValue.findIndex((file: RcFile) => file.uid === res.uid)
              if (~existItemIndex) {
                currentValue[existItemIndex] = res
              } else {
                currentValue.push(res)
              }
              field.onChange(currentValue)
            } else {
              field.onChange(res)
            }
 
            if (res?.['@id']) {
              // RcUpload не дает возмоажности перебить типы onSuccess
              onSuccess?.(res as unknown as FileModel)
            }
          }}
          onError={(_, __, rcFile) =>
            setValue(
              name,
              (getValues(name) as (FileModel & { uid: string })[])?.filter(file => file.uid !== rcFile.uid) || []
            )
          }
          {...rest}
        >
          {children &&
            (!max || (getValues(name)?.length || 0) < max) &&
            cloneElement(children, {
              ...children.props,
              value: field.value,
              disabled: rest.disabled,
            })}
        </RCUpload>
      )}
    />
  )
}