From a8b8cd7d51cafea253f3e7b2e3d1e76054d97135 Mon Sep 17 00:00:00 2001
From: Determinant <ted.sybil@gmail.com>
Date: Wed, 6 Feb 2019 18:44:02 -0500
Subject: WIP: settings

---
 src/CustomAnalyzer.js |  75 ++++++++----------
 src/PatternTable.js   |   1 +
 src/Settings.js       | 212 ++++++++++++++++++++++++++++++++++++++++++++------
 src/background.js     |  24 +++++-
 4 files changed, 242 insertions(+), 70 deletions(-)

(limited to 'src')

diff --git a/src/CustomAnalyzer.js b/src/CustomAnalyzer.js
index f76fa5e..f140151 100644
--- a/src/CustomAnalyzer.js
+++ b/src/CustomAnalyzer.js
@@ -38,20 +38,23 @@ const styles = theme => ({
 class CustomAnalyzer extends React.Component {
     state = {
         patterns: [],
-        calendars: [],
-        timeRange: null,
+        calendars: {},
+        startDate: null,
+        endDate: null,
         patternGraphData: default_chart_data,
         calendarGraphData: default_chart_data,
-        activePattern: null
     };
 
     constructor(props) {
         super(props);
         this.msgClient = new MsgClient('main');
-        this.msgClient.sendMsg({ type: msgType.getPatterns }).then(msg => {
+        this.msgClient.sendMsg({
+            type: msgType.getPatterns,
+            data: { id: 'analyze' }
+        }).then(msg => {
             this.setState({ patterns: msg.data.map(p => PatternEntry.revive(p)) });
         });
-        this.msgClient.sendMsg({ type: msgType.getCalendars }).then(msg => {
+        this.msgClient.sendMsg({ type: msgType.getCalendars, data: { enabledOnly: true }}).then(msg => {
             this.setState({ calendars: msg.data });
         });
     }
@@ -59,8 +62,10 @@ class CustomAnalyzer extends React.Component {
     updatePattern = (field, idx, value) => {
         let patterns = this.state.patterns;
         patterns[idx][field] = value;
-        this.setState({ patterns });
-        this.msgClient.sendMsg({ type: msgType.updatePatterns, data: patterns });
+        this.msgClient.sendMsg({
+            type: msgType.updatePatterns,
+            data: { id: 'analyze', patterns }
+        }).then(() => this.setState({ patterns }));
     };
 
     removePattern = idx => {
@@ -68,26 +73,27 @@ class CustomAnalyzer extends React.Component {
         patterns.splice(idx, 1);
         for (let i = 0; i < patterns.length; i++)
             patterns[i].idx = i;
-        this.setState({ patterns });
-        this.msgClient.sendMsg({ type: msgType.updatePatterns, data: patterns });
+        this.msgClient.sendMsg({
+            type: msgType.updatePatterns,
+            data: { id: 'analyze', patterns }
+        }).then(() => this.setState({ patterns }));
     };
 
     newPattern = () => {
         let patterns = [PatternEntry.defaultPatternEntry(0), ...this.state.patterns];
         for (let i = 1; i < patterns.length; i++)
             patterns[i].idx = i;
-        this.setState({ patterns });
-        this.msgClient.sendMsg({ type: msgType.updatePatterns, data: patterns });
+        this.msgClient.sendMsg({
+            type: msgType.updatePatterns,
+            data: { id: 'analyze', patterns }
+        }).then(() => this.setState({ patterns }));
     };
 
     loadPatterns = patterns => {
-        this.setState({ patterns });
-        this.msgClient.sendMsg({ type: msgType.updatePatterns, data: patterns });
-    };
-
-    loadCalendars = calendars => {
-        this.setState({ calendars });
-        this.msgClient.sendMsg({ type: msgType.updateCalendars, data: calendars });
+        this.msgClient.sendMsg({
+            type: msgType.updatePatterns,
+            data: { id: 'analyze', patterns }
+        }).then(() => this.setState({ patterns }));
     };
 
     getCalEvents = (id, start, end) => {
@@ -160,27 +166,10 @@ class CustomAnalyzer extends React.Component {
         });
     };
 
-    load = () => {
-        let colors = gapi.getAuthToken().then(gapi.getColors).then(color => {
-            return color.calendar;
-        });
-        let cals = gapi.getAuthToken().then(gapi.getCalendars);
-        Promise.all([colors, cals]).then(([colors, items]) => {
-            var cals = {};
-            items.forEach(item => {
-                cals[item.id] = {
-                    name: item.summary,
-                    color: colors[item.colorId],
-                    //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());
-            }));
-        });
-    };
+    reset = () => {
+        this.loadPatterns([]);
+        this.setState({ startDate: null, endDate: null });
+    }
 
     render() {
         const { classes } = this.props;
@@ -191,7 +180,7 @@ class CustomAnalyzer extends React.Component {
                     <FormControl fullWidth={true}>
                         <FormGroup>
                             <Typography variant="h6" component="h1" gutterBottom>
-                                Event Patterns
+                                Analyzed Events
                                 <IconButton
                                     style={{marginBottom: '0.12em', marginLeft: '0.5em'}}
                                     onClick={() => this.newPattern()}><AddCircleIcon /></IconButton>
@@ -224,12 +213,12 @@ class CustomAnalyzer extends React.Component {
                         <Grid container spacing={16}>
                             <Grid item md={6} xs={12}>
                                 <FormGroup>
-                                    <Button variant="contained" color="primary" onClick={this.load}>Load</Button>
+                                    <Button variant="contained" color="primary" onClick={this.analyze}>Analyze</Button>
                                 </FormGroup>
                             </Grid>
                             <Grid item md={6} xs={12}>
                                 <FormGroup>
-                                    <Button variant="contained" color="primary" onClick={this.analyze}>Analyze</Button>
+                                    <Button variant="contained" color="primary" onClick={this.reset}>Reset</Button>
                                 </FormGroup>
                             </Grid>
                         </Grid>
@@ -237,7 +226,7 @@ class CustomAnalyzer extends React.Component {
                 </Grid>
                 <Grid item md={6} xs={12}>
                     <Typography variant="h6" component="h1" gutterBottom>
-                        Graph
+                        Results
                     </Typography>
                     <PieChart
                         patternGraphData={this.state.patternGraphData}
diff --git a/src/PatternTable.js b/src/PatternTable.js
index e579921..3e16bbf 100644
--- a/src/PatternTable.js
+++ b/src/PatternTable.js
@@ -43,6 +43,7 @@ class PatternTable extends React.Component {
     state = {
         page: 0,
         rowsPerPage: 5,
+        activePattern: null
     };
 
     handleChangePage = (event, page) => {
diff --git a/src/Settings.js b/src/Settings.js
index 11167a0..e071f9e 100644
--- a/src/Settings.js
+++ b/src/Settings.js
@@ -7,6 +7,7 @@ import Button from '@material-ui/core/Button';
 import FormControl from '@material-ui/core/FormControl';
 import FormGroup from '@material-ui/core/FormGroup';
 import Grid from '@material-ui/core/Grid';
+import RefreshIcon from '@material-ui/icons/Refresh';
 import AddCircleIcon from '@material-ui/icons/AddCircle';
 import IconButton from '@material-ui/core/IconButton';
 import Table from '@material-ui/core/Table';
@@ -14,11 +15,28 @@ import TableBody from '@material-ui/core/TableBody';
 import TableRow from '@material-ui/core/TableRow';
 import TableCell from '@material-ui/core/TableCell';
 import TableHead from '@material-ui/core/TableHead';
+import List from '@material-ui/core/List';
+import ListItem from '@material-ui/core/ListItem';
+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 PatternTable from './PatternTable';
 
 const styles = theme => ({
+    tableHead: {
+        verticalAlign: 'top',
+        textAlign: 'right',
+        lineHeight: '3em',
+    },
+    tableContent: {
+        textAlign: 'left',
+    },
+    calendarList: {
+        maxHeight: 400,
+        overflowY: 'auto'
+    }
 });
 
 const STableCell = withStyles(theme => ({
@@ -27,48 +45,194 @@ const STableCell = withStyles(theme => ({
     },
 }))(TableCell);
 
+const CompactListItem = withStyles(theme => ({
+    dense: {
+        paddingTop: 0,
+        paddingBottom: 0
+    },
+}))(ListItem);
+
 class Settings extends React.Component {
     state = {
-        isLoggedIn: false
+        isLoggedIn: false,
+        patterns: [],
+        calendars: {},
     };
 
     constructor(props) {
         super(props);
+        this.msgClient = new MsgClient('main');
         gapi.getLoggedIn().then(b => this.setState({ isLoggedIn: b }));
+        this.msgClient.sendMsg({
+            type: msgType.getPatterns,
+            data: { id: 'main' }
+        }).then(msg => {
+            this.setState({ patterns: msg.data.map(p => PatternEntry.revive(p)) });
+        });
+        this.msgClient.sendMsg({ type: msgType.getCalendars, data: { enabledOnly: false } }).then(msg => {
+            this.setState({ calendars: msg.data });
+        });
     }
 
     handleLogin = () => {
-        gapi.login().then(() => this.setState({ isLoggedIn: true }));
+        gapi.login().then(() => {
+            this.setState({ isLoggedIn: true });
+            this.loadAll(true);
+        });
     }
 
     handleLogout = () => {
-        gapi.logout().then(() => this.setState({ isLoggedIn: false }));
+        gapi.logout().then(() => {
+            this.setState({ isLoggedIn: false });
+            this.loadPatterns([], 'analyze');
+        });
     }
 
+    handleToggleCalendar = id => {
+        var calendars = {...this.state.calendars};
+        calendars[id].enabled = !calendars[id].enabled;
+        this.msgClient.sendMsg({ type: msgType.updateCalendars, data: calendars }).then(() =>
+            this.setState({ calendars }));
+    }
+
+    loadAll = loadDefaultPatterns => {
+        let colors = gapi.getAuthToken().then(gapi.getColors).then(color => {
+            return color.calendar;
+        });
+        let cals = gapi.getAuthToken().then(gapi.getCalendars);
+        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 (loadDefaultPatterns)
+            {
+                this.loadPatterns(items.map((item, idx) => {
+                    return new PatternEntry(item.summary, idx,
+                        new Pattern(item.id, false, item.summary, item.summary),
+                        Pattern.anyPattern());
+                }), 'main');
+            }
+        });
+    };
+
+    loadCalendars = calendars => {
+        for (let id in this.state.calendars) {
+            if (calendars.hasOwnProperty(id))
+                calendars[id].enabled = this.state.calendars[id].enabled;
+        }
+        this.msgClient.sendMsg({ type: msgType.updateCalendars, data: calendars }).then(() =>
+            this.setState({ calendars }));
+    };
+
+    loadPatterns = (patterns, id) => {
+        this.msgClient.sendMsg({
+            type: msgType.updatePatterns,
+            data: { id, patterns }
+        }).then(() => this.setState({ patterns }));
+    };
+
+    updatePattern = (field, idx, value) => {
+        let patterns = this.state.patterns;
+        patterns[idx][field] = value;
+        this.msgClient.sendMsg({
+            type: msgType.updatePatterns,
+            data: { id: 'main', patterns }
+        }).then(() => this.setState({ patterns }));
+    };
+
+    removePattern = idx => {
+        let patterns = this.state.patterns;
+        patterns.splice(idx, 1);
+        for (let i = 0; i < patterns.length; i++)
+            patterns[i].idx = i;
+        this.msgClient.sendMsg({
+            type: msgType.updatePatterns,
+            data: { id: 'main', patterns }
+        }).then(() => this.setState({ patterns }));
+    };
+
+    newPattern = () => {
+        let patterns = [PatternEntry.defaultPatternEntry(0), ...this.state.patterns];
+        for (let i = 1; i < patterns.length; i++)
+            patterns[i].idx = i;
+        this.msgClient.sendMsg({
+            type: msgType.updatePatterns,
+            data: { id: 'main', patterns }
+        }).then(() => this.setState({ patterns }));
+    };
+
     render() {
         const { classes } = this.props;
         return (
-            <Grid container spacing={16}>
-                <Grid item md={6} xs={12}>
-                    <Typography variant="h6" component="h1" gutterBottom>
-                        General
-                    </Typography>
-                    <Table>
-                        <TableBody>
-                            <TableRow>
-                                <STableCell align='right'>Account</STableCell>
-                                <STableCell align='left'>
-                                    {
-                                        (this.state.isLoggedIn &&
-                                            <Button variant="contained" color="primary" onClick={this.handleLogout}>Logout</Button>) ||
-                                            <Button variant="contained" color="primary" onClick={this.handleLogin}>Login</Button>
-                                    }
-                                </STableCell>
-                            </TableRow>
-                        </TableBody>
-                    </Table>
-                </Grid>
-            </Grid>
+            <div>
+               <Typography variant="h6" component="h1" gutterBottom>
+                   General
+               </Typography>
+               <Table>
+                   <TableBody>
+                       <TableRow>
+                           <STableCell className={classes.tableHead}>Account</STableCell>
+                           <STableCell align='left'>
+                               {
+                                   (this.state.isLoggedIn &&
+                                       <Button variant="contained" color="primary" onClick={this.handleLogout}>Logout</Button>) ||
+                                       <Button variant="contained" color="primary" onClick={this.handleLogin}>Login</Button>
+                               }
+                           </STableCell>
+                       </TableRow>
+                       <TableRow>
+                           <STableCell className={classes.tableHead}>
+                           <IconButton
+                               style={{marginBottom: '0.12em', marginRight: '0.5em'}}
+                               onClick={() => this.loadAll(false)}
+                               disabled={!this.state.isLoggedIn}><RefreshIcon /></IconButton>
+                               Calendars
+                           </STableCell>
+                           <STableCell className={classes.tableContent}>
+                               {(this.state.isLoggedIn &&
+                               <List className={classes.calendarList}>
+                                   {Object.keys(this.state.calendars).map(id =>
+                                       <CompactListItem
+                                           key={id}
+                                           onClick={() => this.handleToggleCalendar(id)}
+                                           disableGutters
+                                           dense button >
+                                       <Checkbox
+                                           checked={this.state.calendars[id].enabled}
+                                           disableRipple />
+                                       <ListItemText primary={this.state.calendars[id].name} />
+                                       </CompactListItem>)}
+                               </List>) || 'Please Login.'}
+                           </STableCell>
+                       </TableRow>
+                       <TableRow>
+                           <STableCell className={classes.tableHead}>
+                               <IconButton
+                                   style={{marginBottom: '0.12em', marginRight: '0.5em'}}
+                                   onClick={() => this.newPattern()}
+                                   disabled={!this.state.isLoggedIn}><AddCircleIcon /></IconButton>
+                               Tracked Events
+                           </STableCell>
+                           <STableCell className={classes.tableContent}>
+                               {(this.state.isLoggedIn &&
+                               <FormControl fullWidth={true}>
+                               <PatternTable
+                                   patterns={this.state.patterns}
+                                   calendars={this.state.calendars}
+                                   onRemovePattern={this.removePattern}
+                                   onUpdatePattern={this.updatePattern} />
+                               </FormControl>) || 'Please Login.'}
+                           </STableCell>
+                       </TableRow>
+                   </TableBody>
+               </Table>
+            </div>
         );
     }
 }
diff --git a/src/background.js b/src/background.js
index 177ec68..e9b0f60 100644
--- a/src/background.js
+++ b/src/background.js
@@ -1,7 +1,8 @@
 import * as gapi from './gapi';
 import { msgType, Msg } from './msg';
 
-let patterns = [];
+let mainPatterns = [];
+let analyzePatterns = [];
 let calendars = {};
 let calData = {};
 
@@ -11,9 +12,18 @@ chrome.runtime.onConnect.addListener(function(port) {
         let msg = Msg.inflate(_msg);
         console.log(msg);
         if (msg.type == msgType.updatePatterns) {
-            patterns = msg.data;
+            if (msg.data.id == 'analyze')
+                analyzePatterns = msg.data.patterns;
+            else
+                mainPatterns = msg.data.patterns;
+            port.postMessage(msg.genResp(null));
         }
         else if (msg.type == msgType.getPatterns) {
+            let patterns;
+            if (msg.data.id == 'analyze')
+                patterns = analyzePatterns;
+            else
+                patterns = mainPatterns;
             port.postMessage(msg.genResp(patterns));
         }
         else if (msg.type == msgType.updateCalendars) {
@@ -22,9 +32,17 @@ chrome.runtime.onConnect.addListener(function(port) {
                 if (!calData.hasOwnProperty(id))
                     calData[id] = new gapi.GCalendar(id, calendars[id].summary);
             }
+            port.postMessage(msg.genResp(null));
         }
         else if (msg.type == msgType.getCalendars) {
-            port.postMessage(msg.genResp(calendars));
+            let cals = calendars;
+            if (msg.data.enabledOnly)
+            {
+                cals = Object.keys(calendars)
+                    .filter(id => calendars[id].enabled)
+                    .reduce((res, id) => (res[id] = calendars[id], res), {});
+            }
+            port.postMessage(msg.genResp(cals));
         }
         else if (msg.type == msgType.getCalEvents) {
             calData[msg.data.id].getEvents(new Date(msg.data.start), new Date(msg.data.end))
-- 
cgit v1.2.3-70-g09d2