diff options
author | Determinant <[email protected]> | 2019-02-06 21:13:23 -0500 |
---|---|---|
committer | Determinant <[email protected]> | 2019-02-06 21:13:23 -0500 |
commit | d88b873c63d8ad5b5336b509f8a4ee35a583e279 (patch) | |
tree | 787ac45d6cc5cac30b7939314a37c283ba161c34 /src | |
parent | a8b8cd7d51cafea253f3e7b2e3d1e76054d97135 (diff) |
add snackbar and dialog
Diffstat (limited to 'src')
-rw-r--r-- | src/CustomAnalyzer.js | 20 | ||||
-rw-r--r-- | src/Dialog.js | 45 | ||||
-rw-r--r-- | src/Settings.js | 53 | ||||
-rw-r--r-- | src/Snackbar.js | 78 | ||||
-rw-r--r-- | src/gapi.js | 4 |
5 files changed, 193 insertions, 7 deletions
diff --git a/src/CustomAnalyzer.js b/src/CustomAnalyzer.js index f140151..62a8e39 100644 --- a/src/CustomAnalyzer.js +++ b/src/CustomAnalyzer.js @@ -18,6 +18,7 @@ import { msgType, MsgClient } from './msg'; import { Pattern, PatternEntry } from './pattern'; import PieChart from './Chart'; import PatternTable from './PatternTable'; +import Snackbar from './Snackbar'; const default_chart_data = [ {name: 'Work', value: 10, color: cyan[300]}, @@ -43,6 +44,9 @@ class CustomAnalyzer extends React.Component { endDate: null, patternGraphData: default_chart_data, calendarGraphData: default_chart_data, + snackBarOpen: false, + snackBarMsg: 'unknown', + snackBarVariant: 'error' }; constructor(props) { @@ -57,6 +61,8 @@ class CustomAnalyzer extends React.Component { this.msgClient.sendMsg({ type: msgType.getCalendars, data: { enabledOnly: true }}).then(msg => { this.setState({ calendars: msg.data }); }); + gapi.getLoggedIn().then(b => !b && + this.handleSnackbarOpen('Not logged in. Operating in offline mode.', 'warning')); } updatePattern = (field, idx, value) => { @@ -171,11 +177,25 @@ class CustomAnalyzer extends React.Component { this.setState({ startDate: null, endDate: null }); } + handleSnackbarClose = (event, reason) => { + if (reason === 'clickaway') return; + this.setState({ snackBarOpen: false }); + } + + handleSnackbarOpen = (msg, variant) => { + this.setState({ snackBarOpen: true, snackBarMsg: msg, snackBarVariant: variant }); + } + render() { const { classes } = this.props; return ( <Grid container spacing={16}> + <Snackbar + message={this.state.snackBarMsg} + open={this.state.snackBarOpen} + variant={this.state.snackBarVariant} + onClose={this.handleSnackbarClose}/> <Grid item md={6} xs={12}> <FormControl fullWidth={true}> <FormGroup> diff --git a/src/Dialog.js b/src/Dialog.js new file mode 100644 index 0000000..7e24176 --- /dev/null +++ b/src/Dialog.js @@ -0,0 +1,45 @@ +import React from 'react'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import Button from '@material-ui/core/Button'; +import Slide from '@material-ui/core/Slide'; + +// modified from https://material-ui.com/demos/dialogs/ + +function Transition(props) { + return <Slide direction="up" {...props} />; +} + +function AlertDialog(props) { + return ( + <Dialog + open={props.open} + TransitionComponent={Transition} + keepMounted + onClose={() => props.handleClose(false)} + aria-labelledby="alert-dialog-slide-title" + aria-describedby="alert-dialog-slide-description"> + <DialogTitle id="alert-dialog-slide-title"> + {props.title} + </DialogTitle> + <DialogContent> + <DialogContentText id="alert-dialog-slide-description"> + {props.message} + </DialogContentText> + </DialogContent> + <DialogActions> + <Button onClick={() => props.handleClose(false)} color="primary"> + No + </Button> + <Button onClick={() => props.handleClose(true)} color="primary"> + Yes + </Button> + </DialogActions> +</Dialog> + ); +} + +export default AlertDialog; diff --git a/src/Settings.js b/src/Settings.js index e071f9e..3865438 100644 --- a/src/Settings.js +++ b/src/Settings.js @@ -23,6 +23,8 @@ import * as gapi from './gapi'; import { msgType, MsgClient } from './msg'; import { Pattern, PatternEntry } from './pattern'; import PatternTable from './PatternTable'; +import Snackbar from './Snackbar'; +import AlertDialog from './Dialog'; const styles = theme => ({ tableHead: { @@ -32,6 +34,7 @@ const styles = theme => ({ }, tableContent: { textAlign: 'left', + maxWidth: 600, }, calendarList: { maxHeight: 400, @@ -57,6 +60,10 @@ class Settings extends React.Component { isLoggedIn: false, patterns: [], calendars: {}, + snackBarOpen: false, + snackBarMsg: 'unknown', + dialogOpen: false, + dialogMsg: {title: '', message: ''}, }; constructor(props) { @@ -72,19 +79,23 @@ class Settings extends React.Component { this.msgClient.sendMsg({ type: msgType.getCalendars, data: { enabledOnly: false } }).then(msg => { this.setState({ calendars: msg.data }); }); + this.dialogPromiseResolver = null; } handleLogin = () => { gapi.login().then(() => { this.setState({ isLoggedIn: true }); this.loadAll(true); - }); + }).catch(() => this.handleSnackbarOpen("Failed to login!")); } handleLogout = () => { - gapi.logout().then(() => { - this.setState({ isLoggedIn: false }); - this.loadPatterns([], 'analyze'); + this.handleDialogOpen("Logout", "Are you sure to logout?").then(ans => { + if (!ans) return; + gapi.logout().then(() => { + this.setState({ isLoggedIn: false }); + //this.loadPatterns([], 'analyze'); + }).catch(() => this.handleSnackbarOpen("Failed to logout!")); }); } @@ -167,10 +178,42 @@ class Settings extends React.Component { }).then(() => this.setState({ patterns })); }; + handleSnackbarClose = (event, reason) => { + if (reason === 'clickaway') return; + this.setState({ snackBarOpen: false }); + } + + handleSnackbarOpen = msg => { + this.setState({ snackBarOpen: true, snackBarMsg: msg }); + } + + handleDialogOpen = (title, message) => { + let pm = new Promise(resolver => { + this.dialogPromiseResolver = resolver + }); + this.setState({ dialogOpen: true, dialogMsg: {title, message} }); + return pm; + } + + handleDialogClose = result => { + this.dialogPromiseResolver(result); + this.setState({ dialogOpen: false }); + } + render() { const { classes } = this.props; return ( <div> + <AlertDialog + title={this.state.dialogMsg.title} + message={this.state.dialogMsg.message} + open={this.state.dialogOpen} + handleClose={this.handleDialogClose}/> + <Snackbar + message={this.state.snackBarMsg} + open={this.state.snackBarOpen} + variant='error' + onClose={this.handleSnackbarClose}/> <Typography variant="h6" component="h1" gutterBottom> General </Typography> @@ -178,7 +221,7 @@ class Settings extends React.Component { <TableBody> <TableRow> <STableCell className={classes.tableHead}>Account</STableCell> - <STableCell align='left'> + <STableCell className={classes.tableContent}> { (this.state.isLoggedIn && <Button variant="contained" color="primary" onClick={this.handleLogout}>Logout</Button>) || diff --git a/src/Snackbar.js b/src/Snackbar.js new file mode 100644 index 0000000..f17863c --- /dev/null +++ b/src/Snackbar.js @@ -0,0 +1,78 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { withStyles } from '@material-ui/core/styles'; +import amber from '@material-ui/core/colors/amber'; +import Snackbar from '@material-ui/core/Snackbar'; +import SnackbarContent from '@material-ui/core/SnackbarContent'; +import ErrorIcon from '@material-ui/icons/Error'; +import WarningIcon from '@material-ui/icons/Warning'; +import CloseIcon from '@material-ui/icons/Close'; +import IconButton from '@material-ui/core/IconButton'; + +// modified from https://material-ui.com/demos/snackbars/ + +const variantIcon = { + error: ErrorIcon, + warning: WarningIcon, +}; + +const styles = theme => ({ + error: { + backgroundColor: theme.palette.error.dark, + }, + warning: { + backgroundColor: amber[700], + }, + icon: { + fontSize: 20, + }, + iconVariant: { + opacity: 0.9, + marginRight: theme.spacing.unit, + }, + message: { + display: 'flex', + alignItems: 'center', + }, +}); + +function CustomSnackbar(props) { + const { classes, className, message, variant, open, onClose, ...other } = props; + const Icon = variantIcon[variant]; + return ( + <Snackbar + anchorOrigin={{ + vertical: 'top', + horizontal: 'center', + }} + open={open} + autoHideDuration={10000} + onClose={onClose}> + <SnackbarContent + className={classNames(classes[variant], className)} + aria-describedby="snackbar-content" + message={ + <span id="snackbar-content" className={classes.message}> + <Icon className={classNames(classes.icon, classes.iconVariant)} /> + {message} + </span> + } + action={[ + <IconButton + key="close" + aria-label="Close" + color="inherit" + className={classes.close} + onClick={onClose} + > + <CloseIcon className={classes.icon} /> + </IconButton>, + ]} + {...other} + /> + </Snackbar> + ); +} + +export default withStyles(styles)(CustomSnackbar); diff --git a/src/gapi.js b/src/gapi.js index 60e9b1a..36af906 100644 --- a/src/gapi.js +++ b/src/gapi.js @@ -34,8 +34,8 @@ export function getLoggedIn() { if (loggedIn === null) { return _getAuthToken(false) - .then(() => {loggedIn = true}) - .catch(() => {loggedIn = false; console.log("here");}) + .then(() => loggedIn = true) + .catch(() => loggedIn = false) .then(() => loggedIn); } else return Promise.resolve(loggedIn); |