From 952318fb56cce16c428a5b41afe27a0ee4d20589 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 11 Apr 2020 21:50:07 -0400 Subject: fixed double click on freeformviews. changed signature for DocumentHierarchyFromJson() to have an 'all' parameter --- src/client/cognitive_services/CognitiveServices.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/cognitive_services') diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 3f3726621..e464aff55 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -192,7 +192,7 @@ export namespace CognitiveServices { let results = await ExecuteQuery(Service.Handwriting, Manager, inkData); if (results) { results.recognitionUnits && (results = results.recognitionUnits); - target[keys[0]] = Docs.Get.DocumentHierarchyFromJson(results, "Ink Analysis"); + target[keys[0]] = Docs.Get.DocumentHierarchyFromJson(results, "Ink Analysis", undefined, false); const recognizedText = results.map((item: any) => item.recognizedText); const recognizedObjects = results.map((item: any) => item.recognizedObject); const individualWords = recognizedText.filter((text: string) => text && text.split(" ").length === 1); -- cgit v1.2.3-70-g09d2 From 3f037a4426f17c0f542a06daceecead29e63803f Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sat, 11 Apr 2020 20:16:44 -0700 Subject: document from json improvements and documentation --- src/client/apis/youtube/YoutubeBox.tsx | 4 +- src/client/cognitive_services/CognitiveServices.ts | 2 +- src/client/documents/Documents.ts | 92 ++++++++++++++++------ .../util/Import & Export/DirectoryImportBox.tsx | 2 +- src/client/util/Import & Export/ImageUtils.ts | 2 +- src/client/views/nodes/ImageBox.tsx | 4 +- 6 files changed, 72 insertions(+), 34 deletions(-) (limited to 'src/client/cognitive_services') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 4e990537f..1575e53fc 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -156,14 +156,14 @@ export class YoutubeBox extends React.Component { @action processVideoDetails = (videoDetails: any[]) => { this.videoDetails = videoDetails; - this.props.Document.cachedDetails = Docs.Get.DocumentHierarchyFromJson(videoDetails, "detailBackUp", undefined, false); + this.props.Document.cachedDetails = Docs.Get.FromJson({ data: videoDetails, title: "detailBackUp" }); } /** * The function that stores the search results in the props document. */ backUpSearchResults = (videos: any[]) => { - this.props.Document.cachedSearchResults = Docs.Get.DocumentHierarchyFromJson(videos, "videosBackUp", undefined, false); + this.props.Document.cachedSearchResults = Docs.Get.FromJson({ data: videos, title: "videosBackUp" }); } /** diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index e464aff55..5d83de233 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -192,7 +192,7 @@ export namespace CognitiveServices { let results = await ExecuteQuery(Service.Handwriting, Manager, inkData); if (results) { results.recognitionUnits && (results = results.recognitionUnits); - target[keys[0]] = Docs.Get.DocumentHierarchyFromJson(results, "Ink Analysis", undefined, false); + target[keys[0]] = Docs.Get.FromJson({ data: results, title: "Ink Analysis" }); const recognizedText = results.map((item: any) => item.recognizedText); const recognizedObjects = results.map((item: any) => item.recognizedObject); const individualWords = recognizedText.filter((text: string) => text && text.split(" ").length === 1); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 30f72a6b2..ec864b268 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -405,7 +405,7 @@ export namespace Docs { const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true }); const deviceProto = Doc.GetProto(doc); deviceProto.hero = new ImageField(constructed[0].url); - Docs.Get.DocumentHierarchyFromJson(device, undefined, deviceProto, false); + Docs.Get.FromJson({ data: device, appendToExisting: { targetDoc: deviceProto } }); Doc.AddDocToList(parentProto, "data", doc); } else if (errors) { console.log(errors); @@ -712,6 +712,15 @@ export namespace Docs { const primitives = ["string", "number", "boolean"]; + export interface JsonConversionOpts { + data: any; + title?: string; + appendToExisting?: { targetDoc: Doc, fieldKey?: string }; + excludeEmptyObjects?: boolean; + } + + const defaultKey = "json"; + /** * This function takes any valid JSON(-like) data, i.e. parsed or unparsed, and at arbitrarily * deep levels of nesting, converts the data and structure into nested documents with the appropriate fields. @@ -729,25 +738,49 @@ export namespace Docs { * All TS/JS objects get converted directly to documents, directly preserving the key value structure. Everything else, * lacking the key value structure, gets stored as a field in a wrapper document. * - * @param input for convenience and flexibility, either a valid JSON string to be parsed, + * @param data for convenience and flexibility, either a valid JSON string to be parsed, * or the result of any JSON.parse() call. - * @param title an optional title to give to the highest parent document in the hierarchy - * @param appendToTarget -??? - * @param all whether fields should be converted even if they contain no data + * @param title an optional title to give to the highest parent document in the hierarchy. + * If whether this function creates a new document or appendToExisting is specified and that document already has a title, + * because this title field can be left undefined for the opposite behavior, including a title will overwrite the existing title. + * @param appendToExisting **if specified**, there are two cases: the json to be converted can be represented as a document, in which ase + * the target document will receive all the conversion results as new fields on it, or, if the json can't be represented as a document, + * the function will assign the converstion results to either the specified key on the target document, or to the "json" key by default. + * If not specified, will create a new entirely generic. + * + * One might choose to specify this field if you want to write to a document returned from a Document.Create function call, + * say a TreeView document that will be rendered, not just an untyped, identityless doc that would otherwise be created + * from a default call to new Doc. + * + * @param excludeEmptyObjects whether non-primitive objects (TypeScript objects and arrays) should be converted even + * if they contain no data. By default, empty objects and arrays are ignored. */ - export function DocumentHierarchyFromJson(input: any, title: string, appendToTarget: Opt, all?:boolean): Opt { - if (input === undefined || input === null || ![...primitives, "object"].includes(typeof input)) { + export function FromJson({ data, title, appendToExisting, excludeEmptyObjects }: JsonConversionOpts): Opt { + if (excludeEmptyObjects === undefined) { + excludeEmptyObjects = true; + } + if (data === undefined || data === null || ![...primitives, "object"].includes(typeof data)) { + return undefined; + } + let resolved: any; + try { + resolved = JSON.parse(typeof data === "string" ? data : JSON.stringify(data)); + } catch (e) { return undefined; } - input = JSON.parse(typeof input === "string" ? input : JSON.stringify(input)); - let converted: Opt; - if (typeof input === "object" && !(input instanceof Array)) { - converted = convertObject(input, title, appendToTarget, all); + let output: Opt; + if (typeof resolved === "object" && !(resolved instanceof Array)) { + output = convertObject(resolved, excludeEmptyObjects, title, appendToExisting?.targetDoc); } else { - (converted = new Doc).json = toField(input); + const result = toField(resolved, excludeEmptyObjects); + if (appendToExisting) { + (output = appendToExisting.targetDoc)[appendToExisting.fieldKey || defaultKey] = result; + } else { + (output = new Doc).json = result; + } } - title && converted && (converted.title = title); - return converted; + title && output && (output.title = title); + return output; } /** @@ -757,13 +790,19 @@ export namespace Docs { * @returns the object mapped from JSON to field values, where each mapping * might involve arbitrary recursion (since toField might itself call convertObject) */ - const convertObject = (object: any, title?: string, target?: Doc, all?:boolean): Opt => { - const objkeys = Object.keys(object); - if (objkeys.length || all) { + const convertObject = (object: any, excludeEmptyObjects: boolean, title?: string, target?: Doc): Opt => { + const hasEntries = Object.keys(object).length; + if (hasEntries || !excludeEmptyObjects) { const resolved = target ?? new Doc; - let result: Opt; - Object.keys(object).map(key => (result = toField(object[key], key)) && (resolved[key] = result)); - title && !resolved.title && (resolved.title = title); + if (hasEntries) { + let result: Opt; + Object.keys(object).map(key => { + if (result = toField(object[key], excludeEmptyObjects, key)) { + (resolved[key] = result); + } + }); + } + title && (resolved.title = title); return resolved; } }; @@ -775,15 +814,16 @@ export namespace Docs { * @returns the list mapped from JSON to field values, where each mapping * might involve arbitrary recursion (since toField might itself call convertList) */ - const convertList = (list: Array): List => { + const convertList = (list: Array, excludeEmptyObjects: boolean): Opt> => { const target = new List(); let result: Opt; - list.map(item => (result = toField(item)) && target.push(result)); - return target; + list.map(item => (result = toField(item, excludeEmptyObjects)) && target.push(result)); + if (target.length || !excludeEmptyObjects) { + return target; + } }; - - const toField = (data: any, title?: string, all?:boolean): Opt => { + const toField = (data: any, excludeEmptyObjects: boolean, title?: string): Opt => { if (data === null || data === undefined) { return undefined; } @@ -791,7 +831,7 @@ export namespace Docs { return data; } if (typeof data === "object") { - return data instanceof Array ? convertList(data) : convertObject(data, title, undefined, all); + return data instanceof Array ? convertList(data, excludeEmptyObjects) : convertObject(data, excludeEmptyObjects, title, undefined); } throw new Error(`How did ${data} of type ${typeof data} end up in JSON?`); }; diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index 01a0eddf8..438904688 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -126,7 +126,7 @@ export default class DirectoryImportBox extends React.Component const document = await Docs.Get.DocumentFromType(type, path, { _width: 300, title: name }); const { data, error } = exifData; if (document) { - Doc.GetProto(document).exif = error || Docs.Get.DocumentHierarchyFromJson(data, "", undefined, false); + Doc.GetProto(document).exif = error || Docs.Get.FromJson({ data }); docs.push(document); } })); diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts index 135454a33..9fae5ff93 100644 --- a/src/client/util/Import & Export/ImageUtils.ts +++ b/src/client/util/Import & Export/ImageUtils.ts @@ -20,7 +20,7 @@ export namespace ImageUtils { nativeHeight, exifData: { error, data } } = await Networking.PostToServer("/inspectImage", { source }); - document.exif = error || Docs.Get.DocumentHierarchyFromJson(data, "", undefined, false); + document.exif = error || Docs.Get.FromJson({ data }); const proto = Doc.GetProto(document); proto["data-nativeWidth"] = nativeWidth; proto["data-nativeHeight"] = nativeHeight; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index c45f14975..de1640027 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -186,9 +186,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent { const converter = (results: any) => { - const faceDocs = new List(); - results.reduce((face: CognitiveServices.Image.Face, faceDocs: List) => faceDocs.push(Docs.Get.DocumentHierarchyFromJson(face, `Face: ${face.faceId}`, undefined, false)!), new List()); - return faceDocs; + return results.map((face: CognitiveServices.Image.Face) => Docs.Get.FromJson({ data: face, title: `Face: ${face.faceId}` })!); }; this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + "-faces"], this.url, Service.Face, converter); } -- cgit v1.2.3-70-g09d2 From 348e794e7ff341a4358be6ed9515fc6ae723092e Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sun, 12 Apr 2020 21:36:26 -0700 Subject: rough pass at collection map view --- package-lock.json | 68 +++++++++++++++----- package.json | 6 +- src/client/Network.ts | 13 ++++ src/client/cognitive_services/CognitiveServices.ts | 1 - src/client/documents/Documents.ts | 9 ++- src/client/views/Main.tsx | 1 - .../views/collections/CollectionMapView.scss | 4 ++ src/client/views/collections/CollectionMapView.tsx | 72 ++++++++++++++++++++++ src/client/views/collections/CollectionView.tsx | 10 ++- src/new_fields/Types.ts | 1 + webpack.config.js | 55 ++++++++++------- 11 files changed, 195 insertions(+), 45 deletions(-) create mode 100644 src/client/views/collections/CollectionMapView.scss create mode 100644 src/client/views/collections/CollectionMapView.tsx (limited to 'src/client/cognitive_services') diff --git a/package-lock.json b/package-lock.json index 1949a8a5c..f40dc7b0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -584,6 +584,20 @@ "@types/node": "*" } }, + "@types/google-maps-react": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/google-maps-react/-/google-maps-react-2.0.4.tgz", + "integrity": "sha512-8EGr84L6ozODnfjWN9xVUFIxFedBbZinuDe5lBEJ757yh/lZqoKkqxrYLqvrLksDRVVwQYwWdFvfEASujmk36A==", + "requires": { + "@types/googlemaps": "*", + "@types/react": "*" + } + }, + "@types/googlemaps": { + "version": "3.39.3", + "resolved": "https://registry.npmjs.org/@types/googlemaps/-/googlemaps-3.39.3.tgz", + "integrity": "sha512-L8O9HAVFZj0TuiS8h5ORthiMsrrhjxTC8XUusp5k47oXCst4VTm+qWKvrAvmYMybZVokbp4Udco1mNwJrTNZPQ==" + }, "@types/isstream": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@types/isstream/-/isstream-0.1.0.tgz", @@ -750,7 +764,7 @@ }, "@types/passport": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.2.tgz", "integrity": "sha512-Pf39AYKf8q+YoONym3150cEwfUD66dtwHJWvbeOzKxnA0GZZ/vAXhNWv9vMhKyRQBQZiQyWQnhYBEBlKW6G8wg==", "requires": { "@types/express": "*" @@ -5615,7 +5629,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5633,11 +5648,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5650,15 +5667,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5761,7 +5781,8 @@ }, "inherits": { "version": "2.0.4", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5771,6 +5792,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5783,17 +5805,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.9.0", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5810,6 +5835,7 @@ "mkdirp": { "version": "0.5.1", "bundled": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5890,7 +5916,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5900,6 +5927,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -5975,7 +6003,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -6005,6 +6034,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6022,6 +6052,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6060,11 +6091,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.1.1", - "bundled": true + "bundled": true, + "optional": true } } }, @@ -6344,6 +6377,11 @@ } } }, + "google-maps-react": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/google-maps-react/-/google-maps-react-2.0.2.tgz", + "integrity": "sha512-6cYauGwt22haDUrWxKQ6yoNOqjiuxHo8YYcmb+aBvNICokdXmZOUB6Ah4vD5VexMVlrwP2PFqA/D8sHpEB52KA==" + }, "google-p12-pem": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.1.tgz", @@ -14376,7 +14414,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -15990,7 +16028,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -18303,7 +18341,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { "string-width": "^1.0.1", diff --git a/package.json b/package.json index 440646c48..c742857ad 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "scripts": { "start-release": "cross-env RELEASE=true NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts", - "start": "cross-env HANDWRITING='61088486d76c4b12ba578775a5f55422' NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts", + "start": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts", "debug": "cross-env NODE_OPTIONS=--max_old_space_size=8192 ts-node-dev --inspect -- src/server/index.ts", "build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 webpack --env production", "test": "mocha -r ts-node/register test/**/*.ts", @@ -83,6 +83,7 @@ "@types/express-validator": "^3.0.0", "@types/formidable": "^1.0.31", "@types/gapi": "0.0.39", + "@types/google-maps-react": "^2.0.4", "@types/jquery": "^3.3.31", "@types/jquery-awesome-cursor": "^0.3.0", "@types/jsonwebtoken": "^8.3.7", @@ -162,6 +163,7 @@ "formidable": "^1.2.1", "golden-layout": "^1.5.9", "google-auth-library": "^4.2.4", + "google-maps-react": "^2.0.2", "googleapis": "^40.0.0", "googlephotos": "^0.2.5", "howler": "^2.1.3", @@ -258,4 +260,4 @@ "xoauth2": "^1.2.0", "youtube": "^0.1.0" } -} +} \ No newline at end of file diff --git a/src/client/Network.ts b/src/client/Network.ts index 6982ecf19..bd0e6e61a 100644 --- a/src/client/Network.ts +++ b/src/client/Network.ts @@ -4,10 +4,23 @@ import { Upload } from "../server/SharedMediaTypes"; export namespace Networking { + const EnvVarCache = new Map(); + export async function FetchFromServer(relativeRoute: string) { return (await fetch(relativeRoute)).text(); } + export async function FetchEnvironmentVariable(varNameLiteral: string) { + let resolved = EnvVarCache.get(varNameLiteral); + if (!resolved) { + resolved = await FetchFromServer(`/environment/${varNameLiteral}`); + if (resolved !== undefined) { + EnvVarCache.set(varNameLiteral, resolved); + } + } + return resolved; + } + export async function PostToServer(relativeRoute: string, body?: any) { const options = { uri: Utils.prepend(relativeRoute), diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 5d83de233..3133bf4b1 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -8,7 +8,6 @@ import { UndoManager } from "../util/UndoManager"; import requestPromise = require("request-promise"); import { List } from "../../new_fields/List"; import { ClientRecommender } from "../ClientRecommender"; -import { ImageBox } from "../views/nodes/ImageBox"; type APIManager = { converter: BodyConverter, requester: RequestExecutor }; type RequestExecutor = (apiKey: string, body: string, service: Service) => Promise; diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 703c049cd..43e379125 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -49,6 +49,8 @@ import { ContextMenuProps } from "../views/ContextMenuItem"; import { ContextMenu } from "../views/ContextMenu"; import { LinkBox } from "../views/nodes/LinkBox"; import { ScreenshotBox } from "../views/nodes/ScreenshotBox"; +import CollectionMapView from "../views/collections/CollectionMapView"; +import LocationField, { LocationData } from "../../new_fields/LocationField"; const requestImageSize = require('../util/request-image-size'); const path = require('path'); @@ -277,8 +279,7 @@ export namespace Docs { }], [DocumentType.SCREENSHOT, { layout: { view: ScreenshotBox, dataField: data }, - options: {} - }] + }], ]); // All document prototypes are initialized with at least these values @@ -624,6 +625,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Linear }, id); } + export function MapDocument(documents: Array, options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), options); + } + export function CarouselDocument(documents: Array, options: DocumentOptions) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Carousel }); } diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 6d705aa44..b21eb9c8f 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -5,7 +5,6 @@ import * as ReactDOM from 'react-dom'; import * as React from 'react'; import { DocServer } from "../DocServer"; import { AssignAllExtensions } from "../../extensions/General/Extensions"; -process.env.HANDWRITING = "61088486d76c4b12ba578775a5f55422"; AssignAllExtensions(); diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss new file mode 100644 index 000000000..c74433902 --- /dev/null +++ b/src/client/views/collections/CollectionMapView.scss @@ -0,0 +1,4 @@ +.collectionMapView-contents { + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx new file mode 100644 index 000000000..49411f61b --- /dev/null +++ b/src/client/views/collections/CollectionMapView.tsx @@ -0,0 +1,72 @@ +import { observer } from "mobx-react"; +import { makeInterface } from "../../../new_fields/Schema"; +import { documentSchema } from "../../../new_fields/documentSchemas"; +import React = require("react"); +import { Map, Marker, MapProps, GoogleApiWrapper } from "google-maps-react"; +import { NumCast, StrCast } from "../../../new_fields/Types"; +import { CollectionSubView } from "./CollectionSubView"; +import { Utils } from "../../../Utils"; + +type MapDocument = makeInterface<[typeof documentSchema]>; +const MapDocument = makeInterface(documentSchema); + +export type LocationData = google.maps.LatLngLiteral & { address?: string }; + +@observer +class CollectionMapView extends CollectionSubView & { google: any }>(MapDocument) { + + render() { + const { childLayoutPairs, props } = this; + const { Document } = props; + const center: LocationData = { lat: NumCast(Document.mapCenterLat), lng: NumCast(Document.mapCenterLng) }; + if (!center.lat) { + center.lat = childLayoutPairs.length ? NumCast(childLayoutPairs[0].layout.locationLat, 0) : 0; + center.lng = childLayoutPairs.length ? NumCast(childLayoutPairs[0].layout.locationLng, 0) : 0; + } + return ( +
+ + {childLayoutPairs.map(({ layout }) => { + const location: LocationData = { + lat: NumCast(childLayoutPairs[0].layout.locationLat, 0), + lng: NumCast(childLayoutPairs[0].layout.locationLng, 0) + }; + const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45)); + return ( + { + Document.mapCenterLat = location.lat; + Document.mapCenterLng = location.lng; + }} + icon={{ + size: iconSize, + scaledSize: iconSize, + url: StrCast(Document.mapIconUrl, "https://www.pinclipart.com/picdir/middle/359-3598915_map-marker-icon-location-icon-png-clipart.png") + }} + /> + ); + })} + +
+ ); + } + +} + +declare var process: { + env: { + GOOGLE_MAPS_API_KEY: string; + } +}; + +export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS_API_KEY })(CollectionMapView) as any; \ No newline at end of file diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 821840e0b..d1dc32829 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,7 +1,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faEye, faEdit } from '@fortawesome/free-regular-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faColumns, faCopy, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons'; +import { faColumns, faCopy, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree, faGlobeAmericas } from '@fortawesome/free-solid-svg-icons'; import { action, observable, computed } from 'mobx'; import { observer } from "mobx-react"; import * as React from 'react'; @@ -44,12 +44,15 @@ import { Docs } from '../../documents/Documents'; import { ScriptField, ComputedField } from '../../../new_fields/ScriptField'; import { InteractionUtils } from '../../util/InteractionUtils'; import { ObjectField } from '../../../new_fields/ObjectField'; +import CollectionMapView from './CollectionMapView'; +import { ClientUtils } from '../../util/ClientUtils'; +import { GoogleApiWrapper } from 'google-maps-react'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; export const COLLECTION_BORDER_WIDTH = 2; const path = require('path'); -library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy); +library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faGlobeAmericas, faEllipsisV, faImage, faEye as any, faCopy); export enum CollectionViewType { Invalid = "invalid", @@ -65,6 +68,7 @@ export enum CollectionViewType { Carousel = "carousel", Linear = "linear", Staff = "staff", + Map = "map" } export interface CollectionRenderProps { @@ -170,6 +174,7 @@ export class CollectionView extends Touchable { case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (); } case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (); } case CollectionViewType.Time: { return (); } + case CollectionViewType.Map: return (); case CollectionViewType.Freeform: default: { this.props.Document._freeformLayoutEngine = undefined; return (); } } @@ -211,6 +216,7 @@ export class CollectionView extends Touchable { subItems.push({ description: "Masonry", event: () => this.props.Document._viewType = CollectionViewType.Masonry, icon: "columns" }); subItems.push({ description: "Carousel", event: () => this.props.Document._viewType = CollectionViewType.Carousel, icon: "columns" }); subItems.push({ description: "Pivot/Time", event: () => this.props.Document._viewType = CollectionViewType.Time, icon: "columns" }); + subItems.push({ description: "Map", event: () => this.props.Document._viewType = CollectionViewType.Map, icon: "globe-americas" }); switch (this.props.Document._viewType) { case CollectionViewType.Freeform: { subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) }); diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts index 0ca35fab2..aa44cefa0 100644 --- a/src/new_fields/Types.ts +++ b/src/new_fields/Types.ts @@ -87,6 +87,7 @@ export function BoolCast(field: FieldResult, defaultVal: boolean | null = false) export function DateCast(field: FieldResult) { return Cast(field, DateField, null); } + export function ScriptCast(field: FieldResult) { return Cast(field, ScriptField, null); } diff --git a/webpack.config.js b/webpack.config.js index 6a14dfcda..655334ef2 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -2,6 +2,14 @@ var path = require('path'); var webpack = require('webpack'); const CopyWebpackPlugin = require("copy-webpack-plugin"); const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); +const env = require('dotenv').config().parsed; +const envKeys = Object.keys(env).reduce((prev, next) => { + if (next.startsWith("DASH_")) { + const resolved = next.replace("DASH_", ""); + prev[`process.env.${resolved}`] = JSON.stringify(env[next]); + } + return prev; +}, {}); module.exports = { mode: 'development', @@ -33,17 +41,18 @@ module.exports = { extensions: ['.js', '.ts', '.tsx'] }, module: { - rules: [ - { + rules: [{ test: [/\.tsx?$/], - use: [ - { loader: 'ts-loader', options: { transpileOnly: true } } - ] + use: [{ + loader: 'ts-loader', + options: { + transpileOnly: true + } + }] }, { test: /\.scss|css$/, - use: [ - { + use: [{ loader: "style-loader" }, { @@ -56,28 +65,30 @@ module.exports = { }, { test: /\.(jpg|png|pdf)$/, - use: [ - { - loader: 'file-loader' - } - ] + use: [{ + loader: 'file-loader' + }] }, { test: /\.(png|jpg|gif)$/i, - use: [ - { - loader: 'url-loader', - options: { - limit: 8192 - } + use: [{ + loader: 'url-loader', + options: { + limit: 8192 } - ] - }] + }] + } + ] }, plugins: [ - new CopyWebpackPlugin([{ from: "deploy", to: path.join(__dirname, "build") }]), + new CopyWebpackPlugin([{ + from: "deploy", + to: path.join(__dirname, "build") + }]), + new webpack.DefinePlugin(envKeys), new ForkTsCheckerWebpackPlugin({ - tslint: true, useTypescriptIncrementalApi: true + tslint: true, + useTypescriptIncrementalApi: true }), new webpack.optimize.OccurrenceOrderPlugin(), new webpack.HotModuleReplacementPlugin(), -- cgit v1.2.3-70-g09d2 From e3a5dc3ce59b1d8f2dde63395505061b304d64c0 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 13 Apr 2020 00:03:13 -0700 Subject: completed migration to webpack-transferred environment variables, rather than a server route --- src/Utils.ts | 5 ----- src/client/Network.ts | 13 ------------- src/client/cognitive_services/CognitiveServices.ts | 2 +- src/client/views/collections/CollectionMapView.tsx | 8 +------- src/server/ApiManagers/UtilManager.ts | 13 ------------- webpack.config.js | 3 +-- 6 files changed, 3 insertions(+), 41 deletions(-) (limited to 'src/client/cognitive_services') diff --git a/src/Utils.ts b/src/Utils.ts index e3ec10dcd..a8cde0624 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -63,11 +63,6 @@ export namespace Utils { return prepend("/corsProxy/") + encodeURIComponent(url); } - export async function getApiKey(target: string): Promise { - const response = await fetch(prepend(`/environment/${target.toUpperCase()}`)); - return response.text(); - } - export function CopyText(text: string) { const textArea = document.createElement("textarea"); textArea.value = text; diff --git a/src/client/Network.ts b/src/client/Network.ts index bd0e6e61a..6982ecf19 100644 --- a/src/client/Network.ts +++ b/src/client/Network.ts @@ -4,23 +4,10 @@ import { Upload } from "../server/SharedMediaTypes"; export namespace Networking { - const EnvVarCache = new Map(); - export async function FetchFromServer(relativeRoute: string) { return (await fetch(relativeRoute)).text(); } - export async function FetchEnvironmentVariable(varNameLiteral: string) { - let resolved = EnvVarCache.get(varNameLiteral); - if (!resolved) { - resolved = await FetchFromServer(`/environment/${varNameLiteral}`); - if (resolved !== undefined) { - EnvVarCache.set(varNameLiteral, resolved); - } - } - return resolved; - } - export async function PostToServer(relativeRoute: string, body?: any) { const options = { uri: Utils.prepend(relativeRoute), diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 3133bf4b1..8c63ae906 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -45,7 +45,7 @@ export enum Confidence { export namespace CognitiveServices { const ExecuteQuery = async (service: Service, manager: APIManager, data: D): Promise => { - const apiKey = await Utils.getApiKey(service); + const apiKey = process.env[service.toUpperCase()]; if (!apiKey) { console.log(`No API key found for ${service}: ensure index.ts has access to a .env file in your root directory.`); return undefined; diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 6ab152836..a99d5be50 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -64,10 +64,4 @@ class CollectionMapView extends CollectionSubView } -declare var process: { - env: { - GOOGLE_MAPS_API_KEY: string; - } -}; - -export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS_API_KEY })(CollectionMapView) as any; \ No newline at end of file +export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS_API_KEY! })(CollectionMapView) as any; \ No newline at end of file diff --git a/src/server/ApiManagers/UtilManager.ts b/src/server/ApiManagers/UtilManager.ts index ad8119bf4..aec523cd0 100644 --- a/src/server/ApiManagers/UtilManager.ts +++ b/src/server/ApiManagers/UtilManager.ts @@ -14,19 +14,6 @@ export default class UtilManager extends ApiManager { protected initialize(register: Registration): void { - register({ - method: Method.GET, - subscription: new RouteSubscriber("environment").add("key"), - secureHandler: ({ req, res }) => { - const { key } = req.params; - const value = process.env[key]; - if (!value) { - console.log(red(`process.env.${key} is not defined.`)); - } - return res.send(value); - } - }); - // register({ // method: Method.POST, // subscription: "/IBMAnalysis", diff --git a/webpack.config.js b/webpack.config.js index 9225093be..c8ef269d4 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -20,8 +20,7 @@ const env = require('dotenv').config().parsed; if (env) { plugins.push(new webpack.DefinePlugin(Object.keys(env).reduce((prev, next) => { if (next.startsWith("DASH_")) { - const resolved = next.replace("DASH_", ""); - prev[`process.env.${resolved}`] = JSON.stringify(env[next]); + prev[`process.env.${next.replace("DASH_", "")}`] = JSON.stringify(env[next]); } return prev; }, {}))); -- cgit v1.2.3-70-g09d2