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 --- 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 + 8 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 src/client/views/collections/CollectionMapView.scss create mode 100644 src/client/views/collections/CollectionMapView.tsx (limited to 'src') 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); } -- cgit v1.2.3-70-g09d2 From 8143af6ed24b4d3b02e8be306e1dd6fba1e206ce Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sun, 12 Apr 2020 21:41:06 -0700 Subject: clean up --- src/client/views/collections/CollectionMapView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 49411f61b..6ab152836 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -39,6 +39,7 @@ class CollectionMapView extends CollectionSubView lng: NumCast(childLayoutPairs[0].layout.locationLng, 0) }; const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45)); + return ( 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") + url: StrCast(Document.mapIconUrl, null) }} /> ); -- 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') 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 From bf622aa5b5cd9d3256ee8e4f26245b9858cccfba Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 13 Apr 2020 00:56:21 -0700 Subject: final env cleanup --- src/client/views/collections/CollectionMapView.tsx | 20 +++++++++++-------- src/server/server_Initialization.ts | 1 - webpack.config.js | 23 ++++++++++++++-------- 3 files changed, 27 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index a99d5be50..b67daeb53 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -6,6 +6,7 @@ import { Map, Marker, MapProps, GoogleApiWrapper } from "google-maps-react"; import { NumCast, StrCast } from "../../../new_fields/Types"; import { CollectionSubView } from "./CollectionSubView"; import { Utils } from "../../../Utils"; +import { Opt } from "../../../new_fields/Doc"; type MapDocument = makeInterface<[typeof documentSchema]>; const MapDocument = makeInterface(documentSchema); @@ -38,8 +39,15 @@ class CollectionMapView extends CollectionSubView 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)); - + let icon: Opt, iconUrl: Opt; + if ((iconUrl = StrCast(Document.mapIconUrl, null))) { + const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45)); + icon = { + size: iconSize, + scaledSize: iconSize, + url: iconUrl + }; + } return ( Document.mapCenterLat = location.lat; Document.mapCenterLng = location.lng; }} - icon={{ - size: iconSize, - scaledSize: iconSize, - url: StrCast(Document.mapIconUrl, null) - }} + icon={icon} /> ); })} @@ -64,4 +68,4 @@ class CollectionMapView extends CollectionSubView } -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! })(CollectionMapView) as any; \ No newline at end of file diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts index 1150118f7..add607761 100644 --- a/src/server/server_Initialization.ts +++ b/src/server/server_Initialization.ts @@ -20,7 +20,6 @@ import * as request from 'request'; import RouteSubscriber from './RouteSubscriber'; import { publicDirectory } from '.'; import { logPort, } from './ActionUtilities'; -import { Utils } from '../Utils'; import { blue, yellow } from 'colors'; import * as cors from "cors"; diff --git a/webpack.config.js b/webpack.config.js index c8ef269d4..6265883fd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -16,16 +16,23 @@ const plugins = [ new webpack.HotModuleReplacementPlugin(), ]; -const env = require('dotenv').config().parsed; -if (env) { - plugins.push(new webpack.DefinePlugin(Object.keys(env).reduce((prev, next) => { - if (next.startsWith("DASH_")) { - prev[`process.env.${next.replace("DASH_", "")}`] = JSON.stringify(env[next]); - } - return prev; - }, {}))); +const dotenv = require('dotenv'); + +function transferEnvironmentVariables() { + const prefix = "_CLIENT_"; + const env = dotenv.config().parsed; + if (env) { + plugins.push(new webpack.DefinePlugin(Object.keys(env).reduce((mapping, envKey) => { + if (envKey.startsWith(prefix)) { + mapping[`process.env.${envKey.replace(prefix, "")}`] = JSON.stringify(env[envKey]); + } + return mapping; + }, {}))); + } } +transferEnvironmentVariables(); + module.exports = { mode: 'development', entry: { -- cgit v1.2.3-70-g09d2