aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDeterminant <[email protected]>2019-02-15 23:02:30 -0500
committerDeterminant <[email protected]>2019-02-15 23:02:30 -0500
commit8d9f2e9ccc54c17027c01d7fee8ac67c51044655 (patch)
tree7be4adfea67980ead1c139b4f719159a036fc40d /src
parenta420162e3c679604ba06bfbb055b1e14fbb48b8e (diff)
clean up code
Diffstat (limited to 'src')
-rw-r--r--src/Analyze.tsx75
-rw-r--r--src/Chart.tsx30
-rw-r--r--src/Dashboard.tsx8
-rw-r--r--src/Dialog.tsx43
-rw-r--r--src/Settings.tsx113
-rw-r--r--src/Snackbar.tsx4
-rw-r--r--src/background.ts8
-rw-r--r--src/duration.ts4
-rw-r--r--src/gapi.ts74
-rw-r--r--src/graph.ts133
-rw-r--r--src/pattern.ts9
11 files changed, 264 insertions, 237 deletions
diff --git a/src/Analyze.tsx b/src/Analyze.tsx
index d3abcf4..9339289 100644
--- a/src/Analyze.tsx
+++ b/src/Analyze.tsx
@@ -1,12 +1,10 @@
import React from 'react';
-import PropTypes from 'prop-types';
import 'react-dates/initialize';
import 'react-dates/lib/css/_datepicker.css';
-import { DateRangePicker } from 'react-dates';
+import { DateRangePicker, FocusedInputShape } from 'react-dates';
import { Theme, withStyles } from '@material-ui/core/styles';
import cyan from '@material-ui/core/colors/cyan';
import deepOrange from '@material-ui/core/colors/deepOrange';
-import CssBaseline from '@material-ui/core/CssBaseline';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import FormControl from '@material-ui/core/FormControl';
@@ -14,15 +12,16 @@ import FormGroup from '@material-ui/core/FormGroup';
import Grid from '@material-ui/core/Grid';
import AddCircleIcon from '@material-ui/icons/AddCircle';
import IconButton from '@material-ui/core/IconButton';
+import moment from 'moment';
+
+import PatternTable from './PatternTable';
+import AlertDialog from './Dialog';
+import Snackbar, { SnackbarVariant } from './Snackbar';
import * as gapi from './gapi';
import { MsgType, MsgClient } from './msg';
import { Pattern, PatternEntry, PatternEntryFlat } from './pattern';
import { AnalyzePieChart } from './Chart';
import { getGraphData } from './graph';
-import PatternTable from './PatternTable';
-import Snackbar, { Variant } from './Snackbar';
-import AlertDialog from './Dialog';
-import moment from 'moment';
const defaultChartData = [
{name: 'Work', value: 10, color: cyan[300]},
@@ -34,7 +33,11 @@ const styles = (theme: Theme) => ({
},
});
-class Analyze extends React.Component<{classes: {buttonSpacer: string}}> {
+type AnalyzeProps = {
+ classes: { buttonSpacer: string }
+};
+
+class Analyze extends React.Component<AnalyzeProps> {
msgClient: MsgClient;
dialogPromiseResolver: (r: boolean) => void;
@@ -47,13 +50,13 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> {
calendarGraphData: defaultChartData,
snackBarOpen: false,
snackBarMsg: 'unknown',
- snackBarVariant: 'error' as Variant,
+ snackBarVariant: 'error' as SnackbarVariant,
dialogOpen: false,
dialogMsg: {title: '', message: ''},
- focusedInput: null as any
+ focusedInput: null as FocusedInputShape
};
- constructor(props: any) {
+ constructor(props: AnalyzeProps) {
super(props);
this.msgClient = new MsgClient('main');
@@ -75,12 +78,13 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> {
});
gapi.getLoggedIn().then(b => !b &&
- this.handleSnackbarOpen('Not logged in. Operating in offline mode.', 'warning'));
+ this.openSnackbar('Not logged in. Operating in offline mode.',
+ 'warning' as SnackbarVariant));
this.dialogPromiseResolver = null;
}
- loadPatterns = (patterns: PatternEntry[]) => {
+ loadPatterns(patterns: PatternEntry[]) {
this.msgClient.sendMsg({
opt: MsgType.updatePatterns,
data: { id: 'analyze', patterns: patterns.map(p => p.deflate()) }
@@ -109,7 +113,7 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> {
this.loadPatterns(patterns);
};
- getCalEvents = async (id: string, start: Date, end: Date): Promise<gapi.GCalendarEvent[]> => {
+ async getCalEvents(id: string, start: Date, end: Date): Promise<gapi.GCalendarEvent[]> {
let { data } = await this.msgClient.sendMsg({
opt: MsgType.getCalEvents,
data: { id,
@@ -121,19 +125,20 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> {
));
}
- analyze = () => {
+ analyze = async () => {
if (!(this.state.startDate && this.state.endDate)) {
- this.handleSnackbarOpen('Please choose a valid time range.', 'error');
+ this.openSnackbar('Please choose a valid time range.',
+ 'error' as SnackbarVariant);
return;
}
let start = this.state.startDate.startOf('day').toDate();
let end = this.state.endDate.startOf('day').toDate();
- getGraphData(start, end,
+ let r = await getGraphData(start, end,
this.state.patterns,
this.state.calendars,
- this.getCalEvents).then(results => {
- this.setState(results);
- });
+ this.getCalEvents);
+ this.setState({ patternGraphData: r.patternGraphData,
+ calendarGraphData: r.calendarGraphData });
}
reset = () => {
@@ -155,23 +160,17 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> {
Pattern.anyPattern(),
cal.color));
}
- console.log(patterns);
this.loadPatterns(patterns);
}
- default = () => {
+ loadDefault = () => {
this.openDialog("Load Default", "Load the calendars as patterns?").then(ans => {
if (!ans) return;
this.loadDefaultPatterns();
});
}
- handleSnackbarClose = (event: React.SyntheticEvent<{}>, reason: string) => {
- if (reason === 'clickaway') return;
- this.setState({ snackBarOpen: false });
- }
-
- handleSnackbarOpen = (msg: string, variant: any) => {
+ openSnackbar(msg: string, variant: SnackbarVariant) {
this.setState({ snackBarOpen: true, snackBarMsg: msg, snackBarVariant: variant });
}
@@ -179,12 +178,17 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> {
let pm = new Promise(resolver => {
this.dialogPromiseResolver = resolver
});
- this.setState({ dialogOpen: true, dialogMsg: {title, message} });
+ this.setState({ dialogOpen: true, dialogMsg: { title, message } });
return pm;
}
- handleDialogClose = (result: boolean) => {
- this.dialogPromiseResolver(result);
+ handleSnackbarClose = (event: React.SyntheticEvent<{}>, reason: string) => {
+ if (reason === 'clickaway') return;
+ this.setState({ snackBarOpen: false });
+ }
+
+ handleDialogClose = (ans: boolean) => {
+ this.dialogPromiseResolver(ans);
this.setState({ dialogOpen: false });
}
@@ -228,12 +232,9 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> {
startDateId="start_date_id"
endDate={this.state.endDate}
endDateId="end_date_id"
- onDatesChange={({ startDate, endDate }:
- { startDate: moment.Moment, endDate: moment.Moment }) => {
- this.setState({ startDate, endDate });
- }}
+ onDatesChange={({ startDate, endDate }) => this.setState({ startDate, endDate })}
focusedInput={this.state.focusedInput}
- onFocusChange={(focusedInput: any) => this.setState({ focusedInput })}
+ onFocusChange={focusedInput => this.setState({ focusedInput })}
isOutsideRange={() => false} />
</div>
</FormGroup>
@@ -241,7 +242,7 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> {
<Grid container spacing={16}>
<Grid item md={4} xs={12}>
<FormGroup>
- <Button variant="contained" color="primary" onClick={this.default}>Load Default</Button>
+ <Button variant="contained" color="primary" onClick={this.loadDefault}>Load Default</Button>
</FormGroup>
</Grid>
<Grid item md={4} xs={12}>
diff --git a/src/Chart.tsx b/src/Chart.tsx
index c7b59c4..e17bc4e 100644
--- a/src/Chart.tsx
+++ b/src/Chart.tsx
@@ -1,5 +1,4 @@
import React from 'react';
-import PropTypes from 'prop-types';
import { Theme, withStyles } from '@material-ui/core/styles';
import Grid from '@material-ui/core/Grid';
import cyan from '@material-ui/core/colors/cyan';
@@ -13,9 +12,10 @@ const styles = (theme: Theme) => ({
}
});
-function customizedLabel(props: {cx: number, cy: number,
- x: number, y: number,
- fill: string, name: string}) {
+function customizedLabel(props: {
+ cx: number, cy: number,
+ x: number, y: number,
+ fill: string, name: string}) {
const {cx, cy, x, y, fill, name} = props;
let anchor = "middle";
const EPS = 2;
@@ -38,12 +38,15 @@ function customizedLabel(props: {cx: number, cy: number,
return (<text x={x} y={y} dx={dx} dy={dy} fill={fill} textAnchor={anchor}>{`${name}`}</text>);
}
-function PatternPieChart(props: {
- classes: {
- patternTableWrapper: string,
- pieChart: string
- },
- data: PatternGraphData[] }) {
+type PatternPieChartProps = {
+ classes: {
+ patternTableWrapper: string,
+ pieChart: string
+ },
+ data: PatternGraphData[]
+};
+
+function PatternPieChart(props: PatternPieChartProps) {
return (
<Grid item xs={12} lg={6}>
<div className={props.classes.patternTableWrapper}>
@@ -67,13 +70,16 @@ function PatternPieChart(props: {
export const StyledPatternPieChart = withStyles(styles)(PatternPieChart);
-function DoublePieChart(props: {
+type DoublePieChartProps = {
classes: {
patternTableWrapper: string,
pieChart: string
},
patternGraphData: PatternGraphData[],
- calendarGraphData: PatternGraphData[] }) {
+ calendarGraphData: PatternGraphData[]
+};
+
+function DoublePieChart(props: DoublePieChartProps) {
return (
<Grid container spacing={0}>
<StyledPatternPieChart data={props.patternGraphData} />
diff --git a/src/Dashboard.tsx b/src/Dashboard.tsx
index 10f727e..ab26666 100644
--- a/src/Dashboard.tsx
+++ b/src/Dashboard.tsx
@@ -1,19 +1,18 @@
import React from 'react';
-import PropTypes from 'prop-types';
import 'typeface-roboto';
import { Theme, withStyles, MuiThemeProvider } from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
-import Paper from '@material-ui/core/Paper';
import Tabs from '@material-ui/core/Tabs';
import Tab, { TabProps } from '@material-ui/core/Tab';
import { LinkProps } from '@material-ui/core/Link';
import Grid from '@material-ui/core/Grid';
import { HashRouter as Router, RouteComponentProps, withRouter, Route, Link, Redirect, Switch } from "react-router-dom";
-import Logo from './Logo';
+
import { theme } from './theme';
+import Logo from './Logo';
import Analyze from './Analyze';
import Settings from './Settings';
@@ -56,7 +55,6 @@ interface DashboardTabsProps extends RouteComponentProps {
};
}
-
class DashboardTabs extends React.Component<DashboardTabsProps> {
handleChangeTab = (event: React.SyntheticEvent<{}>, currentTab: any) => {
this.props.history.push(currentTab);
@@ -93,7 +91,7 @@ class DashboardTabs extends React.Component<DashboardTabsProps> {
}
}
-class Dashboard extends React.Component<{}> {
+class Dashboard extends React.Component {
render() {
let Tabs = withRouter(withStyles(styles)(DashboardTabs));
return (
diff --git a/src/Dialog.tsx b/src/Dialog.tsx
index df72144..984ecbb 100644
--- a/src/Dialog.tsx
+++ b/src/Dialog.tsx
@@ -13,19 +13,21 @@ function Transition(props: any) {
return <Slide direction="up" {...props} />;
}
-function AlertDialog(props: {
- open: boolean,
- handleClose: (r: boolean) => any,
- title: string,
- message: string}) {
+type AlertDialogProps = {
+ open: boolean,
+ handleClose: (r: boolean) => any,
+ title: string,
+ message: string
+};
+
+function AlertDialog(props: AlertDialogProps) {
return (
- <Dialog
- open={props.open}
- TransitionComponent={Transition}
- keepMounted
- onClose={() => props.handleClose(false)}
- aria-labelledby="alert-dialog-slide-title"
- aria-describedby="alert-dialog-slide-description">
+ <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>
@@ -35,15 +37,14 @@ function AlertDialog(props: {
</DialogContentText>
</DialogContent>
<DialogActions>
- <Button onClick={() => props.handleClose(false)} color="primary">
- No
- </Button>
- <Button onClick={() => props.handleClose(true)} color="primary">
- Yes
- </Button>
- </DialogActions>
-</Dialog>
- );
+ <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.tsx b/src/Settings.tsx
index bce962d..ecef23b 100644
--- a/src/Settings.tsx
+++ b/src/Settings.tsx
@@ -1,12 +1,9 @@
import React from 'react';
-import PropTypes from 'prop-types';
import { Theme, withStyles, StyleRules } from '@material-ui/core/styles';
-import CssBaseline from '@material-ui/core/CssBaseline';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import FormControl from '@material-ui/core/FormControl';
import FormGroup from '@material-ui/core/FormGroup';
-import Grid from '@material-ui/core/Grid';
import RefreshIcon from '@material-ui/icons/Refresh';
import AddCircleIcon from '@material-ui/icons/AddCircle';
import IconButton from '@material-ui/core/IconButton';
@@ -19,15 +16,16 @@ import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import Checkbox from '@material-ui/core/Checkbox';
-import * as gapi from './gapi';
-import { MsgType, MsgClient } from './msg';
-import { Pattern, PatternEntry, PatternEntryFlat } from './pattern';
-import PatternTable from './PatternTable';
-import Snackbar from './Snackbar';
-import AlertDialog from './Dialog';
import TextField from '@material-ui/core/TextField';
import MenuItem from '@material-ui/core/MenuItem';
import Select from '@material-ui/core/Select';
+
+import PatternTable from './PatternTable';
+import Snackbar from './Snackbar';
+import AlertDialog from './Dialog';
+import * as gapi from './gapi';
+import { MsgType, MsgClient } from './msg';
+import { Pattern, PatternEntry, PatternEntryFlat } from './pattern';
import { Duration, TrackPeriod, TrackPeriodFlat } from './duration';
const styles = (theme: Theme): StyleRules => ({
@@ -59,14 +57,16 @@ const CompactListItem = withStyles(theme => ({
},
}))(ListItem);
-class TrackedPeriod extends React.Component<{
- name: string
- fromDuration: Duration,
- toDuration: Duration,
- nameOnChange: (name: string) => void,
- fromOnChange: (d: Duration) => void,
- toOnChange: (d: Duration) => void
- }>{
+type TrackedPeriodProps = {
+ name: string
+ fromDuration: Duration,
+ toDuration: Duration,
+ nameOnChange: (name: string) => void,
+ fromOnChange: (d: Duration) => void,
+ toOnChange: (d: Duration) => void
+};
+
+class TrackedPeriod extends React.Component<TrackedPeriodProps> {
valueOnChange = (old: Duration, onChange: (d: Duration) => void) => (event: any) => {
onChange(new Duration(event.target.value, old.unit));
}
@@ -93,12 +93,18 @@ class TrackedPeriod extends React.Component<{
}
render() {
- let { fromDuration, toDuration, nameOnChange, fromOnChange, toOnChange, name } = this.props;
+ let {
+ fromDuration, toDuration,
+ nameOnChange, name,
+ fromOnChange, toOnChange
+ } = this.props;
+
let units = [
<MenuItem key='days' value='days'>Day(s)</MenuItem>,
<MenuItem key='weeks' value='weeks'>Week(s)</MenuItem>,
<MenuItem key='months' value='months'>Month(s)</MenuItem>
];
+
return (
<span>
<TextField
@@ -124,14 +130,15 @@ class TrackedPeriod extends React.Component<{
}
}
-class Settings extends React.Component<{
- classes: {
- tableHead: string,
- tableContent: string,
- calendarList: string,
- }
- }> {
+type SettingsProps = {
+ classes: {
+ tableHead: string,
+ tableContent: string,
+ calendarList: string
+ }
+};
+class Settings extends React.Component<SettingsProps> {
msgClient: MsgClient;
dialogPromiseResolver: (r: boolean) => void;
@@ -147,7 +154,7 @@ class Settings extends React.Component<{
calendarsLoading: false,
};
- constructor(props: any) {
+ constructor(props: SettingsProps) {
super(props);
gapi.getLoggedIn().then(b => this.setState({ isLoggedIn: b }));
@@ -157,7 +164,9 @@ class Settings extends React.Component<{
opt: MsgType.getPatterns,
data: { id: 'main' }
}).then(msg => {
- this.setState({ patterns: msg.data.map((p: PatternEntryFlat) => PatternEntry.inflate(p)) });
+ this.setState({
+ patterns: msg.data.map((p: PatternEntryFlat) => PatternEntry.inflate(p))
+ });
});
this.msgClient.sendMsg({
@@ -183,24 +192,28 @@ class Settings extends React.Component<{
this.dialogPromiseResolver = null;
}
- handleLogin = () => {
- gapi.login().then(() => {
+ handleLogin = async () => {
+ try {
+ await gapi.login();
this.setState({ isLoggedIn: true });
this.loadAll(true);
- }).catch(() => this.handleSnackbarOpen("Failed to login!"));
+ } catch (_) {
+ this.handleSnackbarOpen("Failed to login!");
+ }
}
- handleLogout = () => {
- 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!"));
- });
+ handleLogout = async () => {
+ let ans = await this.handleDialogOpen("Logout", "Are you sure to logout?");
+ if (!ans) return;
+ try {
+ await gapi.logout();
+ this.setState({ isLoggedIn: false });
+ } catch (_) {
+ this.handleSnackbarOpen("Failed to logout!");
+ }
}
- handleToggleCalendar = (id: string) => {
+ toggleCalendar(id: string) {
var calendars = {...this.state.calendars};
calendars[id].enabled = !calendars[id].enabled;
this.msgClient.sendMsg({
@@ -212,9 +225,7 @@ class Settings extends React.Component<{
async loadAll(loadPatterns = false) {
await new Promise(resolver => (this.setState({ calendarsLoading: true }, resolver)));
- let pm_colors = gapi.getAuthToken().then(gapi.getColors).then(color => {
- return color.calendar;
- });
+ let pm_colors = gapi.getAuthToken().then(gapi.getColors).then(color => color.calendar);
let pm_cals = gapi.getAuthToken().then(gapi.getCalendars);
let [colors, _cals] = await Promise.all([pm_colors, pm_cals]);
var cals: { [id: string]: gapi.GCalendarMeta } = {};
@@ -225,8 +236,11 @@ class Settings extends React.Component<{
enabled: true
};
});
- this.loadCalendars(cals);
- if (loadPatterns) this.loadDefaultPatterns();
+
+ let pms = [this.loadCalendars(cals)];
+ if (loadPatterns)
+ pms.push(this.loadDefaultPatterns());
+ await Promise.all(pms);
this.setState({ calendarsLoading: false });
};
@@ -241,11 +255,10 @@ class Settings extends React.Component<{
Pattern.anyPattern(),
cal.color));
}
- console.log(patterns);
this.loadPatterns(patterns, 'main');
}
- loadCalendars = (calendars: {[ id: string ]: gapi.GCalendarMeta }) => {
+ loadCalendars(calendars: {[ id: string ]: gapi.GCalendarMeta }) {
for (let id in this.state.calendars) {
if (calendars.hasOwnProperty(id))
calendars[id].enabled = this.state.calendars[id].enabled;
@@ -254,14 +267,14 @@ class Settings extends React.Component<{
opt: MsgType.updateCalendars,
data: calendars
}).then(() => this.setState({ calendars }));
- };
+ }
- loadPatterns = (patterns: PatternEntry[], id: string) => {
+ loadPatterns(patterns: PatternEntry[], id: string) {
this.msgClient.sendMsg({
opt: MsgType.updatePatterns,
data: { id, patterns: patterns.map(p => p.deflate()) }
}).then(() => this.setState({ patterns }));
- };
+ }
updatePattern = (field: string, idx: number, value: any) => {
let patterns = this.state.patterns;
@@ -376,7 +389,7 @@ class Settings extends React.Component<{
{Object.keys(this.state.calendars).map(id =>
<CompactListItem
key={id}
- onClick={() => this.handleToggleCalendar(id)}
+ onClick={() => this.toggleCalendar(id)}
disableGutters
dense button >
<Checkbox
diff --git a/src/Snackbar.tsx b/src/Snackbar.tsx
index 15ebb38..f700422 100644
--- a/src/Snackbar.tsx
+++ b/src/Snackbar.tsx
@@ -37,7 +37,7 @@ const styles = (theme: Theme) => ({
},
});
-export type Variant = 'error' | 'warning';
+export type SnackbarVariant = 'error' | 'warning';
function CustomSnackbar(props: {
classes: {
@@ -48,7 +48,7 @@ function CustomSnackbar(props: {
iconVariant: string,
close: string
},
- variant: Variant,
+ variant: SnackbarVariant,
className?: string,
open: boolean,
message: string,
diff --git a/src/background.ts b/src/background.ts
index 356ed0d..1a0c1eb 100644
--- a/src/background.ts
+++ b/src/background.ts
@@ -22,7 +22,7 @@ let dirtyMetadata = false;
function loadMetadata() {
return new Promise(resolver => chrome.storage.local.get([
'calendars', 'config', 'mainPatterns', 'analyzePatterns',
- ], function(items) {
+ ], items => {
if (chrome.runtime.lastError)
console.error("error while loading saved metadata");
else if (!items.hasOwnProperty('config'))
@@ -53,7 +53,7 @@ function saveMetadata() {
},
mainPatterns: mainPatterns.map(p => p.deflate()),
analyzePatterns: analyzePatterns.map(p => p.deflate())
- }, function() {
+ }, () => {
console.log('metadata saved');
resolver();
}));
@@ -98,12 +98,12 @@ function updateMainGraphData() {
pms.push(getGraphData(
start.toDate(), end.toDate(), mainPatterns, calendars,
getCalEvents
- ).then(results => {
+ ).then(r => {
mainGraphData[i] = {
name: p.name,
start: start.toDate(),
end: end.toDate(),
- data: results.patternGraphData
+ data: r.patternGraphData
};
}));
}
diff --git a/src/duration.ts b/src/duration.ts
index cb25107..90a1d10 100644
--- a/src/duration.ts
+++ b/src/duration.ts
@@ -57,6 +57,8 @@ export class TrackPeriod {
}
static inflate = (obj: TrackPeriodFlat) => (
- new TrackPeriod(obj.name, Duration.inflate(obj.start), Duration.inflate(obj.end))
+ new TrackPeriod(obj.name,
+ Duration.inflate(obj.start),
+ Duration.inflate(obj.end))
);
}
diff --git a/src/gapi.ts b/src/gapi.ts
index e840f0e..d601e64 100644
--- a/src/gapi.ts
+++ b/src/gapi.ts
@@ -3,6 +3,7 @@
import LRU from "lru-cache";
const gapiBase = 'https://www.googleapis.com/calendar/v3';
+let loggedIn: boolean = null;
enum GApiError {
invalidSyncToken = "invalidSyncToken",
@@ -11,23 +12,20 @@ enum GApiError {
otherError = "otherError",
}
-function to_params(dict: Object) {
+function toParams(dict: Object) {
return Object.entries(dict).filter(([k, v] : string[]) => v)
.map(([k, v]: string[]) => (
`${encodeURIComponent(k)}=${encodeURIComponent(v)}`
)).join('&');
}
-let loggedIn: boolean = null;
-
-function _getAuthToken(interactive = false): Promise<string> {
- return new Promise(resolver =>
+async function _getAuthToken(interactive = false): Promise<string> {
+ let [token, ok]: [string, boolean] = await new Promise(resolver =>
chrome.identity.getAuthToken(
- { interactive }, token => resolver([token, !chrome.runtime.lastError])))
- .then(([token, ok] : [string, boolean]) => {
- if (ok) return token;
- else throw GApiError.notLoggedIn;
- });
+ { interactive },
+ token => resolver([token, !chrome.runtime.lastError])));
+ if (ok) return token;
+ else throw GApiError.notLoggedIn;
}
function _removeCachedAuthToken(token: string) {
@@ -35,35 +33,39 @@ function _removeCachedAuthToken(token: string) {
chrome.identity.removeCachedAuthToken({ token }, () => resolver()));
}
-export function getLoggedIn() {
+export async function getLoggedIn(): Promise<boolean> {
if (loggedIn === null)
{
- return _getAuthToken(false)
- .then(() => loggedIn = true)
- .catch(() => loggedIn = false)
- .then(() => loggedIn);
+ try {
+ await _getAuthToken(false);
+ loggedIn = true;
+ } catch(_) {
+ loggedIn = false;
+ }
}
- else return Promise.resolve(loggedIn);
+ return loggedIn;
}
-export function getAuthToken(): Promise<string> {
- return getLoggedIn().then(b => {
- if (b) return _getAuthToken(false);
- else throw GApiError.notLoggedIn;
- });
+export async function getAuthToken(): Promise<string> {
+ let b = await getLoggedIn();
+ if (b) return _getAuthToken(false);
+ else throw GApiError.notLoggedIn;
}
-export function login() {
- return getLoggedIn().then(b => {
- if (!b) return _getAuthToken(true).then(() => loggedIn = true);
- else throw GApiError.notLoggedOut;
- });
+export async function login(): Promise<void> {
+ let b = await getLoggedIn();
+ if (!b) {
+ await _getAuthToken(true);
+ loggedIn = true;
+ }
+ else throw GApiError.notLoggedOut;
}
-export async function logout() {
+export async function logout(): Promise<void> {
let token = await getAuthToken();
let response = await fetch(
- `https://accounts.google.com/o/oauth2/revoke?${to_params({ token })}`, { method: 'GET' });
+ `https://accounts.google.com/o/oauth2/revoke?${toParams({ token })}`,
+ { method: 'GET' });
//if (response.status === 200)
await _removeCachedAuthToken(token);
//else throw GApiError.otherError;
@@ -82,19 +84,21 @@ export type GCalendarMeta = {
export async function getCalendars(token: string): Promise<any> {
let response = await fetch(
- `${gapiBase}/users/me/calendarList?${to_params({access_token: token})}`, { method: 'GET' });
+ `${gapiBase}/users/me/calendarList?${toParams({access_token: token})}`,
+ { method: 'GET' });
return (await response.json()).items;
}
-export async function getColors(token: string) {
+export async function getColors(token: string): Promise<any> {
let response = await fetch(
- `${gapiBase}/colors?${to_params({access_token: token})}`, { method: 'GET' });
+ `${gapiBase}/colors?${toParams({access_token: token})}`,
+ { method: 'GET' });
return response.json();
}
-async function getEvent(calId: string, eventId: string, token: string) {
+async function getEvent(calId: string, eventId: string, token: string): Promise<any> {
let response = await fetch(
- `${gapiBase}/calendars/${calId}/events/${eventId}?${to_params({access_token: token})}`,
+ `${gapiBase}/calendars/${calId}/events/${eventId}?${toParams({access_token: token})}`,
{ method: 'GET' });
return response.json();
}
@@ -104,11 +108,11 @@ function getEvents(calId: string, token: string,
timeMin=null as string,
timeMax=null as string,
resultsPerRequest=100 as number):
- Promise<{ results: any[], nextSyncToken: string }> {
+ Promise<{ results: any[], nextSyncToken: string }> {
let results = [] as any[];
const singleFetch = async (pageToken: string, syncToken: string):
Promise<{nextSyncToken: string, results: any[]}> => {
- let response = await fetch(`${gapiBase}/calendars/${calId}/events?${to_params({
+ let response = await fetch(`${gapiBase}/calendars/${calId}/events?${toParams({
access_token: token,
pageToken,
syncToken,
diff --git a/src/graph.ts b/src/graph.ts
index d5a65b2..8b385a3 100644
--- a/src/graph.ts
+++ b/src/graph.ts
@@ -1,6 +1,6 @@
+import { defaultChartColor } from './theme';
import { GCalendarEvent, GCalendarMeta } from './gapi';
import { PatternEntry } from './pattern';
-import { defaultChartColor} from './theme';
export type PatternGraphData = {
name: string,
@@ -15,11 +15,13 @@ export type GraphData = {
data: PatternGraphData[]
};
-export function getGraphData(
+export async function getGraphData(
start: Date, end: Date,
patterns: PatternEntry[], calendars: { [id: string]: GCalendarMeta },
- calEventsGetter: (id: string, start: Date, end: Date) => Promise<GCalendarEvent[]>) {
- if (start >= end) return Promise.resolve({ patternGraphData: [], calendarGraphData: [] });
+ calEventsGetter: (id: string, start: Date, end: Date) => Promise<GCalendarEvent[]>):
+ Promise<{ patternGraphData: PatternGraphData[], calendarGraphData: PatternGraphData[] }> {
+
+ if (start >= end) return { patternGraphData: [], calendarGraphData: [] };
let event_pms = [];
for (let id in calendars)
{
@@ -29,69 +31,68 @@ export function getGraphData(
event_pms.push(calEventsGetter(id, start, end)
.then(r => { return { id, events: r, filtered }; }));
}
- return Promise.all(event_pms).then(all_events => {
- let events: {[id: string]: GCalendarEvent[]} = {};
- let patternsByCal: {[id: string]: PatternEntry[]} = {};
- let results: {[idx: number]: number} = {};
- let cal_results: {[id: string]: number} = {};
+ let all_events = await Promise.all(event_pms);
- all_events.forEach(e => {
- events[e.id] = e.events;
- patternsByCal[e.id] = e.filtered;
- });
+ let events: {[id: string]: GCalendarEvent[]} = {};
+ let patternsByCal: {[id: string]: PatternEntry[]} = {};
+ let results: {[idx: number]: number} = {};
+ let cal_results: {[id: string]: number} = {};
- for (let i = 0; i < patterns.length; i++)
- results[i] = 0;
- for (let id in calendars) {
- if (!events[id]) continue;
- events[id].forEach(event => {
- patternsByCal[id].forEach(p => {
- if (!p.event.regex.test(event.summary)) {
- console.log(event.summary, p.event.regex);
- return;
- }
- if (!cal_results.hasOwnProperty(id)) {
- cal_results[id] = 0;
- }
- let duration = (event.end.getTime() - event.start.getTime()) / 60000;
- results[p.idx] += duration;
- cal_results[id] += duration;
- });
- });
- }
- let patternGraphData = [];
- let calendarGraphData = [];
- const filterMarginal = (data: PatternGraphData[]) => {
- let sum = 0;
- let majorParts = [];
- let minorSum = 0;
- data.forEach(d => sum += d.value);
- data.forEach(d => {
- let ratio = d.value / sum;
- if (ratio < 1e-2) minorSum += d.value;
- else majorParts.push(d);
- });
- majorParts.push({
- name: 'Other',
- value: minorSum,
- color: defaultChartColor,
- });
- return majorParts;
- };
- for (let i = 0; i < patterns.length; i++) {
- patternGraphData.push({
- name: patterns[i].name,
- value: results[i] / 60.0,
- color: patterns[i].color.background});
- }
- for (let id in cal_results) {
- calendarGraphData.push({
- name: calendars[id].name,
- value: (cal_results[id] / 60.0),
- color: calendars[id].color.background});
- }
- return {start, end,
- patternGraphData: filterMarginal(patternGraphData),
- calendarGraphData: filterMarginal(calendarGraphData) };
+ all_events.forEach(e => {
+ events[e.id] = e.events;
+ patternsByCal[e.id] = e.filtered;
});
+
+ for (let i = 0; i < patterns.length; i++)
+ results[i] = 0;
+ for (let id in calendars) {
+ if (!events[id]) continue;
+ events[id].forEach(event => {
+ patternsByCal[id].forEach(p => {
+ if (!p.event.regex.test(event.summary)) {
+ console.log(event.summary, p.event.regex);
+ return;
+ }
+ if (!cal_results.hasOwnProperty(id)) {
+ cal_results[id] = 0;
+ }
+ let duration = (event.end.getTime() - event.start.getTime()) / 60000;
+ results[p.idx] += duration;
+ cal_results[id] += duration;
+ });
+ });
+ }
+ let patternGraphData = [];
+ let calendarGraphData = [];
+ const filterMarginal = (data: PatternGraphData[]) => {
+ let sum = 0;
+ let majorParts = [];
+ let minorSum = 0;
+ data.forEach(d => sum += d.value);
+ data.forEach(d => {
+ let ratio = d.value / sum;
+ if (ratio < 1e-2) minorSum += d.value;
+ else majorParts.push(d);
+ });
+ majorParts.push({
+ name: 'Other',
+ value: minorSum,
+ color: defaultChartColor,
+ });
+ return majorParts;
+ };
+ for (let i = 0; i < patterns.length; i++) {
+ patternGraphData.push({
+ name: patterns[i].name,
+ value: results[i] / 60.0,
+ color: patterns[i].color.background});
+ }
+ for (let id in cal_results) {
+ calendarGraphData.push({
+ name: calendars[id].name,
+ value: (cal_results[id] / 60.0),
+ color: calendars[id].color.background});
+ }
+ return {patternGraphData: filterMarginal(patternGraphData),
+ calendarGraphData: filterMarginal(calendarGraphData) };
}
diff --git a/src/pattern.ts b/src/pattern.ts
index d067bf4..d21710f 100644
--- a/src/pattern.ts
+++ b/src/pattern.ts
@@ -77,8 +77,9 @@ export class PatternEntry {
Pattern.emptyPattern(),
Pattern.anyPattern(), {background: null}));
- static inflate = (obj: PatternEntryFlat) => new PatternEntry(
- obj.name, obj.idx,
- Pattern.inflate(obj.cal), Pattern.inflate(obj.event),
- obj.color);
+ static inflate = (obj: PatternEntryFlat) => (
+ new PatternEntry(obj.name, obj.idx,
+ Pattern.inflate(obj.cal),
+ Pattern.inflate(obj.event), obj.color)
+ );
}