aboutsummaryrefslogtreecommitdiff
path: root/src/gapi.js
diff options
context:
space:
mode:
authorDeterminant <ted.sybil@gmail.com>2019-02-13 01:11:31 -0500
committerDeterminant <ted.sybil@gmail.com>2019-02-13 01:11:31 -0500
commitc594888953151ddfb4ca04b7752bfd51edc1d6da (patch)
tree59b6d0b0f514f76d152eee9a4359c08110f73531 /src/gapi.js
parentf28b818cc62c7fff67517a4147e64f08ebd73027 (diff)
WIP: migrate to TypeScriptX
Diffstat (limited to 'src/gapi.js')
-rw-r--r--src/gapi.js335
1 files changed, 0 insertions, 335 deletions
diff --git a/src/gapi.js b/src/gapi.js
deleted file mode 100644
index 3938864..0000000
--- a/src/gapi.js
+++ /dev/null
@@ -1,335 +0,0 @@
-/* global chrome */
-import LRU from "lru-cache";
-const gapi_base = 'https://www.googleapis.com/calendar/v3';
-
-const GApiError = Object.freeze({
- invalidSyncToken: Symbol("invalidSyncToken"),
- notLoggedIn: Symbol("notLoggedIn"),
- notLoggedOut: Symbol("notLoggedOut"),
- otherError: Symbol("otherError"),
-});
-
-function to_params(dict) {
- return Object.entries(dict).filter(([k, v]) => v).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&');
-}
-
-let loggedIn = null;
-
-function _getAuthToken(interactive = false) {
- return new Promise(resolver =>
- chrome.identity.getAuthToken(
- { interactive }, token => resolver([token, !chrome.runtime.lastError])))
- .then(([token, ok]) => {
- if (ok) return token;
- else throw GApiError.notLoggedIn;
- });
-}
-
-function _removeCachedAuthToken(token) {
- return new Promise(resolver =>
- chrome.identity.removeCachedAuthToken({ token }, () => resolver()));
-}
-
-export function getLoggedIn() {
- if (loggedIn === null)
- {
- return _getAuthToken(false)
- .then(() => loggedIn = true)
- .catch(() => loggedIn = false)
- .then(() => loggedIn);
- }
- else return Promise.resolve(loggedIn);
-}
-
-export function getAuthToken() {
- return getLoggedIn().then(b => {
- 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 function logout() {
- return getAuthToken().then(token => {
- return fetch(`https://accounts.google.com/o/oauth2/revoke?${to_params({ token })}`,
- { method: 'GET', async: true }).then(response => {
- //if (response.status === 200)
- return _removeCachedAuthToken(token);
- //else throw GApiError.otherError;
- });
- }).then(() => loggedIn = false);
-}
-
-export function getCalendars(token) {
- return fetch(`${gapi_base}/users/me/calendarList?${to_params({access_token: token})}`,
- { method: 'GET', async: true })
- .then(response => response.json())
- .then(data => data.items);
-}
-
-export function getColors(token) {
- return fetch(`${gapi_base}/colors?${to_params({access_token: token})}`,
- { method: 'GET', async: true })
- .then(response => response.json());
-}
-
-function getEvent(calId, eventId, token) {
- return fetch(`${gapi_base}/calendars/${calId}/events/${eventId}?${to_params({access_token: token})}`,
- { method: 'GET', async: true })
- .then(response => response.json());
-}
-
-function getEvents(calId, token, syncToken=null, timeMin=null, timeMax=null, resultsPerRequest=100) {
- let results = [];
- const singleFetch = (pageToken, syncToken) => fetch(`${gapi_base}/calendars/${calId}/events?${to_params({
- access_token: token,
- pageToken,
- syncToken,
- timeMin,
- timeMax,
- maxResults: resultsPerRequest
- })}`, { method: 'GET', async: true })
- .then(response => {
- if (response.status === 200)
- return response.json();
- else if (response.status === 410)
- throw GApiError.invalidSyncToken;
- else throw GApiError.otherError;
- })
- .then(data => {
- results.push(...data.items);
- if (data.nextPageToken) {
- return singleFetch(data.nextPageToken, '');
- } else {
- return ({
- nextSyncToken: data.nextSyncToken,
- results
- });
- }
- })
-
- return singleFetch('', syncToken);
-}
-
-export class GCalendar {
- constructor(calId, name, options={maxCachedItems: 100, nDaysPerSlot: 10, largeQuery: 10}) {
- this.calId = calId;
- this.name = name;
- this.syncToken = '';
- this.cache = new LRU({
- max: options.maxCachedItems,
- dispose: (k, v) => this.onRemoveSlot(k, v)
- });
- this.eventMeta = {};
- this.options = options;
- this.divider = 8.64e7 * this.options.nDaysPerSlot;
- }
-
- get token() { return getAuthToken(); }
-
- dateToCacheKey(date) {
- return Math.floor(date / this.divider);
- }
-
- dateRangeToCacheKeys(range) {
- return {
- start: this.dateToCacheKey(range.start),
- end: this.dateToCacheKey(new Date(range.end.getTime() - 1))
- };
- }
-
- getSlot(k) {
- if (!this.cache.has(k))
- {
- let res = {};
- this.cache.set(k, res);
- return res;
- }
- else return this.cache.get(k);
- }
-
- onRemoveSlot(k, v) {
- for (let id in v) {
- console.assert(this.eventMeta[id]);
- let keys = this.eventMeta[id].keys;
- keys.delete(k);
- if (keys.size === 0)
- delete this.eventMeta[id];
- }
- }
-
- slotStartDate(k) { return new Date(k * this.divider); }
- slotEndDate(k) { return new Date((k + 1) * this.divider); }
-
- addEvent(e, evict = false) {
- //console.log('adding event', e);
- if (this.eventMeta.hasOwnProperty(e.id))
- this.removeEvent(e);
- let r = this.dateRangeToCacheKeys(e);
- let ks = r.start;
- let ke = r.end;
- let t = this.cache.length;
- let keys = new Set();
- for (let i = ks; i <= ke; i++)
- {
- keys.add(i);
- if (!this.cache.has(i)) t++;
- }
- this.eventMeta[e.id] = {
- keys,
- summary: e.summary,
- };
- if (!evict && t > this.options.maxCachedItems) return;
- if (ks === ke)
- this.getSlot(ks)[e.id] = {
- start: e.start,
- end: e.end,
- id: e.id };
- else
- {
- this.getSlot(ks)[e.id] = {
- start: e.start,
- end: this.slotEndDate(ks),
- id: e.id };
- this.getSlot(ke)[e.id] = {
- start: this.slotStartDate(ke),
- end: e.end,
- id: e.id };
- for (let k = ks + 1; k < ke; k++)
- this.getSlot(k)[e.id] = {
- start: this.slotStartDate(k),
- end: this.slotEndDate(k),
- id: e.id};
- }
- }
-
- removeEvent(e) {
- let keys = this.eventMeta[e.id].keys;
- console.assert(keys);
- keys.forEach(k => delete this.getSlot(k)[e.id]);
- delete this.eventMeta[e.id];
- }
-
- getSlotEvents(k, start, end) {
- let s = this.getSlot(k);
- //console.log(s);
- let results = [];
- for (let id in s) {
- if (!(s[id].start >= end || s[id].end <= start))
- {
- results.push({
- id,
- start: s[id].start < start ? start: s[id].start,
- end: s[id].end > end ? end: s[id].end,
- summary: this.eventMeta[id].summary
- });
- }
- }
- return results;
- }
-
- getCachedEvents(_r) {
- let r = this.dateRangeToCacheKeys(_r);
- let ks = r.start;
- let ke = r.end;
- let results = this.getSlotEvents(ks, _r.start, _r.end);
- for (let k = ks + 1; k < ke; k++)
- {
- let s = this.getSlot(k);
- for (let id in s)
- results.push(s[id]);
- }
- if (ke > ks)
- results.push(...this.getSlotEvents(ke, _r.start, _r.end));
- return results;
- }
-
- sync() {
- return this.token.then(token => getEvents(this.calId, token, this.syncToken).then(r => {
- let pms = r.results.map(e => e.start ? Promise.resolve(e) : getEvent(this.calId, e.id, token));
- return Promise.all(pms).then(results => {
- results.forEach(e => {
- e.start = new Date(e.start.dateTime);
- e.end = new Date(e.end.dateTime);
- if (e.status === 'confirmed')
- this.addEvent(e);
- else if (e.status === 'cancelled')
- this.removeEvent(e);
- });
- this.syncToken = r.nextSyncToken;
- });
- })).catch(e => {
- if (e === GApiError.invalidSyncToken) {
- this.syncToken = '';
- this.sync();
- } else throw e;
- });
- }
-
- getEvents(start, end) {
- let r = this.dateRangeToCacheKeys({ start, end });
- let query = {};
- for (let k = r.start; k <= r.end; k++)
- if (!this.cache.has(k))
- {
- if (!query.hasOwnProperty('start'))
- query.start = k;
- query.end = k;
- }
- //console.log(`start: ${start} end: ${end}`);
- if (query.hasOwnProperty('start'))
- {
- console.assert(query.start <= query.end);
- if (query.end - query.start + 1 > this.options.largeQuery) {
- console.log(`encounter large query, use direct fetch`);
- return this.token.then(token => getEvents(this.calId, token, null,
- start.toISOString(), end.toISOString()).then(r => {
- let results = [];
- r.results.forEach(e => {
- console.assert(e.start);
- e.start = new Date(e.start.dateTime);
- e.end = new Date(e.end.dateTime);
- results.push(e);
- });
- return results.filter(e => !(e.start >= end || e.end <= start)).map(e => {
- return {
- id: e.id,
- start: e.start < start ? start: e.start,
- end: e.end > end ? end: e.end,
- summary: e.summary,
- };
- });
- }));
- }
-
- console.log(`fetching short event list`);
- return this.token.then(token => getEvents(this.calId, token, null,
- this.slotStartDate(query.start).toISOString(),
- this.slotEndDate(query.end).toISOString()).then(r => {
- r.results.forEach(e => {
- if (e.status === 'confirmed')
- {
- console.assert(e.start);
- e.start = new Date(e.start.dateTime);
- e.end = new Date(e.end.dateTime);
- this.addEvent(e, true);
- }
- });
- if (this.syncToken === '')
- this.syncToken = r.nextSyncToken;
- })).then(() => this.sync())
- .then(() => this.getCachedEvents({ start, end }));
- }
- else
- {
- console.log(`cache hit`);
- return this.sync().then(() => this.getCachedEvents({ start, end }));
- }
- }
-}