aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package-lock.json17
-rw-r--r--package.json1
-rw-r--r--src/Analyze.tsx48
-rw-r--r--src/gapi.ts83
-rw-r--r--src/msg.ts24
-rw-r--r--src/pattern.ts24
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<gapi.GCalendarEvent[]> => {
+ 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<GCalendarMeta> {
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<GCalendarEvent[]> {
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<T> {
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 = <T>(obj: {id: number, mt: MsgType, data: T}) => (
- new Msg(obj.id, parseMsgType(obj.mt), obj.data)
+ static inflate = <T>(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<Msg<any>> {
const rcb = this.requestCallback;
let cb;
- let pm = new Promise(resolve => { cb = resolve; });
+ let pm = new Promise<Msg<any>>(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 {