aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CustomAnalyzer.js20
-rw-r--r--src/Dialog.js45
-rw-r--r--src/Settings.js53
-rw-r--r--src/Snackbar.js78
-rw-r--r--src/gapi.js4
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);