From 3854372f4dfba40fade4b3e0dfc6686dd275592f Mon Sep 17 00:00:00 2001 From: Alex <33497058+bexsoft@users.noreply.github.com> Date: Fri, 6 May 2022 13:33:13 -0500 Subject: [PATCH] Fixed multiple issues on object browser upload (#1955) - Fixed issue with double slashes on upload manager - Fixed sub folders not uploading in the correct subpaths location - Fixed an issue upload when a file is already selected - Fixed an issue with create path button with paths finished on slash - Simplified path handling for object browser Signed-off-by: Benjamin Perez --- .../Objects/ListObjects/CreatePathModal.tsx | 62 ++++++---------- .../Objects/ListObjects/ListObjects.tsx | 74 ++++++++++++++----- .../screens/Console/ObjectBrowser/actions.ts | 8 ++ .../screens/Console/ObjectBrowser/reducers.ts | 10 +++ .../screens/Console/ObjectBrowser/types.ts | 11 ++- 5 files changed, 104 insertions(+), 61 deletions(-) diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreatePathModal.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreatePathModal.tsx index a4ff92a55..301aaa012 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreatePathModal.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreatePathModal.tsx @@ -27,7 +27,7 @@ import { } from "../../../../Common/FormComponents/common/styleLibrary"; import { connect } from "react-redux"; import history from "../../../../../../history"; -import { decodeFileName, encodeFileName } from "../../../../../../common/utils"; +import { encodeFileName } from "../../../../../../common/utils"; import { setModalErrorSnackMessage } from "../../../../../../actions"; import { BucketObjectItem } from "./types"; import { CreateNewPathIcon } from "../../../../../../icons"; @@ -40,8 +40,7 @@ interface ICreatePath { folderName: string; onClose: () => any; existingFiles: BucketObjectItem[]; - detailsOpen: boolean; - selectedInternalPaths: string | null; + simplePath: string | null; setModalErrorSnackMessage: typeof setModalErrorSnackMessage; } @@ -59,53 +58,31 @@ const CreatePathModal = ({ setModalErrorSnackMessage, classes, existingFiles, - detailsOpen, - selectedInternalPaths, + simplePath, }: ICreatePath) => { const [pathUrl, setPathUrl] = useState(""); const [isFormValid, setIsFormValid] = useState(false); + const [currentPath, setCurrentPath] = useState(bucketName); - let currentPath = `${bucketName}/${decodeFileName(folderName)}`; + useEffect(() => { + if(simplePath) { + const newPath = `${bucketName}${ + !bucketName.endsWith("/") && !simplePath.startsWith("/") ? "/" : "" + }${simplePath}`; - if (selectedInternalPaths && detailsOpen) { - const decodedPathFileName = decodeFileName(selectedInternalPaths).split( - "/" - ); - - if (decodedPathFileName) { - decodedPathFileName.pop(); - const joinFileName = decodedPathFileName.join("/"); - const joinPaths = `${joinFileName}${ - joinFileName.endsWith("/") ? "" : "/" - }`; - currentPath = `${bucketName}/${joinPaths}`; + setCurrentPath(newPath); } - } + }, [simplePath, bucketName]); const resetForm = () => { setPathUrl(""); }; const createProcess = () => { - let folderPath = ""; + let folderPath = "/"; - if (selectedInternalPaths && detailsOpen) { - const decodedPathFileName = decodeFileName(selectedInternalPaths).split( - "/" - ); - - if (decodedPathFileName) { - decodedPathFileName.pop(); - const joinFileName = decodedPathFileName.join("/"); - folderPath = `${joinFileName}${joinFileName.endsWith("/") ? "" : "/"}`; - } - } else { - if (folderName !== "") { - const decodedFolderName = decodeFileName(folderName); - folderPath = decodedFolderName.endsWith("/") - ? decodedFolderName - : `${decodedFolderName}/`; - } + if (simplePath) { + folderPath = simplePath.endsWith("/") ? simplePath : `${simplePath}/`; } const sharesName = (record: BucketObjectItem) => @@ -118,8 +95,14 @@ const CreatePathModal = ({ }); return; } + + const cleanPathURL = pathUrl + .split("/") + .filter((splitItem) => splitItem.trim() !== "") + .join("/"); + const newPath = `/buckets/${bucketName}/browse/${encodeFileName( - `${folderPath}${pathUrl}/` + `${folderPath}${cleanPathURL}/` )}`; history.push(newPath); onClose(); @@ -205,8 +188,7 @@ const CreatePathModal = ({ }; const mapStateToProps = ({ objectBrowser }: AppState) => ({ - detailsOpen: objectBrowser.objectDetailsOpen, - selectedInternalPaths: objectBrowser.selectedInternalPaths, + simplePath: objectBrowser.simplePath, }); const mapDispatchToProps = { 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 7459c30b7..5846413b9 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 @@ -65,6 +65,7 @@ import { setSearchObjects, setSelectedObjectView, setShowDeletedObjects, + setSimplePathHandler, setVersionsModeEnabled, updateProgress, } from "../../../../ObjectBrowser/actions"; @@ -221,14 +222,15 @@ interface IListObjectsProps { searchObjects: string; showDeleted: boolean; loading: boolean; - setSnackBarMessage: typeof setSnackBarMessage; - setErrorSnackMessage: typeof setErrorSnackMessage; - resetRewind: typeof resetRewind; loadingBucket: boolean; - setBucketInfo: typeof setBucketInfo; bucketInfo: BucketInfo | null; versionsMode: boolean; detailsOpen: boolean; + simplePath: string | null; + setSnackBarMessage: typeof setSnackBarMessage; + setErrorSnackMessage: typeof setErrorSnackMessage; + resetRewind: typeof resetRewind; + setBucketInfo: typeof setBucketInfo; setBucketDetailsLoad: typeof setBucketDetailsLoad; setNewObject: typeof setNewObject; updateProgress: typeof updateProgress; @@ -245,6 +247,7 @@ interface IListObjectsProps { setLoadingObjectsList: typeof setLoadingObjectsList; failObject: typeof failObject; cancelObjectInList: typeof cancelObjectInList; + setSimplePathHandler: typeof setSimplePathHandler; } function useInterval(callback: any, delay: number) { @@ -294,6 +297,7 @@ const ListObjects = ({ searchObjects, versionsMode, openList, + simplePath, setVersionsModeEnabled, showDeleted, detailsOpen, @@ -306,6 +310,7 @@ const ListObjects = ({ setLoadingObjectsList, failObject, cancelObjectInList, + setSimplePathHandler, }: IListObjectsProps) => { const [records, setRecords] = useState([]); const [deleteMultipleOpen, setDeleteMultipleOpen] = useState(false); @@ -482,9 +487,9 @@ const ListObjects = ({ const decodedIPaths = decodeFileName(internalPaths); if (decodedIPaths.endsWith("/") || decodedIPaths === "") { - setLoadingObjectsList(true); setObjectDetailsView(false); - setSearchObjects(""); + setSelectedObjectView(null); + setSimplePathHandler(decodedIPaths === "" ? "/" : decodedIPaths); } else { setLoadingObjectInfo(true); setObjectDetailsView(true); @@ -492,19 +497,27 @@ const ListObjects = ({ setSelectedObjectView( `${decodedIPaths ? `${encodeFileName(decodedIPaths)}` : ``}` ); + setSimplePathHandler( + `${decodedIPaths.split("/").slice(0, -1).join("/")}/` + ); } }, [ internalPaths, - setSearchObjects, rewindDate, rewindEnabled, setLoadingObjectInfo, setLoadingVersions, setObjectDetailsView, setSelectedObjectView, - setLoadingObjectsList, + setSimplePathHandler, ]); + useEffect(() => { + setSearchObjects(""); + setLoadingObjectsList(true); + setSelectedObjects([]); + }, [simplePath, setSearchObjects, setLoadingObjectsList, setSelectedObjects]); + useEffect(() => { if (loading) { if (displayListObjects) { @@ -795,11 +808,8 @@ const ListObjects = ({ const uploadObject = useCallback( (files: File[], folderPath: string): void => { let pathPrefix = ""; - if (internalPaths) { - const decodedPath = decodeFileName(internalPaths); - pathPrefix = decodedPath.endsWith("/") - ? decodedPath - : decodedPath + "/"; + if (simplePath) { + pathPrefix = simplePath.endsWith("/") ? simplePath : simplePath + "/"; } const upload = ( @@ -812,13 +822,23 @@ const ListObjects = ({ return new Promise((resolve, reject) => { 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", "") !== "" - ? get(file, "webkitRelativePath", "") - : folderPath; + + const filePath = get(file, "path", ""); + const fileWebkitRelativePath = get(file, "webkitRelativePath", ""); + + let relativeFolderPath = folderPath; + + // File was uploaded via drag & drop + if (filePath !== "") { + relativeFolderPath = filePath; + } else if (fileWebkitRelativePath !== "") { + // File was uploaded using upload button + relativeFolderPath = fileWebkitRelativePath; + } if (path !== "" || relativeFolderPath !== "") { const finalFolderPath = relativeFolderPath @@ -826,9 +846,20 @@ const ListObjects = ({ .slice(0, -1) .join("/"); + const pathClean = path.endsWith("/") ? path.slice(0, -1) : path; + encodedPath = encodeFileName( - `${path}${finalFolderPath}${ - !finalFolderPath.endsWith("/") ? "/" : "" + `${pathClean}${ + !pathClean.endsWith("/") && + finalFolderPath !== "" && + !finalFolderPath.startsWith("/") + ? "/" + : "" + }${finalFolderPath}${ + !finalFolderPath.endsWith("/") || + (finalFolderPath.trim() === "" && !path.endsWith("/")) + ? "/" + : "" }` ); } @@ -947,6 +978,7 @@ const ListObjects = ({ } // We force objects list reload after all promises were handled setLoadingObjectsList(true); + setSelectedObjects([]); }); }; @@ -955,7 +987,6 @@ const ListObjects = ({ [ bucketName, completeObject, - internalPaths, openList, setNewObject, setErrorSnackMessage, @@ -963,6 +994,7 @@ const ListObjects = ({ setLoadingObjectsList, cancelObjectInList, failObject, + simplePath, ] ); @@ -1502,6 +1534,7 @@ const mapStateToProps = ({ objectBrowser, buckets }: AppState) => ({ detailsOpen: objectBrowser.objectDetailsOpen, selectedInternalPaths: objectBrowser.selectedInternalPaths, loading: objectBrowser.loadingObjects, + simplePath: objectBrowser.simplePath, }); const mapDispatchToProps = { @@ -1524,6 +1557,7 @@ const mapDispatchToProps = { setLoadingObjectInfo, setLoadingObjectsList, cancelObjectInList, + setSimplePathHandler, }; const connector = connect(mapStateToProps, mapDispatchToProps); diff --git a/portal-ui/src/screens/Console/ObjectBrowser/actions.ts b/portal-ui/src/screens/Console/ObjectBrowser/actions.ts index 0a91e6ae1..5eed8267f 100644 --- a/portal-ui/src/screens/Console/ObjectBrowser/actions.ts +++ b/portal-ui/src/screens/Console/ObjectBrowser/actions.ts @@ -37,6 +37,7 @@ import { REWIND_RESET_REWIND, REWIND_SET_ENABLE, BUCKET_BROWSER_SET_SELECTED_OBJECT, + BUCKET_BROWSER_SET_SIMPLE_PATH, IFileItem, } from "./types"; @@ -199,3 +200,10 @@ export const setSelectedObjectView = (object: string | null) => { object, }; }; + +export const setSimplePathHandler = (path: string | null) => { + return { + type: BUCKET_BROWSER_SET_SIMPLE_PATH, + path, + }; +}; diff --git a/portal-ui/src/screens/Console/ObjectBrowser/reducers.ts b/portal-ui/src/screens/Console/ObjectBrowser/reducers.ts index e38dcf863..dc241d5c8 100644 --- a/portal-ui/src/screens/Console/ObjectBrowser/reducers.ts +++ b/portal-ui/src/screens/Console/ObjectBrowser/reducers.ts @@ -39,6 +39,7 @@ import { OBJECT_MANAGER_SET_LOADING, OBJECT_MANAGER_ERROR_IN_OBJECT, OBJECT_MANAGER_CANCEL_OBJECT, + BUCKET_BROWSER_SET_SIMPLE_PATH, } from "./types"; const defaultRewind = { @@ -67,6 +68,7 @@ const initialState: ObjectBrowserState = { selectedVersion: "", showDeleted: false, selectedInternalPaths: null, + simplePath: null, }; export function objectBrowserReducer( @@ -295,12 +297,20 @@ export function objectBrowserReducer( return { ...state, objectDetailsOpen: action.status, + selectedInternalPaths: action.status + ? state.selectedInternalPaths + : null, }; case BUCKET_BROWSER_SET_SELECTED_OBJECT: return { ...state, selectedInternalPaths: action.object, }; + case BUCKET_BROWSER_SET_SIMPLE_PATH: + return { + ...state, + simplePath: action.path, + }; default: return state; } diff --git a/portal-ui/src/screens/Console/ObjectBrowser/types.ts b/portal-ui/src/screens/Console/ObjectBrowser/types.ts index e410cb932..49663ff36 100644 --- a/portal-ui/src/screens/Console/ObjectBrowser/types.ts +++ b/portal-ui/src/screens/Console/ObjectBrowser/types.ts @@ -47,6 +47,8 @@ export const BUCKET_BROWSER_OBJECT_DETAILS_STATE = "BUCKET_BROWSER/OBJECT_DETAILS_STATE"; export const BUCKET_BROWSER_SET_SELECTED_OBJECT = "BUCKET_BROWSER/SET_SELECTED_OBJECT"; +export const BUCKET_BROWSER_SET_SIMPLE_PATH = + "BUCKET_BROWSER/SET_SIMPLE_PATH"; export interface Route { route: string; @@ -74,6 +76,7 @@ export interface ObjectBrowserState { showDeleted: boolean; objectDetailsOpen: boolean; selectedInternalPaths: string | null; + simplePath: string | null; } export interface ObjectBrowserReducer { @@ -209,6 +212,11 @@ interface CancelObjectInManager { instanceID: string; } +interface SetBrowserPath { + type: typeof BUCKET_BROWSER_SET_SIMPLE_PATH; + path: string; +} + export type ObjectBrowserActionTypes = | RewindSetEnabled | RewindReset @@ -231,4 +239,5 @@ export type ObjectBrowserActionTypes = | SetObjectDetailsState | SetSelectedObject | SetObjectManagerLoading - | CancelObjectInManager; + | CancelObjectInManager + | SetBrowserPath;