import { action, IReactionDisposer, makeObservable, observable, 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 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; // }; public fetchOrGenerateAccessToken = async (): Promise => { const response = await Networking.FetchFromServer('/readGoogleAccessToken'); // This will return a JSON object with { access_token, user_info } if already linked try { const parsed = JSON.parse(response) as { access_token: string; user_info: { name: string; picture: string } }; runInAction(() => { this.success = true; this.credentials = parsed; }); return parsed.access_token; } catch { console.warn('Not linked yet or invalid JSON. open auth...'); // This is an auth URL — redirect the user to /refreshGoogle if (typeof response === 'string' && response.startsWith('http')) { if (window.confirm('Authorize Dash to access your Google tasks?')) { window.open(response)?.focus(); return undefined; } } throw new Error('Unable to fetch Google 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?.user_info?.picture ? ( <> 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);