From a30d29b437e876da0159a8bd21999f8cc9d4ba29 Mon Sep 17 00:00:00 2001 From: Alex <33497058+bexsoft@users.noreply.github.com> Date: Wed, 13 Apr 2022 22:06:31 -0600 Subject: [PATCH] Added URL support to object view (#1831) - Added URL support to object view - Refactored & simplified requests Signed-off-by: Benjamin Perez --- .../Objects/ListObjects/CreateFolderModal.tsx | 13 +- .../Objects/ListObjects/ListObjects.tsx | 398 +++++++++--------- .../ListObjects/ListObjectsHelpers.tsx | 6 +- .../Objects/ListObjects/ObjectDetailPanel.tsx | 2 +- .../Objects/ListObjects/RewindEnable.tsx | 8 +- .../ListBuckets/Objects/ListObjects/types.tsx | 22 +- .../Objects/Preview/PreviewFileContent.tsx | 4 +- .../Objects/Preview/PreviewFileModal.tsx | 4 +- .../Buckets/ListBuckets/Objects/utils.ts | 8 +- .../CheckboxWrapper/CheckboxWrapper.tsx | 1 + .../Common/ObjectManager/ObjectHandled.tsx | 2 +- .../Common/ObjectManager/ObjectManager.tsx | 2 +- .../Console/Common/PageHeader/PageHeader.tsx | 2 +- .../ObjectBrowser/BrowserBreadcrumbs.tsx | 42 +- .../screens/Console/ObjectBrowser/actions.ts | 176 ++------ .../screens/Console/ObjectBrowser/reducers.ts | 73 ++-- .../screens/Console/ObjectBrowser/types.ts | 215 ++++++++++ 17 files changed, 542 insertions(+), 436 deletions(-) create mode 100644 portal-ui/src/screens/Console/ObjectBrowser/types.ts diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreateFolderModal.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreateFolderModal.tsx index e6861ffc6..f4badd341 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreateFolderModal.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreateFolderModal.tsx @@ -29,7 +29,7 @@ import { connect } from "react-redux"; import history from "../../../../../../history"; import { decodeFileName, encodeFileName } from "../../../../../../common/utils"; import { setModalErrorSnackMessage } from "../../../../../../actions"; -import { BucketObject } from "./types"; +import { BucketObjectItem } from "./types"; import { CreateNewPathIcon } from "../../../../../../icons"; interface ICreateFolder { @@ -37,9 +37,9 @@ interface ICreateFolder { modalOpen: boolean; bucketName: string; folderName: string; - setModalErrorSnackMessage: typeof setModalErrorSnackMessage; onClose: () => any; - existingFiles: BucketObject[]; + existingFiles: BucketObjectItem[]; + setModalErrorSnackMessage: typeof setModalErrorSnackMessage; } const styles = (theme: Theme) => @@ -74,8 +74,9 @@ const CreateFolderModal = ({ ? decodedFolderName : `${decodedFolderName}/`; } - const sharesName = (record: BucketObject) => + const sharesName = (record: BucketObjectItem) => record.name === folderPath + pathUrl; + if (existingFiles.findIndex(sharesName) !== -1) { setModalErrorSnackMessage({ errorMessage: "Folder cannot have the same name as an existing file", @@ -84,8 +85,8 @@ const CreateFolderModal = ({ return; } const newPath = `/buckets/${bucketName}/browse/${encodeFileName( - `${folderPath}${pathUrl}` - )}/`; + `${folderPath}${pathUrl}/` + )}`; history.push(newPath); onClose(); }; 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 c454b885b..04c005ae7 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 @@ -1,5 +1,5 @@ // This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. +// Copyright (c) 2022 MinIO, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by @@ -30,12 +30,7 @@ import withStyles from "@mui/styles/withStyles"; import { withRouter } from "react-router-dom"; import Grid from "@mui/material/Grid"; import get from "lodash/get"; -import { - BucketObject, - BucketObjectsList, - RewindObject, - RewindObjectList, -} from "./types"; +import { BucketObjectItem, BucketObjectItemsList } from "./types"; import api from "../../../../../../common/api"; import TableWrapper, { ItemActions, @@ -60,14 +55,18 @@ import { completeObject, openList, resetRewind, + setLoadingObjectInfo, + setLoadingObjectsList, setLoadingVersions, setNewObject, + setObjectDetailsView, setSearchObjects, + setSelectedObjectView, setShowDeletedObjects, setVersionsModeEnabled, updateProgress, } from "../../../../ObjectBrowser/actions"; -import { Route } from "../../../../ObjectBrowser/reducers"; +import { Route } from "../../../../ObjectBrowser/types"; import { download, extensionPreview, sortListObjects } from "../utils"; import { @@ -219,6 +218,7 @@ interface IListObjectsProps { bucketToRewind: string; searchObjects: string; showDeleted: boolean; + loading: boolean; setSnackBarMessage: typeof setSnackBarMessage; setErrorSnackMessage: typeof setErrorSnackMessage; resetRewind: typeof resetRewind; @@ -226,15 +226,21 @@ interface IListObjectsProps { setBucketInfo: typeof setBucketInfo; bucketInfo: BucketInfo | null; versionsMode: boolean; + detailsOpen: boolean; setBucketDetailsLoad: typeof setBucketDetailsLoad; setNewObject: typeof setNewObject; updateProgress: typeof updateProgress; completeObject: typeof completeObject; openList: typeof openList; setSearchObjects: typeof setSearchObjects; + selectedInternalPaths: string | null; setVersionsModeEnabled: typeof setVersionsModeEnabled; setShowDeletedObjects: typeof setShowDeletedObjects; setLoadingVersions: typeof setLoadingVersions; + setObjectDetailsView: typeof setObjectDetailsView; + setSelectedObjectView: typeof setSelectedObjectView; + setLoadingObjectInfo: typeof setLoadingObjectInfo; + setLoadingObjectsList: typeof setLoadingObjectsList; } function useInterval(callback: any, delay: number) { @@ -268,6 +274,7 @@ const ListObjects = ({ history, rewindEnabled, rewindDate, + loading, bucketToRewind, setSnackBarMessage, setErrorSnackMessage, @@ -285,13 +292,16 @@ const ListObjects = ({ openList, setVersionsModeEnabled, showDeleted, + detailsOpen, setShowDeletedObjects, setLoadingVersions, + setObjectDetailsView, + selectedInternalPaths, + setSelectedObjectView, + setLoadingObjectInfo, + setLoadingObjectsList, }: IListObjectsProps) => { - const [records, setRecords] = useState([]); - const [loading, setLoading] = useState(true); - const [rewind, setRewind] = useState([]); - const [loadingRewind, setLoadingRewind] = useState(false); + const [records, setRecords] = useState([]); const [deleteMultipleOpen, setDeleteMultipleOpen] = useState(false); const [loadingStartTime, setLoadingStartTime] = useState(0); const [loadingMessage, setLoadingMessage] = @@ -303,9 +313,8 @@ const ListObjects = ({ const [rewindSelect, setRewindSelect] = useState(false); const [selectedObjects, setSelectedObjects] = useState([]); const [previewOpen, setPreviewOpen] = useState(false); - const [selectedPreview, setSelectedPreview] = useState( - null - ); + const [selectedPreview, setSelectedPreview] = + useState(null); const [shareFileModalOpen, setShareFileModalOpen] = useState(false); const [sortDirection, setSortDirection] = useState< "ASC" | "DESC" | undefined @@ -314,10 +323,6 @@ const ListObjects = ({ const [iniLoad, setIniLoad] = useState(false); const [canShareFile, setCanShareFile] = useState(false); const [canPreviewFile, setCanPreviewFile] = useState(false); - const [detailsOpen, setDetailsOpen] = useState(false); - const [selectedInternalPaths, setSelectedInternalPaths] = useState< - string | null - >(null); const [quota, setQuota] = useState(null); const internalPaths = get(match.params, "subpaths", ""); @@ -375,14 +380,14 @@ const ListObjects = ({ useEffect(() => { if (selectedObjects.length > 0) { - setDetailsOpen(true); + setObjectDetailsView(true); return; } if (selectedObjects.length === 0 && selectedInternalPaths === null) { - setDetailsOpen(false); + setObjectDetailsView(false); } - }, [selectedObjects, selectedInternalPaths]); + }, [selectedObjects, selectedInternalPaths, setObjectDetailsView]); const displayDeleteObject = hasPermission(bucketName, [ IAM_SCOPES.S3_DELETE_OBJECT, @@ -442,6 +447,7 @@ const ListObjects = ({ }); } else { setLoadingVersioning(false); + setRecords([]); } } }, [bucketName, loadingVersioning, setErrorSnackMessage, displayListObjects]); @@ -460,74 +466,44 @@ const ListObjects = ({ setLoadingLocking(false); }); } else { + setRecords([]); setLoadingLocking(false); } } }, [bucketName, loadingLocking, setErrorSnackMessage, displayListObjects]); - // Rewind useEffect(() => { - if (rewindEnabled) { - if (bucketToRewind !== bucketName) { - resetRewind(); - return; - } + const decodedIPaths = decodeFileName(internalPaths); - if (rewindDate) { - setLoadingRewind(true); - const rewindParsed = rewindDate.toISOString(); - let pathPrefix = ""; - if (internalPaths) { - const decodedPath = decodeFileName(internalPaths); - pathPrefix = decodedPath.endsWith("/") - ? decodedPath - : decodedPath + "/"; - } - api - .invoke( - "GET", - `/api/v1/buckets/${bucketName}/rewind/${rewindParsed}${ - pathPrefix ? `?prefix=${encodeFileName(pathPrefix)}` : `` - }` - ) - .then((res: RewindObjectList) => { - setLoadingRewind(false); - if (res.objects) { - // We omit files from the same path - const filteredObjects = res.objects.filter((object) => { - return object.name !== decodeFileName(internalPaths); - }); - - setRewind(filteredObjects); - } else { - setRewind([]); - } - }) - .catch((err: ErrorResponseHandler) => { - setLoadingRewind(false); - setErrorSnackMessage(err); - }); - } + if (decodedIPaths.endsWith("/") || decodedIPaths === "") { + setLoadingObjectsList(true); + setObjectDetailsView(false); + setSearchObjects(""); + } else { + console.log("AQUI", decodedIPaths); + setLoadingObjectInfo(true); + setObjectDetailsView(true); + setLoadingVersions(true); + setSelectedObjectView( + `${decodedIPaths ? `${encodeFileName(decodedIPaths)}` : ``}` + ); } }, [ - rewindEnabled, - rewindDate, - bucketToRewind, - bucketName, - match, - setErrorSnackMessage, - resetRewind, internalPaths, + setSearchObjects, + rewindDate, + rewindEnabled, + setLoadingObjectInfo, + setLoadingVersions, + setObjectDetailsView, + setSelectedObjectView, + setLoadingObjectsList, ]); useEffect(() => { - setLoading(true); - setDetailsOpen(false); - setSearchObjects(""); - }, [internalPaths, setSearchObjects]); - - useEffect(() => { + console.log("SET PATH"); if (loading) { + console.log("STE"); if (displayListObjects) { let pathPrefix = ""; if (internalPaths) { @@ -541,9 +517,23 @@ const ListObjects = ({ setLoadingStartTime(currentTimestamp); setLoadingMessage(defLoading); + // We get URL to look into let urlTake = `/api/v1/buckets/${bucketName}/objects`; - if (showDeleted) { + // Is rewind enabled?, we use Rewind API + if (rewindEnabled) { + if (bucketToRewind !== bucketName) { + resetRewind(); + return; + } + + if (rewindDate) { + const rewindParsed = rewindDate.toISOString(); + + urlTake = `/api/v1/buckets/${bucketName}/rewind/${rewindParsed}`; + } + } else if (showDeleted) { + // Do we want to display deleted items too?, we use rewind to current time to show everything const currDate = new Date(); const currDateISO = currDate.toISOString(); @@ -557,11 +547,12 @@ const ListObjects = ({ pathPrefix ? `?prefix=${encodeFileName(pathPrefix)}` : `` }` ) - .then((res: BucketObjectsList) => { - const records: BucketObject[] = res.objects || []; - const folders: BucketObject[] = []; - const files: BucketObject[] = []; + .then((res: BucketObjectItemsList) => { + const records: BucketObjectItem[] = res.objects || []; + const folders: BucketObjectItem[] = []; + const files: BucketObjectItem[] = []; + // We separate items between folders or files to display folders at the beginning always. records.forEach((record) => { // We omit files from the same path if (record.name !== decodeFileName(internalPaths)) { @@ -574,10 +565,14 @@ const ListObjects = ({ } } }); + const recordsInElement = [...folders, ...files]; - setRecords(recordsInElement); - // In case no objects were retrieved, We check if item is a file - if (!res.objects && pathPrefix !== "") { + + if (recordsInElement.length === 0 && pathPrefix !== "") { + let pathTest = `/api/v1/buckets/${bucketName}/objects${ + internalPaths ? `?prefix=${internalPaths}` : "" + }`; + if (rewindEnabled) { const rewindParsed = rewindDate.toISOString(); @@ -588,89 +583,96 @@ const ListObjects = ({ ? decodedPath : decodedPath + "/"; } - api - .invoke( - "GET", - `/api/v1/buckets/${bucketName}/rewind/${rewindParsed}${ - pathPrefix ? `?prefix=${encodeFileName(pathPrefix)}` : `` - }` - ) - .then((res: RewindObjectList) => { - //It is a file since it has elements in the object, setting file flag and waiting for component mount - if (res.objects === null) { - //setFileModeEnabled(true); - setLoadingRewind(false); - setLoading(false); - } else { - // It is a folder, we remove loader - setLoadingRewind(false); - setLoading(false); - //setFileModeEnabled(false); - } - }) - .catch((err: ErrorResponseHandler) => { - setLoadingRewind(false); - setLoading(false); - setErrorSnackMessage(err); - }); - } else { - api - .invoke( - "GET", - `/api/v1/buckets/${bucketName}/objects${ - internalPaths ? `?prefix=${internalPaths}` : `` - }` - ) - .then((res: BucketObjectsList) => { - //It is a file since it has elements in the object, setting file flag and waiting for component mount - if (!res.objects) { - // It is a folder, we remove loader - //setFileModeEnabled(false); - setLoading(false); - } else { - // This code prevents the program from opening a file when a substring of that file is entered as a new folder. - // Previously, if there was a file test1.txt and the folder test was created with the same prefix, the program - // would open test1.txt instead - let found = false; - let pathPrefixChopped = pathPrefix.slice( - 0, - pathPrefix.length - 1 - ); - for (let i = 0; i < res.objects.length; i++) { - if (res.objects[i].name === pathPrefixChopped) { - found = true; - } - } - if ( - (res.objects.length === 1 && - res.objects[0].name.endsWith("/")) || - !found - ) { - //setFileModeEnabled(false); - } else { - //setFileModeEnabled(true); - } - setLoading(false); - } - }) - .catch((err: ErrorResponseHandler) => { - setLoading(false); - setErrorSnackMessage(err); - }); + pathTest = `/api/v1/buckets/${bucketName}/rewind/${rewindParsed}${ + pathPrefix ? `?prefix=${encodeFileName(pathPrefix)}` : `` + }`; } + + api + .invoke("GET", pathTest) + .then((res: BucketObjectItemsList) => { + //It is a file since it has elements in the object, setting file flag and waiting for component mount + if (!res.objects) { + // It is a folder, we remove loader & set original results list + setLoadingObjectsList(false); + setRecords(recordsInElement); + console.log("1"); + } else { + // This code prevents the program from opening a file when a substring of that file is entered as a new folder. + // Previously, if there was a file test1.txt and the folder test was created with the same prefix, the program + // would open test1.txt instead + let found = false; + let pathPrefixChopped = pathPrefix.slice( + 0, + pathPrefix.length - 1 + ); + for (let i = 0; i < res.objects.length; i++) { + if (res.objects[i].name === pathPrefixChopped) { + found = true; + } + } + if ( + (res.objects.length === 1 && + res.objects[0].name.endsWith("/")) || + !found + ) { + // This is a folder, we set the original results list + console.log("2"); + setRecords(recordsInElement); + } else { + console.log("3"); + // This is a file. We change URL & Open file details view. + setObjectDetailsView(true); + setSelectedObjectView(internalPaths); + + // We split the selected object URL & remove the last item to fetch the files list for the parent folder + const parentPath = `${decodeFileName(internalPaths) + .split("/") + .slice(0, -1) + .join("/")}/`; + + api + .invoke( + "GET", + `${urlTake}${ + pathPrefix + ? `?prefix=${encodeFileName(parentPath)}` + : `` + }` + ) + .then((res: BucketObjectItemsList) => { + console.log("4"); + const records: BucketObjectItem[] = res.objects || []; + + setRecords(records); + }) + .catch(() => { + console.log("5"); + }); + } + + setLoadingObjectsList(false); + } + }) + .catch((err: ErrorResponseHandler) => { + console.log("6"); + setLoadingObjectsList(false); + setErrorSnackMessage(err); + }); } else { - //setFileModeEnabled(false); - setLoading(false); + console.log("7", recordsInElement); + setRecords(recordsInElement); + setLoadingObjectsList(false); } }) .catch((err: ErrorResponseHandler) => { - setLoading(false); + setLoadingObjectsList(false); setErrorSnackMessage(err); }); } else { - setLoadingRewind(false); - setLoading(false); + console.log("8"); + setLoadingObjectsList(false); } } }, [ @@ -684,6 +686,11 @@ const ListObjects = ({ bucketInfo, showDeleted, displayListObjects, + bucketToRewind, + resetRewind, + setObjectDetailsView, + setSelectedObjectView, + setLoadingObjectsList, ]); // bucket info @@ -714,7 +721,7 @@ const ListObjects = ({ if (refresh) { setSnackBarMessage(`Objects deleted successfully.`); setSelectedObjects([]); - setLoading(true); + setLoadingObjectsList(true); } }; @@ -738,7 +745,7 @@ const ListObjects = ({ e.target.value = ""; }; - const downloadObject = (object: BucketObject | RewindObject) => { + const downloadObject = (object: BucketObjectItem) => { const identityDownload = encodeFileName( `${bucketName}-${object.name}-${new Date().getTime()}-${Math.random()}` ); @@ -769,19 +776,15 @@ const ListObjects = ({ const openPath = (idElement: string) => { setSelectedObjects([]); - if (idElement.endsWith("/")) { - const newPath = `/buckets/${bucketName}/browse${ - idElement ? `/${encodeFileName(idElement)}` : `` - }`; - history.push(newPath); - return; - } - setDetailsOpen(true); + const newPath = `/buckets/${bucketName}/browse${ + idElement ? `/${encodeFileName(idElement)}` : `` + }`; + history.push(newPath); + + setObjectDetailsView(true); setLoadingVersions(true); - setSelectedInternalPaths( - `${idElement ? `${encodeFileName(idElement)}` : ``}` - ); + setSelectedObjectView(`${idElement ? `${encodeFileName(idElement)}` : ``}`); }; const uploadObject = useCallback( @@ -894,7 +897,7 @@ const ListObjects = ({ }; xhr.onloadend = () => { if (files.length === 0) { - setLoading(true); + setLoadingObjectsList(true); } }; @@ -925,7 +928,6 @@ const ListObjects = ({ errorMessage: "There were some errors during file upload", detailedError: `Uploaded files ${successUploadedFiles}/${totalFiles}`, }; - console.log("upload results", results); setErrorSnackMessage(err); } }); @@ -941,6 +943,7 @@ const ListObjects = ({ setNewObject, setErrorSnackMessage, updateProgress, + setLoadingObjectsList, ] ); @@ -971,9 +974,9 @@ const ListObjects = ({ const openPreview = () => { if (selectedObjects.length === 1) { - let fileObject: BucketObject | undefined; + let fileObject: BucketObjectItem | undefined; - const findFunction = (currValue: BucketObject | RewindObject) => + const findFunction = (currValue: BucketObjectItem) => selectedObjects.includes(currValue.name); fileObject = filteredRecords.find(findFunction); @@ -987,9 +990,9 @@ const ListObjects = ({ const openShare = () => { if (selectedObjects.length === 1) { - let fileObject: BucketObject | undefined; + let fileObject: BucketObjectItem | undefined; - const findFunction = (currValue: BucketObject | RewindObject) => + const findFunction = (currValue: BucketObjectItem) => selectedObjects.includes(currValue.name); fileObject = filteredRecords.find(findFunction); @@ -1006,7 +1009,7 @@ const ListObjects = ({ setSelectedPreview(null); }; - const filteredRecords = records.filter((b: BucketObject) => { + const filteredRecords = records.filter((b: BucketObjectItem) => { if (searchObjects === "") { return true; } else { @@ -1019,7 +1022,7 @@ const ListObjects = ({ } }); - const rewindCloseModal = (refresh: boolean) => { + const rewindCloseModal = () => { setRewindSelect(false); }; @@ -1043,7 +1046,7 @@ const ListObjects = ({ elements = elements.filter((element) => element !== value); } setSelectedObjects(elements); - setSelectedInternalPaths(null); + setSelectedObjectView(null); return elements; }; @@ -1052,16 +1055,16 @@ const ListObjects = ({ const newSortDirection = get(sortData, "sortDirection", "DESC"); setCurrentSortField(sortData.sortBy); setSortDirection(newSortDirection); - setLoading(true); + setLoadingObjectsList(true); }; const pageTitle = decodeFileName(internalPaths); const currentPath = pageTitle.split("/").filter((i: string) => i !== ""); - const plSelect = rewindEnabled ? rewind : filteredRecords; + const plSelect = filteredRecords; const sortASC = plSelect.sort(sortListObjects(currentSortField)); - let payload: BucketObject[] | RewindObject[] = []; + let payload: BucketObjectItem[] = []; if (sortDirection === "ASC") { payload = sortASC; @@ -1070,7 +1073,7 @@ const ListObjects = ({ } const selectAllItems = () => { - setSelectedInternalPaths(null); + setSelectedObjectView(null); if (selectedObjects.length === payload.length) { setSelectedObjects([]); @@ -1083,16 +1086,12 @@ const ListObjects = ({ const downloadSelected = () => { if (selectedObjects.length !== 0) { - let itemsToDownload: BucketObject[] | RewindObject[] = []; + let itemsToDownload: BucketObjectItem[] = []; - const filterFunction = (currValue: BucketObject | RewindObject) => + const filterFunction = (currValue: BucketObjectItem) => selectedObjects.includes(currValue.name); - if (rewindEnabled) { - itemsToDownload = rewind.filter(filterFunction); - } else { - itemsToDownload = filteredRecords.filter(filterFunction); - } + itemsToDownload = filteredRecords.filter(filterFunction); itemsToDownload.forEach((filteredItem) => { downloadObject(filteredItem); @@ -1105,13 +1104,13 @@ const ListObjects = ({ } const onClosePanel = (forceRefresh: boolean) => { - setDetailsOpen(false); - setSelectedInternalPaths(null); + setObjectDetailsView(false); + setSelectedObjectView(null); setSelectedObjects([]); setVersionsModeEnabled(false); if (forceRefresh) { - setLoading(true); + setLoadingObjectsList(true); } }; @@ -1287,7 +1286,7 @@ const ListObjects = ({ if (versionsMode) { setLoadingVersions(true); } else { - setLoading(true); + setLoadingObjectsList(true); } }} disabled={ @@ -1385,7 +1384,7 @@ const ListObjects = ({ columns={ rewindEnabled ? rewindModeColumns : listModeColumns } - isLoading={rewindEnabled ? loadingRewind : loading} + isLoading={loading} loadingMessage={loadingMessage} entityName="Objects" idField="name" @@ -1461,6 +1460,9 @@ const mapStateToProps = ({ objectBrowser, buckets }: AppState) => ({ bucketInfo: buckets.bucketDetails.bucketInfo, searchObjects: objectBrowser.searchObjects, showDeleted: objectBrowser.showDeleted, + detailsOpen: objectBrowser.objectDetailsOpen, + selectedInternalPaths: objectBrowser.selectedInternalPaths, + loading: objectBrowser.loadingObjects, }); const mapDispatchToProps = { @@ -1477,6 +1479,10 @@ const mapDispatchToProps = { setVersionsModeEnabled, setShowDeletedObjects, setLoadingVersions, + setObjectDetailsView, + setSelectedObjectView, + setLoadingObjectInfo, + setLoadingObjectsList, }; const connector = connect(mapStateToProps, mapDispatchToProps); diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjectsHelpers.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjectsHelpers.tsx index 7bab0b942..4897eee89 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjectsHelpers.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjectsHelpers.tsx @@ -16,20 +16,20 @@ import React from "react"; import * as reactMoment from "react-moment"; -import { BucketObject } from "./types"; +import { BucketObjectItem } from "./types"; import { niceBytes } from "../../../../../../common/utils"; import { displayFileIconName } from "./utils"; // Functions -export const displayParsedDate = (object: BucketObject) => { +export const displayParsedDate = (object: BucketObjectItem) => { if (object.name.endsWith("/")) { return ""; } return {object.last_modified}; }; -export const displayNiceBytes = (object: BucketObject) => { +export const displayNiceBytes = (object: BucketObjectItem) => { if (object.name.endsWith("/") || !object.size) { return "-"; } diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx index 7116f458d..c1337ec26 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx @@ -201,7 +201,7 @@ const ObjectDetailPanel = ({ }, [internalPaths, bucketName, setLoadingObjectInfo]); useEffect(() => { - if (distributedSetup && allInfoElements.length >= 1) { + if (distributedSetup && allInfoElements && allInfoElements.length >= 1) { let infoElement = allInfoElements.find((el: IFileInfo) => el.is_latest) || emptyFile; diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/RewindEnable.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/RewindEnable.tsx index 381d4b195..6b79faff1 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/RewindEnable.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/RewindEnable.tsx @@ -21,7 +21,7 @@ import createStyles from "@mui/styles/createStyles"; import withStyles from "@mui/styles/withStyles"; import { Button, LinearProgress } from "@mui/material"; import Grid from "@mui/material/Grid"; -import { ObjectBrowserReducer } from "../../../../ObjectBrowser/reducers"; +import { ObjectBrowserReducer } from "../../../../ObjectBrowser/types"; import { modalBasic } from "../../../../Common/FormComponents/common/styleLibrary"; import { resetRewind, @@ -32,7 +32,7 @@ import DateTimePickerWrapper from "../../../../Common/FormComponents/DateTimePic import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper"; interface IRewindEnable { - closeModalAndRefresh: (reload: boolean) => void; + closeModalAndRefresh: () => void; classes: any; open: boolean; bucketName: string; @@ -80,14 +80,14 @@ const RewindEnable = ({ setRewindEnabling(true); setRewindEnable(true, bucketName, dateSelected); } - closeModalAndRefresh(true); + closeModalAndRefresh(); }; return ( { - closeModalAndRefresh(false); + closeModalAndRefresh(); }} title={`Rewind - ${bucketName}`} > diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/types.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/types.tsx index 46f88e420..df6999e66 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/types.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/types.tsx @@ -14,29 +14,17 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -export interface BucketObject { +export interface BucketObjectItem { name: string; size: number; etag?: string; last_modified: Date; - content_type: string; + content_type?: string; version_id: string; delete_flag?: boolean; } -export interface BucketObjectsList { - objects: BucketObject[]; - total: number; -} - -export interface RewindObject { - last_modified: string; - delete_flag: boolean; - name: string; - version_id: string; - size: number; -} - -export interface RewindObjectList { - objects: RewindObject[]; +export interface BucketObjectItemsList { + objects: BucketObjectItem[]; + total?: number; } diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/Preview/PreviewFileContent.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/Preview/PreviewFileContent.tsx index de7f93b8d..f0c8169b1 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/Preview/PreviewFileContent.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/Preview/PreviewFileContent.tsx @@ -18,7 +18,7 @@ import React, { Fragment, useState } from "react"; import createStyles from "@mui/styles/createStyles"; import withStyles from "@mui/styles/withStyles"; import { Grid, LinearProgress } from "@mui/material"; -import { BucketObject } from "../ListObjects/types"; +import { BucketObjectItem } from "../ListObjects/types"; import { extensionPreview } from "../utils"; import { encodeFileName } from "../../../../../../common/utils"; import clsx from "clsx"; @@ -59,7 +59,7 @@ const styles = () => interface IPreviewFileProps { bucketName: string; - object: BucketObject | null; + object: BucketObjectItem | null; isFullscreen?: boolean; classes: any; } diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/Preview/PreviewFileModal.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/Preview/PreviewFileModal.tsx index 0c769f99e..300888db6 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/Preview/PreviewFileModal.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/Preview/PreviewFileModal.tsx @@ -17,13 +17,13 @@ import React, { Fragment } from "react"; import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper"; import PreviewFileContent from "./PreviewFileContent"; -import { BucketObject } from "../ListObjects/types"; +import { BucketObjectItem } from "../ListObjects/types"; import { ObjectPreviewIcon } from "../../../../../../icons"; interface IPreviewFileProps { open: boolean; bucketName: string; - object: BucketObject | null; + object: BucketObjectItem | null; onClosePreview: () => void; } diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/utils.ts b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/utils.ts index 3da418089..c548b6c6b 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/utils.ts +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/utils.ts @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { BucketObject, RewindObject } from "./ListObjects/types"; +import { BucketObjectItem } from "./ListObjects/types"; export const download = ( bucketName: string, @@ -136,14 +136,14 @@ export const extensionPreview = ( export const sortListObjects = (fieldSort: string) => { switch (fieldSort) { case "name": - return (a: BucketObject | RewindObject, b: BucketObject | RewindObject) => + return (a: BucketObjectItem, b: BucketObjectItem) => a.name.localeCompare(b.name); case "last_modified": - return (a: BucketObject | RewindObject, b: BucketObject | RewindObject) => + return (a: BucketObjectItem, b: BucketObjectItem) => new Date(a.last_modified).getTime() - new Date(b.last_modified).getTime(); case "size": - return (a: BucketObject | RewindObject, b: BucketObject | RewindObject) => + return (a: BucketObjectItem, b: BucketObjectItem) => (a.size || -1) - (b.size || -1); } }; diff --git a/portal-ui/src/screens/Console/Common/FormComponents/CheckboxWrapper/CheckboxWrapper.tsx b/portal-ui/src/screens/Console/Common/FormComponents/CheckboxWrapper/CheckboxWrapper.tsx index a5a191db9..01d4a0419 100644 --- a/portal-ui/src/screens/Console/Common/FormComponents/CheckboxWrapper/CheckboxWrapper.tsx +++ b/portal-ui/src/screens/Console/Common/FormComponents/CheckboxWrapper/CheckboxWrapper.tsx @@ -53,6 +53,7 @@ const styles = (theme: Theme) => margin: "15px 0", marginBottom: 0, flexBasis: "initial", + flexWrap: "nowrap", }, noTopMargin: { marginTop: 0, diff --git a/portal-ui/src/screens/Console/Common/ObjectManager/ObjectHandled.tsx b/portal-ui/src/screens/Console/Common/ObjectManager/ObjectHandled.tsx index 7930c1b0b..79e1a794c 100644 --- a/portal-ui/src/screens/Console/Common/ObjectManager/ObjectHandled.tsx +++ b/portal-ui/src/screens/Console/Common/ObjectManager/ObjectHandled.tsx @@ -19,7 +19,7 @@ import { Theme } from "@mui/material/styles"; import { Tooltip } from "@mui/material"; import createStyles from "@mui/styles/createStyles"; import withStyles from "@mui/styles/withStyles"; -import { IFileItem } from "../../ObjectBrowser/reducers"; +import { IFileItem } from "../../ObjectBrowser/types"; import ProgressBarWrapper from "../ProgressBarWrapper/ProgressBarWrapper"; import { DownloadStatIcon, UploadStatIcon } from "../../../../icons"; import clsx from "clsx"; diff --git a/portal-ui/src/screens/Console/Common/ObjectManager/ObjectManager.tsx b/portal-ui/src/screens/Console/Common/ObjectManager/ObjectManager.tsx index 8dc80ba74..9f3558d1d 100644 --- a/portal-ui/src/screens/Console/Common/ObjectManager/ObjectManager.tsx +++ b/portal-ui/src/screens/Console/Common/ObjectManager/ObjectManager.tsx @@ -21,7 +21,7 @@ import createStyles from "@mui/styles/createStyles"; import withStyles from "@mui/styles/withStyles"; import { Tooltip, IconButton } from "@mui/material"; import { AppState } from "../../../../store"; -import { IFileItem } from "../../ObjectBrowser/reducers"; +import { IFileItem } from "../../ObjectBrowser/types"; import { deleteFromList, cleanList } from "../../ObjectBrowser/actions"; import { TrashIcon } from "../../../../icons"; import ObjectHandled from "./ObjectHandled"; diff --git a/portal-ui/src/screens/Console/Common/PageHeader/PageHeader.tsx b/portal-ui/src/screens/Console/Common/PageHeader/PageHeader.tsx index 5bb47b565..886dec7c1 100644 --- a/portal-ui/src/screens/Console/Common/PageHeader/PageHeader.tsx +++ b/portal-ui/src/screens/Console/Common/PageHeader/PageHeader.tsx @@ -25,7 +25,7 @@ import IconButton from "@mui/material/IconButton"; import { AppState } from "../../../../store"; import OperatorLogo from "../../../../icons/OperatorLogo"; import ConsoleLogo from "../../../../icons/ConsoleLogo"; -import { IFileItem } from "../../ObjectBrowser/reducers"; +import { IFileItem } from "../../ObjectBrowser/types"; import { toggleList } from "../../ObjectBrowser/actions"; import { ObjectManagerIcon } from "../../../../icons"; diff --git a/portal-ui/src/screens/Console/ObjectBrowser/BrowserBreadcrumbs.tsx b/portal-ui/src/screens/Console/ObjectBrowser/BrowserBreadcrumbs.tsx index 16356b006..f78e4ec19 100644 --- a/portal-ui/src/screens/Console/ObjectBrowser/BrowserBreadcrumbs.tsx +++ b/portal-ui/src/screens/Console/ObjectBrowser/BrowserBreadcrumbs.tsx @@ -1,5 +1,5 @@ // This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. +// Copyright (c) 2022 MinIO, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by @@ -15,24 +15,24 @@ // along with this program. If not, see . import React, { Fragment, useState } from "react"; +import { connect } from "react-redux"; import get from "lodash/get"; import Grid from "@mui/material/Grid"; -import { connect } from "react-redux"; import withStyles from "@mui/styles/withStyles"; -import { Theme } from "@mui/material/styles"; import createStyles from "@mui/styles/createStyles"; -import { ObjectBrowserState } from "./reducers"; -import { objectBrowserCommon } from "../Common/FormComponents/common/styleLibrary"; +import { Theme } from "@mui/material/styles"; import { Link } from "react-router-dom"; +import { Button, IconButton, Tooltip } from "@mui/material"; +import { ObjectBrowserState } from "./types"; +import { objectBrowserCommon } from "../Common/FormComponents/common/styleLibrary"; import { encodeFileName } from "../../../common/utils"; import { BackCaretIcon, NewPathIcon } from "../../../icons"; -import { Button, IconButton, Tooltip } from "@mui/material"; -import history from "../../../history"; import { hasPermission } from "../../../common/SecureComponent"; import { IAM_SCOPES } from "../../../common/SecureComponent/permissions"; -import withSuspense from "../Common/Components/withSuspense"; -import { BucketObject } from "../Buckets/ListBuckets/Objects/ListObjects/types"; +import { BucketObjectItem } from "../Buckets/ListBuckets/Objects/ListObjects/types"; import { setVersionsModeEnabled } from "./actions"; +import history from "../../../history"; +import withSuspense from "../Common/Components/withSuspense"; const CreateFolderModal = withSuspense( React.lazy( @@ -52,7 +52,7 @@ interface IObjectBrowser { versionsMode: boolean; versionedFile: string; hidePathButton?: boolean; - existingFiles: BucketObject[]; + existingFiles: BucketObjectItem[]; additionalOptions?: React.ReactNode; setVersionsModeEnabled: typeof setVersionsModeEnabled; } @@ -86,7 +86,7 @@ const BrowserBreadcrumbs = ({ const lastBreadcrumbsIndex = splitPaths.length - 1; let breadcrumbsMap = splitPaths.map((objectItem: string, index: number) => { - const subSplit = splitPaths.slice(0, index + 1).join("/"); + const subSplit = `${splitPaths.slice(0, index + 1).join("/")}/`; const route = `/buckets/${bucketName}/browse/${ subSplit ? `${encodeFileName(subSplit)}` : `` }`; @@ -98,14 +98,18 @@ const BrowserBreadcrumbs = ({ return ( / - { - setVersionsModeEnabled(false); - }} - > - {objectItem} - + {index === lastBreadcrumbsIndex ? ( + {objectItem} + ) : ( + { + setVersionsModeEnabled(false); + }} + > + {objectItem} + + )} ); }); diff --git a/portal-ui/src/screens/Console/ObjectBrowser/actions.ts b/portal-ui/src/screens/Console/ObjectBrowser/actions.ts index 13ecbf55f..713f6563a 100644 --- a/portal-ui/src/screens/Console/ObjectBrowser/actions.ts +++ b/portal-ui/src/screens/Console/ObjectBrowser/actions.ts @@ -1,5 +1,5 @@ // This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. +// Copyright (c) 2022 MinIO, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by @@ -14,137 +14,28 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { IFileItem } from "./reducers"; - -export const REWIND_SET_ENABLE = "REWIND/SET_ENABLE"; -export const REWIND_RESET_REWIND = "REWIND/RESET_REWIND"; - -export const OBJECT_MANAGER_NEW_OBJECT = "OBJECT_MANAGER/NEW_OBJECT"; -export const OBJECT_MANAGER_UPDATE_PROGRESS_OBJECT = - "OBJECT_MANAGER/UPDATE_PROGRESS_OBJECT"; -export const OBJECT_MANAGER_COMPLETE_OBJECT = "OBJECT_MANAGER/COMPLETE_OBJECT"; -export const OBJECT_MANAGER_DELETE_FROM_OBJECT_LIST = - "OBJECT_MANAGER/DELETE_FROM_OBJECT_LIST"; -export const OBJECT_MANAGER_CLEAN_LIST = "OBJECT_MANAGER/CLEAN_LIST"; -export const OBJECT_MANAGER_TOGGLE_LIST = "OBJECT_MANAGER/TOGGLE_LIST"; -export const OBJECT_MANAGER_OPEN_LIST = "OBJECT_MANAGER/OPEN_LIST"; -export const OBJECT_MANAGER_CLOSE_LIST = "OBJECT_MANAGER/CLOSE_LIST"; -export const OBJECT_MANAGER_SET_SEARCH_OBJECT = - "OBJECT_MANAGER/SET_SEARCH_OBJECT"; - -export const BUCKET_BROWSER_VERSIONS_MODE_ENABLED = - "BUCKET_BROWSER/VERSIONS_MODE_ENABLED"; -export const BUCKET_BROWSER_VERSIONS_SET_SEARCH = - "BUCKET_BROWSER/VERSIONS_SET_SEARCH"; -export const BUCKET_BROWSER_SET_SELECTED_VERSION = - "BUCKET_BROWSER/SET_SELECTED_VERSION"; -export const BUCKET_BROWSER_SHOW_DELETED = "BUCKET_BROWSER/SHOW_DELETED"; -export const BUCKET_BROWSER_LOAD_VERSIONS = "BUCKET_BROWSER/LOAD_VERSIONS"; -export const BUCKET_BROWSER_LOAD_OBJECT_DETAILS = - "BUCKET_BROWSER/LOAD_OBJECT_DETAILS"; - -interface RewindSetEnabled { - type: typeof REWIND_SET_ENABLE; - bucket: string; - state: boolean; - dateRewind: any; -} - -interface RewindReset { - type: typeof REWIND_RESET_REWIND; -} - -interface VersionsModeEnabled { - type: typeof BUCKET_BROWSER_VERSIONS_MODE_ENABLED; - status: boolean; - objectName: string; -} - -interface OMNewObject { - type: typeof OBJECT_MANAGER_NEW_OBJECT; - newObject: IFileItem; -} - -interface OMUpdateProgress { - type: typeof OBJECT_MANAGER_UPDATE_PROGRESS_OBJECT; - instanceID: string; - progress: number; -} - -interface OMCompleteObject { - type: typeof OBJECT_MANAGER_COMPLETE_OBJECT; - instanceID: string; -} - -interface OMDeleteFromList { - type: typeof OBJECT_MANAGER_DELETE_FROM_OBJECT_LIST; - instanceID: string; -} - -interface OMCleanList { - type: typeof OBJECT_MANAGER_CLEAN_LIST; -} - -interface OMToggleList { - type: typeof OBJECT_MANAGER_TOGGLE_LIST; -} - -interface OMOpenList { - type: typeof OBJECT_MANAGER_OPEN_LIST; -} - -interface OMCloseList { - type: typeof OBJECT_MANAGER_CLOSE_LIST; -} - -interface SetSearchObjects { - type: typeof OBJECT_MANAGER_SET_SEARCH_OBJECT; - searchString: string; -} - -interface SetSearchVersions { - type: typeof BUCKET_BROWSER_VERSIONS_SET_SEARCH; - searchString: string; -} - -interface SetSelectedversion { - type: typeof BUCKET_BROWSER_SET_SELECTED_VERSION; - selectedVersion: string; -} - -interface SetShowDeletedObjects { - type: typeof BUCKET_BROWSER_SHOW_DELETED; - status: boolean; -} - -interface SetLoadingVersions { - type: typeof BUCKET_BROWSER_LOAD_VERSIONS; - status: boolean; -} - -interface SetLoadingObjectInfo { - type: typeof BUCKET_BROWSER_LOAD_OBJECT_DETAILS; - status: boolean; -} - -export type ObjectBrowserActionTypes = - | RewindSetEnabled - | RewindReset - | VersionsModeEnabled - | OMNewObject - | OMUpdateProgress - | OMCompleteObject - | OMDeleteFromList - | OMCleanList - | OMToggleList - | OMOpenList - | OMCloseList - | SetSearchObjects - | SetSearchVersions - | SetSelectedversion - | SetShowDeletedObjects - | SetLoadingVersions - | SetLoadingObjectInfo; +import { + BUCKET_BROWSER_LOAD_OBJECT_DETAILS, + BUCKET_BROWSER_LOAD_VERSIONS, + BUCKET_BROWSER_OBJECT_DETAILS_STATE, + BUCKET_BROWSER_SET_SELECTED_VERSION, + BUCKET_BROWSER_SHOW_DELETED, + BUCKET_BROWSER_VERSIONS_MODE_ENABLED, + BUCKET_BROWSER_VERSIONS_SET_SEARCH, + OBJECT_MANAGER_CLEAN_LIST, + OBJECT_MANAGER_CLOSE_LIST, + OBJECT_MANAGER_COMPLETE_OBJECT, + OBJECT_MANAGER_DELETE_FROM_OBJECT_LIST, + OBJECT_MANAGER_NEW_OBJECT, + OBJECT_MANAGER_OPEN_LIST, + OBJECT_MANAGER_SET_SEARCH_OBJECT, + OBJECT_MANAGER_TOGGLE_LIST, + OBJECT_MANAGER_UPDATE_PROGRESS_OBJECT, + REWIND_RESET_REWIND, + REWIND_SET_ENABLE, + IFileItem, + BUCKET_BROWSER_SET_SELECTED_OBJECT, OBJECT_MANAGER_SET_LOADING, +} from "./types"; export const setRewindEnable = ( state: boolean, @@ -270,3 +161,24 @@ export const setLoadingObjectInfo = (status: boolean) => { status, }; }; + +export const setObjectDetailsView = (status: boolean) => { + return { + type: BUCKET_BROWSER_OBJECT_DETAILS_STATE, + status, + }; +}; + +export const setSelectedObjectView = (object: string | null) => { + return { + type: BUCKET_BROWSER_SET_SELECTED_OBJECT, + object, + }; +}; + +export const setLoadingObjectsList = (status: boolean) => { + return { + type: OBJECT_MANAGER_SET_LOADING, + status, + } +}; diff --git a/portal-ui/src/screens/Console/ObjectBrowser/reducers.ts b/portal-ui/src/screens/Console/ObjectBrowser/reducers.ts index 1dbb88985..333c5ab6c 100644 --- a/portal-ui/src/screens/Console/ObjectBrowser/reducers.ts +++ b/portal-ui/src/screens/Console/ObjectBrowser/reducers.ts @@ -1,5 +1,5 @@ // This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. +// Copyright (c) 2022 MinIO, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by @@ -13,11 +13,11 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . + import { REWIND_SET_ENABLE, REWIND_RESET_REWIND, BUCKET_BROWSER_VERSIONS_MODE_ENABLED, - ObjectBrowserActionTypes, OBJECT_MANAGER_NEW_OBJECT, OBJECT_MANAGER_UPDATE_PROGRESS_OBJECT, OBJECT_MANAGER_COMPLETE_OBJECT, @@ -32,51 +32,12 @@ import { BUCKET_BROWSER_SHOW_DELETED, BUCKET_BROWSER_LOAD_VERSIONS, BUCKET_BROWSER_LOAD_OBJECT_DETAILS, -} from "./actions"; - -export interface Route { - route: string; - label: string; - type: string; -} - -export interface RewindItem { - rewindEnabled: boolean; - bucketToRewind: string; - dateToRewind: any; -} - -export interface ObjectBrowserState { - rewind: RewindItem; - objectManager: ObjectManager; - searchObjects: string; - loadingVersions: boolean; - loadingObjectInfo: boolean; - versionsMode: boolean; - versionedFile: string; - searchVersions: string; - selectedVersion: string; - showDeleted: boolean; -} - -export interface ObjectBrowserReducer { - objectBrowser: ObjectBrowserState; -} - -export interface ObjectManager { - objectsToManage: IFileItem[]; - managerOpen: boolean; -} - -export interface IFileItem { - type: "download" | "upload"; - instanceID: string; - bucketName: string; - prefix: string; - percentage: number; - done: boolean; - waitingForFile: boolean; -} + BUCKET_BROWSER_OBJECT_DETAILS_STATE, + ObjectBrowserState, + ObjectBrowserActionTypes, + BUCKET_BROWSER_SET_SELECTED_OBJECT, + OBJECT_MANAGER_SET_LOADING, +} from "./types"; const defaultRewind = { rewindEnabled: false, @@ -86,6 +47,8 @@ const defaultRewind = { const initialState: ObjectBrowserState = { versionsMode: false, + loadingObjects: true, + objectDetailsOpen: false, loadingVersions: true, loadingObjectInfo: true, rewind: { @@ -100,6 +63,7 @@ const initialState: ObjectBrowserState = { searchVersions: "", selectedVersion: "", showDeleted: false, + selectedInternalPaths: null, }; export function objectBrowserReducer( @@ -244,6 +208,11 @@ export function objectBrowserReducer( ...state, searchObjects: action.searchString, }; + case OBJECT_MANAGER_SET_LOADING: + return { + ...state, + loadingObjects: action.status, + }; case BUCKET_BROWSER_VERSIONS_SET_SEARCH: return { ...state, @@ -269,6 +238,16 @@ export function objectBrowserReducer( ...state, loadingObjectInfo: action.status, }; + case BUCKET_BROWSER_OBJECT_DETAILS_STATE: + return { + ...state, + objectDetailsOpen: action.status, + }; + case BUCKET_BROWSER_SET_SELECTED_OBJECT: + return { + ...state, + selectedInternalPaths: action.object, + }; default: return state; } diff --git a/portal-ui/src/screens/Console/ObjectBrowser/types.ts b/portal-ui/src/screens/Console/ObjectBrowser/types.ts new file mode 100644 index 000000000..022ec63f8 --- /dev/null +++ b/portal-ui/src/screens/Console/ObjectBrowser/types.ts @@ -0,0 +1,215 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2022 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +export const REWIND_SET_ENABLE = "REWIND/SET_ENABLE"; +export const REWIND_RESET_REWIND = "REWIND/RESET_REWIND"; + +export const OBJECT_MANAGER_SET_LOADING = "OBJECT_MANAGER/SET_LOADING"; +export const OBJECT_MANAGER_NEW_OBJECT = "OBJECT_MANAGER/NEW_OBJECT"; +export const OBJECT_MANAGER_UPDATE_PROGRESS_OBJECT = + "OBJECT_MANAGER/UPDATE_PROGRESS_OBJECT"; +export const OBJECT_MANAGER_COMPLETE_OBJECT = "OBJECT_MANAGER/COMPLETE_OBJECT"; +export const OBJECT_MANAGER_DELETE_FROM_OBJECT_LIST = + "OBJECT_MANAGER/DELETE_FROM_OBJECT_LIST"; +export const OBJECT_MANAGER_CLEAN_LIST = "OBJECT_MANAGER/CLEAN_LIST"; +export const OBJECT_MANAGER_TOGGLE_LIST = "OBJECT_MANAGER/TOGGLE_LIST"; +export const OBJECT_MANAGER_OPEN_LIST = "OBJECT_MANAGER/OPEN_LIST"; +export const OBJECT_MANAGER_CLOSE_LIST = "OBJECT_MANAGER/CLOSE_LIST"; +export const OBJECT_MANAGER_SET_SEARCH_OBJECT = + "OBJECT_MANAGER/SET_SEARCH_OBJECT"; + +export const BUCKET_BROWSER_VERSIONS_MODE_ENABLED = + "BUCKET_BROWSER/VERSIONS_MODE_ENABLED"; +export const BUCKET_BROWSER_VERSIONS_SET_SEARCH = + "BUCKET_BROWSER/VERSIONS_SET_SEARCH"; +export const BUCKET_BROWSER_SET_SELECTED_VERSION = + "BUCKET_BROWSER/SET_SELECTED_VERSION"; +export const BUCKET_BROWSER_SHOW_DELETED = "BUCKET_BROWSER/SHOW_DELETED"; +export const BUCKET_BROWSER_LOAD_VERSIONS = "BUCKET_BROWSER/LOAD_VERSIONS"; +export const BUCKET_BROWSER_LOAD_OBJECT_DETAILS = + "BUCKET_BROWSER/LOAD_OBJECT_DETAILS"; +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 interface Route { + route: string; + label: string; + type: string; +} + +export interface RewindItem { + rewindEnabled: boolean; + bucketToRewind: string; + dateToRewind: any; +} + +export interface ObjectBrowserState { + rewind: RewindItem; + objectManager: ObjectManager; + searchObjects: string; + loadingVersions: boolean; + loadingObjects: boolean; + loadingObjectInfo: boolean; + versionsMode: boolean; + versionedFile: string; + searchVersions: string; + selectedVersion: string; + showDeleted: boolean; + objectDetailsOpen: boolean; + selectedInternalPaths: string | null; +} + +export interface ObjectBrowserReducer { + objectBrowser: ObjectBrowserState; +} + +export interface ObjectManager { + objectsToManage: IFileItem[]; + managerOpen: boolean; +} + +export interface IFileItem { + type: "download" | "upload"; + instanceID: string; + bucketName: string; + prefix: string; + percentage: number; + done: boolean; + waitingForFile: boolean; +} + +interface RewindSetEnabled { + type: typeof REWIND_SET_ENABLE; + bucket: string; + state: boolean; + dateRewind: any; +} + +interface RewindReset { + type: typeof REWIND_RESET_REWIND; +} + +interface VersionsModeEnabled { + type: typeof BUCKET_BROWSER_VERSIONS_MODE_ENABLED; + status: boolean; + objectName: string; +} + +interface OMNewObject { + type: typeof OBJECT_MANAGER_NEW_OBJECT; + newObject: IFileItem; +} + +interface OMUpdateProgress { + type: typeof OBJECT_MANAGER_UPDATE_PROGRESS_OBJECT; + instanceID: string; + progress: number; +} + +interface OMCompleteObject { + type: typeof OBJECT_MANAGER_COMPLETE_OBJECT; + instanceID: string; +} + +interface OMDeleteFromList { + type: typeof OBJECT_MANAGER_DELETE_FROM_OBJECT_LIST; + instanceID: string; +} + +interface OMCleanList { + type: typeof OBJECT_MANAGER_CLEAN_LIST; +} + +interface OMToggleList { + type: typeof OBJECT_MANAGER_TOGGLE_LIST; +} + +interface OMOpenList { + type: typeof OBJECT_MANAGER_OPEN_LIST; +} + +interface OMCloseList { + type: typeof OBJECT_MANAGER_CLOSE_LIST; +} + +interface SetSearchObjects { + type: typeof OBJECT_MANAGER_SET_SEARCH_OBJECT; + searchString: string; +} + +interface SetSearchVersions { + type: typeof BUCKET_BROWSER_VERSIONS_SET_SEARCH; + searchString: string; +} + +interface SetSelectedversion { + type: typeof BUCKET_BROWSER_SET_SELECTED_VERSION; + selectedVersion: string; +} + +interface SetShowDeletedObjects { + type: typeof BUCKET_BROWSER_SHOW_DELETED; + status: boolean; +} + +interface SetLoadingVersions { + type: typeof BUCKET_BROWSER_LOAD_VERSIONS; + status: boolean; +} + +interface SetLoadingObjectInfo { + type: typeof BUCKET_BROWSER_LOAD_OBJECT_DETAILS; + status: boolean; +} + +interface SetObjectDetailsState { + type: typeof BUCKET_BROWSER_OBJECT_DETAILS_STATE; + status: boolean; +} + +interface SetSelectedObject { + type: typeof BUCKET_BROWSER_SET_SELECTED_OBJECT; + object: string | null; +} + +interface SetObjectManagerLoading { + type: typeof OBJECT_MANAGER_SET_LOADING; + status: boolean; +} + +export type ObjectBrowserActionTypes = + | RewindSetEnabled + | RewindReset + | VersionsModeEnabled + | OMNewObject + | OMUpdateProgress + | OMCompleteObject + | OMDeleteFromList + | OMCleanList + | OMToggleList + | OMOpenList + | OMCloseList + | SetSearchObjects + | SetSearchVersions + | SetSelectedversion + | SetShowDeletedObjects + | SetLoadingVersions + | SetLoadingObjectInfo + | SetObjectDetailsState + | SetSelectedObject + | SetObjectManagerLoading;