From 8d9f2e9ccc54c17027c01d7fee8ac67c51044655 Mon Sep 17 00:00:00 2001 From: Determinant Date: Fri, 15 Feb 2019 23:02:30 -0500 Subject: clean up code --- src/Analyze.tsx | 75 +++++++++++++++--------------- src/Chart.tsx | 30 +++++++----- src/Dashboard.tsx | 8 ++-- src/Dialog.tsx | 43 +++++++++--------- src/Settings.tsx | 113 ++++++++++++++++++++++++++-------------------- src/Snackbar.tsx | 4 +- src/background.ts | 8 ++-- src/duration.ts | 4 +- src/gapi.ts | 74 ++++++++++++++++-------------- src/graph.ts | 133 +++++++++++++++++++++++++++--------------------------- src/pattern.ts | 9 ++-- 11 files changed, 264 insertions(+), 237 deletions(-) (limited to 'src') diff --git a/src/Analyze.tsx b/src/Analyze.tsx index d3abcf4..9339289 100644 --- a/src/Analyze.tsx +++ b/src/Analyze.tsx @@ -1,12 +1,10 @@ import React from 'react'; -import PropTypes from 'prop-types'; import 'react-dates/initialize'; import 'react-dates/lib/css/_datepicker.css'; -import { DateRangePicker } from 'react-dates'; +import { DateRangePicker, FocusedInputShape } from 'react-dates'; import { Theme, withStyles } from '@material-ui/core/styles'; import cyan from '@material-ui/core/colors/cyan'; import deepOrange from '@material-ui/core/colors/deepOrange'; -import CssBaseline from '@material-ui/core/CssBaseline'; import Typography from '@material-ui/core/Typography'; import Button from '@material-ui/core/Button'; import FormControl from '@material-ui/core/FormControl'; @@ -14,15 +12,16 @@ import FormGroup from '@material-ui/core/FormGroup'; import Grid from '@material-ui/core/Grid'; import AddCircleIcon from '@material-ui/icons/AddCircle'; import IconButton from '@material-ui/core/IconButton'; +import moment from 'moment'; + +import PatternTable from './PatternTable'; +import AlertDialog from './Dialog'; +import Snackbar, { SnackbarVariant } from './Snackbar'; import * as gapi from './gapi'; import { MsgType, MsgClient } from './msg'; import { Pattern, PatternEntry, PatternEntryFlat } from './pattern'; import { AnalyzePieChart } from './Chart'; import { getGraphData } from './graph'; -import PatternTable from './PatternTable'; -import Snackbar, { Variant } from './Snackbar'; -import AlertDialog from './Dialog'; -import moment from 'moment'; const defaultChartData = [ {name: 'Work', value: 10, color: cyan[300]}, @@ -34,7 +33,11 @@ const styles = (theme: Theme) => ({ }, }); -class Analyze extends React.Component<{classes: {buttonSpacer: string}}> { +type AnalyzeProps = { + classes: { buttonSpacer: string } +}; + +class Analyze extends React.Component { msgClient: MsgClient; dialogPromiseResolver: (r: boolean) => void; @@ -47,13 +50,13 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> { calendarGraphData: defaultChartData, snackBarOpen: false, snackBarMsg: 'unknown', - snackBarVariant: 'error' as Variant, + snackBarVariant: 'error' as SnackbarVariant, dialogOpen: false, dialogMsg: {title: '', message: ''}, - focusedInput: null as any + focusedInput: null as FocusedInputShape }; - constructor(props: any) { + constructor(props: AnalyzeProps) { super(props); this.msgClient = new MsgClient('main'); @@ -75,12 +78,13 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> { }); gapi.getLoggedIn().then(b => !b && - this.handleSnackbarOpen('Not logged in. Operating in offline mode.', 'warning')); + this.openSnackbar('Not logged in. Operating in offline mode.', + 'warning' as SnackbarVariant)); this.dialogPromiseResolver = null; } - loadPatterns = (patterns: PatternEntry[]) => { + loadPatterns(patterns: PatternEntry[]) { this.msgClient.sendMsg({ opt: MsgType.updatePatterns, data: { id: 'analyze', patterns: patterns.map(p => p.deflate()) } @@ -109,7 +113,7 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> { this.loadPatterns(patterns); }; - getCalEvents = async (id: string, start: Date, end: Date): Promise => { + async getCalEvents(id: string, start: Date, end: Date): Promise { let { data } = await this.msgClient.sendMsg({ opt: MsgType.getCalEvents, data: { id, @@ -121,19 +125,20 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> { )); } - analyze = () => { + analyze = async () => { if (!(this.state.startDate && this.state.endDate)) { - this.handleSnackbarOpen('Please choose a valid time range.', 'error'); + this.openSnackbar('Please choose a valid time range.', + 'error' as SnackbarVariant); return; } let start = this.state.startDate.startOf('day').toDate(); let end = this.state.endDate.startOf('day').toDate(); - getGraphData(start, end, + let r = await getGraphData(start, end, this.state.patterns, this.state.calendars, - this.getCalEvents).then(results => { - this.setState(results); - }); + this.getCalEvents); + this.setState({ patternGraphData: r.patternGraphData, + calendarGraphData: r.calendarGraphData }); } reset = () => { @@ -155,23 +160,17 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> { Pattern.anyPattern(), cal.color)); } - console.log(patterns); this.loadPatterns(patterns); } - default = () => { + loadDefault = () => { this.openDialog("Load Default", "Load the calendars as patterns?").then(ans => { if (!ans) return; this.loadDefaultPatterns(); }); } - handleSnackbarClose = (event: React.SyntheticEvent<{}>, reason: string) => { - if (reason === 'clickaway') return; - this.setState({ snackBarOpen: false }); - } - - handleSnackbarOpen = (msg: string, variant: any) => { + openSnackbar(msg: string, variant: SnackbarVariant) { this.setState({ snackBarOpen: true, snackBarMsg: msg, snackBarVariant: variant }); } @@ -179,12 +178,17 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> { let pm = new Promise(resolver => { this.dialogPromiseResolver = resolver }); - this.setState({ dialogOpen: true, dialogMsg: {title, message} }); + this.setState({ dialogOpen: true, dialogMsg: { title, message } }); return pm; } - handleDialogClose = (result: boolean) => { - this.dialogPromiseResolver(result); + handleSnackbarClose = (event: React.SyntheticEvent<{}>, reason: string) => { + if (reason === 'clickaway') return; + this.setState({ snackBarOpen: false }); + } + + handleDialogClose = (ans: boolean) => { + this.dialogPromiseResolver(ans); this.setState({ dialogOpen: false }); } @@ -228,12 +232,9 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> { startDateId="start_date_id" endDate={this.state.endDate} endDateId="end_date_id" - onDatesChange={({ startDate, endDate }: - { startDate: moment.Moment, endDate: moment.Moment }) => { - this.setState({ startDate, endDate }); - }} + onDatesChange={({ startDate, endDate }) => this.setState({ startDate, endDate })} focusedInput={this.state.focusedInput} - onFocusChange={(focusedInput: any) => this.setState({ focusedInput })} + onFocusChange={focusedInput => this.setState({ focusedInput })} isOutsideRange={() => false} /> @@ -241,7 +242,7 @@ class Analyze extends React.Component<{classes: {buttonSpacer: string}}> { - + diff --git a/src/Chart.tsx b/src/Chart.tsx index c7b59c4..e17bc4e 100644 --- a/src/Chart.tsx +++ b/src/Chart.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { Theme, withStyles } from '@material-ui/core/styles'; import Grid from '@material-ui/core/Grid'; import cyan from '@material-ui/core/colors/cyan'; @@ -13,9 +12,10 @@ const styles = (theme: Theme) => ({ } }); -function customizedLabel(props: {cx: number, cy: number, - x: number, y: number, - fill: string, name: string}) { +function customizedLabel(props: { + cx: number, cy: number, + x: number, y: number, + fill: string, name: string}) { const {cx, cy, x, y, fill, name} = props; let anchor = "middle"; const EPS = 2; @@ -38,12 +38,15 @@ function customizedLabel(props: {cx: number, cy: number, return ({`${name}`}); } -function PatternPieChart(props: { - classes: { - patternTableWrapper: string, - pieChart: string - }, - data: PatternGraphData[] }) { +type PatternPieChartProps = { + classes: { + patternTableWrapper: string, + pieChart: string + }, + data: PatternGraphData[] +}; + +function PatternPieChart(props: PatternPieChartProps) { return (
@@ -67,13 +70,16 @@ function PatternPieChart(props: { export const StyledPatternPieChart = withStyles(styles)(PatternPieChart); -function DoublePieChart(props: { +type DoublePieChartProps = { classes: { patternTableWrapper: string, pieChart: string }, patternGraphData: PatternGraphData[], - calendarGraphData: PatternGraphData[] }) { + calendarGraphData: PatternGraphData[] +}; + +function DoublePieChart(props: DoublePieChartProps) { return ( diff --git a/src/Dashboard.tsx b/src/Dashboard.tsx index 10f727e..ab26666 100644 --- a/src/Dashboard.tsx +++ b/src/Dashboard.tsx @@ -1,19 +1,18 @@ import React from 'react'; -import PropTypes from 'prop-types'; import 'typeface-roboto'; import { Theme, withStyles, MuiThemeProvider } from '@material-ui/core/styles'; import CssBaseline from '@material-ui/core/CssBaseline'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import Typography from '@material-ui/core/Typography'; -import Paper from '@material-ui/core/Paper'; import Tabs from '@material-ui/core/Tabs'; import Tab, { TabProps } from '@material-ui/core/Tab'; import { LinkProps } from '@material-ui/core/Link'; import Grid from '@material-ui/core/Grid'; import { HashRouter as Router, RouteComponentProps, withRouter, Route, Link, Redirect, Switch } from "react-router-dom"; -import Logo from './Logo'; + import { theme } from './theme'; +import Logo from './Logo'; import Analyze from './Analyze'; import Settings from './Settings'; @@ -56,7 +55,6 @@ interface DashboardTabsProps extends RouteComponentProps { }; } - class DashboardTabs extends React.Component { handleChangeTab = (event: React.SyntheticEvent<{}>, currentTab: any) => { this.props.history.push(currentTab); @@ -93,7 +91,7 @@ class DashboardTabs extends React.Component { } } -class Dashboard extends React.Component<{}> { +class Dashboard extends React.Component { render() { let Tabs = withRouter(withStyles(styles)(DashboardTabs)); return ( diff --git a/src/Dialog.tsx b/src/Dialog.tsx index df72144..984ecbb 100644 --- a/src/Dialog.tsx +++ b/src/Dialog.tsx @@ -13,19 +13,21 @@ function Transition(props: any) { return ; } -function AlertDialog(props: { - open: boolean, - handleClose: (r: boolean) => any, - title: string, - message: string}) { +type AlertDialogProps = { + open: boolean, + handleClose: (r: boolean) => any, + title: string, + message: string +}; + +function AlertDialog(props: AlertDialogProps) { return ( - props.handleClose(false)} - aria-labelledby="alert-dialog-slide-title" - aria-describedby="alert-dialog-slide-description"> + props.handleClose(false)} + aria-labelledby="alert-dialog-slide-title" + aria-describedby="alert-dialog-slide-description"> {props.title} @@ -35,15 +37,14 @@ function AlertDialog(props: { - - - - - ); + + + + ); } export default AlertDialog; diff --git a/src/Settings.tsx b/src/Settings.tsx index bce962d..ecef23b 100644 --- a/src/Settings.tsx +++ b/src/Settings.tsx @@ -1,12 +1,9 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { Theme, withStyles, StyleRules } from '@material-ui/core/styles'; -import CssBaseline from '@material-ui/core/CssBaseline'; import Typography from '@material-ui/core/Typography'; 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'; @@ -19,15 +16,16 @@ 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, PatternEntryFlat } from './pattern'; -import PatternTable from './PatternTable'; -import Snackbar from './Snackbar'; -import AlertDialog from './Dialog'; import TextField from '@material-ui/core/TextField'; import MenuItem from '@material-ui/core/MenuItem'; import Select from '@material-ui/core/Select'; + +import PatternTable from './PatternTable'; +import Snackbar from './Snackbar'; +import AlertDialog from './Dialog'; +import * as gapi from './gapi'; +import { MsgType, MsgClient } from './msg'; +import { Pattern, PatternEntry, PatternEntryFlat } from './pattern'; import { Duration, TrackPeriod, TrackPeriodFlat } from './duration'; const styles = (theme: Theme): StyleRules => ({ @@ -59,14 +57,16 @@ const CompactListItem = withStyles(theme => ({ }, }))(ListItem); -class TrackedPeriod extends React.Component<{ - name: string - fromDuration: Duration, - toDuration: Duration, - nameOnChange: (name: string) => void, - fromOnChange: (d: Duration) => void, - toOnChange: (d: Duration) => void - }>{ +type TrackedPeriodProps = { + name: string + fromDuration: Duration, + toDuration: Duration, + nameOnChange: (name: string) => void, + fromOnChange: (d: Duration) => void, + toOnChange: (d: Duration) => void +}; + +class TrackedPeriod extends React.Component { valueOnChange = (old: Duration, onChange: (d: Duration) => void) => (event: any) => { onChange(new Duration(event.target.value, old.unit)); } @@ -93,12 +93,18 @@ class TrackedPeriod extends React.Component<{ } render() { - let { fromDuration, toDuration, nameOnChange, fromOnChange, toOnChange, name } = this.props; + let { + fromDuration, toDuration, + nameOnChange, name, + fromOnChange, toOnChange + } = this.props; + let units = [ Day(s), Week(s), Month(s) ]; + return ( { +type SettingsProps = { + classes: { + tableHead: string, + tableContent: string, + calendarList: string + } +}; +class Settings extends React.Component { msgClient: MsgClient; dialogPromiseResolver: (r: boolean) => void; @@ -147,7 +154,7 @@ class Settings extends React.Component<{ calendarsLoading: false, }; - constructor(props: any) { + constructor(props: SettingsProps) { super(props); gapi.getLoggedIn().then(b => this.setState({ isLoggedIn: b })); @@ -157,7 +164,9 @@ class Settings extends React.Component<{ opt: MsgType.getPatterns, data: { id: 'main' } }).then(msg => { - this.setState({ patterns: msg.data.map((p: PatternEntryFlat) => PatternEntry.inflate(p)) }); + this.setState({ + patterns: msg.data.map((p: PatternEntryFlat) => PatternEntry.inflate(p)) + }); }); this.msgClient.sendMsg({ @@ -183,24 +192,28 @@ class Settings extends React.Component<{ this.dialogPromiseResolver = null; } - handleLogin = () => { - gapi.login().then(() => { + handleLogin = async () => { + try { + await gapi.login(); this.setState({ isLoggedIn: true }); this.loadAll(true); - }).catch(() => this.handleSnackbarOpen("Failed to login!")); + } catch (_) { + this.handleSnackbarOpen("Failed to login!"); + } } - handleLogout = () => { - this.handleDialogOpen("Logout", "Are you sure to logout?").then(ans => { - if (!ans) return; - gapi.logout().then(() => { - this.setState({ isLoggedIn: false }); - //this.loadPatterns([], 'analyze'); - }).catch(() => this.handleSnackbarOpen("Failed to logout!")); - }); + handleLogout = async () => { + let ans = await this.handleDialogOpen("Logout", "Are you sure to logout?"); + if (!ans) return; + try { + await gapi.logout(); + this.setState({ isLoggedIn: false }); + } catch (_) { + this.handleSnackbarOpen("Failed to logout!"); + } } - handleToggleCalendar = (id: string) => { + toggleCalendar(id: string) { var calendars = {...this.state.calendars}; calendars[id].enabled = !calendars[id].enabled; this.msgClient.sendMsg({ @@ -212,9 +225,7 @@ class Settings extends React.Component<{ async loadAll(loadPatterns = false) { await new Promise(resolver => (this.setState({ calendarsLoading: true }, resolver))); - let pm_colors = gapi.getAuthToken().then(gapi.getColors).then(color => { - return color.calendar; - }); + let pm_colors = gapi.getAuthToken().then(gapi.getColors).then(color => color.calendar); let pm_cals = gapi.getAuthToken().then(gapi.getCalendars); let [colors, _cals] = await Promise.all([pm_colors, pm_cals]); var cals: { [id: string]: gapi.GCalendarMeta } = {}; @@ -225,8 +236,11 @@ class Settings extends React.Component<{ enabled: true }; }); - this.loadCalendars(cals); - if (loadPatterns) this.loadDefaultPatterns(); + + let pms = [this.loadCalendars(cals)]; + if (loadPatterns) + pms.push(this.loadDefaultPatterns()); + await Promise.all(pms); this.setState({ calendarsLoading: false }); }; @@ -241,11 +255,10 @@ class Settings extends React.Component<{ Pattern.anyPattern(), cal.color)); } - console.log(patterns); this.loadPatterns(patterns, 'main'); } - loadCalendars = (calendars: {[ id: string ]: gapi.GCalendarMeta }) => { + loadCalendars(calendars: {[ id: string ]: gapi.GCalendarMeta }) { for (let id in this.state.calendars) { if (calendars.hasOwnProperty(id)) calendars[id].enabled = this.state.calendars[id].enabled; @@ -254,14 +267,14 @@ class Settings extends React.Component<{ opt: MsgType.updateCalendars, data: calendars }).then(() => this.setState({ calendars })); - }; + } - loadPatterns = (patterns: PatternEntry[], id: string) => { + loadPatterns(patterns: PatternEntry[], id: string) { this.msgClient.sendMsg({ opt: MsgType.updatePatterns, data: { id, patterns: patterns.map(p => p.deflate()) } }).then(() => this.setState({ patterns })); - }; + } updatePattern = (field: string, idx: number, value: any) => { let patterns = this.state.patterns; @@ -376,7 +389,7 @@ class Settings extends React.Component<{ {Object.keys(this.state.calendars).map(id => this.handleToggleCalendar(id)} + onClick={() => this.toggleCalendar(id)} disableGutters dense button > ({ }, }); -export type Variant = 'error' | 'warning'; +export type SnackbarVariant = 'error' | 'warning'; function CustomSnackbar(props: { classes: { @@ -48,7 +48,7 @@ function CustomSnackbar(props: { iconVariant: string, close: string }, - variant: Variant, + variant: SnackbarVariant, className?: string, open: boolean, message: string, diff --git a/src/background.ts b/src/background.ts index 356ed0d..1a0c1eb 100644 --- a/src/background.ts +++ b/src/background.ts @@ -22,7 +22,7 @@ let dirtyMetadata = false; function loadMetadata() { return new Promise(resolver => chrome.storage.local.get([ 'calendars', 'config', 'mainPatterns', 'analyzePatterns', - ], function(items) { + ], items => { if (chrome.runtime.lastError) console.error("error while loading saved metadata"); else if (!items.hasOwnProperty('config')) @@ -53,7 +53,7 @@ function saveMetadata() { }, mainPatterns: mainPatterns.map(p => p.deflate()), analyzePatterns: analyzePatterns.map(p => p.deflate()) - }, function() { + }, () => { console.log('metadata saved'); resolver(); })); @@ -98,12 +98,12 @@ function updateMainGraphData() { pms.push(getGraphData( start.toDate(), end.toDate(), mainPatterns, calendars, getCalEvents - ).then(results => { + ).then(r => { mainGraphData[i] = { name: p.name, start: start.toDate(), end: end.toDate(), - data: results.patternGraphData + data: r.patternGraphData }; })); } diff --git a/src/duration.ts b/src/duration.ts index cb25107..90a1d10 100644 --- a/src/duration.ts +++ b/src/duration.ts @@ -57,6 +57,8 @@ export class TrackPeriod { } static inflate = (obj: TrackPeriodFlat) => ( - new TrackPeriod(obj.name, Duration.inflate(obj.start), Duration.inflate(obj.end)) + new TrackPeriod(obj.name, + Duration.inflate(obj.start), + Duration.inflate(obj.end)) ); } diff --git a/src/gapi.ts b/src/gapi.ts index e840f0e..d601e64 100644 --- a/src/gapi.ts +++ b/src/gapi.ts @@ -3,6 +3,7 @@ import LRU from "lru-cache"; const gapiBase = 'https://www.googleapis.com/calendar/v3'; +let loggedIn: boolean = null; enum GApiError { invalidSyncToken = "invalidSyncToken", @@ -11,23 +12,20 @@ enum GApiError { otherError = "otherError", } -function to_params(dict: Object) { +function toParams(dict: Object) { return Object.entries(dict).filter(([k, v] : string[]) => v) .map(([k, v]: string[]) => ( `${encodeURIComponent(k)}=${encodeURIComponent(v)}` )).join('&'); } -let loggedIn: boolean = null; - -function _getAuthToken(interactive = false): Promise { - return new Promise(resolver => +async function _getAuthToken(interactive = false): Promise { + let [token, ok]: [string, boolean] = await new Promise(resolver => chrome.identity.getAuthToken( - { interactive }, token => resolver([token, !chrome.runtime.lastError]))) - .then(([token, ok] : [string, boolean]) => { - if (ok) return token; - else throw GApiError.notLoggedIn; - }); + { interactive }, + token => resolver([token, !chrome.runtime.lastError]))); + if (ok) return token; + else throw GApiError.notLoggedIn; } function _removeCachedAuthToken(token: string) { @@ -35,35 +33,39 @@ function _removeCachedAuthToken(token: string) { chrome.identity.removeCachedAuthToken({ token }, () => resolver())); } -export function getLoggedIn() { +export async function getLoggedIn(): Promise { if (loggedIn === null) { - return _getAuthToken(false) - .then(() => loggedIn = true) - .catch(() => loggedIn = false) - .then(() => loggedIn); + try { + await _getAuthToken(false); + loggedIn = true; + } catch(_) { + loggedIn = false; + } } - else return Promise.resolve(loggedIn); + return loggedIn; } -export function getAuthToken(): Promise { - return getLoggedIn().then(b => { - if (b) return _getAuthToken(false); - else throw GApiError.notLoggedIn; - }); +export async function getAuthToken(): Promise { + let b = await getLoggedIn(); + if (b) return _getAuthToken(false); + else throw GApiError.notLoggedIn; } -export function login() { - return getLoggedIn().then(b => { - if (!b) return _getAuthToken(true).then(() => loggedIn = true); - else throw GApiError.notLoggedOut; - }); +export async function login(): Promise { + let b = await getLoggedIn(); + if (!b) { + await _getAuthToken(true); + loggedIn = true; + } + else throw GApiError.notLoggedOut; } -export async function logout() { +export async function logout(): Promise { let token = await getAuthToken(); let response = await fetch( - `https://accounts.google.com/o/oauth2/revoke?${to_params({ token })}`, { method: 'GET' }); + `https://accounts.google.com/o/oauth2/revoke?${toParams({ token })}`, + { method: 'GET' }); //if (response.status === 200) await _removeCachedAuthToken(token); //else throw GApiError.otherError; @@ -82,19 +84,21 @@ export type GCalendarMeta = { export async function getCalendars(token: string): Promise { let response = await fetch( - `${gapiBase}/users/me/calendarList?${to_params({access_token: token})}`, { method: 'GET' }); + `${gapiBase}/users/me/calendarList?${toParams({access_token: token})}`, + { method: 'GET' }); return (await response.json()).items; } -export async function getColors(token: string) { +export async function getColors(token: string): Promise { let response = await fetch( - `${gapiBase}/colors?${to_params({access_token: token})}`, { method: 'GET' }); + `${gapiBase}/colors?${toParams({access_token: token})}`, + { method: 'GET' }); return response.json(); } -async function getEvent(calId: string, eventId: string, token: string) { +async function getEvent(calId: string, eventId: string, token: string): Promise { let response = await fetch( - `${gapiBase}/calendars/${calId}/events/${eventId}?${to_params({access_token: token})}`, + `${gapiBase}/calendars/${calId}/events/${eventId}?${toParams({access_token: token})}`, { method: 'GET' }); return response.json(); } @@ -104,11 +108,11 @@ function getEvents(calId: string, token: string, timeMin=null as string, timeMax=null as string, resultsPerRequest=100 as number): - Promise<{ results: any[], nextSyncToken: string }> { + Promise<{ results: any[], nextSyncToken: string }> { let results = [] as any[]; const singleFetch = async (pageToken: string, syncToken: string): Promise<{nextSyncToken: string, results: any[]}> => { - let response = await fetch(`${gapiBase}/calendars/${calId}/events?${to_params({ + let response = await fetch(`${gapiBase}/calendars/${calId}/events?${toParams({ access_token: token, pageToken, syncToken, diff --git a/src/graph.ts b/src/graph.ts index d5a65b2..8b385a3 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -1,6 +1,6 @@ +import { defaultChartColor } from './theme'; import { GCalendarEvent, GCalendarMeta } from './gapi'; import { PatternEntry } from './pattern'; -import { defaultChartColor} from './theme'; export type PatternGraphData = { name: string, @@ -15,11 +15,13 @@ export type GraphData = { data: PatternGraphData[] }; -export function getGraphData( +export async function getGraphData( start: Date, end: Date, patterns: PatternEntry[], calendars: { [id: string]: GCalendarMeta }, - calEventsGetter: (id: string, start: Date, end: Date) => Promise) { - if (start >= end) return Promise.resolve({ patternGraphData: [], calendarGraphData: [] }); + calEventsGetter: (id: string, start: Date, end: Date) => Promise): + Promise<{ patternGraphData: PatternGraphData[], calendarGraphData: PatternGraphData[] }> { + + if (start >= end) return { patternGraphData: [], calendarGraphData: [] }; let event_pms = []; for (let id in calendars) { @@ -29,69 +31,68 @@ export function getGraphData( event_pms.push(calEventsGetter(id, start, end) .then(r => { return { id, events: r, filtered }; })); } - return Promise.all(event_pms).then(all_events => { - let events: {[id: string]: GCalendarEvent[]} = {}; - let patternsByCal: {[id: string]: PatternEntry[]} = {}; - let results: {[idx: number]: number} = {}; - let cal_results: {[id: string]: number} = {}; + let all_events = await Promise.all(event_pms); - all_events.forEach(e => { - events[e.id] = e.events; - patternsByCal[e.id] = e.filtered; - }); + let events: {[id: string]: GCalendarEvent[]} = {}; + let patternsByCal: {[id: string]: PatternEntry[]} = {}; + let results: {[idx: number]: number} = {}; + let cal_results: {[id: string]: number} = {}; - for (let i = 0; i < patterns.length; i++) - results[i] = 0; - for (let id in calendars) { - if (!events[id]) continue; - events[id].forEach(event => { - patternsByCal[id].forEach(p => { - if (!p.event.regex.test(event.summary)) { - console.log(event.summary, p.event.regex); - return; - } - if (!cal_results.hasOwnProperty(id)) { - cal_results[id] = 0; - } - let duration = (event.end.getTime() - event.start.getTime()) / 60000; - results[p.idx] += duration; - cal_results[id] += duration; - }); - }); - } - let patternGraphData = []; - let calendarGraphData = []; - const filterMarginal = (data: PatternGraphData[]) => { - let sum = 0; - let majorParts = []; - let minorSum = 0; - data.forEach(d => sum += d.value); - data.forEach(d => { - let ratio = d.value / sum; - if (ratio < 1e-2) minorSum += d.value; - else majorParts.push(d); - }); - majorParts.push({ - name: 'Other', - value: minorSum, - color: defaultChartColor, - }); - return majorParts; - }; - for (let i = 0; i < patterns.length; i++) { - patternGraphData.push({ - name: patterns[i].name, - value: results[i] / 60.0, - color: patterns[i].color.background}); - } - for (let id in cal_results) { - calendarGraphData.push({ - name: calendars[id].name, - value: (cal_results[id] / 60.0), - color: calendars[id].color.background}); - } - return {start, end, - patternGraphData: filterMarginal(patternGraphData), - calendarGraphData: filterMarginal(calendarGraphData) }; + all_events.forEach(e => { + events[e.id] = e.events; + patternsByCal[e.id] = e.filtered; }); + + for (let i = 0; i < patterns.length; i++) + results[i] = 0; + for (let id in calendars) { + if (!events[id]) continue; + events[id].forEach(event => { + patternsByCal[id].forEach(p => { + if (!p.event.regex.test(event.summary)) { + console.log(event.summary, p.event.regex); + return; + } + if (!cal_results.hasOwnProperty(id)) { + cal_results[id] = 0; + } + let duration = (event.end.getTime() - event.start.getTime()) / 60000; + results[p.idx] += duration; + cal_results[id] += duration; + }); + }); + } + let patternGraphData = []; + let calendarGraphData = []; + const filterMarginal = (data: PatternGraphData[]) => { + let sum = 0; + let majorParts = []; + let minorSum = 0; + data.forEach(d => sum += d.value); + data.forEach(d => { + let ratio = d.value / sum; + if (ratio < 1e-2) minorSum += d.value; + else majorParts.push(d); + }); + majorParts.push({ + name: 'Other', + value: minorSum, + color: defaultChartColor, + }); + return majorParts; + }; + for (let i = 0; i < patterns.length; i++) { + patternGraphData.push({ + name: patterns[i].name, + value: results[i] / 60.0, + color: patterns[i].color.background}); + } + for (let id in cal_results) { + calendarGraphData.push({ + name: calendars[id].name, + value: (cal_results[id] / 60.0), + color: calendars[id].color.background}); + } + return {patternGraphData: filterMarginal(patternGraphData), + calendarGraphData: filterMarginal(calendarGraphData) }; } diff --git a/src/pattern.ts b/src/pattern.ts index d067bf4..d21710f 100644 --- a/src/pattern.ts +++ b/src/pattern.ts @@ -77,8 +77,9 @@ export class PatternEntry { Pattern.emptyPattern(), Pattern.anyPattern(), {background: null})); - static inflate = (obj: PatternEntryFlat) => new PatternEntry( - obj.name, obj.idx, - Pattern.inflate(obj.cal), Pattern.inflate(obj.event), - obj.color); + static inflate = (obj: PatternEntryFlat) => ( + new PatternEntry(obj.name, obj.idx, + Pattern.inflate(obj.cal), + Pattern.inflate(obj.event), obj.color) + ); } -- cgit v1.2.3-70-g09d2