add loading status to block user action when doing api call; add error message when fail to do api call

This commit is contained in:
Songyi Huang 2020-09-22 22:41:59 -07:00
parent 882e0d0289
commit b6711547b0
2 changed files with 67 additions and 25 deletions

View File

@ -15,11 +15,10 @@ import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog"; import Dialog from "@material-ui/core/Dialog";
import DialogTitle from "@material-ui/core/DialogTitle"; import DialogTitle from "@material-ui/core/DialogTitle";
import DialogContent from "@material-ui/core/DialogContent"; import DialogContent from "@material-ui/core/DialogContent";
import DialogContentText from "@material-ui/core/DialogContentText";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import DialogActions from "@material-ui/core/DialogActions"; import DialogActions from "@material-ui/core/DialogActions";
import {Message, Upload} from 'element-react'; import {Message, Upload, Loading} from 'element-react';
import {apiUrl} from "../utils/config"; import {apiUrl} from "../utils/config";
const drawerWidth = 250; const drawerWidth = 250;
@ -78,6 +77,7 @@ const useStyles = makeStyles((theme) => ({
function MenuBar (props) { function MenuBar (props) {
const classes = useStyles(); const classes = useStyles();
const [uploadDialogLoading, setUploadDialogLoading] = React.useState(false);
const [open, setOpen] = React.useState({game: true, agent: true}); const [open, setOpen] = React.useState({game: true, agent: true});
@ -114,9 +114,10 @@ function MenuBar (props) {
bodyFormData.append('entry', uploadForm.entry); bodyFormData.append('entry', uploadForm.entry);
bodyFormData.append('game', uploadForm.game); bodyFormData.append('game', uploadForm.game);
bodyFormData.append('model', uploadRef.current.state.fileList[0].raw); bodyFormData.append('model', uploadRef.current.state.fileList[0].raw);
setUploadDialogLoading(true);
axios.post(`${apiUrl}/tournament/upload_agent`, bodyFormData, {headers: {'Content-Type': 'multipart/form-data'}}) axios.post(`${apiUrl}/tournament/upload_agent`, bodyFormData, {headers: {'Content-Type': 'multipart/form-data'}})
.then(res => { .then(res => {
setTimeout(() => {setUploadDialogLoading(false)}, 250);
Message({ Message({
message: "Successfully uploaded model", message: "Successfully uploaded model",
type: "success", type: "success",
@ -126,6 +127,15 @@ function MenuBar (props) {
setUploadDialogOpen(false); setUploadDialogOpen(false);
setUploadForm({...uploadFormInitValue}); setUploadForm({...uploadFormInitValue});
}) })
.catch(err => {
setTimeout(() => {setUploadDialogLoading(false)}, 250);
Message({
message: "Failed to upload model",
type: "error",
showClose: true,
});
console.log(err);
})
}; };
const history = useHistory(); const history = useHistory();
@ -196,7 +206,13 @@ function MenuBar (props) {
<Button variant="contained" color="primary" onClick={openUploadDialog} className={classes.button}> <Button variant="contained" color="primary" onClick={openUploadDialog} className={classes.button}>
Upload Model Upload Model
</Button> </Button>
<Dialog open={uploadDialogOpen} onClose={handleUploadDialogClose} aria-labelledby="form-dialog-title"> <Dialog
open={uploadDialogOpen}
onClose={handleUploadDialogClose}
aria-labelledby="form-dialog-title"
disableBackdropClick={true}
>
<Loading loading={uploadDialogLoading}>
<DialogTitle id="form-dialog-title">Upload Model</DialogTitle> <DialogTitle id="form-dialog-title">Upload Model</DialogTitle>
<DialogContent> <DialogContent>
<Upload <Upload
@ -247,6 +263,7 @@ function MenuBar (props) {
Upload Upload
</Button> </Button>
</DialogActions> </DialogActions>
</Loading>
</Dialog> </Dialog>
</Drawer> </Drawer>
) )

View File

@ -21,7 +21,7 @@ import withStyles from "@material-ui/core/styles/withStyles";
import PlayCircleOutlineIcon from '@material-ui/icons/PlayCircleOutline'; import PlayCircleOutlineIcon from '@material-ui/icons/PlayCircleOutline';
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import {useHistory} from "react-router-dom"; import {useHistory} from "react-router-dom";
import {Message} from "element-react"; import {Message, Loading} from "element-react";
const gameList = [ const gameList = [
{game: 'leduc-holdem', dispName: 'Leduc Hold\'em'}, {game: 'leduc-holdem', dispName: 'Leduc Hold\'em'},
@ -224,33 +224,48 @@ const EnhancedTableToolbar = (props) => {
const history = useHistory(); const history = useHistory();
const functionalButton = () => { const FunctionalButton = () => {
const [buttonLoading, setButtonLoading] = React.useState(false);
if (routeInfo.type === 'game'){ if (routeInfo.type === 'game'){
const handleLaunchTournament = (gameName) => { const handleLaunchTournament = (gameName) => {
// todo: customize eval num // todo: customize eval num
// todo: add global loading when waiting for API response setButtonLoading(true);
axios.get(`${apiUrl}/tournament/launch?eval_num=200&name=${gameName}`) axios.get(`${apiUrl}/tournament/launch?eval_num=200&name=${gameName}`)
.then(res => { .then(res => {
Message({ setTimeout(() => {setButtonLoading(false)}, 250);
message: "Successfully launched tournament", Message({
type: "success", message: "Successfully launched tournament",
showClose: true type: "success",
}); showClose: true
}) });
} })
.catch(err => {
console.log(err);
Message({
message: "Failed to launch tournament",
type: "error",
showClose: true,
});
setTimeout(() => {setButtonLoading(false)}, 250);
})
}
return ( return (
<div className={classes.button}> <div className={classes.button}>
<Button variant="contained" color="primary" onClick={() => handleLaunchTournament(routeInfo.name)}> <Loading loading={buttonLoading}>
Launch Tournament <Button variant="contained" color="primary" onClick={() => handleLaunchTournament(routeInfo.name)}>
</Button> Launch Tournament
</Button>
</Loading>
</div> </div>
) )
} }
else if (routeInfo.type ==='agent') { else if (routeInfo.type ==='agent') {
const delButtonDisabled = defaultModelList.includes(routeInfo.name); const delButtonDisabled = defaultModelList.includes(routeInfo.name);
const handleDelModel = (agentName) => { const handleDelModel = (agentName) => {
setButtonLoading(true);
axios.get(`${apiUrl}/tournament/delete_agent?name=${agentName}`) axios.get(`${apiUrl}/tournament/delete_agent?name=${agentName}`)
.then(res => { .then(res => {
setTimeout(() => {setButtonLoading(false)}, 250);
Message({ Message({
message: "Successfully deleted model", message: "Successfully deleted model",
type: "success", type: "success",
@ -259,12 +274,22 @@ const EnhancedTableToolbar = (props) => {
setReloadMenu(reloadMenu+1); setReloadMenu(reloadMenu+1);
history.push(`/leaderboard?type=game&name=leduc-holdem`); history.push(`/leaderboard?type=game&name=leduc-holdem`);
}) })
.catch(err => {
Message({
message: "Failed to delete model",
type: "error",
showClose: true,
});
setTimeout(() => {setButtonLoading(false)}, 250);
})
}; };
return ( return (
<div className={classes.button}> <div className={classes.button}>
<Button variant="contained" onClick={() => handleDelModel(routeInfo.name)} color="primary" disabled={delButtonDisabled}> <Loading loading={buttonLoading}>
Delete Model <Button variant="contained" onClick={() => handleDelModel(routeInfo.name)} color="primary" disabled={delButtonDisabled}>
</Button> Delete Model
</Button>
</Loading>
</div> </div>
) )
} }
@ -284,7 +309,7 @@ const EnhancedTableToolbar = (props) => {
<Typography color="textPrimary">{name}</Typography> <Typography color="textPrimary">{name}</Typography>
</Breadcrumbs> </Breadcrumbs>
<div className={classes.button}> <div className={classes.button}>
{functionalButton()} <FunctionalButton />
</div> </div>
</Toolbar> </Toolbar>
); );