Skip to content

Commit 19195e0

Browse files
dvaldiviabexsoft
andauthored
Interactive Feedback when list objects take a long time (#655)
* Interactive Feedback when list objects take a long time * Remove cancel button Co-authored-by: Alex <[email protected]>
1 parent 7ce36ba commit 19195e0

File tree

2 files changed

+79
-11
lines changed

2 files changed

+79
-11
lines changed

portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
// You should have received a copy of the GNU Affero General Public License
1515
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1616

17-
import React, { useEffect, useState } from "react";
17+
import React, { useEffect, useRef, useState } from "react";
1818
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
1919
import Grid from "@material-ui/core/Grid";
2020
import TextField from "@material-ui/core/TextField";
@@ -33,7 +33,7 @@ import {
3333
searchField,
3434
} from "../../../../Common/FormComponents/common/styleLibrary";
3535
import PageHeader from "../../../../Common/PageHeader/PageHeader";
36-
import { Button, Input } from "@material-ui/core";
36+
import { Button, Input, Typography } from "@material-ui/core";
3737
import * as reactMoment from "react-moment";
3838
import { CreateIcon } from "../../../../../../icons";
3939
import BrowserBreadcrumbs from "../../../../ObjectBrowser/BrowserBreadcrumbs";
@@ -150,6 +150,30 @@ interface IListObjectsProps {
150150
fileDownloadStarted: typeof fileDownloadStarted;
151151
}
152152

153+
function useInterval(callback: any, delay: number) {
154+
const savedCallback = useRef<Function | null>(null);
155+
156+
// Remember the latest callback.
157+
useEffect(() => {
158+
savedCallback.current = callback;
159+
}, [callback]);
160+
161+
// Set up the interval.
162+
useEffect(() => {
163+
function tick() {
164+
if (savedCallback !== undefined && savedCallback.current) {
165+
savedCallback.current();
166+
}
167+
}
168+
if (delay !== null) {
169+
let id = setInterval(tick, delay);
170+
return () => clearInterval(id);
171+
}
172+
}, [delay]);
173+
}
174+
175+
const defLoading = <Typography component="h3">Loading...</Typography>;
176+
153177
const ListObjects = ({
154178
classes,
155179
match,
@@ -171,6 +195,41 @@ const ListObjects = ({
171195
const [selectedObject, setSelectedObject] = useState<string>("");
172196
const [selectedBucket, setSelectedBucket] = useState<string>("");
173197
const [filterObjects, setFilterObjects] = useState<string>("");
198+
const [loadingPromise, setLoadingPromise] = useState<Promise<any> | null>(
199+
null
200+
);
201+
const [loadingStartTime, setLoadingStartTime] = useState<number>(0);
202+
const [loadingMessage, setLoadingMessage] = useState<React.ReactNode>(
203+
defLoading
204+
);
205+
206+
const updateMessage = () => {
207+
let timeDelta = Date.now() - loadingStartTime;
208+
209+
if (timeDelta / 1000 >= 6) {
210+
setLoadingMessage(
211+
<React.Fragment>
212+
<Typography component="h3">
213+
This operation is taking longer than expected... (
214+
{Math.ceil(timeDelta / 1000)}s)
215+
</Typography>
216+
</React.Fragment>
217+
);
218+
} else if (timeDelta / 1000 >= 3) {
219+
setLoadingMessage(
220+
<Typography component="h3">
221+
This operation is taking longer than expected...
222+
</Typography>
223+
);
224+
}
225+
};
226+
227+
useInterval(() => {
228+
// Your custom logic here
229+
if (loading) {
230+
updateMessage();
231+
}
232+
}, 1000);
174233

175234
useEffect(() => {
176235
const bucketName = match.params["bucket"];
@@ -206,7 +265,11 @@ const ListObjects = ({
206265
extraPath = `?prefix=${internalPaths}/`;
207266
}
208267

209-
api
268+
let currentTimestamp = Date.now() + 0;
269+
setLoadingStartTime(currentTimestamp);
270+
setLoadingMessage(defLoading);
271+
272+
let p = api
210273
.invoke("GET", `/api/v1/buckets/${bucketName}/objects${extraPath}`)
211274
.then((res: BucketObjectsList) => {
212275
setSelectedBucket(bucketName);
@@ -239,6 +302,7 @@ const ListObjects = ({
239302
setLoading(false);
240303
setErrorSnackMessage(err);
241304
});
305+
setLoadingPromise(p);
242306
}
243307
}, [loading, match, setLastAsFile, setErrorSnackMessage]);
244308

@@ -558,6 +622,7 @@ const ListObjects = ({
558622
},
559623
]}
560624
isLoading={loading}
625+
loadingMessage={loadingMessage}
561626
entityName="Objects"
562627
idField="name"
563628
records={filteredRecords}

portal-ui/src/screens/Console/Common/TableWrapper/TableWrapper.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@
1313
//
1414
// You should have received a copy of the GNU Affero General Public License
1515
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16-
import React, { useState, Fragment } from "react";
16+
import React, { Fragment, useState } from "react";
1717
import get from "lodash/get";
1818
import isString from "lodash/isString";
1919
import {
20-
LinearProgress,
21-
Paper,
22-
Grid,
2320
Checkbox,
24-
Typography,
21+
Grid,
2522
IconButton,
23+
LinearProgress,
24+
Paper,
2625
Popover,
26+
Typography,
2727
} from "@material-ui/core";
28-
import { Table, Column, AutoSizer, InfiniteLoader } from "react-virtualized";
28+
import { AutoSizer, Column, InfiniteLoader, Table } from "react-virtualized";
2929
import { createStyles, withStyles } from "@material-ui/core/styles";
3030
import CircularProgress from "@material-ui/core/CircularProgress";
3131
import ViewColumnIcon from "@material-ui/icons/ViewColumn";
@@ -43,11 +43,12 @@ import CheckboxWrapper from "../FormComponents/CheckboxWrapper/CheckboxWrapper";
4343

4444
interface ItemActions {
4545
type: string;
46-
onClick?(valueToSend: any): any;
4746
to?: string;
4847
sendOnlyId?: boolean;
4948
hideButtonFunction?: (itemValue: any) => boolean;
5049
showLoaderFunction?: (itemValue: any) => boolean;
50+
51+
onClick?(valueToSend: any): any;
5152
}
5253

5354
interface IColumns {
@@ -83,6 +84,7 @@ interface TableWrapperProps {
8384
onSelect?: (e: React.ChangeEvent<HTMLInputElement>) => any;
8485
idField: string;
8586
isLoading: boolean;
87+
loadingMessage?: React.ReactNode;
8688
records: any[];
8789
classes: any;
8890
entityName: string;
@@ -491,6 +493,7 @@ const TableWrapper = ({
491493
onSelect,
492494
records,
493495
isLoading,
496+
loadingMessage = <Typography component="h3">Loading...</Typography>,
494497
entityName,
495498
selectedItems,
496499
idField,
@@ -600,7 +603,7 @@ const TableWrapper = ({
600603
{isLoading && (
601604
<Grid container className={classes.loadingBox}>
602605
<Grid item xs={12} style={{ textAlign: "center" }}>
603-
<Typography component="h3">Loading...</Typography>
606+
{loadingMessage}
604607
</Grid>
605608
<Grid item xs={12}>
606609
<LinearProgress />

0 commit comments

Comments
 (0)