aboutsummaryrefslogtreecommitdiff
path: root/src/client/apis
diff options
context:
space:
mode:
authorSam Wilkins <35748010+samwilkins333@users.noreply.github.com>2019-10-09 05:00:23 -0400
committerSam Wilkins <35748010+samwilkins333@users.noreply.github.com>2019-10-09 05:00:23 -0400
commit45b97084b3d49d521ff39963f250e9cd9efe3f8e (patch)
treebc14459c084b24bed8885d9ceed6d98ec7ed562e /src/client/apis
parentc255ab09e47dee88b9c5a1f9fe619952cba6fa01 (diff)
client side google api authentication UI
Diffstat (limited to 'src/client/apis')
-rw-r--r--src/client/apis/AuthenticationManager.tsx90
-rw-r--r--src/client/apis/google_docs/GooglePhotosClientUtils.ts43
2 files changed, 121 insertions, 12 deletions
diff --git a/src/client/apis/AuthenticationManager.tsx b/src/client/apis/AuthenticationManager.tsx
new file mode 100644
index 000000000..75a50d8f9
--- /dev/null
+++ b/src/client/apis/AuthenticationManager.tsx
@@ -0,0 +1,90 @@
+import { observable, action, reaction, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import * as React from "react";
+import MainViewModal from "../views/MainViewModal";
+import { Opt } from "../../new_fields/Doc";
+import { Identified } from "../Network";
+import { RouteStore } from "../../server/RouteStore";
+
+@observer
+export default class AuthenticationManager extends React.Component<{}> {
+ public static Instance: AuthenticationManager;
+ @observable private openState = false;
+ private authenticationLink: Opt<string> = undefined;
+ @observable private authenticationCode: Opt<string> = undefined;
+ @observable private clickedState = false;
+
+ private get isOpen() {
+ return this.openState;
+ }
+
+ private set isOpen(value: boolean) {
+ runInAction(() => this.openState = value);
+ }
+
+ private get hasBeenClicked() {
+ return this.clickedState;
+ }
+
+ private set hasBeenClicked(value: boolean) {
+ runInAction(() => this.clickedState = value);
+ }
+
+ public executeFullRoutine = async (authenticationLink: string) => {
+ this.authenticationLink = authenticationLink;
+ this.isOpen = true;
+ return new Promise<string>(async resolve => {
+ const disposer = reaction(
+ () => this.authenticationCode,
+ authenticationCode => {
+ if (authenticationCode) {
+ Identified.PostToServer(RouteStore.writeGooglePhotosAccessToken, { authenticationCode }).then(token => {
+ this.isOpen = false;
+ this.hasBeenClicked = false;
+ resolve(token);
+ disposer();
+ });
+ }
+ }
+ );
+ });
+ }
+
+ constructor(props: {}) {
+ super(props);
+ AuthenticationManager.Instance = this;
+ }
+
+ private handleClick = () => {
+ window.open(this.authenticationLink);
+ this.hasBeenClicked = true;
+ }
+
+ private handlePaste = action((e: React.ChangeEvent<HTMLInputElement>) => {
+ this.authenticationCode = e.currentTarget.value;
+ })
+
+ private get renderPrompt() {
+ return (
+ <div style={{ display: "flex", flexDirection: "column" }}>
+ <button onClick={this.handleClick}>Please click here to authorize a Google account...</button>
+ {this.clickedState ? <input
+ onChange={this.handlePaste}
+ placeholder={"Please paste the external authetication code here..."}
+ style={{ marginTop: 15 }}
+ /> : (null)}
+ </div>
+ )
+ }
+
+ render() {
+ return (
+ <MainViewModal
+ isDisplayed={this.openState}
+ interactive={true}
+ contents={this.renderPrompt}
+ />
+ );
+ }
+
+} \ No newline at end of file
diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index 29cc042b6..dd1492f51 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -13,14 +13,20 @@ import { Docs, DocumentOptions } from "../../documents/Documents";
import { NewMediaItemResult, MediaItem } from "../../../server/apis/google/SharedTypes";
import { AssertionError } from "assert";
import { DocumentView } from "../../views/nodes/DocumentView";
-import { DocumentManager } from "../../util/DocumentManager";
import { Identified } from "../../Network";
+import AuthenticationManager from "../AuthenticationManager";
+import { List } from "../../../new_fields/List";
export namespace GooglePhotos {
+ const AuthenticationUrl = "https://accounts.google.com/o/oauth2/v2/auth";
+
const endpoint = async () => {
- const accessToken = await Identified.FetchFromServer(RouteStore.googlePhotosAccessToken);
- return new Photos(accessToken);
+ let response = await Identified.FetchFromServer(RouteStore.readGooglePhotosAccessToken);
+ if (new RegExp(AuthenticationUrl).test(response)) {
+ response = await AuthenticationManager.Instance.executeFullRoutine(response);
+ }
+ return new Photos(response);
};
export enum MediaType {
@@ -89,9 +95,14 @@ export namespace GooglePhotos {
}
const resolved = title ? title : (StrCast(collection.title) || `Dash Collection (${collection[Id]}`);
const { id, productUrl } = await Create.Album(resolved);
- const newMediaItemResults = await Transactions.UploadImages(images, { id }, descriptionKey);
- if (newMediaItemResults) {
- const mediaItems = newMediaItemResults.map(item => item.mediaItem);
+ const response = await Transactions.UploadImages(images, { id }, descriptionKey);
+ if (response) {
+ const { results, failed } = response;
+ let index: Opt<number>;
+ while ((index = failed.pop()) !== undefined) {
+ Doc.RemoveDocFromList(dataDocument, "data", images.splice(index, 1)[0]);
+ }
+ const mediaItems: MediaItem[] = results.map(item => item.mediaItem);
if (mediaItems.length !== images.length) {
throw new AssertionError({ actual: mediaItems.length, expected: images.length });
}
@@ -99,6 +110,9 @@ export namespace GooglePhotos {
for (let i = 0; i < images.length; i++) {
const image = Doc.GetProto(images[i]);
const mediaItem = mediaItems[i];
+ if (!mediaItem) {
+ continue;
+ }
image.googlePhotosId = mediaItem.id;
image.googlePhotosAlbumUrl = productUrl;
image.googlePhotosUrl = mediaItem.productUrl || mediaItem.baseUrl;
@@ -304,17 +318,22 @@ export namespace GooglePhotos {
};
export const UploadThenFetch = async (sources: Doc[], album?: AlbumReference, descriptionKey = "caption") => {
- const newMediaItems = await UploadImages(sources, album, descriptionKey);
- if (!newMediaItems) {
+ const response = await UploadImages(sources, album, descriptionKey);
+ if (!response) {
return undefined;
}
- const baseUrls: string[] = await Promise.all(newMediaItems.map(item => {
+ const baseUrls: string[] = await Promise.all(response.results.map(item => {
return new Promise<string>(resolve => Query.GetImage(item.mediaItem.id).then(item => resolve(item.baseUrl)));
}));
return baseUrls;
};
- export const UploadImages = async (sources: Doc[], album?: AlbumReference, descriptionKey = "caption"): Promise<Opt<NewMediaItemResult[]>> => {
+ export interface ImageUploadResults {
+ results: NewMediaItemResult[];
+ failed: number[];
+ }
+
+ export const UploadImages = async (sources: Doc[], album?: AlbumReference, descriptionKey = "caption"): Promise<Opt<ImageUploadResults>> => {
if (album && "title" in album) {
album = await Create.Album(album.title);
}
@@ -331,8 +350,8 @@ export namespace GooglePhotos {
media.push({ url, description });
}
if (media.length) {
- const uploads: NewMediaItemResult[] = await Identified.PostToServer(RouteStore.googlePhotosMediaUpload, { media, album });
- return uploads;
+ const results = await Identified.PostToServer(RouteStore.googlePhotosMediaUpload, { media, album });
+ return results;
}
};