aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDeterminant <ted.sybil@gmail.com>2019-02-14 23:59:16 -0500
committerDeterminant <ted.sybil@gmail.com>2019-02-14 23:59:16 -0500
commit495e0d4518871e950afcd38d7d3afeb5477076dc (patch)
treefd2bc9f49340a1c7f4f530e6aaa6607d4a4efacf /src
parent6738cb883c37f2e388f1c86c84bd4c34a98699bd (diff)
finish migration
Diffstat (limited to 'src')
-rw-r--r--src/Analyze.tsx6
-rw-r--r--src/PatternTable.tsx14
-rw-r--r--src/RegexField.tsx51
-rw-r--r--src/Settings.tsx84
-rw-r--r--src/Snackbar.tsx25
-rw-r--r--src/background.ts19
-rw-r--r--src/duration.ts4
-rw-r--r--src/gapi.ts4
-rw-r--r--src/graph.ts5
-rw-r--r--src/msg.ts5
10 files changed, 121 insertions, 96 deletions
diff --git a/src/Analyze.tsx b/src/Analyze.tsx
index 8f55bd8..d3abcf4 100644
--- a/src/Analyze.tsx
+++ b/src/Analyze.tsx
@@ -20,7 +20,7 @@ import { Pattern, PatternEntry, PatternEntryFlat } from './pattern';
import { AnalyzePieChart } from './Chart';
import { getGraphData } from './graph';
import PatternTable from './PatternTable';
-import Snackbar from './Snackbar';
+import Snackbar, { Variant } from './Snackbar';
import AlertDialog from './Dialog';
import moment from 'moment';
@@ -47,7 +47,7 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> {
calendarGraphData: defaultChartData,
snackBarOpen: false,
snackBarMsg: 'unknown',
- snackBarVariant: 'error',
+ snackBarVariant: 'error' as Variant,
dialogOpen: false,
dialogMsg: {title: '', message: ''},
focusedInput: null as any
@@ -114,7 +114,7 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> {
opt: MsgType.getCalEvents,
data: { id,
start: start.getTime(),
- end: end.getTime() }
+ end: end.getTime() }
});
return data.map((_e: gapi.GCalendarEventFlat) => (
gapi.GCalendarEvent.inflate(_e)
diff --git a/src/PatternTable.tsx b/src/PatternTable.tsx
index 60acd3c..2b05287 100644
--- a/src/PatternTable.tsx
+++ b/src/PatternTable.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Theme, withStyles, withTheme } from '@material-ui/core/styles';
+import { Theme, withStyles, withTheme, StyleRules } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
@@ -16,7 +16,7 @@ import { theme, defaultChartColor } from './theme';
import { PatternEntry, PatternEntryColor } from './pattern';
import { GCalendarMeta } from './gapi';
-const styles = (theme: Theme) => ({
+const styles = (theme: Theme): StyleRules => ({
deleteButton: {
width: 0,
position: 'absolute',
@@ -74,8 +74,8 @@ function NameField(props: {
const patternHead = [
{label: "Name", elem: withStyles(nameFieldstyles)(NameField)},
- {label: "Calendar", elem: withTheme(theme)(CalendarField)},
- {label: "Event", elem: withTheme(theme)(EventField)}] as {label: string, elem: any}[];
+ {label: "Calendar", elem: withTheme()(CalendarField)},
+ {label: "Event", elem: withTheme()(EventField)}] as {label: string, elem: any}[];
class PatternTable extends React.Component<{
classes: {
@@ -178,15 +178,15 @@ class PatternTable extends React.Component<{
}}>
<MaterialColorPicker
initColor={this.state.colorPickerDefault}
- onSelect={(event: any) => {
+ onSelect={(event: { target: { value: any }}) => {
console.log("select");
this.chosenColor = event.target.value;
}}
onSubmit={this.handleColorPickerClose}
onReset={() => {}}
style={{width: 400, backgroundColor: '#c7c7c7'}}
- submitLabel='Apply'
- resetLabel='Undo'
+ submitLabel='Ok'
+ resetLabel='Reset'
/>
</Popover>
<div className={classes.patternTableWrapper}>
diff --git a/src/RegexField.tsx b/src/RegexField.tsx
index e3fa9f4..9483103 100644
--- a/src/RegexField.tsx
+++ b/src/RegexField.tsx
@@ -1,13 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { withStyles } from '@material-ui/core/styles';
+import { Theme, withStyles } from '@material-ui/core/styles';
import Select from '@material-ui/core/Select';
import MenuItem from '@material-ui/core/MenuItem';
import TextField from '@material-ui/core/TextField';
import FormControl from '@material-ui/core/FormControl';
-import { Pattern } from './pattern';
+import { Pattern, PatternEntry } from './pattern';
+import { GCalendarMeta } from './gapi';
-const styles = theme => ({
+const styles = (theme: Theme) => ({
fieldNoRegex: {
width: 200
},
@@ -16,12 +17,21 @@ const styles = theme => ({
}
});
-class RegexField extends React.Component {
+class RegexField extends React.Component<{
+ classes: {
+ fieldRegex: string,
+ fieldNoRegex: string
+ },
+ options: {[id: string]: Pattern},
+ theme: Theme,
+ value: Pattern,
+ onChange: (p: Pattern) => void
+ }>{
render() {
const { classes } = this.props;
- let items = [];
+ let items: React.ReactElement<typeof MenuItem>[] = [];
var pitems = this.props.options;
- const p0 = new Pattern.emptyPattern();
+ const p0 = Pattern.emptyPattern();
pitems[p0.id] = p0;
for (let id in pitems)
{
@@ -29,7 +39,7 @@ class RegexField extends React.Component {
<span style={{color: this.props.theme.palette.primary.dark}}>Custom</span>;
items.push(<MenuItem key={id} value={id}>{label}</MenuItem>);
}
- const selectOnClick = event => {
+ const selectOnClick = (event: { target: { value: any }}) => {
let value;
if (pitems[event.target.value].label == null) {
value = new Pattern(0, true,
@@ -42,7 +52,7 @@ class RegexField extends React.Component {
this.props.onChange(value);
};
- const regexTextOnChange = event => this.props.onChange(
+ const regexTextOnChange = (event: { target: { value: any }}) => this.props.onChange(
new Pattern(0, true, event.target.value, null));
const className = this.props.value.isRegex ? classes.fieldRegex: classes.fieldNoRegex;
@@ -64,14 +74,15 @@ class RegexField extends React.Component {
}
}
-RegexField.propTypes = {
- classes: PropTypes.object.isRequired,
-};
-
const RegexFieldWithStyles = withStyles(styles)(RegexField);
-export function CalendarField(props) {
- let options = {};
+export function CalendarField(props: {
+ calendars: {[id: string]: GCalendarMeta},
+ theme: Theme,
+ onChange: (field: string, value: Pattern) => void,
+ value: PatternEntry
+ }) {
+ let options: {[id: string]: Pattern} = {};
for (let id in props.calendars) {
options[id] = new Pattern(id, false,
props.calendars[id].name,
@@ -85,10 +96,14 @@ export function CalendarField(props) {
theme={props.theme} />);
}
-export function EventField(props) {
- let any = Pattern.anyPattern();
- let options = {};
- options[any.id] = any;
+export function EventField(props: {
+ theme: Theme,
+ value: PatternEntry,
+ onChange: (field: string, value: Pattern) => void
+ }) {
+ let wildcard = Pattern.anyPattern();
+ let options: { [id: string]: Pattern } = {};
+ options[wildcard.id] = wildcard;
return (
<RegexFieldWithStyles
value={props.value.event}
diff --git a/src/Settings.tsx b/src/Settings.tsx
index e08a89b..bce962d 100644
--- a/src/Settings.tsx
+++ b/src/Settings.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Theme, withStyles } from '@material-ui/core/styles';
+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';
@@ -21,16 +21,16 @@ 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 } from './pattern';
+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 { Duration } from './duration';
+import { Duration, TrackPeriod, TrackPeriodFlat } from './duration';
-const styles = (theme: Theme) => ({
+const styles = (theme: Theme): StyleRules => ({
tableHead: {
verticalAlign: 'top',
textAlign: 'right',
@@ -137,9 +137,9 @@ class Settings extends React.Component<{
state = {
isLoggedIn: false,
- patterns: [],
- calendars: {},
- config: {},
+ patterns: [] as PatternEntry[],
+ calendars: {} as {[id: string]: gapi.GCalendarMeta},
+ config: {} as { trackedPeriods: TrackPeriod[] },
snackBarOpen: false,
snackBarMsg: 'unknown',
dialogOpen: false,
@@ -147,7 +147,7 @@ class Settings extends React.Component<{
calendarsLoading: false,
};
- constructor(props) {
+ constructor(props: any) {
super(props);
gapi.getLoggedIn().then(b => this.setState({ isLoggedIn: b }));
@@ -157,7 +157,7 @@ class Settings extends React.Component<{
opt: MsgType.getPatterns,
data: { id: 'main' }
}).then(msg => {
- this.setState({ patterns: msg.data.map(p => PatternEntry.inflate(p)) });
+ this.setState({ patterns: msg.data.map((p: PatternEntryFlat) => PatternEntry.inflate(p)) });
});
this.msgClient.sendMsg({
@@ -173,7 +173,7 @@ class Settings extends React.Component<{
}).then(msg => {
let config = {
trackedPeriods: msg.data.trackedPeriods.map((p: TrackPeriodFlat) => (
- new TrackPeriod.inflate(p)
+ TrackPeriod.inflate(p)
))
};
console.log(msg.data.trackedPeriods);
@@ -200,7 +200,7 @@ class Settings extends React.Component<{
});
}
- handleToggleCalendar = id => {
+ handleToggleCalendar = (id: string) => {
var calendars = {...this.state.calendars};
calendars[id].enabled = !calendars[id].enabled;
this.msgClient.sendMsg({
@@ -212,23 +212,21 @@ class Settings extends React.Component<{
async loadAll(loadPatterns = false) {
await new Promise(resolver => (this.setState({ calendarsLoading: true }, resolver)));
- let colors = gapi.getAuthToken().then(gapi.getColors).then(color => {
+ let pm_colors = gapi.getAuthToken().then(gapi.getColors).then(color => {
return color.calendar;
});
- let cals = gapi.getAuthToken().then(gapi.getCalendars);
- await Promise.all([colors, cals]).then(([colors, items]) => {
- var cals = {};
- items.forEach(item => {
- cals[item.id] = {
- name: item.summary,
- color: colors[item.colorId],
- enabled: true
- //cal: new gapi.GCalendar(item.id, item.summary)
- }});
- this.loadCalendars(cals);
- if (loadPatterns)
- this.loadDefaultPatterns();
+ let pm_cals = gapi.getAuthToken().then(gapi.getCalendars);
+ let [colors, _cals] = await Promise.all([pm_colors, pm_cals]);
+ var cals: { [id: string]: gapi.GCalendarMeta } = {};
+ _cals.forEach((cal: any) => {
+ cals[cal.id] = {
+ name: cal.summary,
+ color: colors[cal.colorId],
+ enabled: true
+ };
});
+ this.loadCalendars(cals);
+ if (loadPatterns) this.loadDefaultPatterns();
this.setState({ calendarsLoading: false });
};
@@ -247,7 +245,7 @@ class Settings extends React.Component<{
this.loadPatterns(patterns, 'main');
}
- loadCalendars = calendars => {
+ 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;
@@ -258,44 +256,44 @@ class Settings extends React.Component<{
}).then(() => this.setState({ calendars }));
};
- loadPatterns = (patterns, id) => {
+ 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, idx, value) => {
+ updatePattern = (field: string, idx: number, value: any) => {
let patterns = this.state.patterns;
- patterns[idx][field] = value;
- this.loadPatterns(patterns);
+ (patterns[idx] as {[key: string]: any})[field] = value;
+ this.loadPatterns(patterns, 'main');
};
- removePattern = idx => {
+ removePattern = (idx: number) => {
let patterns = this.state.patterns;
patterns.splice(idx, 1);
for (let i = 0; i < patterns.length; i++)
patterns[i].idx = i;
- this.loadPatterns(patterns);
+ this.loadPatterns(patterns, 'main');
};
newPattern = () => {
let patterns = [PatternEntry.defaultPatternEntry(0), ...this.state.patterns];
for (let i = 1; i < patterns.length; i++)
patterns[i].idx = i;
- this.loadPatterns(patterns);
+ this.loadPatterns(patterns, 'main');
};
- handleSnackbarClose = (event, reason) => {
+ handleSnackbarClose = (event: any, reason: string) => {
if (reason === 'clickaway') return;
this.setState({ snackBarOpen: false });
}
- handleSnackbarOpen = msg => {
+ handleSnackbarOpen = (msg: string) => {
this.setState({ snackBarOpen: true, snackBarMsg: msg });
}
- handleDialogOpen = (title, message) => {
+ handleDialogOpen = (title: string, message: string) => {
let pm = new Promise(resolver => {
this.dialogPromiseResolver = resolver
});
@@ -303,31 +301,31 @@ class Settings extends React.Component<{
return pm;
}
- handleDialogClose = result => {
+ handleDialogClose = (result: boolean) => {
this.dialogPromiseResolver(result);
this.setState({ dialogOpen: false });
}
- updateTrackedPeriods = trackedPeriods => {
+ updateTrackedPeriods = (trackedPeriods: TrackPeriod[]) => {
this.msgClient.sendMsg({
opt: MsgType.updateConfig,
data: { trackedPeriods: trackedPeriods.map(p => p.deflate()) }
}).then(() => this.setState({...this.state.config, trackedPeriods }));
}
- handlePeriodNameChange = idx => name => {
+ handlePeriodNameChange = (idx: number) => (name: string) => {
let trackedPeriods = [...this.state.config.trackedPeriods];
trackedPeriods[idx].name = name;
this.updateTrackedPeriods(trackedPeriods);
}
- handlePeriodFromChange = idx => duration => {
+ handlePeriodFromChange = (idx: number) => (duration: Duration) => {
let trackedPeriods = [...this.state.config.trackedPeriods];
trackedPeriods[idx].start = duration;
this.updateTrackedPeriods(trackedPeriods);
}
- handlePeriodToChange = idx => duration => {
+ handlePeriodToChange = (idx: number) => (duration: Duration) => {
let trackedPeriods = [...this.state.config.trackedPeriods];
trackedPeriods[idx].end = duration;
this.updateTrackedPeriods(trackedPeriods);
@@ -439,4 +437,6 @@ class Settings extends React.Component<{
}
}
-export default withStyles(styles)(Settings);
+const StyledSettings = withStyles(styles)(Settings);
+
+export default StyledSettings;
diff --git a/src/Snackbar.tsx b/src/Snackbar.tsx
index f17863c..15ebb38 100644
--- a/src/Snackbar.tsx
+++ b/src/Snackbar.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
-import { withStyles } from '@material-ui/core/styles';
+import { Theme, 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';
@@ -17,7 +17,7 @@ const variantIcon = {
warning: WarningIcon,
};
-const styles = theme => ({
+const styles = (theme: Theme) => ({
error: {
backgroundColor: theme.palette.error.dark,
},
@@ -37,8 +37,24 @@ const styles = theme => ({
},
});
-function CustomSnackbar(props) {
- const { classes, className, message, variant, open, onClose, ...other } = props;
+export type Variant = 'error' | 'warning';
+
+function CustomSnackbar(props: {
+ classes: {
+ error: string,
+ warning: string,
+ message: string,
+ icon: string,
+ iconVariant: string,
+ close: string
+ },
+ variant: Variant,
+ className?: string,
+ open: boolean,
+ message: string,
+ onClose: (event: React.SyntheticEvent<{}>, reason?: string) => void
+ }) {
+ const { classes, className, message, variant, open, onClose } = props;
const Icon = variantIcon[variant];
return (
<Snackbar
@@ -69,7 +85,6 @@ function CustomSnackbar(props) {
<CloseIcon className={classes.icon} />
</IconButton>,
]}
- {...other}
/>
</Snackbar>
);
diff --git a/src/background.ts b/src/background.ts
index 6a3e3bc..356ed0d 100644
--- a/src/background.ts
+++ b/src/background.ts
@@ -31,11 +31,7 @@ function loadMetadata() {
{
console.log('metadata loaded');
config = {
- trackedPeriods: items.config.trackedPeriods.map((p: TrackPeriod) => ({
- name: p.name,
- start: Duration.inflate(p.start),
- end: Duration.inflate(p.end),
- }))
+ trackedPeriods: items.config.trackedPeriods.map((p: TrackPeriodFlat) => TrackPeriod.inflate(p))
};
calendars = items.calendars;
mainPatterns = items.mainPatterns.map((p: PatternEntryFlat) => PatternEntry.inflate(p));
@@ -68,6 +64,7 @@ async function getCalEvents(id: string, start: Date, end: Date) {
calData[id] = new gapi.GCalendar(id, calendars[id].name);
try {
let res = await calData[id].getEvents(new Date(start), new Date(end));
+ console.log(res);
return res;
} catch(err) {
console.log(`cannot load calendar ${id}`, err);
@@ -170,13 +167,7 @@ chrome.runtime.onConnect.addListener(function(port) {
case MsgType.getCalEvents: {
getCalEvents(msg.data.id, new Date(msg.data.start), new Date(msg.data.end)).then(data => {
console.log(data);
- let resp = msg.genResp(data.map(e => {
- return {
- id: e.id,
- start: e.start.getTime(),
- end: e.end.getTime()
- }
- }));
+ let resp = msg.genResp(data.map(e => e.deflate()));
console.log(resp);
port.postMessage(resp);
});
@@ -195,8 +186,8 @@ chrome.runtime.onConnect.addListener(function(port) {
case MsgType.getConfig: {
let res: {[prop: string]: any} = {};
msg.data.forEach((prop: string) => {
- if (prop == 'trackedPeriods')
- res.trackedPeriods = config.trackedPeriods.map(p => p.deflate())
+ if (prop === 'trackedPeriods')
+ res.trackedPeriods = config.trackedPeriods.map(p => p.deflate());
});
port.postMessage(msg.genResp(res));
break;
diff --git a/src/duration.ts b/src/duration.ts
index ebd270d..cb25107 100644
--- a/src/duration.ts
+++ b/src/duration.ts
@@ -15,9 +15,9 @@ export class Duration {
this.unit = unit
}
- isValid() { return moment.duration(this.value, this.unit).isValid(); }
+ isValid() { return moment.duration(parseInt(this.value), this.unit).isValid(); }
toMoment() {
- let m = moment.duration(this.value, this.unit);
+ let m = moment.duration(parseInt(this.value), this.unit);
if (m.isValid()) return m;
return null;
}
diff --git a/src/gapi.ts b/src/gapi.ts
index efcd6e3..e840f0e 100644
--- a/src/gapi.ts
+++ b/src/gapi.ts
@@ -18,7 +18,7 @@ function to_params(dict: Object) {
)).join('&');
}
-let loggedIn = false;
+let loggedIn: boolean = null;
function _getAuthToken(interactive = false): Promise<string> {
return new Promise(resolver =>
@@ -80,7 +80,7 @@ export type GCalendarMeta = {
enabled: boolean
};
-export async function getCalendars(token: string): Promise<GCalendarMeta> {
+export async function getCalendars(token: string): Promise<any> {
let response = await fetch(
`${gapiBase}/users/me/calendarList?${to_params({access_token: token})}`, { method: 'GET' });
return (await response.json()).items;
diff --git a/src/graph.ts b/src/graph.ts
index 828e07f..d5a65b2 100644
--- a/src/graph.ts
+++ b/src/graph.ts
@@ -46,7 +46,10 @@ export function getGraphData(
if (!events[id]) continue;
events[id].forEach(event => {
patternsByCal[id].forEach(p => {
- if (!p.event.regex.test(event.summary)) return;
+ if (!p.event.regex.test(event.summary)) {
+ console.log(event.summary, p.event.regex);
+ return;
+ }
if (!cal_results.hasOwnProperty(id)) {
cal_results[id] = 0;
}
diff --git a/src/msg.ts b/src/msg.ts
index f6edd4a..158fd8d 100644
--- a/src/msg.ts
+++ b/src/msg.ts
@@ -19,10 +19,11 @@ function parseMsgType(s: string): MsgType {
case "getPatterns": return MsgType.getPatterns;
case "updateCalendars" : return MsgType.updateCalendars;
case "getCalendars": return MsgType.getCalendars;
+ case "getCalEvents": return MsgType.getCalEvents;
case "updateConfig": return MsgType.updateConfig;
case "getConfig": return MsgType.getConfig;
case "getGraphData": return MsgType.getGraphData;
- default: console.error("unreachable");
+ default: console.error(`unknown MsgType: ${s}`);
}
}
@@ -58,6 +59,7 @@ export class MsgClient {
constructor(channelName: string) {
let port = chrome.runtime.connect({name: channelName});
+ this.requestCallback = {inFlight: {}, ids: [], maxId: 0};
const rcb = this.requestCallback;
port.onMessage.addListener((msg) => {
console.log(msg);
@@ -67,7 +69,6 @@ export class MsgClient {
cb(msg);
});
this.port = port;
- this.requestCallback = {inFlight: {}, ids: [], maxId: 0};
}
sendMsg({ opt, data }: { opt: MsgType, data: any }): Promise<Msg<any>> {