aboutsummaryrefslogblamecommitdiff
path: root/src/gapi.ts
blob: e840f0ea932e35410ac3dc1921307b2dd3bb5325 (plain) (tree)



















                                                                
                             


















































                                                                                                  









                              
                                                                 























































                                                                                                    
                                




                           
              




                


































                                                                                       


















































































































                                                                                       


                                                                 
                       

                                              













                                                   




                                                 




























                                                                                                      
                                                                        

























                                                                                






                                                                              




























                                                                                  
/* 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: boolean = null;

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<any> {
    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(),