From 8b24b7bf2409549a5714fac32d52efb05d7d6621 Mon Sep 17 00:00:00 2001 From: Determinant Date: Fri, 1 Feb 2019 16:05:33 -0500 Subject: maintain data store in background --- src/App.js | 115 ++++++++++++++++++++++++++++++++++++++++------------ src/PatternTable.js | 6 +-- src/RegexField.js | 6 +-- src/background.js | 58 ++++++++++++++++++++++++++ src/gapi.js | 22 +++++----- src/pattern.js | 4 ++ 6 files changed, 168 insertions(+), 43 deletions(-) create mode 100644 src/background.js (limited to 'src') 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 { @@ -234,7 +295,7 @@ class Dashboard extends React.Component { - + 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 { this.props.onUpdatePattern(s.field, p.idx, event.target.value)}/> )}) } @@ -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 ( { + 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)); } -- cgit v1.2.3