From 3f4b595779e5c720260a8436457e473e30e04ced Mon Sep 17 00:00:00 2001 From: jinapurapu <65002498+jinapurapu@users.noreply.github.com> Date: Thu, 30 Dec 2021 13:43:33 -0800 Subject: [PATCH] Improved folder drop upload behavior (#1331) --- portal-ui/package.json | 197 +++++++++--------- .../Objects/ListObjects/ListObjects.tsx | 77 +++++-- portal-ui/yarn.lock | 29 ++- 3 files changed, 183 insertions(+), 120 deletions(-) diff --git a/portal-ui/package.json b/portal-ui/package.json index 390ab0584..f1f8b2916 100644 --- a/portal-ui/package.json +++ b/portal-ui/package.json @@ -1,100 +1,101 @@ { - "name": "portal-ui", - "version": "0.1.0", - "homepage": ".", - "private": true, - "dependencies": { - "@date-io/moment": "1.x", - "@emotion/react": "^11.4.1", - "@emotion/styled": "^11.3.0", - "@hot-loader/react-dom": "17.0.1", - "@mui/icons-material": "^5.0.4", - "@mui/lab": "^5.0.0-alpha.30", - "@mui/material": "^5.0.4", - "@mui/styled-engine-sc": "^5.0.3", - "@mui/styles": "^5.0.1", - "@types/history": "^4.7.3", - "@types/jest": "24.0.23", - "@types/lodash": "^4.14.149", - "@types/node": "12.12.8", - "@types/react": "17.0.0", - "@types/react-copy-to-clipboard": "^4.3.0", - "@types/react-dom": "16.9.4", - "@types/react-grid-layout": "^1.1.1", - "@types/react-redux": "^7.1.5", - "@types/react-router": "^5.1.3", - "@types/react-router-dom": "^5.1.2", - "@types/react-virtualized": "^9.21.10", - "@types/superagent": "^4.1.12", - "@types/webpack-env": "^1.14.1", - "@types/websocket": "^1.0.0", - "ansi-to-react": "^6.0.5", - "chart.js": "^2.9.3", - "codemirror": "^5.52.2", - "history": "^4.10.1", - "local-storage-fallback": "^4.1.1", - "lodash": "^4.17.21", - "moment": "^2.29.1", - "react": "^17.0.2", - "react-async-hook": "^3.6.1", - "react-chartjs-2": "^2.9.0", - "react-codemirror2": "^7.1.0", - "react-copy-to-clipboard": "^5.0.2", - "react-dom": "17.0.1", - "react-grid-layout": "^1.2.0", - "react-hot-loader": "^4.13.0", - "react-moment": "^1.1.1", - "react-redux": "^7.1.3", - "react-router-dom": "^5.1.2", - "react-virtualized": "^9.22.2", - "react-window-infinite-loader": "^1.0.5", - "recharts": "^2.1.1", - "redux": "^4.0.5", - "redux-thunk": "^2.3.0", - "styled-components": "^5.3.1", - "superagent": "^6.1.0", - "typeface-roboto": "^0.0.75", - "use-debounce": "^5.0.1", - "websocket": "^1.0.31" - }, - "scripts": { - "start": "PORT=5005 react-app-rewired start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": "react-app" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "proxy": "http://localhost:9090/", - "devDependencies": { - "@types/recharts": "^1.8.22", - "prettier": "2.5.1", - "react-app-rewire-hot-loader": "^2.0.1", - "react-app-rewired": "^2.1.6", - "react-scripts": "4.0.3", - "typescript": "^4.4.3" - }, - "resolutions": { - "@types/jest/**/ansi-regex": "^5.0.1", - "react-scripts/**/ansi-regex": "^5.0.1", - "react-scripts/**/nth-check": "^2.0.1", - "react-scripts/**/set-value": "^4.0.1", - "react-scripts/**/immer": "^9.0.6", - "react-scripts/**/glob-parent": "^5.1.2", - "react-scripts/**/browserslist": "^4.16.5", - "react-scripts/**/ansi-html": "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz" - } + "name": "portal-ui", + "version": "0.1.0", + "homepage": ".", + "private": true, + "dependencies": { + "@date-io/moment": "1.x", + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "@hot-loader/react-dom": "17.0.1", + "@mui/icons-material": "^5.0.4", + "@mui/lab": "^5.0.0-alpha.30", + "@mui/material": "^5.0.4", + "@mui/styled-engine-sc": "^5.0.3", + "@mui/styles": "^5.0.1", + "@types/history": "^4.7.3", + "@types/jest": "24.0.23", + "@types/lodash": "^4.14.149", + "@types/node": "12.12.8", + "@types/react": "17.0.0", + "@types/react-copy-to-clipboard": "^4.3.0", + "@types/react-dom": "16.9.4", + "@types/react-grid-layout": "^1.1.1", + "@types/react-redux": "^7.1.5", + "@types/react-router": "^5.1.3", + "@types/react-router-dom": "^5.1.2", + "@types/react-virtualized": "^9.21.10", + "@types/superagent": "^4.1.12", + "@types/webpack-env": "^1.14.1", + "@types/websocket": "^1.0.0", + "ansi-to-react": "^6.0.5", + "chart.js": "^2.9.3", + "codemirror": "^5.52.2", + "history": "^4.10.1", + "local-storage-fallback": "^4.1.1", + "lodash": "^4.17.21", + "moment": "^2.29.1", + "react": "^17.0.2", + "react-async-hook": "^3.6.1", + "react-chartjs-2": "^2.9.0", + "react-codemirror2": "^7.1.0", + "react-copy-to-clipboard": "^5.0.2", + "react-dom": "17.0.1", + "react-dropzone": "^11.4.2", + "react-grid-layout": "^1.2.0", + "react-hot-loader": "^4.13.0", + "react-moment": "^1.1.1", + "react-redux": "^7.1.3", + "react-router-dom": "^5.1.2", + "react-virtualized": "^9.22.2", + "react-window-infinite-loader": "^1.0.5", + "recharts": "^2.1.1", + "redux": "^4.0.5", + "redux-thunk": "^2.3.0", + "styled-components": "^5.3.1", + "superagent": "^6.1.0", + "typeface-roboto": "^0.0.75", + "use-debounce": "^5.0.1", + "websocket": "^1.0.31" + }, + "scripts": { + "start": "PORT=5005 react-app-rewired start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "proxy": "http://localhost:9090/", + "devDependencies": { + "@types/recharts": "^1.8.22", + "prettier": "2.3.2", + "react-app-rewire-hot-loader": "^2.0.1", + "react-app-rewired": "^2.1.6", + "react-scripts": "4.0.3", + "typescript": "^4.4.3" + }, + "resolutions": { + "@types/jest/**/ansi-regex": "^5.0.1", + "react-scripts/**/ansi-regex": "^5.0.1", + "react-scripts/**/nth-check": "^2.0.1", + "react-scripts/**/set-value": "^4.0.1", + "react-scripts/**/immer": "^9.0.6", + "react-scripts/**/glob-parent": "^5.1.2", + "react-scripts/**/browserslist": "^4.16.5", + "react-scripts/**/ansi-html": "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz" + } } diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx index e056903f0..3cf76d65e 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx @@ -16,6 +16,8 @@ import React, { Fragment, useEffect, useRef, useState } from "react"; import { connect } from "react-redux"; +import {useDropzone} from 'react-dropzone' + import { Theme } from "@mui/material/styles"; import createStyles from "@mui/styles/createStyles"; import withStyles from "@mui/styles/withStyles"; @@ -83,6 +85,7 @@ import withSuspense from "../../../../Common/Components/withSuspense"; import { displayName } from "./utils"; import { DownloadIcon, UploadFolderIcon } from "../../../../../../icons"; + const AddFolderIcon = React.lazy( () => import("../../../../../../icons/AddFolderIcon") ); @@ -250,6 +253,10 @@ const ListObjects = ({ >("ASC"); const [currentSortField, setCurrentSortField] = useState("name"); const [iniLoad, setIniLoad] = useState(false); + + + + const internalPaths = get(match.params, "subpaths", ""); const bucketName = match.params["bucketName"]; @@ -572,29 +579,38 @@ const ListObjects = ({ setCreateFolderOpen(false); }; - const upload = (e: any, bucketName: string, path: string) => { - if ( + const handleUploadButton = (e: any) => { + if ( e === null || e === undefined || - e.target === null || - e.target === undefined + e.target.files === null || + e.target.files === undefined ) { return; } e.preventDefault(); + var newFiles : File[] = []; - let files = e.target.files; + for (var i=0; i { + if (files.length > 0) { - for (let file of files) { + for (let file of files) { let uploadUrl = `api/v1/buckets/${bucketName}/objects/upload`; const fileName = file.name; const blobFile = new Blob([file], { type: file.type }); let encodedPath = ""; - - const relativeFolderPath = get(file, "webkitRelativePath", ""); - + const relativeFolderPath = get(file, "webkitRelativePath", "") !== "" + ? get(file, "webkitRelativePath", "") + : folderPath + if (path !== "" || relativeFolderPath !== "") { const finalFolderPath = relativeFolderPath .split("/") @@ -672,13 +688,14 @@ const ListObjects = ({ }; const formData = new FormData(); - formData.append(file.size, blobFile, fileName); - + if (file.size !== undefined){ + formData.append(file.size.toString(), blobFile, fileName); + xhr.send(formData); + } } } - e.target.value = null; }; const displayParsedDate = (object: BucketObject) => { @@ -741,15 +758,26 @@ const ListObjects = ({ return; }; - const uploadObject = (e: any): void => { + const uploadObject = (files: File[], folderPath: string): void => { let pathPrefix = ""; if (internalPaths) { const decodedPath = decodeFileName(internalPaths); pathPrefix = decodedPath.endsWith("/") ? decodedPath : decodedPath + "/"; - } - upload(e, bucketName, pathPrefix); + } + + upload(files, bucketName, pathPrefix, folderPath); + }; + const onDrop = React.useCallback(acceptedFiles => { + + let newFolderPath: string = acceptedFiles[0].path; + uploadObject(acceptedFiles , newFolderPath); + + }, [uploadObject]); + + const {getRootProps, getInputProps} = useDropzone({noClick: true, onDrop}); + const openPreview = (fileObject: BucketObject) => { setSelectedPreview(fileObject); setPreviewOpen(true); @@ -969,6 +997,8 @@ const ListObjects = ({ } }; + + return ( {shareFileModalOpen && selectedPreview && ( @@ -1086,7 +1116,7 @@ const ListObjects = ({ uploadObject(e)} + onChange={handleUploadButton} id="file-input" style={{ display: "none" }} ref={fileUpload} @@ -1110,11 +1140,19 @@ const ListObjects = ({ > + uploadObject(e)} + onChange={handleUploadButton} id="file-input" style={{ display: "none" }} ref={folderUpload} @@ -1212,6 +1250,8 @@ const ListObjects = ({
+
+ - +
+
); }; diff --git a/portal-ui/yarn.lock b/portal-ui/yarn.lock index bea5c027a..0223846d2 100644 --- a/portal-ui/yarn.lock +++ b/portal-ui/yarn.lock @@ -3045,6 +3045,11 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +attr-accept@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b" + integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg== + autoprefixer@^9.6.1: version "9.8.8" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.8.tgz#fd4bd4595385fa6f06599de749a4d5f7a474957a" @@ -5640,6 +5645,13 @@ file-loader@6.1.1: loader-utils "^2.0.0" schema-utils "^3.0.0" +file-selector@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.2.4.tgz#7b98286f9dbb9925f420130ea5ed0a69238d4d80" + integrity sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA== + dependencies: + tslib "^2.0.3" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -9612,10 +9624,10 @@ prepend-http@^1.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= -prettier@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" - integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== +prettier@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" + integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ== pretty-bytes@^5.3.0: version "5.6.0" @@ -9956,6 +9968,15 @@ react-draggable@^4.0.0, react-draggable@^4.0.3: clsx "^1.1.1" prop-types "^15.6.0" +react-dropzone@^11.4.2: + version "11.4.2" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-11.4.2.tgz#1eb99e9def4cc7520f4f58e85c853ce52c483d56" + integrity sha512-ocYzYn7Qgp0tFc1gQtUTOaHHSzVTwhWHxxY+r7cj2jJTPfMTZB5GWSJHdIVoxsl+EQENpjJ/6Zvcw0BqKZQ+Eg== + dependencies: + attr-accept "^2.2.1" + file-selector "^0.2.2" + prop-types "^15.7.2" + react-error-overlay@^6.0.9: version "6.0.9" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"