/* global chrome */
import LRU from "lru-cache";
const gapiBase = 'https://www.googleapis.com/calendar/v3';
enum GApiError {
invalidSyncToken = "invalidSyncToken",
notLoggedIn = "notLoggedIn",
notLoggedOut = "notLoggedOut",
otherError = "otherError",
}
function to_params(dict: Object) {
return Object.entries(dict).filter(([k, v] : string[]) => v)
.map(([k, v]: string[]) => (
`${encodeURIComponent(k)}=${encodeURIComponent(v)}`
)).join('&');
}
let loggedIn = false;
function _getAuthToken(interactive = false): Promise<string> {
return 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;
});
}
function _removeCachedAuthToken(token: string) {
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(): Promise<string> {
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 async function logout() {
let token = await getAuthToken();
let response = await fetch(
`https://accounts.google.com/o/oauth2/revoke?${to_params({ token })}`, { method: 'GET' });
//if (response.status === 200)
await _removeCachedAuthToken(token);
//else throw GApiError.otherError;
loggedIn = false;
}
export type GCalendarColor = {
background: string
};
export type GCalendarMeta = {
name: string,
color: GCalendarColor,
enabled: boolean
};
export async function getCalendars(token: string): Promise<GCalendarMeta> {
let response = await fetch(
`${gapiBase}/users/me/calendarList?${to_params({access_token: token})}`, { method: 'GET' });
return (await response.json()).items;
}
export async function getColors(token: string) {
let response = await fetch(
`${gapiBase}/colors?${to_params({access_token: token})}`, { method: 'GET' });
return response.json();
}
async function getEvent(calId: string, eventId: string, token: string) {
let response = await fetch(
`${gapiBase}/calendars/${calId}/events/${eventId}?${to_params({access_token: token})}`,
{ method: 'GET' });
return response.json();
}
function getEvents(calId: string, token: string,
syncToken=null as string,
timeMin=null as string,
timeMax=null as string,
resultsPerRequest=100 as number):
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({
access_token: token,
pageToken,
syncToken,
timeMin,
timeMax,
maxResults: resultsPerRequest
})}`, { method: 'GET' });
if (response.status === 200)
{
let data = await response.json();
results.push(...data.items);
if (data.nextPageToken) {
return singleFetch(data.nextPageToken, '');
} else {
return ({
nextSyncToken: data.nextSyncToken,
results
});
}
}
else if (response.status === 410)
throw GApiError.invalidSyncToken;
else throw GApiError.otherError;
};
return singleFetch('', syncToken);
}
export type GCalendarOptions = {
maxCachedItems: number,
nDaysPerSlot: number,
largeQuery: number
};
type Event = {
start: Date,
end: Date,
id: string
};
export type GCalendarEventFlat = {
start: string,
end: string,
id: string,
summary: string
};
export class GCalendarEvent {
start: Date;
end: Date;
id: string;
summary: string;
constructor(start: Date, end: Date, id: string, summary: string) {
this.start = start;
this.end = end;
this.id = id;
this.summary = summary;
}
deflate() {
return {
start: this.start.toISOString(),
end: this.end.toISOString(),
id: this.id,
summary: this.summary
};
}
static inflate = (obj: GCalendarEventFlat) => (
new GCalendarEvent(new Date(obj.start), new Date(obj.end)