Add progress bar on UI (#526)

This commit is contained in:
Cesar N
2020-12-18 17:40:03 -06:00
committed by GitHub
parent f5922bb68b
commit 51ba86fa46
6 changed files with 187 additions and 102 deletions

View File

@@ -20,6 +20,8 @@ import {
SERVER_IS_LOADING,
SERVER_NEEDS_RESTART,
USER_LOGGED,
SET_LOADING_PROGRESS,
SET_SNACK_BAR_MESSAGE,
} from "./types";
export function userLoggedIn(loggedIn: boolean) {
@@ -56,3 +58,17 @@ export function serverIsLoading(isLoading: boolean) {
isLoading: isLoading,
};
}
export const setLoadingProgress = (progress: number) => {
return {
type: SET_LOADING_PROGRESS,
loadingProgress: progress,
};
};
export const setSnackBarMessage = (message: string) => {
return {
type: SET_SNACK_BAR_MESSAGE,
snackBarMessage: message,
};
};

View File

@@ -22,6 +22,8 @@ import {
SystemActionTypes,
SystemState,
USER_LOGGED,
SET_LOADING_PROGRESS,
SET_SNACK_BAR_MESSAGE,
} from "./types";
const initialState: SystemState = {
@@ -32,6 +34,8 @@ const initialState: SystemState = {
sidebarOpen: true,
serverNeedsRestart: false,
serverIsLoading: false,
loadingProgress: 100,
snackBarMessage: "",
};
export function systemReducer(
@@ -65,6 +69,16 @@ export function systemReducer(
...state,
serverIsLoading: action.isLoading,
};
case SET_LOADING_PROGRESS:
return {
...state,
loadingProgress: action.loadingProgress,
};
case SET_SNACK_BAR_MESSAGE:
return {
...state,
snackBarMessage: action.snackBarMessage,
};
default:
return state;
}

View File

@@ -37,7 +37,6 @@ import { isNullOrUndefined } from "util";
import { Button, Input } from "@material-ui/core";
import * as reactMoment from "react-moment";
import { CreateIcon } from "../../../../../../icons";
import Snackbar from "@material-ui/core/Snackbar";
import BrowserBreadcrumbs from "../../../../ObjectBrowser/BrowserBreadcrumbs";
import get from "lodash/get";
import { withRouter } from "react-router-dom";
@@ -51,6 +50,10 @@ import { ObjectBrowserState, Route } from "../../../../ObjectBrowser/reducers";
import CreateFolderModal from "./CreateFolderModal";
import UploadFile from "../../../../../../icons/UploadFile";
import { download } from "../utils";
import {
setLoadingProgress,
setSnackBarMessage,
} from "../../../../../../actions";
const commonIcon = {
backgroundRepeat: "no-repeat",
@@ -134,6 +137,8 @@ interface IListObjectsProps {
setAllRoutes: (path: string) => any;
routesList: Route[];
setLastAsFile: () => any;
setLoadingProgress: typeof setLoadingProgress;
setSnackBarMessage: typeof setSnackBarMessage;
}
interface ObjectBrowserReducer {
@@ -147,6 +152,8 @@ const ListObjects = ({
setAllRoutes,
routesList,
setLastAsFile,
setLoadingProgress,
setSnackBarMessage,
}: IListObjectsProps) => {
const [records, setRecords] = useState<BucketObject[]>([]);
const [loading, setLoading] = useState<boolean>(true);
@@ -155,8 +162,6 @@ const ListObjects = ({
const [selectedObject, setSelectedObject] = useState<string>("");
const [selectedBucket, setSelectedBucket] = useState<string>("");
const [filterObjects, setFilterObjects] = useState<string>("");
const [openSnackbar, setOpenSnackbar] = useState<boolean>(false);
const [snackBarMessage, setSnackbarMessage] = useState<string>("");
useEffect(() => {
const bucketName = match.params["bucket"];
@@ -185,26 +190,28 @@ const ListObjects = ({
});
};
let extraPath = "";
if (internalPaths) {
extraPath = `?prefix=${internalPaths}/`;
}
if (loading) {
let extraPath = "";
if (internalPaths) {
extraPath = `?prefix=${internalPaths}/`;
}
api
.invoke("GET", `/api/v1/buckets/${bucketName}/objects${extraPath}`)
.then((res: BucketObjectsList) => {
setSelectedBucket(bucketName);
setRecords(res.objects || []);
// In case no objects were retrieved, We check if item is a file
if (!res.objects && extraPath !== "") {
verifyIfIsFile();
return;
}
setLoading(false);
})
.catch((err: any) => {
setLoading(false);
});
api
.invoke("GET", `/api/v1/buckets/${bucketName}/objects${extraPath}`)
.then((res: BucketObjectsList) => {
setSelectedBucket(bucketName);
setRecords(res.objects || []);
// In case no objects were retrieved, We check if item is a file
if (!res.objects && extraPath !== "") {
verifyIfIsFile();
return;
}
setLoading(false);
})
.catch((err: any) => {
setLoading(false);
});
}
}, [loading, match, setLastAsFile]);
useEffect(() => {
@@ -218,7 +225,7 @@ const ListObjects = ({
setDeleteOpen(false);
if (refresh) {
showSnackBarMessage(`Object '${selectedObject}' deleted successfully.`);
setSnackBarMessage(`Object '${selectedObject}' deleted successfully.`);
setLoading(true);
}
};
@@ -227,16 +234,6 @@ const ListObjects = ({
setCreateFolderOpen(false);
};
const showSnackBarMessage = (text: string) => {
setSnackbarMessage(text);
setOpenSnackbar(true);
};
const closeSnackBar = () => {
setSnackbarMessage("");
setOpenSnackbar(false);
};
const upload = (e: any, bucketName: string, path: string) => {
if (isNullOrUndefined(e) || isNullOrUndefined(e.target)) {
return;
@@ -252,31 +249,34 @@ const ListObjects = ({
xhr.open("POST", uploadUrl, true);
xhr.withCredentials = false;
xhr.onload = function (event) {
// TODO: handle status
if (xhr.status === 401 || xhr.status === 403) {
showSnackBarMessage("An error occurred while uploading the file.");
}
if (xhr.status === 500) {
showSnackBarMessage("An error occurred while uploading the file.");
xhr.onload = function(event) {
if (
xhr.status === 401 ||
xhr.status === 403 ||
xhr.status === 400 ||
xhr.status === 500
) {
setSnackBarMessage("An error occurred while uploading the file.");
}
if (xhr.status === 200) {
showSnackBarMessage("Object uploaded successfully.");
setLoading(true);
setSnackBarMessage("Object uploaded successfully.");
}
};
xhr.upload.addEventListener("error", (event) => {
// TODO: handle error
showSnackBarMessage("An error occurred while uploading the file.");
setSnackBarMessage("An error occurred while uploading the file.");
});
xhr.upload.addEventListener("progress", (event) => {
// TODO: handle progress with event.loaded, event.total
setLoadingProgress(Math.floor((event.loaded * 100) / event.total));
});
xhr.onerror = () => {
showSnackBarMessage("An error occurred while uploading the file.");
setSnackBarMessage("An error occurred while uploading the file.");
};
xhr.onloadend = () => {
setLoading(true);
setLoadingProgress(100);
};
const formData = new FormData();
@@ -346,23 +346,9 @@ const ListObjects = ({
path = `${splitPaths.slice(2).join("/")}/`;
}
let file = e.target.files[0];
showSnackBarMessage(`Uploading: ${file.name}`);
upload(e, selectedBucket, path);
};
const snackBarAction = (
<Button
color="secondary"
size="small"
onClick={() => {
closeSnackBar();
}}
>
Dismiss
</Button>
);
const tableActions = [
{ type: "view", onClick: openPath, sendOnlyId: true },
{ type: "download", onClick: downloadObject },
@@ -419,11 +405,6 @@ const ListObjects = ({
onClose={closeAddFolderModal}
/>
)}
<Snackbar
open={openSnackbar}
message={snackBarMessage}
action={snackBarAction}
/>
<PageHeader label="Object Browser" />
<Grid container>
<Grid item xs={12} className={classes.container}>
@@ -526,6 +507,8 @@ const mapDispatchToProps = {
addRoute,
setAllRoutes,
setLastAsFile,
setLoadingProgress,
setSnackBarMessage,
};
const connector = connect(mapStateToProps, mapDispatchToProps);

View File

@@ -1,7 +1,6 @@
import React, { useEffect, useState } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import CopyToClipboard from "react-copy-to-clipboard";
import Snackbar from "@material-ui/core/Snackbar";
import Grid from "@material-ui/core/Grid";
import Button from "@material-ui/core/Button";
import { modalBasic } from "../../../../Common/FormComponents/common/styleLibrary";
@@ -12,6 +11,8 @@ import api from "../../../../../../common/api";
import { IFileInfo } from "./types";
import PredefinedList from "../../../../Common/FormComponents/PredefinedList/PredefinedList";
import ErrorBlock from "../../../../../shared/ErrorBlock";
import { setSnackBarMessage } from "../../../../../../actions";
import { connect } from "react-redux";
const styles = (theme: Theme) =>
createStyles({
@@ -30,6 +31,7 @@ interface IShareFileProps {
bucketName: string;
dataObject: IFileInfo;
closeModalAndRefresh: () => void;
setSnackBarMessage: typeof setSnackBarMessage;
}
const ShareFile = ({
@@ -38,24 +40,13 @@ const ShareFile = ({
closeModalAndRefresh,
bucketName,
dataObject,
setSnackBarMessage,
}: IShareFileProps) => {
const [shareURL, setShareURL] = useState<string>("");
const [isLoadingFile, setIsLoadingFile] = useState<boolean>(false);
const [error, setError] = useState<string>("");
const [selectedDate, setSelectedDate] = useState<string>("");
const [dateValid, setDateValid] = useState<boolean>(true);
const [openSnack, setOpenSnack] = useState<boolean>(false);
const [snackBarMessage, setSnackbarMessage] = useState<string>("");
const showSnackBarMessage = (text: string) => {
setSnackbarMessage(text);
setOpenSnack(true);
};
const closeSnackBar = () => {
setSnackbarMessage("");
setOpenSnack(false);
};
const dateChanged = (newDate: string, isValid: boolean) => {
setDateValid(isValid);
@@ -115,27 +106,8 @@ const ShareFile = ({
}
}, [dataObject, selectedDate, bucketName, dateValid, setShareURL]);
const snackBarAction = (
<Button
color="secondary"
size="small"
onClick={() => {
closeSnackBar();
}}
>
Dismiss
</Button>
);
return (
<React.Fragment>
{openSnack && (
<Snackbar
open={openSnack}
message={snackBarMessage}
action={snackBarAction}
/>
)}
<ModalWrapper
title="Share File"
modalOpen={open}
@@ -169,7 +141,7 @@ const ShareFile = ({
color="primary"
startIcon={<CopyIcon />}
onClick={() => {
showSnackBarMessage("Share URL Copied to clipboard");
setSnackBarMessage("Share URL Copied to clipboard");
}}
disabled={shareURL === "" || isLoadingFile}
>
@@ -184,4 +156,10 @@ const ShareFile = ({
);
};
export default withStyles(styles)(ShareFile);
const mapState = (state: IShareFileProps) => ({});
const connector = connect(mapState, {
setSnackBarMessage,
});
export default withStyles(styles)(connector(ShareFile));

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment } from "react";
import React, { Fragment, useState, useEffect } from "react";
import clsx from "clsx";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { Button, LinearProgress } from "@material-ui/core";
@@ -29,6 +29,7 @@ import {
serverIsLoading,
serverNeedsRestart,
setMenuOpen,
setSnackBarMessage,
} from "../../actions";
import { ISessionResponse } from "./types";
import Buckets from "./Buckets/Buckets";
@@ -50,6 +51,7 @@ import Trace from "./Trace/Trace";
import Logs from "./Logs/Logs";
import Heal from "./Heal/Heal";
import Watch from "./Watch/Watch";
import Snackbar from "@material-ui/core/Snackbar";
const drawerWidth = 245;
@@ -57,6 +59,9 @@ const styles = (theme: Theme) =>
createStyles({
root: {
display: "flex",
"& .MuiPaper-root": {
borderRadius: "0px 0px 15px 15px",
},
},
toolbar: {
background: theme.palette.background.default,
@@ -147,6 +152,34 @@ const styles = (theme: Theme) =>
lineHeight: "60px",
textAlign: "center",
},
snackBar: {
backgroundColor: "#081F44",
fontWeight: "bold",
fontFamily: "Lato, sans-serif",
fontSize: 14,
padding: "0px 20px 0px 20px;",
"& div": {
textAlign: "center",
padding: "6px 30px",
},
},
snackBarExternal: {
top: "-17px",
position: "absolute",
minWidth: "348px",
whiteSpace: "nowrap",
height: "33px",
},
snackDiv: {
top: "17px",
left: "50%",
position: "absolute",
},
progress: {
height: "3px",
backgroundColor: "#eaeaea",
},
});
interface IConsoleProps {
@@ -159,6 +192,9 @@ interface IConsoleProps {
serverNeedsRestart: typeof serverNeedsRestart;
serverIsLoading: typeof serverIsLoading;
session: ISessionResponse;
loadingProgress: number;
snackBarMessage: string;
setSnackBarMessage: typeof setSnackBarMessage;
}
const Console = ({
@@ -169,7 +205,12 @@ const Console = ({
serverNeedsRestart,
serverIsLoading,
session,
loadingProgress,
snackBarMessage,
setSnackBarMessage,
}: IConsoleProps) => {
const [openSnackbar, setOpenSnackbar] = useState<boolean>(false);
const restartServer = () => {
serverIsLoading(true);
api
@@ -278,6 +319,20 @@ const Console = ({
];
const allowedRoutes = routes.filter((route: any) => allowedPages[route.path]);
const closeSnackBar = () => {
setOpenSnackbar(false);
setSnackBarMessage("");
};
useEffect(() => {
if (snackBarMessage === "") {
setOpenSnackbar(false);
return;
}
// Open SnackBar
setOpenSnackbar(true);
}, [snackBarMessage]);
return (
<Fragment>
{session.status === "ok" ? (
@@ -302,7 +357,7 @@ const Console = ({
{isServerLoading ? (
<Fragment>
The server is restarting.
<LinearProgress />
<LinearProgress className={classes.progress} />
</Fragment>
) : (
<Fragment>
@@ -321,6 +376,27 @@ const Console = ({
)}
</div>
)}
{loadingProgress < 100 && (
<LinearProgress
className={classes.progress}
variant="determinate"
value={loadingProgress}
/>
)}
<div className={classes.snackDiv}>
<Snackbar
open={openSnackbar}
onClose={() => {
closeSnackBar();
}}
message={snackBarMessage}
autoHideDuration={5000}
className={classes.snackBarExternal}
ContentProps={{
className: classes.snackBar,
}}
/>
</div>
<Container className={classes.container}>
<Router history={history}>
<Switch>
@@ -350,12 +426,15 @@ const mapState = (state: AppState) => ({
needsRestart: state.system.serverNeedsRestart,
isServerLoading: state.system.serverIsLoading,
session: state.console.session,
loadingProgress: state.system.loadingProgress,
snackBarMessage: state.system.snackBarMessage,
});
const connector = connect(mapState, {
setMenuOpen,
serverNeedsRestart,
serverIsLoading,
setSnackBarMessage,
});
export default withStyles(styles)(connector(Console));

View File

@@ -22,6 +22,8 @@ export interface SystemState {
userName: string;
serverNeedsRestart: boolean;
serverIsLoading: boolean;
loadingProgress: number;
snackBarMessage: string;
}
export const USER_LOGGED = "USER_LOGGED";
@@ -29,6 +31,8 @@ export const OPERATOR_MODE = "OPERATOR_MODE";
export const MENU_OPEN = "MENU_OPEN";
export const SERVER_NEEDS_RESTART = "SERVER_NEEDS_RESTART";
export const SERVER_IS_LOADING = "SERVER_IS_LOADING";
export const SET_LOADING_PROGRESS = "SET_LOADING_PROGRESS";
export const SET_SNACK_BAR_MESSAGE = "SET_SNACK_BAR_MESSAGE";
interface UserLoggedAction {
type: typeof USER_LOGGED;
@@ -54,10 +58,21 @@ interface ServerIsLoading {
type: typeof SERVER_IS_LOADING;
isLoading: boolean;
}
interface SetLoadingProgress {
type: typeof SET_LOADING_PROGRESS;
loadingProgress: number;
}
interface SetSnackBarMessage {
type: typeof SET_SNACK_BAR_MESSAGE;
snackBarMessage: string;
}
export type SystemActionTypes =
| UserLoggedAction
| OperatorModeAction
| SetMenuOpenAction
| ServerNeedsRestartAction
| ServerIsLoading;
| ServerIsLoading
| SetLoadingProgress
| SetSnackBarMessage;