import { action, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Opt } from '../../fields/Doc'; import { Networking } from '../Network'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; import { MainViewModal } from '../views/MainViewModal'; import './GoogleAuthenticationManager.scss'; import { ObservableReactComponent } from '../views/ObservableReactComponent'; const AuthenticationUrl = 'https://accounts.google.com/o/oauth2/v2/auth'; const prompt = 'Paste authorization code here...'; @observer export class GoogleAuthenticationManager extends ObservableReactComponent { // eslint-disable-next-line no-use-before-define public static Instance: GoogleAuthenticationManager; private authenticationLink: Opt = undefined; @observable private openState = false; @observable private authenticationCode: Opt = undefined; @observable private showPasteTargetState = false; @observable private success: Opt = undefined; @observable private displayLauncher = true; @observable private credentials: { user_info: { name: string; picture: string }; access_token: string } | undefined = undefined; private disposer: Opt; constructor(props: object) { super(props); makeObservable(this); GoogleAuthenticationManager.Instance = this; } private set isOpen(value: boolean) { runInAction(() => (this.openState = value)); } private set shouldShowPasteTarget(value: boolean) { runInAction(() => (this.showPasteTargetState = value)); } public cancel() { this.openState && this.resetState(0, 0); } public fetchOrGenerateAccessToken = async (displayIfFound = false) => { const response = await Networking.FetchFromServer('/readGoogleAccessToken'); // if this is an authentication url, activate the UI to register the new access token if (new RegExp(AuthenticationUrl).test(response)) { this.isOpen = true; this.authenticationLink = response; // GETS STUCK AT THIS PROMISE!! return new Promise(resolve => { this.disposer?.(); this.disposer = reaction( () => this.authenticationCode, async authenticationCode => { if (authenticationCode && /\d{1}\/[\w-]{55}/.test(authenticationCode)) { resolve(authenticationCode); this.disposer?.(); // const response2 = await Networking.PostToServer('/writeGoogleAccessToken', { authenticationCode }); // runInAction(() => { // this.success = true; // this.credentials = response2 as { user_info: { name: string; picture: string }; access_token: string }; // }); // resolve((response2 as { access_token: string }).access_token); this.resetState(); } } ); }); } // otherwise, we already have a valid, stored access token and user info const response2 = JSON.parse(response) as { user_info: { name: string; picture: string }; access_token: string }; if (displayIfFound) { runInAction(() => { this.success = true; this.credentials = response2; }); this.resetState(-1, -1); this.isOpen = true; } return (response2 as { access_token: string }).access_token; }; resetState = action((visibleForMS: number = 3000, fadesOutInMS: number = 500) => { if (!visibleForMS && !fadesOutInMS) { runInAction(() => { this.isOpen = false; this.success = undefined; this.displayLauncher = true; this.credentials = undefined; this.shouldShowPasteTarget = false; this.authenticationCode = undefined; }); return; } this.authenticationCode = undefined; this.displayLauncher = false; this.shouldShowPasteTarget = false; if (visibleForMS > 0 && fadesOutInMS > 0) { setTimeout( action(() => { this.isOpen = false; setTimeout( action(() => { this.success = undefined; this.displayLauncher = true; this.credentials = undefined; }), fadesOutInMS ); }), visibleForMS ); } }); private get renderPrompt() { return (
{this.displayLauncher ? ( ) : null} {this.showPasteTargetState ? (this.authenticationCode = e.currentTarget.value))} placeholder={prompt} /> : null} {this.credentials ? ( <> Welcome to Dash, {this.credentials.user_info.name}
{ await Networking.FetchFromServer('/revokeGoogleAccessToken'); this.resetState(0, 0); }}> Disconnect Account
) : null}
); } private get dialogueBoxStyle() { const borderColor = this.success === undefined ? 'black' : this.success ? 'green' : 'red'; return { borderColor, transition: '0.2s borderColor ease', zIndex: 1002 }; } render() { return (
(this.isOpen = false))} />
); } } ScriptingGlobals.add('GoogleAuthenticationManager', GoogleAuthenticationManager);