Add progress bar on UI (#526)
This commit is contained in:
@@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user