diff --git a/src/files-ui/components/dropzone/components/dropzone/Dropzone.tsx b/src/files-ui/components/dropzone/components/dropzone/Dropzone.tsx index 3282af7481da1e432f225fe7d0ed51681880d02b..5572c22a0d49054ede35c3d1353ed7962639b78c 100644 --- a/src/files-ui/components/dropzone/components/dropzone/Dropzone.tsx +++ b/src/files-ui/components/dropzone/components/dropzone/Dropzone.tsx @@ -1,3 +1,4 @@ + import * as React from "react"; import { DropzoneLocalizerSelector, @@ -23,6 +24,10 @@ import { sleepTransition, toUploadableExtFileList, cleanInput, + isUploadAbleExtFile, + sanitizeArrExtFile, + unexpectedErrorUploadResult, + getRandomInt, } from "../../../../core"; import { mergeProps } from "../../../overridable"; import InputHidden from "../../../input-hidden/InputHidden"; @@ -155,7 +160,10 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { * Uploads each file in the array of ExtFiles * First, sets all the files in preparing status and awaits `preparingTime` miliseconds. * If `preparingTime` is not given or its value is false or 0, there won´t be a preparing phase. + * This is only for the first file, the rest of files will have preparing time until the file before was uploaded + * The first file will jump from undef to "uploading" * Then onChange event will be called to update the files outside. + * * If `onCancel` event ocurrs outside on any on the * FileItems(e.g. by clicking the cancel button on `FileItem`), * the extFileInstance will change its status from 'preparing' to undefined. If so, @@ -171,27 +179,26 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { * @returns nothing */ const uploadfiles = async (localFiles: ExtFile[]): Promise<void> => { + // set uploading flag to true setIsUploading(true); - console.log("upload start:", localFiles, url); - // set flag to true - // recieve on the new list - // initialize new list of ExtFileInstances - let arrOfExtFilesInstances: ExtFileInstance[] = []; - //avoid to call upload if not allowed - if (isUploading || localFiles.length === 0 || !arrOfExtFilesInstances) { + // avoid calling upload if not allowed + // flag is already true or there isnt files + // url was not provided + if (isUploading || localFiles.length === 0 || !url) { setIsUploading(false); return; } + // initialize a new list of ExtFileInstances + let arrOfExtFilesInstances: ExtFileInstance[] = []; + const totalNumber: number = localFiles.length; console.log("upload start: totalNumber", totalNumber); - const missingUpload: number = localFiles.filter((x: ExtFile) => { - return ( - (!validateFilesFlag || (validateFilesFlag && x.valid)) && - x.uploadStatus !== "success" - ); - }).length; + const missingUpload: number = localFiles.filter((extFile: ExtFile) => + isUploadAbleExtFile(extFile, validateFilesFlag) + ).length; + console.log("upload start: missingUpload", missingUpload); let totalRejected: number = 0; @@ -200,7 +207,8 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { const uploadingMessenger: FunctionLabel = DropzoneLocalizer.uploadingMessage as FunctionLabel; - if (!(missingUpload > 0 && url)) { + //no missing to upload + if (!(missingUpload > 0)) { console.log("upload start: noFilesMessage", missingUpload); setLocalMessage(DropzoneLocalizer.noFilesMessage as string); @@ -211,10 +219,8 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { setLocalMessage(uploadingMessenger(`${missingUpload}/${totalNumber}`)); // setIsUploading(true); //PREPARING stage - //use methods to update on static class - //obtain a fresher dui file list - console.log("***** before setFileListMapPreparing"); - console.table(localFiles); + console.log("validateFilesFlag", validateFilesFlag); + arrOfExtFilesInstances = ExtFileManager.setFileListMapPreparing( dropzoneId, @@ -223,8 +229,6 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { cleanOnUpload as boolean ) || []; - console.log("***** FileManagerLog setFileListMapPreparing"); - console.table(arrOfExtFilesInstances); const newExtFileLocal: ExtFile[] = [...arrOfExtFilesInstances].map((x) => x.toExtFile() ); @@ -242,178 +246,112 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { //AWAIT when preparing time is given //general sleep for all files await sleepPreparing(preparingTime); - // workaround for preventing getting the uploadStatus as undefined - /* arrOfExtFilesInstances.forEach((F) => { - F.uploadStatus = "preparing"; - }); */ - //variable for storing responses - //console.log("uploadfiles after sleep response",response); + console.log("FileManagerLog after sleep", arrOfExtFilesInstances); //return; let serverResponses: Array<UploadResponse> = []; + //Uplad files one by one for (let i = 0; i < arrOfExtFilesInstances.length; i++) { const currentExtFileInstance: ExtFileInstance = arrOfExtFilesInstances[i]; + console.log( "FileManagerLog currentExtFileInstance " + i, currentExtFileInstance ); - if (currentExtFileInstance.uploadStatus === "preparing") { + if ( + currentExtFileInstance.uploadStatus === "preparing" && + !currentExtFileInstance.extraData?.deleted + ) { //set stage to "uploading" in one file and notify change // PREPARING => UPLOADING + await sleepTransition(); + instantPreparingToUploadOne(currentExtFileInstance); + + //messge in footer setLocalMessage( uploadingMessenger(`${++currentCountUpload}/${missingUpload}`) ); - //CHANGE - handleFilesChange([...arrOfExtFilesInstances], true); + + //CHANGE FILES + handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true); //UPLOADING => UPLOAD() //upload one file and notify about change let uploadResponse: UploadResponse; - try { - uploadResponse = fakeUpload - ? await fakeFuiUpload(currentExtFileInstance, DropzoneLocalizer) - : await uploadExtFile( - currentExtFileInstance, - url, - method, - headers, - uploadLabel - ); - } catch (error) { - uploadResponse = { - id: currentExtFileInstance.id, - serverResponse: { - success: false, - message: "Error on upload: unexpected error " + error, - payload: {}, - }, - uploadedFile: { ...currentExtFileInstance }, - }; + + if (fakeUpload) { + uploadResponse = await fakeFuiUpload( + currentExtFileInstance, + DropzoneLocalizer + ); + let fakeProgress = 0; + while (fakeProgress < 100) { + fakeProgress += getRandomInt(21, 35); + currentExtFileInstance.progress = + fakeProgress > 100 ? 100 : fakeProgress; + await sleepTransition(1000); + handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true); + } + } else { + try { + uploadResponse = await uploadExtFile( + currentExtFileInstance, + url, + method, + headers, + uploadLabel + ); + } catch (error) { + uploadResponse = unexpectedErrorUploadResult( + currentExtFileInstance.toExtFile() + ); + } } const { uploadedFile } = uploadResponse; + console.log("fake uploadResponse uploadedFile", uploadedFile); + //update instances currentExtFileInstance.uploadStatus = uploadedFile.uploadStatus; currentExtFileInstance.uploadMessage = uploadedFile.uploadMessage; + console.log("fake uploadResponse currentExtFileInstance", currentExtFileInstance); + + //add fake progress only on fakeupload - if (fakeUpload) { + /* if (fakeUpload) { console.log( "Adding fake progress", fakeUpload, uploadedFile.progress ); currentExtFileInstance.progress = uploadedFile.progress; - } + } */ //CHANGE if (!(currentExtFileInstance.uploadStatus === "aborted")) await sleepTransition(); - handleFilesChange( - arrOfExtFilesInstances.map((x: ExtFileInstance) => x.toExtFile()), - true - ); + handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true); if (uploadedFile.uploadStatus === "error") { totalRejected++; } serverResponses.push(uploadResponse); + } else { + handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true); } } - /* arrOfExtFilesInstances.forEach(async (currentExtFileInstance) => { - console.log("FileManagerLog current", currentExtFileInstance); - if (currentExtFileInstance.uploadStatus === "preparing") { - //set stage to "uploading" in one file and notify change - // PREPARING => UPLOADING - await instantPreparingToUploadOne(currentExtFileInstance); - setLocalMessage( - uploadingMessenger(`${++currentCountUpload}/${missingUpload}`) - ); - //CHANGE - handleFilesChange([...arrOfExtFilesInstances], true); - - //UPLOADING => UPLOAD() - //upload one file and notify about change - const uploadResponse: UploadResponse = fakeUpload - ? await fakeFuiUpload(currentExtFileInstance, DropzoneLocalizer) - : await uploadOnePromiseXHR( - currentExtFileInstance, - url, - method, - headers, - uploadLabel - ); - - const { uploadedFile } = uploadResponse; - //update instances - currentExtFileInstance.uploadStatus = uploadedFile.uploadStatus; - currentExtFileInstance.uploadMessage = uploadedFile.uploadMessage; - //CHNAGE - if (!(currentExtFileInstance.uploadStatus === "aborted")) - await sleepTransition(); - handleFilesChange( - arrOfExtFilesInstances.map((x: ExtFileInstance) => x.toExtFile()), - true - ); - if (uploadedFile.uploadStatus === "error") { - totalRejected++; - } - - serverResponses.push(uploadResponse); - } - }); */ - - /* for (let i = 0; i < arrOfExtFilesInstances.length; i++) { - console.log("FileManagerLog current", arrOfExtFilesInstances[i]); - //all missing filesalways have "preparing" as uploadStatus prop - if (arrOfExtFilesInstances[i].uploadStatus === "preparing") { - //set stage to "uploading" in one file and notify change - // PREPARING => UPLOADING - await instantPreparingToUploadOne(arrOfExtFilesInstances[i]); - setLocalMessage( - uploadingMessenger(`${++currentCountUpload}/${missingUpload}`) - ); - //CHANGE - handleFilesChange([...arrOfExtFilesInstances], true); + handleFilesChange(sanitizeArrExtFile(arrOfExtFilesInstances), true); - //UPLOADING => UPLOAD() - //upload one file and notify about change - const uploadResponse: UploadResponse = fakeUpload - ? await fakeFuiUpload(arrOfExtFilesInstances[i], DropzoneLocalizer) - : await uploadOnePromiseXHR( - arrOfExtFilesInstances[i], - url, - method, - headers, - uploadLabel - ); - - const { uploadedFile } = uploadResponse; - //update instances - arrOfExtFilesInstances[i].uploadStatus = uploadedFile.uploadStatus; - arrOfExtFilesInstances[i].uploadMessage = uploadedFile.uploadMessage; - //CHNAGE - if (!(arrOfExtFilesInstances[i].uploadStatus === "aborted")) - await sleepTransition(); - handleFilesChange( - arrOfExtFilesInstances.map((x: ExtFileInstance) => x.toExtFile()), - true - ); - if (uploadedFile.uploadStatus === "error") { - totalRejected++; - } - - serverResponses.push(uploadResponse); - } - } */ // upload group finished :D onUploadFinish?.(serverResponses); + const finishUploadMessenger: FunctionLabel = DropzoneLocalizer.uploadFinished as FunctionLabel; setLocalMessage( @@ -425,17 +363,21 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { const handleAbortUpload = () => { const listExtFileLocal: ExtFileInstance[] | undefined = ExtFileManager.getExtFileInstanceList(dropzoneId); + console.log("Aborting", listExtFileLocal, dropzoneId); + if (!listExtFileLocal) return; - listExtFileLocal.forEach((extFile) => { - extFile.xhr?.abort(); + listExtFileLocal.forEach((extFileInstance: ExtFileInstance) => { + if ( + extFileInstance.uploadStatus === "uploading" || + extFileInstance.uploadStatus === "preparing" + ) { + if (extFileInstance.xhr !== null && extFileInstance.xhr !== undefined) + extFileInstance.xhr.abort(); + extFileInstance.uploadStatus = "aborted"; + extFileInstance.uploadMessage = "Upload was aborted by user"; + } }); }; - // the current number of valid files - // update number of valid files - /* const numberOfValidFiles: number = useNumberOfValidFiles( - localFiles, - validateFilesFlag - ); */ //the final className const dropzoneClassName: string | undefined = useDropzoneClassName( @@ -471,11 +413,11 @@ const Dropzone: React.FC<DropzoneProps> = (props: DropzoneProps) => { }, [maxFileSize, accept, maxFiles, localization]); /** - * Performs the changes in the FuiFile list. - * Makes a new array of FuiFiles according to the "behaviour" prop. + * Performs the changes in the extFile list. + * Makes a new array of extFile according to the "behaviour" prop. * If isUploading state is not true and the behaviour props is equal to "add", - * the incoming extFileList is added at the end of the current list of fuiFiles. - * Otherwise, the complete fuiFile list replaced by the incomming fuiFileList + * the incoming extFileList is added at the end of the current list of extFile. + * Otherwise, the complete extFile list is replaced by the incomming extFile list * @param extFileList the new fileList * @param isUploading a flag that dscribes whther the uploading process is active or not */ diff --git a/src/files-ui/core/upload/utils.upload.ts b/src/files-ui/core/upload/utils.upload.ts index 00f45cd67588c65c97a158749f5f076c4e2881b7..1aa9f906dbac726642af88e8f817a56d57bab5f1 100644 --- a/src/files-ui/core/upload/utils.upload.ts +++ b/src/files-ui/core/upload/utils.upload.ts @@ -1,3 +1,4 @@ + import { ExtFile, ExtFileInstance, ServerResponse, UploadResponse, UPLOADSTATUS } from "../types" export const unexpectedErrorUploadResult = (extFile: ExtFile): UploadResponse => { @@ -6,10 +7,13 @@ export const unexpectedErrorUploadResult = (extFile: ExtFile): UploadResponse => uploadedFile: { ...extFile, - uploadMessage: "Unable to upload. xhr object was not provided", + uploadMessage: "Unexpected error", uploadStatus: "error" }, serverResponse: { + success: false, + message: "Error on upload: unexpected error ", + payload: {}, } } } @@ -24,20 +28,23 @@ export const unableToUploadResult = ( uploadStatus: "error" }, serverResponse: { + success: false, + message: "Error on upload: Unable to upload. XHR was not provided ", + payload: {}, } } } export const completeUploadResult = ( extFile: ExtFile, serverResponse: ServerResponse, - result: UPLOADSTATUS + uploadStatusresult: UPLOADSTATUS ): UploadResponse => { return { id: extFile.id, uploadedFile: { ...extFile, uploadMessage: serverResponse.message, - uploadStatus: result + uploadStatus: uploadStatusresult }, serverResponse: serverResponse } @@ -104,14 +111,56 @@ export const preparingToUploadOne = ( /** * Sleeps for 1200 miliseconds for showing a better transition * on uploading + * @param time the time to sleep in miliseconds * @returns true is everything is ok */ -export const sleepTransition = ( +export const sleepTransition = (time = 1500 ): Promise<boolean> => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(true); - }, 1200); + }, time); }); } +export const sanitizeArrExtFile = (arrExtFile: ExtFileInstance[]): ExtFile[] => { + return arrExtFile.filter((extFileInstance: ExtFileInstance) => + !extFileInstance.extraData?.deleted) + .map((extFileInstance: ExtFileInstance) => { + if (extFileInstance.uploadStatus === "aborted" + && !extFileInstance.uploadMessage) { + extFileInstance.uploadMessage = "Upload aborted by user"; + //extFileInstance.uploadStatus = "error"; + } + + return extFileInstance.toExtFile() + }); +} +/** + * + * @param extFileInstance + * @param extFileobj + */ +export const setNextUploadStatus = ( + extFileInstance: ExtFileInstance, + extFileobj: ExtFile) => { + + const prevStatus: UPLOADSTATUS | undefined = extFileInstance.uploadStatus; + const nextStstaus: UPLOADSTATUS | undefined = extFileobj.uploadStatus; + + if ( + prevStatus === "preparing" && + ["aborted", undefined].includes(nextStstaus) + ) { + extFileInstance.uploadStatus = undefined; + } else if ( + prevStatus === "uploading" && + ["aborted", undefined].includes(nextStstaus) + ) { + extFileInstance.uploadStatus = "aborted"; + + } + extFileInstance.uploadMessage = extFileobj.uploadMessage; + +} + diff --git a/src/files-ui/core/utils/fakeupload.utils.ts b/src/files-ui/core/utils/fakeupload.utils.ts index 1131cd6158201cfa112a66ab2a92e82eb5e6f63f..54d20f3934178967237087a3cb09bb90efa4f8a4 100644 --- a/src/files-ui/core/utils/fakeupload.utils.ts +++ b/src/files-ui/core/utils/fakeupload.utils.ts @@ -1,3 +1,4 @@ + import { DropzoneLocalizerSelector } from "../localization"; import { ExtFile, ExtFileInstance, UploadResponse } from "../types"; @@ -105,9 +106,11 @@ export const uploadOneExtFile = ( * @returns a duiUploadResponse object that describes the result */ export const fakeFuiUpload = ( - extFile: ExtFileInstance, + extFileInstance: ExtFileInstance, DropzoneLocalizer = DropzoneLocalizerSelector("EN-en") ): Promise<UploadResponse> => { + const extFile:ExtFile = extFileInstance.toExtFile(); + return new Promise((resolve, reject) => { setTimeout(() => { const randomNumber: number = Math.floor(Math.random() * 10); @@ -143,4 +146,5 @@ export const fakeFuiUpload = ( } }, 1700); }); -}; \ No newline at end of file +}; + diff --git a/src/pages/api/FileMosaicApi.jsx b/src/pages/api/FileMosaicApi.jsx index 1946f0de9be2f2ebc03d0162b7b4120f9c4bfbd6..7e2324e8ddd44cb2e4106b5deefd8286119c7c44 100644 --- a/src/pages/api/FileMosaicApi.jsx +++ b/src/pages/api/FileMosaicApi.jsx @@ -1,4 +1,4 @@ -import { Alert } from "@mui/material"; +import { Alert, IconButton, Tooltip } from "@mui/material"; import * as React from "react"; import DescParagraph from "../../components/demo-components/desc-paragraph/DescParagraph"; import SubTitle from "../../components/demo-components/sub-title/SubTitle"; @@ -7,7 +7,7 @@ import RightMenuContainer from "../../components/layout-pages/RightMenuContainer import MainTitle from "../../components/main-title/MainTitle"; import RightMenu from "../../components/RightMenu/RightMenu"; import { FileMosaicAPIPropsRows } from "../../data/FileMosaicAPIPropsRows"; - +import FilterListIcon from "@mui/icons-material/FilterList"; import PropsTableApi from "./PropsTableApi"; const rightMenuItems = [ @@ -39,14 +39,15 @@ const FileMosaicApi = (props) => { visit the component demo pages: <ul> <li> - <a href="/components/filemosaic">FileMosaic. </a> + <a href="/components/filemosaic">FileMosaic</a> + </li> + <li> + <a href="/components/dropzone">Dropzone</a> </li> </ul> </Alert> </section> <section id="filemosaic-props"> - <SubTitle content="Props" /> - <PropsTableApi rows={FileMosaicAPIPropsRows} /> </section> </MainContentContainer> @@ -57,4 +58,4 @@ const FileMosaicApi = (props) => { </React.Fragment> ); }; -export default FileMosaicApi; +export default FileMosaicApi; \ No newline at end of file