From 6ebb7f7ed0b603425c618bde9129ad09b1308c4e Mon Sep 17 00:00:00 2001 From: Determinant Date: Wed, 13 Feb 2019 23:09:47 -0500 Subject: ... --- package-lock.json | 17 ++++++++++++ package.json | 1 + src/Analyze.tsx | 48 +++++++++++++++++--------------- src/gapi.ts | 83 +++++++++++++++++++++++++++++++++++++++++++------------ src/msg.ts | 24 ++++++++-------- src/pattern.ts | 24 ++++++++-------- 6 files changed, 134 insertions(+), 63 deletions(-) diff --git a/package-lock.json b/package-lock.json index 331f290..1b4fef3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1023,6 +1023,15 @@ "indefinite-observable": "^1.0.1" } }, + "@types/material-ui": { + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@types/material-ui/-/material-ui-0.21.6.tgz", + "integrity": "sha512-zq0FVny7dwE94CvY0Z7ejKcGD5IYjqHPZNkC2Wb4r8z/K+YuqRWKcIG7YP5flow46vVeOkojBPHDnic7wSv4yw==", + "requires": { + "@types/react": "*", + "@types/react-addons-linked-state-mixin": "*" + } + }, "@types/prop-types": { "version": "15.5.8", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.8.tgz", @@ -1037,6 +1046,14 @@ "csstype": "^2.2.0" } }, + "@types/react-addons-linked-state-mixin": { + "version": "0.14.20", + "resolved": "https://registry.npmjs.org/@types/react-addons-linked-state-mixin/-/react-addons-linked-state-mixin-0.14.20.tgz", + "integrity": "sha512-17M8ymjR/vvyaQnLNuLSQipxtUrxaIq19phbWKKz1drIXeVQx+AnqMVVVIClno/gPheJWcLVCbf+yXXbbRalIg==", + "requires": { + "@types/react": "*" + } + }, "@types/react-transition-group": { "version": "2.0.15", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-2.0.15.tgz", diff --git a/package.json b/package.json index 75f0a66..2a7d1e3 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@material-ui/core": "^3.9.2", "@material-ui/icons": "^3.0.2", "@types/chrome": "0.0.79", + "@types/material-ui": "^0.21.6", "@types/react": "^16.8.2", "lru-cache": "^5.1.1", "moment": "^2.24.0", diff --git a/src/Analyze.tsx b/src/Analyze.tsx index 5450998..d9777a7 100644 --- a/src/Analyze.tsx +++ b/src/Analyze.tsx @@ -16,14 +16,14 @@ import AddCircleIcon from '@material-ui/icons/AddCircle'; import IconButton from '@material-ui/core/IconButton'; import * as gapi from './gapi'; import { MsgType, MsgClient } from './msg'; -import { Pattern, PatternEntry } from './pattern'; +import { Pattern, PatternEntry, PatternEntryFlat } from './pattern'; import { AnalyzePieChart, getChartData } from './Chart'; import PatternTable from './PatternTable'; import Snackbar from './Snackbar'; import AlertDialog from './Dialog'; import moment from 'moment'; -const default_chart_data = [ +const defaultChartData = [ {name: 'Work', value: 10, color: cyan[300]}, {name: 'Wasted', value: 10, color: deepOrange[300]}]; @@ -35,14 +35,15 @@ const styles = (theme: Theme) => ({ class Analyze extends React.Component { msgClient: MsgClient; + dialogPromiseResolver: (r: boolean) => void; state = { patterns: [] as PatternEntry[], - calendars: {}, + calendars: {} as { [id: string]: gapi.GCalendarMeta }, startDate: null as moment.Moment, endDate: null as moment.Moment, - patternGraphData: default_chart_data, - calendarGraphData: default_chart_data, + patternGraphData: defaultChartData, + calendarGraphData: defaultChartData, snackBarOpen: false, snackBarMsg: 'unknown', snackBarVariant: 'error', @@ -56,14 +57,16 @@ class Analyze extends React.Component { this.msgClient = new MsgClient('main'); this.msgClient.sendMsg({ - type: MsgType.getPatterns, + opt: MsgType.getPatterns, data: { id: 'analyze' } }).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({ - type: MsgType.getCalendars, + opt: MsgType.getCalendars, data: { enabledOnly: true } }).then(msg => { this.setState({ calendars: msg.data }); @@ -77,14 +80,15 @@ class Analyze extends React.Component { loadPatterns = (patterns: PatternEntry[]) => { this.msgClient.sendMsg({ - type: MsgType.updatePatterns, + opt: MsgType.updatePatterns, data: { id: 'analyze', patterns: patterns.map(p => p.deflate()) } }).then(() => this.setState({ patterns })); }; updatePattern = (field: string, idx: number, value: PatternEntry[]) => { let patterns = this.state.patterns; - patterns[idx][field] = value; + // hack here + (patterns[idx] as {[key: string]: any})[field] = value; this.loadPatterns(patterns); }; @@ -103,16 +107,16 @@ class Analyze extends React.Component { this.loadPatterns(patterns); }; - getCalEvents = (id: string, start: Date, end: Date) => { - return this.msgClient.sendMsg({ type: MsgType.getCalEvents, data: { id, - start: start.getTime(), - end: end.getTime() } }) - .then(({ data }) => data.map(e => { - return { - id: e.id, - start: new Date(e.start), - end: new Date(e.end) } - })); + getCalEvents = async (id: string, start: Date, end: Date): Promise => { + let { data } = await this.msgClient.sendMsg({ + opt: MsgType.getCalEvents, + data: { id, + start: start.getTime(), + end: end.getTime() } + }); + return data.map((_e: gapi.GCalendarEventFlat) => ( + gapi.GCalendarEvent.inflate(_e) + )); } analyze = () => { @@ -160,12 +164,12 @@ class Analyze extends React.Component { }); } - handleSnackbarClose = (event, reason) => { + handleSnackbarClose = (event: React.SyntheticEvent<{}>, reason: string) => { if (reason === 'clickaway') return; this.setState({ snackBarOpen: false }); } - handleSnackbarOpen = (msg, variant) => { + handleSnackbarOpen = (msg: string, variant) => { this.setState({ snackBarOpen: true, snackBarMsg: msg, snackBarVariant: variant }); } diff --git a/src/gapi.ts b/src/gapi.ts index bf45ffc..efcd6e3 100644 --- a/src/gapi.ts +++ b/src/gapi.ts @@ -70,7 +70,17 @@ export async function logout() { loggedIn = false; } -export async function getCalendars(token: string) { +export type GCalendarColor = { + background: string +}; + +export type GCalendarMeta = { + name: string, + color: GCalendarColor, + enabled: boolean +}; + +export async function getCalendars(token: string): Promise { let response = await fetch( `${gapiBase}/users/me/calendarList?${to_params({access_token: token})}`, { method: 'GET' }); return (await response.json()).items; @@ -127,19 +137,53 @@ function getEvents(calId: string, token: string, return singleFetch('', syncToken); } -type GCalendarOptions = { +export type GCalendarOptions = { maxCachedItems: number, nDaysPerSlot: number, largeQuery: number }; -type GCalendarEvent = { +type Event = { start: Date, end: Date, id: string }; -type GCalendarSlot = { [id: string]: GCalendarEvent }; +export type GCalendarEventFlat = { + start: string, + end: string, + id: string, + summary: string +}; + +export class GCalendarEvent { + start: Date; + end: Date; + id: string; + summary: string; + + constructor(start: Date, end: Date, id: string, summary: string) { + this.start = start; + this.end = end; + this.id = id; + this.summary = summary; + } + + deflate() { + return { + start: this.start.toISOString(), + end: this.end.toISOString(), + id: this.id, + summary: this.summary + }; + } + + static inflate = (obj: GCalendarEventFlat) => ( + new GCalendarEvent(new Date(obj.start), new Date(obj.end), obj.id, obj.summary) + ); +} + +type GCalendarSlot = { [id: string]: Event }; export class GCalendar { calId: string; @@ -255,12 +299,12 @@ export class GCalendar { for (let id in s) { if (!(s[id].start >= r.end || s[id].end <= r.start)) { - results.push({ + results.push(new GCalendarEvent( + s[id].start < r.start ? r.start: s[id].start, + s[id].end > r.end ? r.end: s[id].end, id, - start: s[id].start < r.start ? r.start: s[id].start, - end: s[id].end > r.end ? r.end: s[id].end, - summary: this.eventMeta[id].summary - }); + this.eventMeta[id].summary + )); } } return results; @@ -275,7 +319,11 @@ export class GCalendar { { let s = this.getSlot(k); for (let id in s) - results.push({...s[id], summary: this.eventMeta[id].summary}); + results.push(new GCalendarEvent( + s[id].start, + s[id].end, + s[id].id, + this.eventMeta[id].summary)); } if (ke > ks) results.push(...this.getSlotEvents(ke, _r)); @@ -305,7 +353,7 @@ export class GCalendar { } } - async getEvents(start: Date, end: Date) { + async getEvents(start: Date, end: Date): Promise { let r = this.dateRangeToCacheKeys({ start, end }); let query = { start: null as number, @@ -332,12 +380,13 @@ export class GCalendar { e.start = new Date(e.start.dateTime); e.end = new Date(e.end.dateTime); return e; - }).filter(e => !(e.start >= end || e.end <= start)).map(e => ({ - id: e.id, - start: e.start < start ? start: e.start, - end: e.end > end ? end: e.end, - summary: e.summary, - })); + }).filter(e => !(e.start >= end || e.end <= start)).map(e => ( + new GCalendarEvent( + e.start < start ? start: e.start, + e.end > end ? end: e.end, + e.id, + e.summary) + )); } console.log(`fetching short event list`); diff --git a/src/msg.ts b/src/msg.ts index 12eb2bc..f6edd4a 100644 --- a/src/msg.ts +++ b/src/msg.ts @@ -11,7 +11,7 @@ export enum MsgType { getGraphData = "getGraphData" } -function stringifyMsgType(mt: MsgType): string { return MsgType[mt]; } +function stringifyMsgType(opt: MsgType): string { return MsgType[opt]; } function parseMsgType(s: string): MsgType { switch (s) { @@ -28,23 +28,23 @@ function parseMsgType(s: string): MsgType { export class Msg { id: number; - mt: MsgType; + opt: MsgType; data: T; - constructor(id: number, mt: MsgType, data: T) { + constructor(id: number, opt: MsgType, data: T) { this.id = id; - this.mt = mt; + this.opt = opt; this.data = data; } - genResp(data: T) { return new Msg(this.id, this.mt, data); } + genResp(data: T) { return new Msg(this.id, this.opt, data); } deflate() { return { id: this.id, - mt: stringifyMsgType(this.mt), + opt: stringifyMsgType(this.opt), data: this.data } } - static inflate = (obj: {id: number, mt: MsgType, data: T}) => ( - new Msg(obj.id, parseMsgType(obj.mt), obj.data) + static inflate = (obj: {id: number, opt: MsgType, data: T}) => ( + new Msg(obj.id, parseMsgType(obj.opt), obj.data) ); } @@ -59,7 +59,7 @@ export class MsgClient { constructor(channelName: string) { let port = chrome.runtime.connect({name: channelName}); const rcb = this.requestCallback; - port.onMessage.addListener(function(msg) { + port.onMessage.addListener((msg) => { console.log(msg); let cb = rcb.inFlight[msg.id]; console.assert(cb !== undefined); @@ -70,10 +70,10 @@ export class MsgClient { this.requestCallback = {inFlight: {}, ids: [], maxId: 0}; } - sendMsg({ mt, data }: { mt: MsgType, data: any }) { + sendMsg({ opt, data }: { opt: MsgType, data: any }): Promise> { const rcb = this.requestCallback; let cb; - let pm = new Promise(resolve => { cb = resolve; }); + let pm = new Promise>(resolve => { cb = resolve; }); let id; if (rcb.ids.length > 0) { id = rcb.ids.pop(); @@ -81,7 +81,7 @@ export class MsgClient { id = rcb.maxId++; } rcb.inFlight[id] = cb; - this.port.postMessage((new Msg(id, mt, data)).deflate()); + this.port.postMessage((new Msg(id, opt, data)).deflate()); return pm; } } diff --git a/src/pattern.ts b/src/pattern.ts index cae35a9..d067bf4 100644 --- a/src/pattern.ts +++ b/src/pattern.ts @@ -1,8 +1,8 @@ -interface PatternFlat { - id: number | string; - isRegex: boolean; - value: string; - label: string; +export type PatternFlat = { + id: number | string, + isRegex: boolean, + value: string, + label: string } export class Pattern { @@ -33,16 +33,16 @@ export class Pattern { static inflate = (obj: PatternFlat) => new Pattern(obj.id, obj.isRegex, obj.value, obj.label); } -interface PatternEntryColor { +export type PatternEntryColor = { background: string } -interface PatternEntryFlat { - name: string; - idx: number; - cal: PatternFlat; - event: PatternFlat; - color: PatternEntryColor; +export type PatternEntryFlat = { + name: string, + idx: number, + cal: PatternFlat, + event: PatternFlat, + color: PatternEntryColor } export class PatternEntry { -- cgit v1.2.3-70-g09d2