aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/App.js115
-rw-r--r--src/PatternTable.js6
-rw-r--r--src/RegexField.js6
-rw-r--r--src/background.js58
-rw-r--r--src/gapi.js22
-rw-r--r--src/pattern.js4
6 files changed, 168 insertions, 43 deletions
diff --git a/src/App.js b/src/App.js
index 0b0d6d5..efdb446 100755
--- a/src/App.js
+++ b/src/App.js
@@ -1,3 +1,4 @@
+/* global chrome */
import React from 'react';
import PropTypes from 'prop-types';
import 'typeface-roboto';
@@ -69,6 +70,7 @@ const styles = theme => ({
class Dashboard extends React.Component {
state = {
patterns: [],
+ calendars: [],
timeRange: null,
token: gapi.getAuthToken(),
patternGraphData: default_chart_data,
@@ -76,14 +78,33 @@ class Dashboard extends React.Component {
activePattern: null
};
- cached = {
- calendars: {}
- };
+ constructor(props) {
+ super(props);
+ let port = chrome.runtime.connect({name: 'main'});
+ const getCallBack = t => this.requestCallback[t];
+ port.onMessage.addListener(function(msg) {
+ console.log(msg);
+ let t = getCallBack(msg.type);
+ let e = t.inFlight[msg.id];
+ console.assert(e !== undefined);
+ t.ids.push(msg.id);
+ e(msg);
+ });
+ this.port = port;
+ this.requestCallback = {};
+ this.sendMsg({ type: 1 }).then(msg => {
+ this.setState({ patterns: msg.data.map(p => PatternEntry.revive(p)) });
+ });
+ this.sendMsg({ type: 3 }).then(msg => {
+ this.setState({ calendars: msg.data });
+ });
+ }
updatePattern = (field, idx, value) => {
let patterns = this.state.patterns;
patterns[idx][field] = value;
this.setState({ patterns });
+ this.sendMsg({ type: 0, data: patterns });
};
removePattern = idx => {
@@ -92,15 +113,57 @@ class Dashboard extends React.Component {
for (let i = 0; i < patterns.length; i++)
patterns[i].idx = i;
this.setState({ patterns });
+ this.sendMsg({ type: 0, data: patterns });
};
newPattern = () => {
- let patterns = [PatternEntry.defaultPatternEntry(), ...this.state.patterns];
+ let patterns = [PatternEntry.defaultPatternEntry(0), ...this.state.patterns];
for (let i = 1; i < patterns.length; i++)
patterns[i].idx = i;
this.setState({ patterns });
+ this.sendMsg({ type: 0, data: patterns });
+ };
+
+ loadPatterns = patterns => {
+ this.setState({ patterns });
+ this.sendMsg({ type: 0, data: patterns });
};
+ loadCalendars = calendars => {
+ this.setState({ calendars });
+ this.sendMsg({ type: 5, data: calendars });
+ };
+
+ sendMsg = msg => {
+ if (!this.requestCallback.hasOwnProperty(msg.type))
+ this.requestCallback[msg.type] = {inFlight: {}, ids: [], maxId: 0};
+ let t = this.requestCallback[msg.type];
+ let cb;
+ let pm = new Promise(resolve => { cb = resolve; });
+ let id;
+ if (t.ids.length > 0) {
+ id = t.ids.pop();
+ } else {
+ id = t.maxId++;
+ }
+ t.inFlight[id] = cb;
+ msg.id = id;
+ this.port.postMessage(msg);
+ return pm;
+ }
+
+ getCalEvents = (id, start, end) => {
+ return this.sendMsg({ type: 4, 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) }
+ }));
+ }
+
analyze = () => {
if (!(this.state.startDate && this.state.endDate)) {
alert("Please choose a valid time range.");
@@ -108,26 +171,23 @@ class Dashboard extends React.Component {
}
let start = this.state.startDate.startOf('day').toDate();
let end = this.state.endDate.startOf('day').toDate();
- console.log(start, end);
let event_pms = [];
- for (let id in this.cached.calendars)
- event_pms.push(this.cached.calendars[id].cal.getEvents(start, end)
- .then(r => { return { id, events: r }; })
- .catch(e => {
- console.log(`cannot load calendar ${id}`);
- return { id, events: [] };
- }));
-
+ let cals = this.state.calendars;
+ for (let id in cals)
+ event_pms.push(this.getCalEvents(id, start, end)
+ .then(r => { return { id, events: r }; }));
+ console.log(cals);
Promise.all(event_pms).then(all_events => {
+ console.log(all_events);
let events = {};
let results = {}; // pattern idx => time
let cal_results = {}; // cal id => time
all_events.forEach(e => events[e.id] = e.events);
for (let i = 0; i < this.state.patterns.length; i++)
results[i] = 0;
- for (let id in this.cached.calendars) {
+ for (let id in cals) {
if (!events[id]) continue;
- let patterns = filterPatterns(this.state.patterns, this.cached.calendars[id].name);
+ let patterns = filterPatterns(this.state.patterns, cals[id].name);
events[id].forEach(event => {
patterns.forEach(p => {
if (!p.event.regex.test(event.summary)) return;
@@ -147,34 +207,35 @@ class Dashboard extends React.Component {
}
for (let id in cal_results) {
calendarGraphData.push({
- name: this.cached.calendars[id].name,
+ name: cals[id].name,
value: (cal_results[id] / 60.0),
- color: this.cached.calendars[id].color.background});
+ color: cals[id].color.background});
}
- //console.log(patternGraphData, calendarGraphData);
+ console.log(patternGraphData, calendarGraphData);
this.setState({ patternGraphData, calendarGraphData });
});
};
- loadPatterns = () => {
+ load = () => {
let token = this.state.token;
let colors = token.then(gapi.getColors).then(color => {
return color.calendar;
});
let cals = token.then(gapi.getCalendars);
Promise.all([colors, cals]).then(([colors, items]) => {
+ var cals = {};
items.forEach(item => {
- this.cached.calendars[item.id] = {
+ cals[item.id] = {
name: item.summary,
color: colors[item.colorId],
- cal: new gapi.GCalendar(item.id, item.summary)
- };
- });
- this.setState({ patterns: items.map((item, idx) => {
+ //cal: new gapi.GCalendar(item.id, item.summary)
+ }});
+ this.loadCalendars(cals);
+ this.loadPatterns(items.map((item, idx) => {
return new PatternEntry(item.summary, idx,
new Pattern(item.id, false, item.summary, item.summary),
Pattern.anyPattern());
- })});
+ }));
});
};
@@ -208,7 +269,7 @@ class Dashboard extends React.Component {
</Typography>
<PatternTable
patterns={this.state.patterns}
- cached={this.cached}
+ calendars={this.state.calendars}
onRemovePattern={this.removePattern}
onUpdatePattern={this.updatePattern} />
</FormGroup>
@@ -234,7 +295,7 @@ class Dashboard extends React.Component {
<Grid container spacing={16}>
<Grid item md={6} xs={12}>
<FormGroup>
- <Button variant="contained" color="primary" onClick={this.loadPatterns}>Load</Button>
+ <Button variant="contained" color="primary" onClick={this.load}>Load</Button>
</FormGroup>
</Grid>
<Grid item md={6} xs={12}>
diff --git a/src/PatternTable.js b/src/PatternTable.js
index 19ac292..e579921 100644
--- a/src/PatternTable.js
+++ b/src/PatternTable.js
@@ -54,7 +54,7 @@ class PatternTable extends React.Component {
}
render() {
- const { classes, cached, patterns } = this.props;
+ const { classes, calendars, patterns } = this.props;
const { rowsPerPage, page } = this.state;
const nDummy = rowsPerPage - Math.min(rowsPerPage, patterns.length - page * rowsPerPage);
let rows = patterns.slice(page * rowsPerPage, (page + 1) * rowsPerPage).map(p => (
@@ -68,7 +68,7 @@ class PatternTable extends React.Component {
<TableCell>
<CustomText
value={p[s.field]}
- cached={cached}
+ calendars={calendars}
onChange={event => this.props.onUpdatePattern(s.field, p.idx, event.target.value)}/>
</TableCell>)})
}
@@ -115,7 +115,7 @@ class PatternTable extends React.Component {
PatternTable.propTypes = {
classes: PropTypes.object.isRequired,
patterns: PropTypes.array.isRequired,
- cached: PropTypes.object.isRequired,
+ calendars: PropTypes.object.isRequired,
onRemovePattern: PropTypes.func.isRequired,
onUpdatePattern: PropTypes.func.isRequired,
};
diff --git a/src/RegexField.js b/src/RegexField.js
index 926a3bc..d4080a1 100644
--- a/src/RegexField.js
+++ b/src/RegexField.js
@@ -72,10 +72,10 @@ const RegexFieldWithStyles = withStyles(styles)(RegexField);
export function CalendarField(props) {
let options = {};
- for (let id in props.cached.calendars) {
+ for (let id in props.calendars) {
options[id] = new Pattern(id, false,
- props.cached.calendars[id].name,
- props.cached.calendars[id].name);
+ props.calendars[id].name,
+ props.calendars[id].name);
}
return (
<RegexFieldWithStyles
diff --git a/src/background.js b/src/background.js
new file mode 100644
index 0000000..7bc0318
--- /dev/null
+++ b/src/background.js
@@ -0,0 +1,58 @@
+import * as gapi from './gapi';
+
+let patterns = [];
+let calendars = {};
+let calData = {};
+
+chrome.runtime.onConnect.addListener(function(port) {
+ console.assert(port.name == 'main');
+ port.onMessage.addListener(function(msg) {
+ console.log(msg);
+ if (msg.type == 0) {
+ patterns = msg.data;
+ }
+ else if (msg.type == 1) {
+ port.postMessage({ id: msg.id, type: 1, data: patterns });
+ }
+ else if (msg.type == 2) {
+ calendars = msg.data;
+ }
+ else if (msg.type == 3) {
+ port.postMessage({ id: msg.id, type: 3, data: calendars });
+ }
+ else if (msg.type == 4) {
+ calData[msg.data.id].getEvents(new Date(msg.data.start), new Date(msg.data.end))
+ .catch(e => {
+ console.log(`cannot load calendar ${msg.data.id}`);
+ return [];
+ })
+ .then(data => {
+ console.log(data);
+ let resp = { id: msg.id, type: 4, data: data.map(e => {
+ return {
+ id: e.id,
+ start: e.start.getTime(),
+ end: e.end.getTime()
+ }
+ })};
+ console.log(resp);
+ port.postMessage(resp);
+ });
+ }
+ else if (msg.type == 5) {
+ calendars = msg.data;
+ for (let id in calendars) {
+ if (!calData.hasOwnProperty(id))
+ calData[id] = new gapi.GCalendar(id, calendars[id].summary);
+ }
+ }
+ else {
+ console.error("unknown msg type");
+ }
+ });
+});
+
+chrome.browserAction.onClicked.addListener(function() {
+ chrome.tabs.create({url: 'index.html'});
+});
+
diff --git a/src/gapi.js b/src/gapi.js
index 34fc9e9..67884c9 100644
--- a/src/gapi.js
+++ b/src/gapi.js
@@ -46,7 +46,7 @@ function getEvents(calId, token, syncToken, resultsPerRequest=100) {
.then(response => {
if (response.status === 200)
return response.json();
- else if (response.status == 410)
+ else if (response.status === 410)
throw GApiError.invalidSyncToken;
else throw GApiError.otherErrors;
})
@@ -94,22 +94,26 @@ export class GCalendar {
this.getSlot(ks)[e.id] = {
start: e.start,
end: e.end,
- id: e.id };
+ id: e.id,
+ summary: e.summary};
else
{
this.getSlot(ks)[e.id] = {
start: e.start,
end: GCalendar.slotEndDate(ks),
- id: e.id };
+ id: e.id,
+ summary: e.summary};
this.getSlot(ke)[e.id] = {
start: GCalendar.slotStartDate(ke),
end: e.end,
- id: e.id };
+ id: e.id,
+ summary: e.summary};
for (let k = ks + 1; k < ke; k++)
this.getSlot(k)[e.id] = {
start: GCalendar.slotStartDate(k),
end: GCalendar.slotEndDate(k),
- id: e.id };
+ id: e.id,
+ summary: e.summary};
}
}
@@ -126,13 +130,11 @@ export class GCalendar {
for (let id in s) {
if (!(s[id].start >= end || s[id].end <= start))
{
- let nstart = s[id].start < start ? start: s[id].start;
- let nend = s[id].end > end ? end: s[id].end;
- if (nstart > nend) console.log(s[id], start, end);
results.push({
id,
start: s[id].start < start ? start: s[id].start,
- end: s[id].end > end ? end: s[id].end
+ end: s[id].end > end ? end: s[id].end,
+ summary: s[id].summary
});
}
}
@@ -167,7 +169,7 @@ export class GCalendar {
this.removeEvent(e);
}));
})).catch(e => {
- if (e == GApiError.invalidSyncToken) {
+ if (e === GApiError.invalidSyncToken) {
this.syncToken = '';
this.sync();
} else throw e;
diff --git a/src/pattern.js b/src/pattern.js
index ad94253..c7dafbd 100644
--- a/src/pattern.js
+++ b/src/pattern.js
@@ -10,6 +10,7 @@ export class Pattern {
get isEmpty() { return this.label === null; }
static emptyPattern = () => new Pattern(0, true, '', null);
static anyPattern = () => new Pattern('any', true, '.*', 'Any');
+ static revive = obj => new Pattern(obj.id, obj.isRegex, obj.value, obj.label);
}
export class PatternEntry {
@@ -21,4 +22,7 @@ export class PatternEntry {
}
static defaultPatternEntry = (idx) => new PatternEntry('', idx, Pattern.emptyPattern(), Pattern.anyPattern());
+ static revive = obj => new PatternEntry(
+ obj.name, obj.idx,
+ Pattern.revive(obj.cal), Pattern.revive(obj.event));
}