react-dropzone#FileRejection TypeScript Examples

The following examples show how to use react-dropzone#FileRejection. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.tsx    From whiteboard-demo with MIT License 6 votes vote down vote up
private onDropFiles = async (
        acceptedFiles: File[],
        rejectedFiles: FileRejection[],
        event: React.DragEvent<HTMLDivElement>): Promise<void> => {
        event.persist();
        const {room, apiOrigin} = this.props;
        try {
            const uploadManager = new UploadManager(this.client, room, apiOrigin, this.props.region);
            await Promise.all([
                uploadManager.uploadImageFiles(acceptedFiles, event.clientX, event.clientY, this.onProgress),
            ]);
        } catch (error) {
            room.setMemberState({
                currentApplianceName: ApplianceNames.pencil,
            });
        }
    }
Example #2
Source File: UploadBoxCompact.tsx    From nextclade with MIT License 6 votes vote down vote up
export function makeOnDrop({ t, onUpload, setErrors }: MakeOnDropParams) {
  function handleError(error: Error) {
    if (error instanceof UploadErrorTooManyFiles) {
      setErrors((prevErrors) => [...prevErrors, t('Only one file is expected')])
    } else if (error instanceof UploadErrorUnknown) {
      setErrors((prevErrors) => [...prevErrors, t('Unknown error')])
    } else {
      throw error
    }
  }

  async function processFiles(acceptedFiles: File[], rejectedFiles: FileRejection[]) {
    const nFiles = acceptedFiles.length + rejectedFiles.length

    if (nFiles > 1) {
      throw new UploadErrorTooManyFiles(nFiles)
    }

    if (acceptedFiles.length !== 1) {
      throw new UploadErrorTooManyFiles(acceptedFiles.length)
    }

    const file = acceptedFiles[0]
    onUpload(file)
  }

  async function onDrop(acceptedFiles: File[], rejectedFiles: FileRejection[]) {
    setErrors([])
    try {
      await processFiles(acceptedFiles, rejectedFiles)
    } catch (error: unknown) {
      handleError(sanitizeError(error))
    }
  }

  // eslint-disable-next-line no-void
  return (acceptedFiles: File[], rejectedFiles: FileRejection[]) => void onDrop(acceptedFiles, rejectedFiles)
}
Example #3
Source File: MultiFileSelect.tsx    From atlas with GNU General Public License v3.0 4 votes vote down vote up
MultiFileSelect: React.FC<MultiFileSelectProps> = React.memo(
  ({ onVideoChange, onThumbnailChange, files, maxImageSize, maxVideoSize, editMode = false, disabled, className }) => {
    const dialogRef = useRef<ImageCropModalImperativeHandle>(null)
    const [step, setStep] = useState<FileType>('video')
    const [isImgLoading, setIsImgLoading] = useState(false)
    const [isVideoLoading, setIsVideoLoading] = useState(false)
    const [rawImageFile, setRawImageFile] = useState<File | null>(null)
    const thumbnailStepRef = useRef<HTMLDivElement>(null)
    const [error, setError] = useState<string | null>(null)

    const [underlineWidth, setUnderlineWidth] = useState(0)
    const [underlineLeft, setUnderlineLeft] = useState(0)

    useResizeObserver({
      box: 'border-box',
      ref: thumbnailStepRef,
      onResize: () => {
        setUnderlineWidth(thumbnailStepRef?.current?.offsetWidth || 0)
        setUnderlineLeft(step === 'image' ? thumbnailStepRef?.current?.offsetLeft || 0 : 0)
      },
    })

    useLayoutEffect(() => {
      if (thumbnailStepRef?.current?.offsetWidth) {
        setUnderlineWidth(thumbnailStepRef?.current?.offsetWidth)
      }
    }, [])

    useLayoutEffect(() => {
      if (thumbnailStepRef?.current?.offsetLeft) {
        setUnderlineLeft(step === 'image' ? thumbnailStepRef?.current?.offsetLeft : 0)
      }
    }, [step])

    useEffect(() => {
      if (isImgLoading || isVideoLoading) {
        return
      }
      if (editMode || files.video) {
        setStep('image')
      } else {
        setStep('video')
      }
    }, [editMode, files.video, isImgLoading, isVideoLoading])

    useEffect(() => {
      if (!isVideoLoading && !isImgLoading) {
        return
      }
      if (error) {
        setIsVideoLoading(false)
        return
      }
      const timeout = setTimeout(() => {
        if (isVideoLoading) {
          setIsVideoLoading(false)
          setStep('image')
        }
        if (isImgLoading) {
          setIsImgLoading(false)
        }
      }, 1000)

      return () => clearTimeout(timeout)
    }, [error, isImgLoading, isVideoLoading])

    const updateVideoFile = async (file: File) => {
      try {
        const videoMetadata = await getVideoMetadata(file)
        const updatedVideo: VideoInputFile = {
          duration: videoMetadata.duration,
          mediaPixelHeight: videoMetadata.height,
          mediaPixelWidth: videoMetadata.width,
          size: videoMetadata.sizeInBytes,
          mimeType: videoMetadata.mimeType,
          blob: file,
          title: file.name,
        }
        onVideoChange(updatedVideo)
      } catch (e) {
        handleFileSelectError?.('file-invalid-type', step)
      }
    }

    const updateThumbnailFile = (
      croppedBlob: Blob,
      croppedUrl: string,
      assetDimensions: AssetDimensions,
      imageCropData: ImageCropData
    ) => {
      const updatedThumbnail: ImageInputFile = {
        originalBlob: rawImageFile,
        blob: croppedBlob,
        url: croppedUrl,
        assetDimensions,
        imageCropData,
      }
      onThumbnailChange(updatedThumbnail)
      setIsImgLoading(true)
    }

    const handleUploadFile = async (file: File) => {
      if (step === 'video') {
        setIsVideoLoading(true)
        updateVideoFile(file)
      }
      if (step === 'image') {
        try {
          await validateImage(file)
          setRawImageFile(file)
          dialogRef.current?.open(file)
        } catch (error) {
          handleFileSelectError?.('file-invalid-type', step)
        }
      }
    }

    const handleReAdjustThumbnail = () => {
      if (files.thumbnail?.originalBlob) {
        dialogRef.current?.open(files.thumbnail.originalBlob)
      }
    }

    const handleFileSelectError = useCallback((errorCode: FileErrorType | null, fileType: FileType) => {
      if (!errorCode) {
        setError(null)
      } else if (errorCode === 'file-invalid-type') {
        setError(
          fileType === 'video'
            ? `Maximum 10GB. Preferred format is WebM (VP9/VP8) or MP4 (H.264)`
            : `Preferred 16:9 image ratio`
        )
      } else if (errorCode === 'file-too-large') {
        setError('File too large')
      } else {
        SentryLogger.error('Unknown file select error', 'MultiFileSelect', null, { error: { code: errorCode } })
        setError('Unknown error')
      }
    }, [])

    const handleDeleteFile = useCallback(
      (fileType: FileType) => {
        if (fileType === 'video') {
          onVideoChange(null)
          setIsVideoLoading(false)
        }
        if (fileType === 'image') {
          onThumbnailChange(null)
          setIsImgLoading(false)
        }
      },
      [onThumbnailChange, onVideoChange]
    )

    const handleFileRejections = async (fileRejections: FileRejection[]) => {
      if (!fileRejections.length) {
        return
      }

      const { errors } = fileRejections[0]
      if (!errors.length) {
        return
      }

      const firstError = errors[0]
      handleFileSelectError?.(firstError.code, step)
    }
    const stepsActive =
      (editMode && !files.thumbnail?.url) || (!editMode && !(files.thumbnail?.originalBlob && files.video?.blob))

    const handleDeleteVideoFile = () => handleDeleteFile('video')
    const handleDeleteImageFile = () => handleDeleteFile('image')

    return (
      <MultiFileSelectContainer className={className}>
        <FileSelect
          maxSize={step === 'video' ? maxVideoSize : maxImageSize}
          onUploadFile={handleUploadFile}
          onReAdjustThumbnail={handleReAdjustThumbnail}
          isLoading={isVideoLoading || isImgLoading}
          fileType={step}
          title={step === 'video' ? VIDEO_SELECT_TITLE : THUMBNAIL_SELECT_TITLE}
          thumbnailUrl={files.thumbnail?.url}
          paragraph={
            step === 'video'
              ? `Maximum 10GB. Preferred format is WebM (VP9/VP8) or MP4 (H.264)`
              : `Preferred 16:9 image ratio`
          }
          onDropRejected={handleFileRejections}
        />
        <StepsContainer>
          <Step
            type="file"
            number={1}
            title={
              editMode
                ? 'Video file'
                : files.video
                ? (files.video.blob as File).name || 'Video file'
                : VIDEO_SELECT_TITLE
            }
            variant={getStepVariant(step === 'video' && stepsActive, !!files.video)}
            disabled={editMode || disabled}
            onDelete={handleDeleteVideoFile}
            isLoading={isVideoLoading}
          />
          <StepDivider>
            <SvgActionChevronR />
          </StepDivider>
          <Step
            type="file"
            number={2}
            title={
              files.thumbnail
                ? files.thumbnail.originalBlob
                  ? (files.thumbnail.originalBlob as File).name
                  : files.thumbnail.url
                  ? 'Thumbnail image'
                  : THUMBNAIL_SELECT_TITLE
                : THUMBNAIL_SELECT_TITLE
            }
            variant={getStepVariant(step === 'image' && stepsActive, !!files.thumbnail?.url)}
            onDelete={handleDeleteImageFile}
            ref={thumbnailStepRef}
            isLoading={isImgLoading}
            disabled={disabled}
          />
          {stepsActive && (
            <CSSTransition in={step === 'image'} timeout={400} classNames="underline">
              <AnimatedUnderline
                style={{
                  width: underlineWidth,
                  left: underlineLeft,
                }}
              />
            </CSSTransition>
          )}
        </StepsContainer>
        <ImageCropModal ref={dialogRef} imageType="videoThumbnail" onConfirm={updateThumbnailFile} />
      </MultiFileSelectContainer>
    )
  }
)
Example #4
Source File: UploadMedia.tsx    From aqualink-app with MIT License 4 votes vote down vote up
UploadMedia = ({
  siteId,
  siteName,
  changeTab,
  classes,
}: UploadMediaProps) => {
  const history = useHistory();
  const [files, setFiles] = useState<File[]>([]);
  const [previews, setPreviews] = useState<string[]>([]);
  const [metadata, setMetadata] = useState<Metadata[]>([]);
  const user = useSelector(userInfoSelector);
  const survey = useSelector(surveyDetailsSelector);
  const surveyPointOptions =
    useSelector(siteDetailsSelector)?.surveyPoints || [];
  const [alertMessage, setAlertMessage] = useState<string | null>(null);
  const [alertOpen, setAlertOpen] = useState<boolean>(false);
  const [loading, setLoading] = useState<boolean>(false);
  const [featuredFile, setFeaturedFile] = useState<number>(0);
  const missingObservations =
    metadata.findIndex((item) => item.observation === null) > -1;

  const handleFileDrop = useCallback(
    (acceptedFiles: File[], fileRejections) => {
      // TODO - add explicit error warnings.
      fileRejections.forEach((rejection: FileRejection) => {
        // eslint-disable-next-line no-console
        console.log(rejection.errors, rejection.file);
      });
      setFiles([...files, ...acceptedFiles]);
      setPreviews([
        ...previews,
        ...acceptedFiles.map((file) => URL.createObjectURL(file)),
      ]);
      setMetadata([
        ...metadata,
        ...acceptedFiles.map(() => ({
          observation: null,
          surveyPoint: "",
          comments: "",
        })),
      ]);
    },
    [files, previews, metadata]
  );

  const handleSurveyPointOptionAdd =
    (index: number) => (newPointName: string, newPoints: SurveyPoints[]) => {
      const newPointId = newPoints.find(
        (point) => point.name === newPointName
      )?.id;

      const newMetadata = metadata.map((item, key) =>
        key === index ? { ...item, surveyPoint: `${newPointId}` } : item
      );
      setMetadata(newMetadata);
    };

  const deleteCard = (index: number) => {
    setPreviews(previews.filter((item, key) => key !== index));
    setFiles(files.filter((item, key) => key !== index));
    setMetadata(metadata.filter((item, key) => key !== index));
    if (index === featuredFile) {
      setFeaturedFile(0);
    }
  };

  const removeCards = () => {
    setFiles([]);
    setMetadata([]);
    setPreviews([]);
  };

  const setFeatured = useCallback((index: number) => {
    setFeaturedFile(index);
  }, []);

  const onMediaSubmit = () => {
    const promises = files.map((file, index) => {
      const formData = new FormData();
      formData.append("file", file);
      return uploadServices
        .uploadMedia(formData, `${siteId}`, user?.token)
        .then((response) => {
          const url = response.data;
          const surveyId = survey?.id;
          const surveyMediaData: SurveyMediaData = {
            url,
            surveyPointId: metadata[index].surveyPoint
              ? parseInt(metadata[index].surveyPoint, 10)
              : (undefined as unknown as number),
            observations: metadata[index].observation,
            comments: metadata[index].comments || undefined,
            metadata: "{}",
            token: user?.token,
            featured: index === featuredFile,
            hidden: false,
          };
          return surveyServices.addSurveyMedia(
            `${siteId}`,
            `${surveyId}`,
            surveyMediaData
          );
        });
    });
    setLoading(true);
    Promise.all(promises)
      .then(() => {
        setFiles([]);
        setMetadata([]);
        setPreviews([]);
        setFeaturedFile(0);
        // eslint-disable-next-line fp/no-mutating-methods
        history.push(`/sites/${siteId}/survey_details/${survey?.id}`);
      })
      .catch((err) => {
        setAlertMessage(err.message);
        setAlertOpen(true);
      })
      .finally(() => setLoading(false));
  };

  const handleSurveyPointChange = (index: number) => {
    return (event: ChangeEvent<{ value: unknown }>) => {
      const surveyPoint = event.target.value as string;
      const newMetadata = metadata.map((item, key) => {
        if (key === index) {
          return {
            ...item,
            surveyPoint,
          };
        }
        return item;
      });
      setMetadata(newMetadata);
    };
  };

  const handleObservationChange = (index: number) => {
    return (event: ChangeEvent<{ value: unknown }>) => {
      const observation = event.target.value as SurveyMediaData["observations"];
      const newMetadata = metadata.map((item, key) => {
        if (key === index) {
          return {
            ...item,
            observation,
          };
        }
        return item;
      });
      setMetadata(newMetadata);
    };
  };

  const handleCommentsChange = (index: number) => {
    return (event: ChangeEvent<{ value: unknown }>) => {
      const comments = event.target.value as string;
      const newMetadata = metadata.map((item, key) => {
        if (key === index) {
          return {
            ...item,
            comments,
          };
        }
        return item;
      });
      setMetadata(newMetadata);
    };
  };

  const fileCards = previews.map((preview, index) => {
    return (
      <MediaCard
        key={preview}
        siteId={siteId}
        index={index}
        preview={preview}
        file={files[index]}
        surveyPointOptions={surveyPointOptions}
        handleSurveyPointOptionAdd={handleSurveyPointOptionAdd(index)}
        surveyPoint={metadata?.[index]?.surveyPoint || ""}
        observation={metadata?.[index]?.observation || ""}
        comments={metadata?.[index]?.comments || ""}
        deleteCard={deleteCard}
        setFeatured={setFeatured}
        featuredFile={featuredFile}
        handleCommentsChange={handleCommentsChange(index)}
        handleObservationChange={handleObservationChange(index)}
        handleSurveyPointChange={handleSurveyPointChange(index)}
      />
    );
  });

  return (
    <>
      {loading && <LinearProgress />}
      <Grid item xs={12}>
        <Collapse in={alertOpen}>
          <Alert
            severity="error"
            action={
              <IconButton
                aria-label="close"
                color="inherit"
                size="small"
                onClick={() => {
                  setAlertOpen(false);
                }}
              >
                <CloseIcon fontSize="inherit" />
              </IconButton>
            }
          >
            {alertMessage}
          </Alert>
        </Collapse>
      </Grid>
      <Grid className={classes.root} container justify="center" item xs={12}>
        <Grid container alignItems="center" item xs={10}>
          <Grid item>
            <IconButton
              edge="start"
              color="primary"
              aria-label="menu"
              onClick={() => changeTab(0)}
            >
              <ArrowBack />
            </IconButton>
          </Grid>
          <Grid item className={classes.siteName}>
            {siteName && (
              <Typography variant="h5">{`${siteName.toUpperCase()} MEDIA UPLOAD`}</Typography>
            )}
          </Grid>
        </Grid>
        <Grid container justify="center" item xs={4}>
          <Dropzone
            accept={["image/png", "image/jpeg", "image/gif"]}
            onDrop={handleFileDrop}
            maxSize={maxUploadSize}
          >
            {({ getRootProps, getInputProps }) => (
              <Grid
                container
                justify="center"
                {...getRootProps({ className: classes.dropzone })}
              >
                <input {...getInputProps()} />
                <Grid container justify="center" item xs={12}>
                  <CloudUploadOutlined fontSize="large" color="primary" />
                </Grid>
                <Grid container justify="center" item xs={12}>
                  <Typography variant="h5">
                    Drag and drop or click here
                  </Typography>
                </Grid>
                <Grid container justify="center" item xs={12}>
                  <Typography variant="subtitle2">
                    Supported formats: .jpg .png .gif Max 40mb.
                  </Typography>
                </Grid>
              </Grid>
            )}
          </Dropzone>
        </Grid>
        <Grid style={{ marginBottom: "2rem" }} container item xs={11} lg={9}>
          {fileCards}
        </Grid>
        {files && files.length > 0 && (
          <Grid
            style={{ margin: "4rem 0 2rem 0" }}
            container
            justify="flex-end"
            item
            xs={9}
          >
            <Button
              style={{ marginRight: "1rem" }}
              color="primary"
              variant="outlined"
              onClick={removeCards}
            >
              Cancel
            </Button>
            <Tooltip
              title={missingObservations ? "Missing Observation Info" : ""}
            >
              <div>
                <Button
                  disabled={loading || missingObservations}
                  onClick={onMediaSubmit}
                  color="primary"
                  variant="contained"
                >
                  {loading ? "Uploading..." : "Save"}
                </Button>
              </div>
            </Tooltip>
          </Grid>
        )}
      </Grid>
    </>
  );
}
Example #5
Source File: UploadBox.tsx    From nextclade with MIT License 4 votes vote down vote up
export function makeOnDrop({ t, onUpload, setErrors }: MakeOnDropParams) {
  function handleError(error: Error) {
    if (error instanceof UploadErrorTooManyFiles) {
      setErrors((prevErrors) => [...prevErrors, t('Only one file is expected')])
    } else if (error instanceof UploadErrorUnknown) {
      setErrors((prevErrors) => [...prevErrors, t('Unknown error')])
    } else {
      throw error
    }
  }

  async function processFiles(acceptedFiles: File[], rejectedFiles: FileRejection[]) {
    const nFiles = acceptedFiles.length + rejectedFiles.length

    if (nFiles > 1) {
      throw new UploadErrorTooManyFiles(nFiles)
    }

    if (acceptedFiles.length !== 1) {
      throw new UploadErrorTooManyFiles(acceptedFiles.length)
    }

    const file = acceptedFiles[0]
    onUpload(file)
  }

  async function onDrop(acceptedFiles: File[], rejectedFiles: FileRejection[]) {
    setErrors([])
    try {
      await processFiles(acceptedFiles, rejectedFiles)
    } catch (error: unknown) {
      handleError(sanitizeError(error))
    }
  }

  return (acceptedFiles: File[], rejectedFiles: FileRejection[]) => {
    // eslint-disable-next-line no-void
    void onDrop(acceptedFiles, rejectedFiles)
  }
}