aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2019-09-10 20:01:55 -0400
committerSam Wilkins <samwilkins333@gmail.com>2019-09-10 20:01:55 -0400
commit628eef55533118b1f2312b86b2ac5f7b64f7fc4a (patch)
tree00747c1b6ee2b9f120108f6d19a5b017c3c1f419 /src
parentb24c475d8cd36af860fc374b0c5621b0d096be1d (diff)
lots of refactoring, beginning autotagging
Diffstat (limited to 'src')
-rw-r--r--src/Utils.ts5
-rw-r--r--src/client/apis/google_docs/GooglePhotosClientUtils.ts339
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx5
-rw-r--r--src/client/views/MainView.tsx18
-rw-r--r--src/client/views/nodes/DocumentView.tsx7
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx6
-rw-r--r--src/new_fields/RichTextUtils.ts13
-rw-r--r--src/server/apis/google/GooglePhotosUploadUtils.ts7
-rw-r--r--src/server/apis/google/SharedTypes.ts21
-rw-r--r--src/server/credentials/google_docs_token.json2
-rw-r--r--src/server/index.ts21
11 files changed, 284 insertions, 160 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 959b89fe5..71d88683a 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -37,6 +37,11 @@ export class Utils {
public static prepend(extension: string): string {
return window.location.origin + extension;
}
+
+ public static fileUrl(filename: string): string {
+ return this.prepend(`/file/${filename}`);
+ }
+
public static CorsProxy(url: string): string {
return this.prepend(RouteStore.corsProxy + "/") + encodeURIComponent(url);
}
diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index fddcf3aa5..b1de24d1a 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -1,8 +1,8 @@
import { PostToServer, Utils } from "../../../Utils";
import { RouteStore } from "../../../server/RouteStore";
import { ImageField } from "../../../new_fields/URLField";
-import { Cast } from "../../../new_fields/Types";
-import { Doc, Opt } from "../../../new_fields/Doc";
+import { Cast, StrCast } from "../../../new_fields/Types";
+import { Doc, Opt, DocListCastAsync } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import Photos = require('googlephotos');
import { RichTextField } from "../../../new_fields/RichTextField";
@@ -10,32 +10,15 @@ import { RichTextUtils } from "../../../new_fields/RichTextUtils";
import { EditorState } from "prosemirror-state";
import { FormattedTextBox } from "../../views/nodes/FormattedTextBox";
import { Docs, DocumentOptions } from "../../documents/Documents";
-import { type } from "os";
-
-export namespace GooglePhotosClientUtils {
-
- export enum ContentCategories {
- NONE = 'NONE',
- LANDSCAPES = 'LANDSCAPES',
- RECEIPTS = 'RECEIPTS',
- CITYSCAPES = 'CITYSCAPES',
- LANDMARKS = 'LANDMARKS',
- SELFIES = 'SELFIES',
- PEOPLE = 'PEOPLE',
- PETS = 'PETS',
- WEDDINGS = 'WEDDINGS',
- BIRTHDAYS = 'BIRTHDAYS',
- DOCUMENTS = 'DOCUMENTS',
- TRAVEL = 'TRAVEL',
- ANIMALS = 'ANIMALS',
- FOOD = 'FOOD',
- SPORT = 'SPORT',
- NIGHT = 'NIGHT',
- PERFORMANCES = 'PERFORMANCES',
- WHITEBOARDS = 'WHITEBOARDS',
- SCREENSHOTS = 'SCREENSHOTS',
- UTILITY = 'UTILITY'
- }
+import { MediaItemCreationResult, NewMediaItemResult, MediaItem } from "../../../server/apis/google/SharedTypes";
+
+export namespace GooglePhotos {
+
+ const endpoint = async () => {
+ const getToken = Utils.prepend(RouteStore.googlePhotosAccessToken);
+ const token = await (await fetch(getToken)).text();
+ return new Photos(token);
+ };
export enum MediaType {
ALL_MEDIA = 'ALL_MEDIA',
@@ -44,118 +27,238 @@ export namespace GooglePhotosClientUtils {
}
export type AlbumReference = { id: string } | { title: string };
- export const endpoint = () => fetch(Utils.prepend(RouteStore.googlePhotosAccessToken)).then(async response => new Photos(await response.text()));
export interface MediaInput {
url: string;
description: string;
}
- export const UploadImages = async (sources: (Doc | string)[], album?: AlbumReference, descriptionKey = "caption") => {
- if (album && "title" in album) {
- album = await (await endpoint()).albums.create(album.title);
+ export const ContentCategories = {
+ NONE: 'NONE',
+ LANDSCAPES: 'LANDSCAPES',
+ RECEIPTS: 'RECEIPTS',
+ CITYSCAPES: 'CITYSCAPES',
+ LANDMARKS: 'LANDMARKS',
+ SELFIES: 'SELFIES',
+ PEOPLE: 'PEOPLE',
+ PETS: 'PETS',
+ WEDDINGS: 'WEDDINGS',
+ BIRTHDAYS: 'BIRTHDAYS',
+ DOCUMENTS: 'DOCUMENTS',
+ TRAVEL: 'TRAVEL',
+ ANIMALS: 'ANIMALS',
+ FOOD: 'FOOD',
+ SPORT: 'SPORT',
+ NIGHT: 'NIGHT',
+ PERFORMANCES: 'PERFORMANCES',
+ WHITEBOARDS: 'WHITEBOARDS',
+ SCREENSHOTS: 'SCREENSHOTS',
+ UTILITY: 'UTILITY'
+ };
+
+ export namespace Export {
+
+ export interface AlbumCreationResult {
+ albumId: string;
+ mediaItems: MediaItem[];
}
- const media: MediaInput[] = [];
- sources.forEach(source => {
- let url: string;
- let description: string;
- if (source instanceof Doc) {
- const data = Cast(Doc.GetProto(source).data, ImageField);
- if (!data) {
- return;
+
+ export const CollectionToAlbum = async (collection: Doc, title?: string, descriptionKey?: string): Promise<Opt<AlbumCreationResult>> => {
+ const dataDocument = Doc.GetProto(collection);
+ const images = ((await DocListCastAsync(dataDocument.data)) || []).filter(doc => Cast(doc.data, ImageField));
+ if (!images || !images.length) {
+ return undefined;
+ }
+ const resolved = title ? title : (StrCast(collection.title) || `Dash Collection (${collection[Id]}`);
+ const { id } = await Create.Album(resolved);
+ const result = await Transactions.UploadImages(images, { id }, descriptionKey);
+ if (result) {
+ const mediaItems = result.newMediaItemResults.map(item => item.mediaItem);
+ return { albumId: id, mediaItems };
+ }
+ };
+
+ }
+
+ export namespace Import {
+
+ export type CollectionConstructor = (data: Array<Doc>, options: DocumentOptions, ...args: any) => Doc;
+
+ export const CollectionFromSearch = async (constructor: CollectionConstructor, requested: Opt<Partial<Query.SearchOptions>>): Promise<Doc> => {
+ let response = await Query.Search(requested);
+ let uploads = await Transactions.WriteMediaItemsToServer(response);
+ const children = uploads.map((upload: Transactions.UploadInformation) => {
+ let document = Docs.Create.ImageDocument(Utils.fileUrl(upload.fileNames.clean));
+ document.fillColumn = true;
+ document.contentSize = upload.contentSize;
+ return document;
+ });
+ const options = { width: 500, height: 500 };
+ return constructor(children, options);
+ };
+
+ }
+
+ export namespace Query {
+
+ export const AppendImageMetadata = (sources: (Doc | string)[]) => {
+ let keys = Object.keys(ContentCategories);
+ let included: string[] = [];
+ let excluded: string[] = [];
+ for (let i = 0; i < keys.length; i++) {
+ for (let j = 0; j < keys.length; j++) {
+ let value = ContentCategories[keys[i] as keyof typeof ContentCategories];
+ if (j === i) {
+ included.push(value);
+ } else {
+ excluded.push(value);
+ }
}
- url = data.url.href;
- description = parseDescription(source, descriptionKey);
- } else {
- url = source;
- description = Utils.GenerateGuid();
+ //...
+ included = excluded = [];
}
- media.push({ url, description });
- });
- if (media.length) {
- return PostToServer(RouteStore.googlePhotosMediaUpload, { media, album });
+ };
+
+ interface DateRange {
+ after: Date;
+ before: Date;
}
- };
- const parseDescription = (document: Doc, descriptionKey: string) => {
- let description: string = Utils.prepend("/doc/" + document[Id]);
- const target = document[descriptionKey];
- if (typeof target === "string") {
- description = target;
- } else if (target instanceof RichTextField) {
- description = RichTextUtils.ToPlainText(EditorState.fromJSON(FormattedTextBox.Instance.config, JSON.parse(target.Data)));
+ const DefaultSearchOptions: SearchOptions = {
+ pageSize: 20,
+ included: [],
+ excluded: [],
+ date: undefined,
+ includeArchivedMedia: true,
+ type: MediaType.ALL_MEDIA,
+ };
+
+ export interface SearchOptions {
+ pageSize: number;
+ included: ContentCategories[];
+ excluded: ContentCategories[];
+ date: Opt<Date | DateRange>;
+ includeArchivedMedia: boolean;
+ type: MediaType;
}
- return description;
- };
- export interface DateRange {
- after: Date;
- before: Date;
- }
- export interface SearchOptions {
- pageSize: number;
- included: ContentCategories[];
- excluded: ContentCategories[];
- date: Opt<Date | DateRange>;
- includeArchivedMedia: boolean;
- type: MediaType;
+ export interface SearchResponse {
+ mediaItems: any[];
+ nextPageToken: string;
+ }
+
+ export const Search = async (requested: Opt<Partial<SearchOptions>>): Promise<SearchResponse> => {
+ const options = requested || DefaultSearchOptions;
+ const photos = await endpoint();
+ const filters = new photos.Filters(options.includeArchivedMedia === undefined ? true : options.includeArchivedMedia);
+
+ const included = options.included || [];
+ const excluded = options.excluded || [];
+ const contentFilter = new photos.ContentFilter();
+ included.length && included.forEach(category => contentFilter.addIncludedContentCategories(category));
+ excluded.length && excluded.forEach(category => contentFilter.addExcludedContentCategories(category));
+ filters.setContentFilter(contentFilter);
+
+ const date = options.date;
+ if (date) {
+ const dateFilter = new photos.DateFilter();
+ if (date instanceof Date) {
+ dateFilter.addDate(date);
+ } else {
+ dateFilter.addRange(date.after, date.before);
+ }
+ filters.setDateFilter(dateFilter);
+ }
+
+ filters.setMediaTypeFilter(new photos.MediaTypeFilter(options.type || MediaType.ALL_MEDIA));
+
+ return new Promise<SearchResponse>(resolve => {
+ photos.mediaItems.search(filters, options.pageSize || 20).then(resolve);
+ });
+ };
+
+ export const GetImage = async (mediaItemId: string): Promise<Transactions.MediaItem> => {
+ return (await endpoint()).mediaItems.get(mediaItemId);
+ };
+
}
- const DefaultSearchOptions: SearchOptions = {
- pageSize: 20,
- included: [],
- excluded: [],
- date: undefined,
- includeArchivedMedia: true,
- type: MediaType.ALL_MEDIA,
- };
+ export namespace Create {
+
+ export const Album = async (title: string) => {
+ return (await endpoint()).albums.create(title);
+ };
- export interface SearchResponse {
- mediaItems: any[];
- nextPageToken: string;
}
- export type CollectionConstructor = (data: Array<Doc>, options: DocumentOptions, ...args: any) => Doc;
- export const CollectionFromSearch = async (provider: CollectionConstructor, requested: Opt<Partial<SearchOptions>>): Promise<Doc> => {
- let downloads = await Search(requested);
- return provider(downloads.map((download: any) => {
- let document = Docs.Create.ImageDocument(Utils.prepend(`/files/${download.fileNames.clean}`));
- document.fillColumn = true;
- document.contentSize = download.contentSize;
- return document;
- }), { width: 500, height: 500 });
- };
+ export namespace Transactions {
- export const Search = async (requested: Opt<Partial<SearchOptions>>): Promise<any> => {
- const options = requested || DefaultSearchOptions;
- const photos = await endpoint();
- const filters = new photos.Filters(options.includeArchivedMedia === undefined ? true : options.includeArchivedMedia);
-
- const included = options.included || [];
- const excluded = options.excluded || [];
- const contentFilter = new photos.ContentFilter();
- included.length && included.forEach(category => contentFilter.addIncludedContentCategories(category));
- excluded.length && excluded.forEach(category => contentFilter.addExcludedContentCategories(category));
- filters.setContentFilter(contentFilter);
-
- const date = options.date;
- if (date) {
- const dateFilter = new photos.DateFilter();
- if (date instanceof Date) {
- dateFilter.addDate(date);
- } else {
- dateFilter.addRange(date.after, date.before);
- }
- filters.setDateFilter(dateFilter);
+ export interface UploadInformation {
+ mediaPaths: string[];
+ fileNames: { [key: string]: string };
+ contentSize?: number;
+ contentType?: string;
+ }
+
+ export interface MediaItem {
+ id: string;
+ filename: string;
+ baseUrl: string;
}
- filters.setMediaTypeFilter(new photos.MediaTypeFilter(options.type || MediaType.ALL_MEDIA));
+ export const WriteMediaItemsToServer = async (body: { mediaItems: any[] }): Promise<UploadInformation[]> => {
+ const uploads = await PostToServer(RouteStore.googlePhotosMediaDownload, body);
+ return uploads;
+ };
- return new Promise<Doc>(resolve => {
- photos.mediaItems.search(filters, options.pageSize || 20).then(async (response: SearchResponse) => {
- response && resolve(await PostToServer(RouteStore.googlePhotosMediaDownload, response));
+ export const UploadThenFetch = async (sources: (Doc | string)[], album?: AlbumReference, descriptionKey = "caption") => {
+ const result = await UploadImages(sources, album, descriptionKey);
+ if (!result) {
+ return undefined;
+ }
+ const baseUrls: string[] = await Promise.all(result.newMediaItemResults.map((result: any) => {
+ return new Promise<string>(resolve => Query.GetImage(result.mediaItem.id).then(item => resolve(item.baseUrl)));
+ }));
+ return baseUrls;
+ };
+
+ export const UploadImages = async (sources: (Doc | string)[], album?: AlbumReference, descriptionKey = "caption"): Promise<Opt<MediaItemCreationResult>> => {
+ if (album && "title" in album) {
+ album = await Create.Album(album.title);
+ }
+ const media: MediaInput[] = [];
+ sources.forEach(source => {
+ let url: string;
+ let description: string;
+ if (source instanceof Doc) {
+ const data = Cast(Doc.GetProto(source).data, ImageField);
+ if (!data) {
+ return;
+ }
+ url = data.url.href;
+ description = parseDescription(source, descriptionKey);
+ } else {
+ url = source;
+ description = Utils.GenerateGuid();
+ }
+ media.push({ url, description });
});
- });
- };
+ if (media.length) {
+ return PostToServer(RouteStore.googlePhotosMediaUpload, { media, album });
+ }
+ };
+
+ const parseDescription = (document: Doc, descriptionKey: string) => {
+ let description: string = Utils.prepend("/doc/" + document[Id]);
+ const target = document[descriptionKey];
+ if (typeof target === "string") {
+ description = target;
+ } else if (target instanceof RichTextField) {
+ description = RichTextUtils.ToPlainText(EditorState.fromJSON(FormattedTextBox.Instance.config, JSON.parse(target.Data)));
+ }
+ return description;
+ };
+
+ }
} \ No newline at end of file
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index a19fd39b7..d58c02ce5 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -18,7 +18,7 @@ import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { Cast, BoolCast, NumCast } from "../../../new_fields/Types";
import { listSpec } from "../../../new_fields/Schema";
-import { GooglePhotosClientUtils } from "../../apis/google_docs/GooglePhotosClientUtils";
+import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils";
const unsupported = ["text/html", "text/plain"];
interface FileResponse {
@@ -117,8 +117,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
console.log(`(${this.quota - this.remaining}/${this.quota}) ${upload.name}`);
}));
- await GooglePhotosClientUtils.UploadImages(docs, { title: directory });
- console.log("Finished upload!");
+ await GooglePhotos.Transactions.UploadImages(docs, { title: directory });
for (let i = 0; i < docs.length; i++) {
let doc = docs[i];
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 0c0ed9072..8d10a91ce 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -40,7 +40,7 @@ import { PreviewCursor } from './PreviewCursor';
import { FilterBox } from './search/FilterBox';
import PresModeMenu from './presentationview/PresentationModeMenu';
import { PresBox } from './nodes/PresBox';
-import { GooglePhotosClientUtils } from '../apis/google_docs/GooglePhotosClientUtils';
+import { GooglePhotos } from '../apis/google_docs/GooglePhotosClientUtils';
import { ImageField } from '../../new_fields/URLField';
import { LinkFollowBox } from './linking/LinkFollowBox';
import { DocumentManager } from '../util/DocumentManager';
@@ -149,14 +149,14 @@ export class MainView extends React.Component {
}, { fireImmediately: true });
}
- executeGooglePhotosRoutine = async () => {
- let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg";
- let doc = Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" });
- doc.caption = "Well isn't this a nice cat image!";
- let photos = await GooglePhotosClientUtils.endpoint();
- let albumId = (await photos.albums.list(50)).albums.filter((album: any) => album.title === "This is a generically created album!")[0].id;
- console.log(await GooglePhotosClientUtils.UploadImages([doc], { id: albumId }));
- }
+ // executeGooglePhotosRoutine = async () => {
+ // let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg";
+ // let doc = Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" });
+ // doc.caption = "Well isn't this a nice cat image!";
+ // let photos = await GooglePhotos.endpoint();
+ // let albumId = (await photos.albums.list(50)).albums.filter((album: any) => album.title === "This is a generically created album!")[0].id;
+ // console.log(await GooglePhotos.UploadImages([doc], { id: albumId }));
+ // }
componentWillUnMount() {
window.removeEventListener("keydown", KeyManager.Instance.handle);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index cb9346a8b..a51f783ad 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -41,7 +41,7 @@ import "./DocumentView.scss";
import { FormattedTextBox } from './FormattedTextBox';
import React = require("react");
import { DocumentType } from '../../documents/DocumentTypes';
-import { GooglePhotosClientUtils } from '../../apis/google_docs/GooglePhotosClientUtils';
+import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { ImageField } from '../../../new_fields/URLField';
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
@@ -591,7 +591,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" });
cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" });
if (Cast(this.props.Document.data, ImageField)) {
- cm.addItem({ description: "Export to Google Photos", event: () => GooglePhotosClientUtils.UploadImages([this.props.Document]), icon: "caret-square-right" });
+ cm.addItem({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
+ }
+ if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) {
+ cm.addItem({ description: "Export to Google Photos Album", event: () => GooglePhotos.Export.CollectionToAlbum(this.props.Document).then(console.log), icon: "caret-square-right" });
}
let existingMake = ContextMenu.Instance.findByDescription("Make...");
let makes: ContextMenuProps[] = existingMake && "subitems" in existingMake ? existingMake.subitems : [];
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index fda9ea33f..9d83fbd04 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -177,7 +177,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
syncNodeSelection(view: any, sel: any) {
if (sel instanceof NodeSelection) {
var desc = view.docView.descAt(sel.from);
- if (desc != view.lastSelectedViewDesc) {
+ if (desc !== view.lastSelectedViewDesc) {
if (view.lastSelectedViewDesc) {
view.lastSelectedViewDesc.deselectNode();
view.lastSelectedViewDesc = null;
@@ -463,7 +463,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
let redo = async () => {
if (this._editorView && reference) {
- let content = RichTextUtils.GoogleDocs.Export(this._editorView.state);
+ let content = await RichTextUtils.GoogleDocs.Export(this._editorView.state);
let response = await GoogleApiClientUtils.Docs.write({ reference, content, mode });
response && (this.dataDoc[GoogleRef] = response.documentId);
let pushSuccess = response !== undefined && !("errors" in response);
@@ -636,7 +636,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
image(node, view, getPos) { return new ImageResizeView(node, view, getPos); },
star(node, view, getPos) { return new SummarizedView(node, view, getPos); },
ordered_list(node, view, getPos) { return new OrderedListView(node, view, getPos); },
- footnote(node, view, getPos) { return new FootnoteView(node, view, getPos) }
+ footnote(node, view, getPos) { return new FootnoteView(node, view, getPos); }
},
clipboardTextSerializer: this.clipboardTextSerializer,
handlePaste: this.handlePaste,
diff --git a/src/new_fields/RichTextUtils.ts b/src/new_fields/RichTextUtils.ts
index 500b93676..27737782b 100644
--- a/src/new_fields/RichTextUtils.ts
+++ b/src/new_fields/RichTextUtils.ts
@@ -11,7 +11,7 @@ import { Utils, PostToServer } from "../Utils";
import { RouteStore } from "../server/RouteStore";
import { Docs } from "../client/documents/Documents";
import { schema } from "../client/util/RichTextSchema";
-import { GooglePhotosClientUtils } from "../client/apis/google_docs/GooglePhotosClientUtils";
+import { GooglePhotos } from "../client/apis/google_docs/GooglePhotosClientUtils";
export namespace RichTextUtils {
@@ -90,7 +90,7 @@ export namespace RichTextUtils {
export namespace GoogleDocs {
- export const Export = (state: EditorState): GoogleApiClientUtils.Docs.Content => {
+ export const Export = async (state: EditorState): Promise<GoogleApiClientUtils.Docs.Content> => {
let nodes: { [type: string]: Node<any>[] } = {
text: [],
image: []
@@ -107,7 +107,7 @@ export namespace RichTextUtils {
}
}));
let linkRequests = ExtractLinks(nodes.text);
- let imageRequests = ExtractImages(nodes.image);
+ let imageRequests = await ExtractImages(nodes.image);
return {
text,
requests: [...linkRequests, ...imageRequests]
@@ -298,10 +298,13 @@ export namespace RichTextUtils {
const length = node.nodeSize;
const attrs = node.attrs;
const uri = attrs.src;
- const result = (await GooglePhotosClientUtils.UploadImages([uri])).newMediaItemResults;
+ const baseUrls = await GooglePhotos.Transactions.UploadThenFetch([uri]);
+ if (!baseUrls) {
+ continue;
+ }
images.push({
insertInlineImage: {
- uri: result[0].mediaItem.productUrl,
+ uri: baseUrls[0],
objectSize: { width: { magnitude: parseFloat(attrs.width.replace("px", "")), unit: "PT" } },
location: { index: position + length }
}
diff --git a/src/server/apis/google/GooglePhotosUploadUtils.ts b/src/server/apis/google/GooglePhotosUploadUtils.ts
index d1f1f81bd..447ed23ac 100644
--- a/src/server/apis/google/GooglePhotosUploadUtils.ts
+++ b/src/server/apis/google/GooglePhotosUploadUtils.ts
@@ -5,6 +5,7 @@ import { Utils } from '../../../Utils';
import * as path from 'path';
import { Opt } from '../../../new_fields/Doc';
import * as sharp from 'sharp';
+import { MediaItemCreationResult } from './SharedTypes';
const uploadDirectory = path.join(__dirname, "../../public/files/");
@@ -52,8 +53,10 @@ export namespace GooglePhotosUploadUtils {
return new Promise<any>(resolve => request(parameters, (error, _response, body) => resolve(error ? undefined : body)));
};
- export const CreateMediaItems = (newMediaItems: any[], album?: { id: string }) => {
- return new Promise<any>((resolve, reject) => {
+
+
+ export const CreateMediaItems = (newMediaItems: any[], album?: { id: string }): Promise<MediaItemCreationResult> => {
+ return new Promise<MediaItemCreationResult>((resolve, reject) => {
const parameters = {
method: 'POST',
headers: headers('json'),
diff --git a/src/server/apis/google/SharedTypes.ts b/src/server/apis/google/SharedTypes.ts
new file mode 100644
index 000000000..9ad6130b6
--- /dev/null
+++ b/src/server/apis/google/SharedTypes.ts
@@ -0,0 +1,21 @@
+export interface NewMediaItemResult {
+ uploadToken: string;
+ status: { code: number, message: string };
+ mediaItem: MediaItem;
+}
+
+export interface MediaItem {
+ id: string;
+ description: string;
+ productUrl: string;
+ baseUrl: string;
+ mimeType: string;
+ mediaMetadata: {
+ creationTime: string;
+ width: string;
+ height: string;
+ };
+ filename: string;
+}
+
+export type MediaItemCreationResult = { newMediaItemResults: NewMediaItemResult[] }; \ No newline at end of file
diff --git a/src/server/credentials/google_docs_token.json b/src/server/credentials/google_docs_token.json
index 5c142fba1..22d57d744 100644
--- a/src/server/credentials/google_docs_token.json
+++ b/src/server/credentials/google_docs_token.json
@@ -1 +1 @@
-{"access_token":"ya29.Glx_B6G7Q_FYs1LK5VcyV6P6Zg9JkoHO2aC_TsnN7AVxPYWHEpsBSC0WyWX7Ztr8HWhOUYA5JXqnZDkLrK1V3Hb-0GgtyApLRNtEPOWf1dJ7lOm_iKVw2tRvPe7XDQ","refresh_token":"1/HTv_xFHszu2Nf3iiFrUTaeKzC_Vp2-6bpIB06xW_WHI","scope":"https://www.googleapis.com/auth/presentations.readonly https://www.googleapis.com/auth/documents.readonly https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/documents https://www.googleapis.com/auth/photoslibrary https://www.googleapis.com/auth/photoslibrary.appendonly https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/presentations https://www.googleapis.com/auth/photoslibrary.sharing","token_type":"Bearer","expiry_date":1568078116605} \ No newline at end of file
+{"access_token":"ya29.GlyAB5T3dgJqWuYBcLaT94wQo7MZkmzJQZxDB2sSU95mdhW24E3diuFdLeNsUDVI57D3S765RweMnL98d-fdgu1dRxpzkV_J_3rLih99pZ8A4d6jVdm1354UT4py_w","refresh_token":"1/HTv_xFHszu2Nf3iiFrUTaeKzC_Vp2-6bpIB06xW_WHI","scope":"https://www.googleapis.com/auth/presentations.readonly https://www.googleapis.com/auth/documents.readonly https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/documents https://www.googleapis.com/auth/photoslibrary https://www.googleapis.com/auth/photoslibrary.appendonly https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/presentations https://www.googleapis.com/auth/photoslibrary.sharing","token_type":"Bearer","expiry_date":1568161931458} \ No newline at end of file
diff --git a/src/server/index.ts b/src/server/index.ts
index 8469770d5..2c3e76c55 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -795,19 +795,6 @@ const EndpointHandlerMap = new Map<GoogleApiServerUtils.Action, GoogleApiServerU
["update", (api, params) => api.batchUpdate(params)],
]);
-// app.post(RouteStore.googleDocsGet, async (req, res) => {
-// const token = await GoogleApiServerUtils.RetrieveAccessToken({ credentialsPath, tokenPath });
-// request_promise.get({
-// uri: `https://docs.googleapis.com/v1/documents/${req.body.documentId}?fields=inlineObjects`,
-// headers: {
-// 'Authorization': `Bearer ${token}`
-// }
-// }).then(response => {
-// console.log(response);
-// res.send(response);
-// });
-// });
-
app.post(RouteStore.googleDocs + "/:sector/:action", (req, res) => {
let sector: GoogleApiServerUtils.Service = req.params.sector;
let action: GoogleApiServerUtils.Action = req.params.action;
@@ -841,11 +828,11 @@ app.post(RouteStore.googlePhotosMediaUpload, async (req, res) => {
};
}));
if (!newMediaItems.every(item => item)) {
- return res.status(STATUS.EXECUTION_ERROR).send(tokenError);
+ return _error(res, tokenError);
}
GooglePhotosUploadUtils.CreateMediaItems(newMediaItems, req.body.album).then(
- success => res.status(STATUS.OK).send(success),
- () => res.status(STATUS.EXECUTION_ERROR).send(mediaError)
+ mediaItems => _success(res, mediaItems),
+ error => _error(res, mediaError, error)
);
});
@@ -871,7 +858,7 @@ app.post(RouteStore.googlePhotosMediaDownload, async (req, res) => {
_invalid(res, requestError);
});
-const _error = (res: Response, message: string, error: any) => {
+const _error = (res: Response, message: string, error?: any) => {
res.statusMessage = message;
res.status(STATUS.EXECUTION_ERROR).send(error);
};