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 9dc79af4e..c23753d22 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
@@ -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 React, { useEffect, useState } from "react";
+import React, { useEffect, useRef, useState } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import TextField from "@material-ui/core/TextField";
@@ -33,7 +33,7 @@ import {
searchField,
} from "../../../../Common/FormComponents/common/styleLibrary";
import PageHeader from "../../../../Common/PageHeader/PageHeader";
-import { Button, Input } from "@material-ui/core";
+import { Button, Input, Typography } from "@material-ui/core";
import * as reactMoment from "react-moment";
import { CreateIcon } from "../../../../../../icons";
import BrowserBreadcrumbs from "../../../../ObjectBrowser/BrowserBreadcrumbs";
@@ -150,6 +150,30 @@ interface IListObjectsProps {
fileDownloadStarted: typeof fileDownloadStarted;
}
+function useInterval(callback: any, delay: number) {
+ const savedCallback = useRef(null);
+
+ // Remember the latest callback.
+ useEffect(() => {
+ savedCallback.current = callback;
+ }, [callback]);
+
+ // Set up the interval.
+ useEffect(() => {
+ function tick() {
+ if (savedCallback !== undefined && savedCallback.current) {
+ savedCallback.current();
+ }
+ }
+ if (delay !== null) {
+ let id = setInterval(tick, delay);
+ return () => clearInterval(id);
+ }
+ }, [delay]);
+}
+
+const defLoading = Loading...;
+
const ListObjects = ({
classes,
match,
@@ -171,6 +195,41 @@ const ListObjects = ({
const [selectedObject, setSelectedObject] = useState("");
const [selectedBucket, setSelectedBucket] = useState("");
const [filterObjects, setFilterObjects] = useState("");
+ const [loadingPromise, setLoadingPromise] = useState | null>(
+ null
+ );
+ const [loadingStartTime, setLoadingStartTime] = useState(0);
+ const [loadingMessage, setLoadingMessage] = useState(
+ defLoading
+ );
+
+ const updateMessage = () => {
+ let timeDelta = Date.now() - loadingStartTime;
+
+ if (timeDelta / 1000 >= 6) {
+ setLoadingMessage(
+
+
+ This operation is taking longer than expected... (
+ {Math.ceil(timeDelta / 1000)}s)
+
+
+ );
+ } else if (timeDelta / 1000 >= 3) {
+ setLoadingMessage(
+
+ This operation is taking longer than expected...
+
+ );
+ }
+ };
+
+ useInterval(() => {
+ // Your custom logic here
+ if (loading) {
+ updateMessage();
+ }
+ }, 1000);
useEffect(() => {
const bucketName = match.params["bucket"];
@@ -206,7 +265,11 @@ const ListObjects = ({
extraPath = `?prefix=${internalPaths}/`;
}
- api
+ let currentTimestamp = Date.now() + 0;
+ setLoadingStartTime(currentTimestamp);
+ setLoadingMessage(defLoading);
+
+ let p = api
.invoke("GET", `/api/v1/buckets/${bucketName}/objects${extraPath}`)
.then((res: BucketObjectsList) => {
setSelectedBucket(bucketName);
@@ -239,6 +302,7 @@ const ListObjects = ({
setLoading(false);
setErrorSnackMessage(err);
});
+ setLoadingPromise(p);
}
}, [loading, match, setLastAsFile, setErrorSnackMessage]);
@@ -558,6 +622,7 @@ const ListObjects = ({
},
]}
isLoading={loading}
+ loadingMessage={loadingMessage}
entityName="Objects"
idField="name"
records={filteredRecords}
diff --git a/portal-ui/src/screens/Console/Common/TableWrapper/TableWrapper.tsx b/portal-ui/src/screens/Console/Common/TableWrapper/TableWrapper.tsx
index b51430d68..59e0cd09d 100644
--- a/portal-ui/src/screens/Console/Common/TableWrapper/TableWrapper.tsx
+++ b/portal-ui/src/screens/Console/Common/TableWrapper/TableWrapper.tsx
@@ -13,19 +13,19 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-import React, { useState, Fragment } from "react";
+import React, { Fragment, useState } from "react";
import get from "lodash/get";
import isString from "lodash/isString";
import {
+ Checkbox,
+ Grid,
+ IconButton,
LinearProgress,
Paper,
- Grid,
- Checkbox,
- Typography,
- IconButton,
Popover,
+ Typography,
} from "@material-ui/core";
-import { Table, Column, AutoSizer, InfiniteLoader } from "react-virtualized";
+import { AutoSizer, Column, InfiniteLoader, Table } from "react-virtualized";
import { createStyles, withStyles } from "@material-ui/core/styles";
import CircularProgress from "@material-ui/core/CircularProgress";
import ViewColumnIcon from "@material-ui/icons/ViewColumn";
@@ -43,11 +43,12 @@ import CheckboxWrapper from "../FormComponents/CheckboxWrapper/CheckboxWrapper";
interface ItemActions {
type: string;
- onClick?(valueToSend: any): any;
to?: string;
sendOnlyId?: boolean;
hideButtonFunction?: (itemValue: any) => boolean;
showLoaderFunction?: (itemValue: any) => boolean;
+
+ onClick?(valueToSend: any): any;
}
interface IColumns {
@@ -83,6 +84,7 @@ interface TableWrapperProps {
onSelect?: (e: React.ChangeEvent) => any;
idField: string;
isLoading: boolean;
+ loadingMessage?: React.ReactNode;
records: any[];
classes: any;
entityName: string;
@@ -491,6 +493,7 @@ const TableWrapper = ({
onSelect,
records,
isLoading,
+ loadingMessage = Loading...,
entityName,
selectedItems,
idField,
@@ -600,7 +603,7 @@ const TableWrapper = ({
{isLoading && (
- Loading...
+ {loadingMessage}