diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Analyze.tsx | 6 | ||||
-rw-r--r-- | src/PatternTable.tsx | 14 | ||||
-rw-r--r-- | src/RegexField.tsx | 51 | ||||
-rw-r--r-- | src/Settings.tsx | 84 | ||||
-rw-r--r-- | src/Snackbar.tsx | 25 | ||||
-rw-r--r-- | src/background.ts | 19 | ||||
-rw-r--r-- | src/duration.ts | 4 | ||||
-rw-r--r-- | src/gapi.ts | 4 | ||||
-rw-r--r-- | src/graph.ts | 5 | ||||
-rw-r--r-- | src/msg.ts | 5 |
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; } @@ -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>> { |