transloadit / uppy

The next open source file uploader for web browsers :dog:

Home Page:https://uppy.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

uppy pauseResume throw unhandled error

AhmadKajjan-QU opened this issue · comments

Initial checklist

  • I understand this is a bug report and questions should be posted in the Community Forum
  • I searched issues and couldn’t find anything (or linked relevant results below)

Link to runnable example

Uppy React

Steps to reproduce

this is my context code
I was trying to add a resume and pause feature of a file.

import React, { createContext, useContext, useState, useEffect } from "react";
import Uppy from "@uppy/core";
import Tus from "@uppy/tus";

import { calculateFileHash, handleError } from "../utils/functions";
import { useFilesBackend } from "../hooks/useFileBackend";
import { toast } from "react-toastify";
import { UploadData } from "../types";
import { backendEndpoint } from "../config";
import Cookies from "js-cookie";
import { useUser } from "./UserContext";

type UploadContextType = {
  uploads: UploadData[];
  canUpload: boolean;
  addUpload: (userId: string, files: FileList) => void;
  pauseUpload: (filename: string) => void;
  resumeUpload: (filename: string) => void;
};

const UploadContext = createContext<UploadContextType | undefined>(undefined);

export const useUploads = () => {
  const context = useContext(UploadContext);
  if (!context) {
    throw new Error("useUploads must be used within an UploadProvider");
  }
  return context;
};

export const UploadProvider: React.FC<any> = ({ children }) => {
  const [uploads, setUploads] = useState<UploadData[]>([]);
  const [canUpload, setCanUpload] = useState<boolean>(true);
  const { signFiles, setFileUploadingTimeout } = useFilesBackend();
  const {
    user: { id: userId },
  } = useUser();

  // Initialize Uppy
  const uppy = new Uppy({
    autoProceed: false,
    allowMultipleUploads: true,
    restrictions: {
      maxNumberOfFiles: 10,
    },
  }).use(Tus, {
    endpoint: `${backendEndpoint}/uploads`,
    headers: {
      Authorization: `Bearer ${Cookies.get("sheelToken")}`,
    },
    retryDelays: [0, 1000, 3000, 5000],
  });

  const addUpload = async (userId: string, files: FileList) => {
    if (!canUpload) return;
    try {
      setCanUpload(false);
      const filesArray: File[] = [...files];
      const newUploads: UploadData[] = filesArray.map((file) => ({
        filename: file.name,
        file,
        fileHash: "",
        progress: 0,
        error: null,
        complete: false,
        isPaused: false,
        status: "Preparing Files",
        instance: null,
      }));
      setUploads((uploads) => [...uploads, ...newUploads]);
      const calculatedUploads = await Promise.all(
        newUploads.map(async (upload, idx) => {
          return {
            ...upload,
            fileHash: await calculateFileHash(files[idx]),
            status: "Waiting Approval",
          };
        })
      );
      console.log("calculated", calculatedUploads);
      setUploads([...calculatedUploads]);
      await startUploading(calculatedUploads);
    } catch (error) {
      console.error(error);
      setCanUpload(true);
    }
  };

  const startUploading = async (itemsToUploads: UploadData[]) => {
    try {
      const filesToSend = itemsToUploads
        .filter((upload) => upload.status === "Waiting Approval")
        .map((upload) => upload.file);
      await signFiles(
        filesToSend,
        itemsToUploads.map((upload) => upload.fileHash)
      );
      await uploadFiles(itemsToUploads);
    } catch (error) {
      toast.error(handleError(error));
      setUploads([]);
      setCanUpload(true);
    }
  };

  const updateUpload = (
    filename: string,
    updater: (u: UploadData) => UploadData
  ) => {
    setUploads((uploads) =>
      uploads.map((upload) => {
        if (upload.filename === filename) {
          return updater(upload);
        }
        return upload;
      })
    );
  };

  const pauseUpload = (filename: string) => {
    updateUpload(filename, (u) => {
      console.log("pausing", filename, u.id);
      if (u.id) {
        uppy.pauseResume(u.id); // Pause specific file upload

        return {
          ...u,
          isPaused: true,
          status: "Paused",
        };
      } else return u;
    });
  };

  const resumeUpload = (filename: string) => {
    updateUpload(filename, (u) => {
      if (u.id && u.isPaused) {
        uppy.pauseResume(u.id); // Pause specific file upload

        return {
          ...u,
          isPaused: false,
          status: "Uploading",
        };
      } else return u;
    });
  };

  const uploadFiles = (itemsToUploads: UploadData[]) => {
    itemsToUploads.forEach((item) => {
      const { file, filename, fileHash } = item;
      const id = uppy.addFile({
        name: filename,
        type: file.type,
        data: file,
        meta: {
          fileHash: item.fileHash,
          userId: userId,
        },
      });

      const intervalId = setInterval(
        () => setFileUploadingTimeout(fileHash),
        5 * 60 * 1000
      );
      updateUpload(filename, (u) => ({
        ...u,
        id,
        intervalId,
      }));
    });
    uppy.upload();
  };

  useEffect(() => {
    uppy.on("upload-progress", (file: any, progress) => {
      const percentage = (progress.bytesUploaded / progress.bytesTotal) * 100;
      updateUpload(file.name, (u) => ({
        ...u,
        progress: percentage,
        status: "Uploading",
      }));
    });

    uppy.on("upload-success", (file: any) => {
      updateUpload(file.name, (u) => {
        clearInterval(u.intervalId);
        return {
          ...u,
          complete: true,
          status: "Complete",
        };
      });

      // uploadChunkCompleted(file.meta.fileHash, file.name, file.data.size, 0);
      console.log("Upload complete:", file.uploadURL);
    });
    uppy.on("upload-error", (file: any) => {
      updateUpload(file.name, (u) => {
        clearInterval(u.intervalId);
        return {
          ...u,
          complete: true,
          status: "Failed",
        };
      });
    });
    uppy.on("error", (error) => {
      console.error("Failed to upload:", error);

      toast.error(handleError(error));
    });

    if (
      uploads.reduce((acc, curr) => acc + (curr.complete ? 1 : 0), 0) ===
      uploads.length
    ) {
      setCanUpload(true);
    }
  }, [uploads, uppy, updateUpload]);

  return (
    <UploadContext.Provider
      value={{ uploads, canUpload, addUpload, pauseUpload, resumeUpload }}
    >
      {children}
    </UploadContext.Provider>
  );
};

I got the following error when try to pause

Cannot read properties of undefined (reading 'progress')
TypeError: Cannot read properties of undefined (reading 'progress')
    at Uppy.pauseResume (http://localhost:3000/static/js/bundle.js:125589:79)
    at http://localhost:3000/static/js/bundle.js:7731:14
    at http://localhost:3000/static/js/bundle.js:7722:16
    at Array.map (<anonymous>)
    at http://localhost:3000/static/js/bundle.js:7720:35
    at basicStateReducer (http://localhost:3000/static/js/bundle.js:64835:45)
    at updateReducer (http://localhost:3000/static/js/bundle.js:64944:26)
    at updateState (http://localhost:3000/static/js/bundle.js:65230:14)
    at Object.useState (http://localhost:3000/static/js/bundle.js:66027:20)
    at useState (http://localhost:3000/static/js/bundle.js:87999:25)`
am I  making any thing wrong 

Expected behavior

The file should get paused

Actual behavior

I got this unhandled error

Cannot read properties of undefined (reading 'progress')
TypeError: Cannot read properties of undefined (reading 'progress')
at Uppy.pauseResume (http://localhost:3000/static/js/bundle.js:125589:79)
at http://localhost:3000/static/js/bundle.js:7731:14
at http://localhost:3000/static/js/bundle.js:7722:16
at Array.map ()
at http://localhost:3000/static/js/bundle.js:7720:35
at basicStateReducer (http://localhost:3000/static/js/bundle.js:64835:45)
at updateReducer (http://localhost:3000/static/js/bundle.js:64944:26)
at updateState (http://localhost:3000/static/js/bundle.js:65230:14)
at Object.useState (http://localhost:3000/static/js/bundle.js:66027:20)
at useState (http://localhost:3000/static/js/bundle.js:87999:25)

I make uppy as a Ref and it worked

Please read the uppy react docs carefully. Do not put Uppy inside the component as a simple variable or ref.