From 348e794e7ff341a4358be6ed9515fc6ae723092e Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Sun, 12 Apr 2020 21:36:26 -0700
Subject: rough pass at collection map view

---
 src/client/views/collections/CollectionMapView.tsx | 72 ++++++++++++++++++++++
 1 file changed, 72 insertions(+)
 create mode 100644 src/client/views/collections/CollectionMapView.tsx

(limited to 'src/client/views/collections/CollectionMapView.tsx')

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<MapDocument, Partial<MapProps> & { 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 (
+            <div
+                className={"collectionMapView-contents"}
+            >
+                <Map
+                    {...props}
+                    zoom={NumCast(Document.zoom, 10)}
+                    center={center}
+                    initialCenter={center}
+                >
+                    {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 (
+                            <Marker
+                                key={Utils.GenerateGuid()}
+                                label={StrCast(layout.title)}
+                                position={location}
+                                onClick={() => {
+                                    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")
+                                }}
+                            />
+                        );
+                    })}
+                </Map>
+            </div>
+        );
+    }
+
+}
+
+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
-- 
cgit v1.2.3-70-g09d2


From 8143af6ed24b4d3b02e8be306e1dd6fba1e206ce Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
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/client/views/collections/CollectionMapView.tsx')

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<MapDocument, Partial<MapProps>
                             lng: NumCast(childLayoutPairs[0].layout.locationLng, 0)
                         };
                         const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45));
+
                         return (
                             <Marker
                                 key={Utils.GenerateGuid()}
@@ -51,7 +52,7 @@ class CollectionMapView extends CollectionSubView<MapDocument, Partial<MapProps>
                                 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 <samwilkins333@gmail.com>
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/views/collections/CollectionMapView.tsx')

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<string> {
-        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<string, string>();
-
     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 <D>(service: Service, manager: APIManager<D>, data: D): Promise<any> => {
-        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<MapDocument, Partial<MapProps>
 
 }
 
-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 <samwilkins333@gmail.com>
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/client/views/collections/CollectionMapView.tsx')

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<MapDocument, Partial<MapProps>
                             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<google.maps.Icon>, iconUrl: Opt<string>;
+                        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 (
                             <Marker
                                 key={Utils.GenerateGuid()}
@@ -49,11 +57,7 @@ class CollectionMapView extends CollectionSubView<MapDocument, Partial<MapProps>
                                     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<MapDocument, Partial<MapProps>
 
 }
 
-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


From 87268612b9b765d74f1a5317b0894be0ceb1dfd1 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 13 Apr 2020 16:43:50 -0400
Subject: fixed map view pointer events.

---
 src/client/views/collections/CollectionMapView.scss | 3 +++
 src/client/views/collections/CollectionMapView.tsx  | 8 ++++----
 src/client/views/collections/CollectionView.tsx     | 2 +-
 3 files changed, 8 insertions(+), 5 deletions(-)

(limited to 'src/client/views/collections/CollectionMapView.tsx')

diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss
index c74433902..df7853da6 100644
--- a/src/client/views/collections/CollectionMapView.scss
+++ b/src/client/views/collections/CollectionMapView.scss
@@ -1,4 +1,7 @@
 .collectionMapView-contents {
     width: 100%;
     height: 100%;
+}
+.collectionMapView-contents-none {
+    pointer-events: none;
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index b67daeb53..5075bbf7a 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -7,6 +7,7 @@ import { NumCast, StrCast } from "../../../new_fields/Types";
 import { CollectionSubView } from "./CollectionSubView";
 import { Utils } from "../../../Utils";
 import { Opt } from "../../../new_fields/Doc";
+import "./CollectionMapView.scss";
 
 type MapDocument = makeInterface<[typeof documentSchema]>;
 const MapDocument = makeInterface(documentSchema);
@@ -25,11 +26,10 @@ class CollectionMapView extends CollectionSubView<MapDocument, Partial<MapProps>
             center.lng = childLayoutPairs.length ? NumCast(childLayoutPairs[0].layout.locationLng, 0) : 0;
         }
         return (
-            <div
-                className={"collectionMapView-contents"}
-            >
+            <div className={"collectionMapView-contents" + (this.props.active() ? "" : "-none")}
+                onPointerDown={e => (this.props.active() && e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
                 <Map
-                    {...props}
+                    google={this.props.google}
                     zoom={NumCast(Document.zoom, 10)}
                     center={center}
                     initialCenter={center}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 4d04f2050..8cc5146d5 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -106,7 +106,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
         return viewField as any as CollectionViewType;
     }
 
-    active = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction) || this.props.Document.forceActive || this._isChildActive || this.props.renderDepth === 0;
+    active = (outsideReaction?: boolean) => (this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction) || this.props.Document.forceActive || this._isChildActive || this.props.renderDepth === 0) ? true : false;
 
     whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive);
 
-- 
cgit v1.2.3-70-g09d2


From 78986ae808dc9bbb5763e3f74097b7a1bc61f49a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 13 Apr 2020 18:45:52 -0400
Subject: more mapview adjustments

---
 src/client/documents/Documents.ts                  |   4 -
 src/client/views/PreviewCursor.tsx                 |   2 +-
 .../views/collections/CollectionMapView.scss       |   3 -
 src/client/views/collections/CollectionMapView.tsx | 104 ++++++++++++++-------
 4 files changed, 69 insertions(+), 44 deletions(-)

(limited to 'src/client/views/collections/CollectionMapView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 3f46c4013..e6f3b21ca 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -49,10 +49,6 @@ 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";
-import { action } from "mobx";
-const requestImageSize = require('../util/request-image-size');
 const path = require('path');
 
 export interface DocumentOptions {
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 92ba13d2f..df30c1215 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -113,7 +113,7 @@ export class PreviewCursor extends React.Component<{}> {
         addLiveText: (doc: Doc) => void,
         getTransform: () => Transform,
         addDocument: (doc: Doc) => boolean,
-        nudge: (nudgeX: number, nudgeY: number) => void) {
+        nudge: (nudgeX: number, nudgeY: number) => boolean) {
         this._clickPoint = [x, y];
         this._onKeyPress = onKeyPress;
         this._addLiveTextDoc = addLiveText;
diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss
index df7853da6..c74433902 100644
--- a/src/client/views/collections/CollectionMapView.scss
+++ b/src/client/views/collections/CollectionMapView.scss
@@ -1,7 +1,4 @@
 .collectionMapView-contents {
     width: 100%;
     height: 100%;
-}
-.collectionMapView-contents-none {
-    pointer-events: none;
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index 5075bbf7a..569f8ba7a 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -1,13 +1,20 @@
+import { GoogleApiWrapper, Map, MapProps, Marker } from "google-maps-react";
 import { observer } from "mobx-react";
-import { makeInterface } from "../../../new_fields/Schema";
+import { Doc, Opt, DocListCast } from "../../../new_fields/Doc";
 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";
-import { Opt } from "../../../new_fields/Doc";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { makeInterface } from "../../../new_fields/Schema";
+import { Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
+import { TraceMobx } from "../../../new_fields/util";
 import "./CollectionMapView.scss";
+import { CollectionSubView } from "./CollectionSubView";
+import React = require("react");
+import { DocumentManager } from "../../util/DocumentManager";
+import { UndoManager } from "../../util/UndoManager";
+import { returnTrue } from "../../../Utils";
+import { CancellationError } from "bluebird";
+import { ln } from "shelljs";
+import { dfareporting } from "googleapis/build/src/apis/dfareporting";
 
 type MapDocument = makeInterface<[typeof documentSchema]>;
 const MapDocument = makeInterface(documentSchema);
@@ -17,28 +24,65 @@ export type LocationData = google.maps.LatLngLiteral & { address?: string };
 @observer
 class CollectionMapView extends CollectionSubView<MapDocument, Partial<MapProps> & { google: any }>(MapDocument) {
 
+    getLocation = (doc: Opt<Doc>, fieldKey: string, defaultLocation?: LocationData) => {
+        if (doc) {
+            let lat: Opt<number> = Cast(doc[fieldKey + "-lat"], "number", null);
+            let lng: Opt<number> = Cast(doc[fieldKey + "-lng"], "number", null);
+            const address = Cast(doc[fieldKey + "-address"], "string", null);
+            if (address) {
+                // use geo service to convert to lat/lng
+                lat = lat;
+                lng = lng;
+            }
+            if (lat === undefined) lat = defaultLocation?.lat;
+            if (lng === undefined) lng = defaultLocation?.lng;
+            return ({ lat, lng });
+        }
+        return ({ lat: 35.1592238, lng: -98.4466577 });
+    }
+    renderMarker(layout: Doc, icon: Opt<google.maps.Icon>) {
+        const location = this.getLocation(layout, "location");
+        return location.lat === undefined || location.lng === undefined ? (null) :
+            <Marker
+                key={layout[Id]}
+                label={StrCast(layout.title)}
+                position={{ lat: location.lat, lng: location.lng }}
+                onClick={async () => {
+                    this.props.Document.mapCenterLat = location.lat;
+                    this.props.Document.mapCenterLng = location.lng;
+                    if (layout.isLinkButton && DocListCast(layout.links).length) {
+                        const batch = UndoManager.StartBatch("follow link click");
+                        await DocumentManager.Instance.FollowLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => {
+                            this.props.addDocTab(doc, where);
+                            finished?.();
+                        }, false, this.props.ContainingCollectionDoc, batch.end, undefined);
+                    } else {
+                        ScriptCast(layout.onClick)?.script.run({ this: layout, self: Cast(layout.rootDocument, Doc, null) || layout });
+                    }
+                }}
+                icon={icon}
+            />;
+    }
     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;
+        const { childLayoutPairs } = this;
+        const { Document } = this.props;
+        let center = this.getLocation(Document, this.props.fieldKey + "-mapCenter");
+        if (center.lat === undefined) {
+            center = this.getLocation(childLayoutPairs.map(pair => pair.layout).find(returnTrue), "location", { lat: 35.1592238, lng: -98.4466577 });
         }
-        return (
-            <div className={"collectionMapView-contents" + (this.props.active() ? "" : "-none")}
-                onPointerDown={e => (this.props.active() && e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
+        TraceMobx();
+        return center.lat === undefined || center.lng === undefined ? (null) :
+            <div className={"collectionMapView-contents"}
+                style={{ pointerEvents: this.props.active() ? undefined : "none" }}
+                onWheel={e => e.stopPropagation()}
+                onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
                 <Map
                     google={this.props.google}
                     zoom={NumCast(Document.zoom, 10)}
-                    center={center}
-                    initialCenter={center}
+                    center={{ lat: center.lat, lng: center.lng }}
+                    initialCenter={{ lat: center.lat, lng: center.lng }}
                 >
                     {childLayoutPairs.map(({ layout }) => {
-                        const location: LocationData = {
-                            lat: NumCast(childLayoutPairs[0].layout.locationLat, 0),
-                            lng: NumCast(childLayoutPairs[0].layout.locationLng, 0)
-                        };
                         let icon: Opt<google.maps.Icon>, iconUrl: Opt<string>;
                         if ((iconUrl = StrCast(Document.mapIconUrl, null))) {
                             const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45));
@@ -48,22 +92,10 @@ class CollectionMapView extends CollectionSubView<MapDocument, Partial<MapProps>
                                 url: iconUrl
                             };
                         }
-                        return (
-                            <Marker
-                                key={Utils.GenerateGuid()}
-                                label={StrCast(layout.title)}
-                                position={location}
-                                onClick={() => {
-                                    Document.mapCenterLat = location.lat;
-                                    Document.mapCenterLng = location.lng;
-                                }}
-                                icon={icon}
-                            />
-                        );
+                        return this.renderMarker(layout, icon);
                     })}
                 </Map>
-            </div>
-        );
+            </div>;
     }
 
 }
-- 
cgit v1.2.3-70-g09d2


From 479964e7c13f7b9b1102b36575ad8f4cc8dc00c2 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 13 Apr 2020 21:00:50 -0400
Subject: cleaned up some field key stuff for mapview

---
 src/client/views/collections/CollectionMapView.tsx | 87 ++++++++++++----------
 1 file changed, 47 insertions(+), 40 deletions(-)

(limited to 'src/client/views/collections/CollectionMapView.tsx')

diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index 569f8ba7a..44bb22827 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -11,10 +11,6 @@ import { CollectionSubView } from "./CollectionSubView";
 import React = require("react");
 import { DocumentManager } from "../../util/DocumentManager";
 import { UndoManager } from "../../util/UndoManager";
-import { returnTrue } from "../../../Utils";
-import { CancellationError } from "bluebird";
-import { ln } from "shelljs";
-import { dfareporting } from "googleapis/build/src/apis/dfareporting";
 
 type MapDocument = makeInterface<[typeof documentSchema]>;
 const MapDocument = makeInterface(documentSchema);
@@ -24,32 +20,33 @@ export type LocationData = google.maps.LatLngLiteral & { address?: string };
 @observer
 class CollectionMapView extends CollectionSubView<MapDocument, Partial<MapProps> & { google: any }>(MapDocument) {
 
-    getLocation = (doc: Opt<Doc>, fieldKey: string, defaultLocation?: LocationData) => {
+    getLocation = (doc: Opt<Doc>, fieldKey: string) => {
         if (doc) {
             let lat: Opt<number> = Cast(doc[fieldKey + "-lat"], "number", null);
             let lng: Opt<number> = Cast(doc[fieldKey + "-lng"], "number", null);
+            let zoom: Opt<number> = Cast(doc[fieldKey + "-zoom"], "number", null);
             const address = Cast(doc[fieldKey + "-address"], "string", null);
             if (address) {
                 // use geo service to convert to lat/lng
                 lat = lat;
                 lng = lng;
             }
-            if (lat === undefined) lat = defaultLocation?.lat;
-            if (lng === undefined) lng = defaultLocation?.lng;
-            return ({ lat, lng });
+            return lat !== undefined && lng !== undefined ? ({ lat, lng, zoom }) : undefined;
         }
-        return ({ lat: 35.1592238, lng: -98.4466577 });
+        return undefined;
     }
     renderMarker(layout: Doc, icon: Opt<google.maps.Icon>) {
-        const location = this.getLocation(layout, "location");
-        return location.lat === undefined || location.lng === undefined ? (null) :
+        const location = this.getLocation(layout, "mapLocation");
+        return !location ? (null) :
             <Marker
                 key={layout[Id]}
                 label={StrCast(layout.title)}
                 position={{ lat: location.lat, lng: location.lng }}
                 onClick={async () => {
-                    this.props.Document.mapCenterLat = location.lat;
-                    this.props.Document.mapCenterLng = location.lng;
+                    this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = 0;
+                    this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = location.lat;
+                    this.layoutDoc[this.props.fieldKey + "-mapCenter-lng"] = location.lng;
+                    location.zoom && (this.layoutDoc[this.props.fieldKey + "-mapCenter-zoom"] = location.zoom);
                     if (layout.isLinkButton && DocListCast(layout.links).length) {
                         const batch = UndoManager.StartBatch("follow link click");
                         await DocumentManager.Instance.FollowLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => {
@@ -67,35 +64,45 @@ class CollectionMapView extends CollectionSubView<MapDocument, Partial<MapProps>
         const { childLayoutPairs } = this;
         const { Document } = this.props;
         let center = this.getLocation(Document, this.props.fieldKey + "-mapCenter");
-        if (center.lat === undefined) {
-            center = this.getLocation(childLayoutPairs.map(pair => pair.layout).find(returnTrue), "location", { lat: 35.1592238, lng: -98.4466577 });
+        if (center === undefined) {
+            center = childLayoutPairs.map(pair => this.getLocation(pair.layout, "mapLocation")).find(layout => layout);
+            if (center === undefined) {
+                center = { lat: 35.1592238, lng: -98.444512, zoom: 15 }; // nowhere, OK
+            }
         }
         TraceMobx();
-        return center.lat === undefined || center.lng === undefined ? (null) :
-            <div className={"collectionMapView-contents"}
-                style={{ pointerEvents: this.props.active() ? undefined : "none" }}
-                onWheel={e => e.stopPropagation()}
-                onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
-                <Map
-                    google={this.props.google}
-                    zoom={NumCast(Document.zoom, 10)}
-                    center={{ lat: center.lat, lng: center.lng }}
-                    initialCenter={{ lat: center.lat, lng: center.lng }}
-                >
-                    {childLayoutPairs.map(({ layout }) => {
-                        let icon: Opt<google.maps.Icon>, iconUrl: Opt<string>;
-                        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 this.renderMarker(layout, icon);
-                    })}
-                </Map>
-            </div>;
+        return <div className={"collectionMapView-contents"}
+            style={{ pointerEvents: this.props.active() ? undefined : "none" }}
+            onWheel={e => e.stopPropagation()}
+            onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
+            <Map
+                google={this.props.google}
+                zoom={center.zoom || 10}
+                initialCenter={center}
+                center={center}
+                onBoundsChanged={e => console.log(e)}
+                onRecenter={e => console.log(e)}
+                onDragend={(centerMoved, center) => console.log(centerMoved, center)}
+                onProjectionChanged={e => console.log(e)}
+                onCenterChanged={(e => {
+                    Document[this.props.fieldKey + "-mapCenter-lat"] = typeof e?.center?.lat === "number" ? e.center.lat : center!.lat;
+                    Document[this.props.fieldKey + "-mapCenter-lng"] = typeof e?.center?.lng === "number" ? e.center.lng : center!.lng;
+                })}
+            >
+                {childLayoutPairs.map(({ layout }) => {
+                    let icon: Opt<google.maps.Icon>, iconUrl: Opt<string>;
+                    if ((iconUrl = StrCast(Document.mapIconUrl, null))) {
+                        const iconSize = new google.maps.Size(NumCast(layout["mapLocation-iconWidth"], 45), NumCast(layout["mapLocation-iconHeight"], 45));
+                        icon = {
+                            size: iconSize,
+                            scaledSize: iconSize,
+                            url: iconUrl
+                        };
+                    }
+                    return this.renderMarker(layout, icon);
+                })}
+            </Map>
+        </div>;
     }
 
 }
-- 
cgit v1.2.3-70-g09d2


From 14b3f6b3580b1987bdf9f8f865a79087e801d367 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 13 Apr 2020 21:10:10 -0400
Subject: fromlast

---
 src/client/views/collections/CollectionMapView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/collections/CollectionMapView.tsx')

diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index 44bb22827..2bf3d7c32 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -82,7 +82,7 @@ class CollectionMapView extends CollectionSubView<MapDocument, Partial<MapProps>
                 center={center}
                 onBoundsChanged={e => console.log(e)}
                 onRecenter={e => console.log(e)}
-                onDragend={(centerMoved, center) => console.log(centerMoved, center)}
+                onDragend={e => console.log(e)}
                 onProjectionChanged={e => console.log(e)}
                 onCenterChanged={(e => {
                     Document[this.props.fieldKey + "-mapCenter-lat"] = typeof e?.center?.lat === "number" ? e.center.lat : center!.lat;
-- 
cgit v1.2.3-70-g09d2


From a888dacc8e6a6400d52cdded0015e05ac5581d3d Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Tue, 14 Apr 2020 00:31:20 -0700
Subject: chrome dropdown changes and custom loading container for mapview

---
 .../views/collections/CollectionMapView.scss       | 18 +++++++++++++
 src/client/views/collections/CollectionMapView.tsx | 31 +++++++++++++---------
 .../views/collections/CollectionViewChromes.tsx    | 18 ++++++-------
 3 files changed, 46 insertions(+), 21 deletions(-)

(limited to 'src/client/views/collections/CollectionMapView.tsx')

diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss
index c74433902..4956ad946 100644
--- a/src/client/views/collections/CollectionMapView.scss
+++ b/src/client/views/collections/CollectionMapView.scss
@@ -1,4 +1,22 @@
 .collectionMapView-contents {
     width: 100%;
     height: 100%;
+}
+
+.loadingWrapper {
+    width: 100%;
+    height: 100%;
+    background-color: yellow;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    text-align: center;
+
+    .loadingGif {
+        align-self: center;
+        justify-self: center;
+        width: 20%;
+        height: 20%
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index 44bb22827..6d5dcdf1d 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -11,20 +11,21 @@ import { CollectionSubView } from "./CollectionSubView";
 import React = require("react");
 import { DocumentManager } from "../../util/DocumentManager";
 import { UndoManager } from "../../util/UndoManager";
+import * as ReactDOM from 'react-dom';
 
-type MapDocument = makeInterface<[typeof documentSchema]>;
-const MapDocument = makeInterface(documentSchema);
+type MapSchema = makeInterface<[typeof documentSchema]>;
+const MapSchema = makeInterface(documentSchema);
 
 export type LocationData = google.maps.LatLngLiteral & { address?: string };
 
 @observer
-class CollectionMapView extends CollectionSubView<MapDocument, Partial<MapProps> & { google: any }>(MapDocument) {
+class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> & { google: any }>(MapSchema) {
 
     getLocation = (doc: Opt<Doc>, fieldKey: string) => {
         if (doc) {
             let lat: Opt<number> = Cast(doc[fieldKey + "-lat"], "number", null);
             let lng: Opt<number> = Cast(doc[fieldKey + "-lng"], "number", null);
-            let zoom: Opt<number> = Cast(doc[fieldKey + "-zoom"], "number", null);
+            const zoom: Opt<number> = Cast(doc[fieldKey + "-zoom"], "number", null);
             const address = Cast(doc[fieldKey + "-address"], "string", null);
             if (address) {
                 // use geo service to convert to lat/lng
@@ -79,12 +80,12 @@ class CollectionMapView extends CollectionSubView<MapDocument, Partial<MapProps>
                 google={this.props.google}
                 zoom={center.zoom || 10}
                 initialCenter={center}
-                center={center}
-                onBoundsChanged={e => console.log(e)}
-                onRecenter={e => console.log(e)}
-                onDragend={(centerMoved, center) => console.log(centerMoved, center)}
-                onProjectionChanged={e => console.log(e)}
-                onCenterChanged={(e => {
+                onBoundsChanged={(props, map, e) => console.log("ON_BOUNDS_CHANGED", props, map, e)}
+                onRecenter={(props, map, e) => console.log("ON_RECENTER", props, map, e)}
+                onDragend={(centerMoved, center) => console.log("ON_DRAGEND", centerMoved, center)}
+                onProjectionChanged={(props, map, e) => console.log("ON_PROJ_CHANGED", props, map, e)}
+                onCenterChanged={((props, map, e) => {
+                    console.log("ON_CENTER_CHANGED", props, map, e);
                     Document[this.props.fieldKey + "-mapCenter-lat"] = typeof e?.center?.lat === "number" ? e.center.lat : center!.lat;
                     Document[this.props.fieldKey + "-mapCenter-lng"] = typeof e?.center?.lng === "number" ? e.center.lng : center!.lng;
                 })}
@@ -92,7 +93,9 @@ class CollectionMapView extends CollectionSubView<MapDocument, Partial<MapProps>
                 {childLayoutPairs.map(({ layout }) => {
                     let icon: Opt<google.maps.Icon>, iconUrl: Opt<string>;
                     if ((iconUrl = StrCast(Document.mapIconUrl, null))) {
-                        const iconSize = new google.maps.Size(NumCast(layout["mapLocation-iconWidth"], 45), NumCast(layout["mapLocation-iconHeight"], 45));
+                        const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45);
+                        const iconHeight = NumCast(layout["mapLocation-iconHeight"], 45);
+                        const iconSize = new google.maps.Size(iconWidth, iconHeight);
                         icon = {
                             size: iconSize,
                             scaledSize: iconSize,
@@ -107,4 +110,8 @@ class CollectionMapView extends CollectionSubView<MapDocument, Partial<MapProps>
 
 }
 
-export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS! })(CollectionMapView) as any;
\ No newline at end of file
+const LoadingContainer = () => {
+    return <div className={"loadingWrapper"}><img className={"loadingGif"} src={"/assets/loading.gif"} /></div>;
+};
+
+export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS!, LoadingContainer })(CollectionMapView) as any;
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index ba95dce00..0940e9186 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -402,15 +402,15 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                                     onPointerDown={stopPropagation}
                                     onChange={this.viewChanged}
                                     value={StrCast(this.props.CollectionView.props.Document._viewType)}>
-                                    <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="freeform">Freeform</option>
-                                    <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="schema">Schema</option>
-                                    <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="tree">Tree</option>
-                                    <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="stacking">Stacking</option>
-                                    <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="masonry">Masonry</option>
-                                    <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="multicolumn">MultiCol</option>
-                                    <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="multirow">MultiRow</option>
-                                    <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="time">Pivot/Time</option>
-                                    <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="carousel">Carousel</option>
+                                    {Object.values(CollectionViewType).map(type => ["invalid", "docking"].includes(type) ? (null) : (
+                                        <option
+                                            key={Utils.GenerateGuid()}
+                                            className="collectionViewBaseChrome-viewOption"
+                                            onPointerDown={stopPropagation}
+                                            value={type}>
+                                            {type[0].toUpperCase() + type.substring(1)}
+                                        </option>
+                                    ))}
                                 </select>
                             </div>
                         </div>
-- 
cgit v1.2.3-70-g09d2


From 8a537327fb685fa9267e25e550ec0aa7a3ae86cc Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Tue, 14 Apr 2020 04:01:24 -0700
Subject: added bidirectional geocoding for map view

---
 src/client/views/collections/CollectionMapView.tsx | 128 +++++++++++++++------
 webpack.config.js                                  |  64 +++++------
 2 files changed, 126 insertions(+), 66 deletions(-)

(limited to 'src/client/views/collections/CollectionMapView.tsx')

diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index a2d3c328d..8cdc145b8 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -4,37 +4,44 @@ import { Doc, Opt, DocListCast } from "../../../new_fields/Doc";
 import { documentSchema } from "../../../new_fields/documentSchemas";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
+import { Cast, NumCast, ScriptCast, StrCast, BoolCast } from "../../../new_fields/Types";
 import { TraceMobx } from "../../../new_fields/util";
 import "./CollectionMapView.scss";
 import { CollectionSubView } from "./CollectionSubView";
 import React = require("react");
 import { DocumentManager } from "../../util/DocumentManager";
 import { UndoManager } from "../../util/UndoManager";
+import { IReactionDisposer, reaction } from "mobx";
+import requestPromise = require("request-promise");
 
 type MapSchema = makeInterface<[typeof documentSchema]>;
 const MapSchema = makeInterface(documentSchema);
 
-export type LocationData = google.maps.LatLngLiteral & { address?: string };
+export type LocationData = google.maps.LatLngLiteral & {
+    address?: string
+    resolvedAddress?: string;
+    zoom?: number;
+};
+
+const base = "https://maps.googleapis.com/maps/api/geocode/json?";
 
 @observer
 class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> & { google: any }>(MapSchema) {
 
-    getLocation = (doc: Opt<Doc>, fieldKey: string) => {
+    private mapRef = React.createRef<Map>();
+    private addressUpdaters: IReactionDisposer[] = [];
+    private latlngUpdaters: IReactionDisposer[] = [];
+
+    getLocation = (doc: Opt<Doc>, fieldKey: string): Opt<LocationData> => {
         if (doc) {
-            let lat: Opt<number> = Cast(doc[fieldKey + "-lat"], "number", null);
-            let lng: Opt<number> = Cast(doc[fieldKey + "-lng"], "number", null);
+            const lat: Opt<number> = Cast(doc[fieldKey + "-lat"], "number", null);
+            const lng: Opt<number> = Cast(doc[fieldKey + "-lng"], "number", null);
             const zoom: Opt<number> = Cast(doc[fieldKey + "-zoom"], "number", null);
-            const address = Cast(doc[fieldKey + "-address"], "string", null);
-            if (address) {
-                // use geo service to convert to lat/lng
-                lat = lat;
-                lng = lng;
-            }
             return lat !== undefined && lng !== undefined ? ({ lat, lng, zoom }) : undefined;
         }
         return undefined;
     }
+
     renderMarker(layout: Doc, icon: Opt<google.maps.Icon>) {
         const location = this.getLocation(layout, "mapLocation");
         return !location ? (null) :
@@ -43,6 +50,7 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
                 label={StrCast(layout.title)}
                 position={{ lat: location.lat, lng: location.lng }}
                 onClick={async () => {
+                    this.map.panTo(location);
                     this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = 0;
                     this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = location.lat;
                     this.layoutDoc[this.props.fieldKey + "-mapCenter-lng"] = location.lng;
@@ -60,6 +68,73 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
                 icon={icon}
             />;
     }
+
+    private get contents() {
+        this.addressUpdaters.forEach(disposer => disposer());
+        this.addressUpdaters = [];
+        this.latlngUpdaters.forEach(disposer => disposer());
+        this.latlngUpdaters = [];
+        return this.childLayoutPairs.map(({ layout }) => {
+            let icon: Opt<google.maps.Icon>, iconUrl: Opt<string>;
+            if ((iconUrl = StrCast(this.props.Document.mapIconUrl, null))) {
+                const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45);
+                const iconHeight = NumCast(layout["mapLocation-iconHeight"], 45);
+                const iconSize = new google.maps.Size(iconWidth, iconHeight);
+                icon = {
+                    size: iconSize,
+                    scaledSize: iconSize,
+                    url: iconUrl
+                };
+            }
+            this.addressUpdaters.push(reaction(
+                () => ({
+                    lat: NumCast(layout["mapLocation-lat"]),
+                    lng: NumCast(layout["mapLocation-lng"])
+                }),
+                ({ lat, lng }) => {
+                    if (!BoolCast(layout._ignoreNextUpdate)) {
+                        if (lat !== undefined && lng !== undefined) {
+                            const target = `${base}latlng=${lat},${lng}&key=${process.env.GOOGLE_MAPS_GEO!}`;
+                            requestPromise.get(target).then(res => {
+                                layout._ignoreNextUpdate = true;
+                                layout["mapLocation-address"] = JSON.parse(res).results[0]?.formatted_address || "<invalid address>";
+                            });
+                        }
+                    } else {
+                        layout._ignoreNextUpdate = false;
+                    }
+                }
+            ));
+            this.latlngUpdaters.push(reaction(
+                () => ({ address: Cast(layout["mapLocation-address"], "string", null) }),
+                ({ address }) => {
+                    if (!BoolCast(layout._ignoreNextUpdate)) {
+                        if (address && address.length) {
+                            const target = `${base}address=${address.replace(/\s+/g, "+")}&key=${process.env.GOOGLE_MAPS_GEO!}`;
+                            requestPromise.get(target).then(res => {
+                                const result = JSON.parse(res).results[0];
+                                const { lat, lng } = result.geometry.location;
+                                layout._ignoreNextUpdate = true;
+                                layout["mapLocation-lat"] = lat;
+                                layout._ignoreNextUpdate = true;
+                                layout["mapLocation-lng"] = lng;
+                                layout._ignoreNextUpdate = true;
+                                layout["mapLocation-address"] = result.formatted_address;
+                            });
+                        }
+                    } else {
+                        layout._ignoreNextUpdate = false;
+                    }
+                }
+            ));
+            return this.renderMarker(layout, icon);
+        });
+    }
+
+    private get map() {
+        return (this.mapRef.current as any).map;
+    }
+
     render() {
         const { childLayoutPairs } = this;
         const { Document } = this.props;
@@ -76,33 +151,18 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
             onWheel={e => e.stopPropagation()}
             onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
             <Map
+                ref={this.mapRef}
                 google={this.props.google}
                 zoom={center.zoom || 10}
                 initialCenter={center}
-                onBoundsChanged={(props, map, e) => console.log("ON_BOUNDS_CHANGED", props, map, e)}
-                onRecenter={(props, map, e) => console.log("ON_RECENTER", props, map, e)}
-                onDragend={(centerMoved, center) => console.log("ON_DRAGEND", centerMoved, center)}
-                onProjectionChanged={(props, map, e) => console.log("ON_PROJ_CHANGED", props, map, e)}
-                onCenterChanged={((props, map, e) => {
-                    console.log("ON_CENTER_CHANGED", props, map, e);
-                    Document[this.props.fieldKey + "-mapCenter-lat"] = typeof e?.center?.lat === "number" ? e.center.lat : center!.lat;
-                    Document[this.props.fieldKey + "-mapCenter-lng"] = typeof e?.center?.lng === "number" ? e.center.lng : center!.lng;
-                })}
+                center={center}
+                onDragend={() => {
+                    const { center } = this.map;
+                    Document[this.props.fieldKey + "-mapCenter-lat"] = center.lat();
+                    Document[this.props.fieldKey + "-mapCenter-lng"] = center.lng();
+                }}
             >
-                {childLayoutPairs.map(({ layout }) => {
-                    let icon: Opt<google.maps.Icon>, iconUrl: Opt<string>;
-                    if ((iconUrl = StrCast(Document.mapIconUrl, null))) {
-                        const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45);
-                        const iconHeight = NumCast(layout["mapLocation-iconHeight"], 45);
-                        const iconSize = new google.maps.Size(iconWidth, iconHeight);
-                        icon = {
-                            size: iconSize,
-                            scaledSize: iconSize,
-                            url: iconUrl
-                        };
-                    }
-                    return this.renderMarker(layout, icon);
-                })}
+                {this.contents}
             </Map>
         </div>;
     }
diff --git a/webpack.config.js b/webpack.config.js
index 1027f29a6..6265883fd 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -20,7 +20,7 @@ const dotenv = require('dotenv');
 
 function transferEnvironmentVariables() {
     const prefix = "_CLIENT_";
-    const env = dotenv.config({ debug: true }).parsed;
+    const env = dotenv.config().parsed;
     if (env) {
         plugins.push(new webpack.DefinePlugin(Object.keys(env).reduce((mapping, envKey) => {
             if (envKey.startsWith(prefix)) {
@@ -64,42 +64,42 @@ module.exports = {
     },
     module: {
         rules: [{
-            test: [/\.tsx?$/],
-            use: [{
-                loader: 'ts-loader',
-                options: {
-                    transpileOnly: true
-                }
-            }]
-        },
-        {
-            test: /\.scss|css$/,
-            use: [{
-                loader: "style-loader"
+                test: [/\.tsx?$/],
+                use: [{
+                    loader: 'ts-loader',
+                    options: {
+                        transpileOnly: true
+                    }
+                }]
             },
             {
-                loader: "css-loader"
+                test: /\.scss|css$/,
+                use: [{
+                        loader: "style-loader"
+                    },
+                    {
+                        loader: "css-loader"
+                    },
+                    {
+                        loader: "sass-loader"
+                    }
+                ]
             },
             {
-                loader: "sass-loader"
+                test: /\.(jpg|png|pdf)$/,
+                use: [{
+                    loader: 'file-loader'
+                }]
+            },
+            {
+                test: /\.(png|jpg|gif)$/i,
+                use: [{
+                    loader: 'url-loader',
+                    options: {
+                        limit: 8192
+                    }
+                }]
             }
-            ]
-        },
-        {
-            test: /\.(jpg|png|pdf)$/,
-            use: [{
-                loader: 'file-loader'
-            }]
-        },
-        {
-            test: /\.(png|jpg|gif)$/i,
-            use: [{
-                loader: 'url-loader',
-                options: {
-                    limit: 8192
-                }
-            }]
-        }
         ]
     },
     plugins,
-- 
cgit v1.2.3-70-g09d2


From f29782b8cde4a5b0711f619d1bd7c4e223c900b9 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 14 Apr 2020 09:46:40 -0400
Subject: update maps to use local variables not on Doc

---
 src/client/views/collections/CollectionMapView.tsx | 71 ++++++++++++----------
 1 file changed, 38 insertions(+), 33 deletions(-)

(limited to 'src/client/views/collections/CollectionMapView.tsx')

diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index 8cdc145b8..5d0b0c982 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -1,4 +1,4 @@
-import { GoogleApiWrapper, Map, MapProps, Marker } from "google-maps-react";
+import { GoogleApiWrapper, Map as GeoMap, MapProps, Marker } from "google-maps-react";
 import { observer } from "mobx-react";
 import { Doc, Opt, DocListCast } from "../../../new_fields/Doc";
 import { documentSchema } from "../../../new_fields/documentSchemas";
@@ -11,7 +11,7 @@ import { CollectionSubView } from "./CollectionSubView";
 import React = require("react");
 import { DocumentManager } from "../../util/DocumentManager";
 import { UndoManager } from "../../util/UndoManager";
-import { IReactionDisposer, reaction } from "mobx";
+import { IReactionDisposer, reaction, action } from "mobx";
 import requestPromise = require("request-promise");
 
 type MapSchema = makeInterface<[typeof documentSchema]>;
@@ -28,7 +28,7 @@ const base = "https://maps.googleapis.com/maps/api/geocode/json?";
 @observer
 class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> & { google: any }>(MapSchema) {
 
-    private mapRef = React.createRef<Map>();
+    private mapRef = React.createRef<GeoMap>();
     private addressUpdaters: IReactionDisposer[] = [];
     private latlngUpdaters: IReactionDisposer[] = [];
 
@@ -51,7 +51,6 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
                 position={{ lat: location.lat, lng: location.lng }}
                 onClick={async () => {
                     this.map.panTo(location);
-                    this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = 0;
                     this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = location.lat;
                     this.layoutDoc[this.props.fieldKey + "-mapCenter-lng"] = location.lng;
                     location.zoom && (this.layoutDoc[this.props.fieldKey + "-mapCenter-zoom"] = location.zoom);
@@ -69,12 +68,14 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
             />;
     }
 
+    _cancelAddrReq = new Map<string, boolean>();
+    _cancelLocReq = new Map<string, boolean>();
     private get contents() {
         this.addressUpdaters.forEach(disposer => disposer());
         this.addressUpdaters = [];
         this.latlngUpdaters.forEach(disposer => disposer());
         this.latlngUpdaters = [];
-        return this.childLayoutPairs.map(({ layout }) => {
+        return this.childLayoutPairs.map(({ layout, data }) => {
             let icon: Opt<google.maps.Icon>, iconUrl: Opt<string>;
             if ((iconUrl = StrCast(this.props.Document.mapIconUrl, null))) {
                 const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45);
@@ -92,38 +93,42 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
                     lng: NumCast(layout["mapLocation-lng"])
                 }),
                 ({ lat, lng }) => {
-                    if (!BoolCast(layout._ignoreNextUpdate)) {
-                        if (lat !== undefined && lng !== undefined) {
-                            const target = `${base}latlng=${lat},${lng}&key=${process.env.GOOGLE_MAPS_GEO!}`;
-                            requestPromise.get(target).then(res => {
-                                layout._ignoreNextUpdate = true;
-                                layout["mapLocation-address"] = JSON.parse(res).results[0]?.formatted_address || "<invalid address>";
-                            });
-                        }
-                    } else {
-                        layout._ignoreNextUpdate = false;
+                    if (this._cancelLocReq.get(layout[Id])) {
+                        this._cancelLocReq.set(layout[Id], false);
+                    }
+                    else if (lat !== undefined && lng !== undefined) {
+                        const target = `${base}latlng=${lat},${lng}&key=${process.env.GOOGLE_MAPS_GEO!}`;
+                        requestPromise.get(target).then(res => {
+                            const formatted_address = JSON.parse(res).results[0].formatted_address || "<invalid address>";
+                            if (formatted_address !== layout["mapLocation-address"]) {
+                                this._cancelAddrReq.set(layout[Id], true);
+                                Doc.SetInPlace(layout, "mapLocation-address", formatted_address, true);
+                            }
+                        });
                     }
                 }
             ));
             this.latlngUpdaters.push(reaction(
                 () => ({ address: Cast(layout["mapLocation-address"], "string", null) }),
                 ({ address }) => {
-                    if (!BoolCast(layout._ignoreNextUpdate)) {
-                        if (address && address.length) {
-                            const target = `${base}address=${address.replace(/\s+/g, "+")}&key=${process.env.GOOGLE_MAPS_GEO!}`;
-                            requestPromise.get(target).then(res => {
-                                const result = JSON.parse(res).results[0];
-                                const { lat, lng } = result.geometry.location;
-                                layout._ignoreNextUpdate = true;
-                                layout["mapLocation-lat"] = lat;
-                                layout._ignoreNextUpdate = true;
-                                layout["mapLocation-lng"] = lng;
-                                layout._ignoreNextUpdate = true;
-                                layout["mapLocation-address"] = result.formatted_address;
-                            });
-                        }
-                    } else {
-                        layout._ignoreNextUpdate = false;
+                    if (this._cancelAddrReq.get(layout[Id])) {
+                        this._cancelAddrReq.set(layout[Id], false);
+                    }
+                    else if (address?.length) {
+                        const target = `${base}address=${address.replace(/\s+/g, "+")}&key=${process.env.GOOGLE_MAPS_GEO!}`;
+                        requestPromise.get(target).then(action((res: any) => {
+                            const { geometry, formatted_address } = JSON.parse(res).results[0];
+                            const { lat, lng } = geometry.location;
+                            if (layout["mapLocation-lat"] !== lat || layout["mapLocation-lng"] !== lng) {
+                                this._cancelLocReq.set(layout[Id], true);
+                                Doc.SetInPlace(layout, "mapLocation-lat", lat, true);
+                                Doc.SetInPlace(layout, "mapLocation-lng", lng, true);
+                            }
+                            if (formatted_address !== address) {
+                                this._cancelAddrReq.set(layout[Id], true);
+                                Doc.SetInPlace(layout, "mapLocation-address", formatted_address, true);
+                            }
+                        }));
                     }
                 }
             ));
@@ -150,7 +155,7 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
             style={{ pointerEvents: this.props.active() ? undefined : "none" }}
             onWheel={e => e.stopPropagation()}
             onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
-            <Map
+            <GeoMap
                 ref={this.mapRef}
                 google={this.props.google}
                 zoom={center.zoom || 10}
@@ -163,7 +168,7 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
                 }}
             >
                 {this.contents}
-            </Map>
+            </GeoMap>
         </div>;
     }
 
-- 
cgit v1.2.3-70-g09d2


From 26893de4b384678041523a0bbf71f895ad1cd2b7 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 14 Apr 2020 10:50:15 -0400
Subject: added map undo batches.  fixed marker flicker on drag.  split up
 code.

---
 src/client/views/collections/CollectionMapView.tsx | 111 ++++++++++-----------
 src/client/views/nodes/DocumentView.tsx            |   2 +-
 2 files changed, 55 insertions(+), 58 deletions(-)

(limited to 'src/client/views/collections/CollectionMapView.tsx')

diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index 5d0b0c982..fced52275 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -10,8 +10,8 @@ import "./CollectionMapView.scss";
 import { CollectionSubView } from "./CollectionSubView";
 import React = require("react");
 import { DocumentManager } from "../../util/DocumentManager";
-import { UndoManager } from "../../util/UndoManager";
-import { IReactionDisposer, reaction, action } from "mobx";
+import { UndoManager, undoBatch } from "../../util/UndoManager";
+import { IReactionDisposer, reaction, action, computed } from "mobx";
 import requestPromise = require("request-promise");
 
 type MapSchema = makeInterface<[typeof documentSchema]>;
@@ -28,7 +28,13 @@ const base = "https://maps.googleapis.com/maps/api/geocode/json?";
 @observer
 class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> & { google: any }>(MapSchema) {
 
-    private mapRef = React.createRef<GeoMap>();
+    // private mapRef = React.createRef<GeoMap>();
+    // private get map() {
+    //     return (this.mapRef.current as any).map;
+    // }
+
+    private _cancelAddrReq = new Map<string, boolean>();
+    private _cancelLocReq = new Map<string, boolean>();
     private addressUpdaters: IReactionDisposer[] = [];
     private latlngUpdaters: IReactionDisposer[] = [];
 
@@ -42,62 +48,62 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
         return undefined;
     }
 
-    renderMarker(layout: Doc, icon: Opt<google.maps.Icon>) {
+    markerClick = action(async (layout: Doc, location: LocationData) => {
+        //this.map.panTo(location);
+        const batch = UndoManager.StartBatch("marker click");
+        this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = location.lat;
+        this.layoutDoc[this.props.fieldKey + "-mapCenter-lng"] = location.lng;
+        location.zoom && (this.layoutDoc[this.props.fieldKey + "-mapCenter-zoom"] = location.zoom);
+        if (layout.isLinkButton && DocListCast(layout.links).length) {
+            await DocumentManager.Instance.FollowLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => {
+                this.props.addDocTab(doc, where);
+                finished?.();
+            }, false, this.props.ContainingCollectionDoc, batch.end, undefined);
+        } else {
+            ScriptCast(layout.onClick)?.script.run({ this: layout, self: Cast(layout.rootDocument, Doc, null) || layout });
+            batch.end();
+        }
+    });
+
+    renderMarkerIcon(layout: Doc) {
+        const iconUrl = StrCast(this.props.Document.mapIconUrl, null);
+        if (iconUrl) {
+            const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45);
+            const iconHeight = NumCast(layout["mapLocation-iconHeight"], 45);
+            const iconSize = new google.maps.Size(iconWidth, iconHeight);
+            return {
+                size: iconSize,
+                scaledSize: iconSize,
+                url: iconUrl
+            };
+        }
+    }
+    renderMarker(layout: Doc) {
         const location = this.getLocation(layout, "mapLocation");
         return !location ? (null) :
             <Marker
                 key={layout[Id]}
                 label={StrCast(layout.title)}
-                position={{ lat: location.lat, lng: location.lng }}
-                onClick={async () => {
-                    this.map.panTo(location);
-                    this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = location.lat;
-                    this.layoutDoc[this.props.fieldKey + "-mapCenter-lng"] = location.lng;
-                    location.zoom && (this.layoutDoc[this.props.fieldKey + "-mapCenter-zoom"] = location.zoom);
-                    if (layout.isLinkButton && DocListCast(layout.links).length) {
-                        const batch = UndoManager.StartBatch("follow link click");
-                        await DocumentManager.Instance.FollowLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => {
-                            this.props.addDocTab(doc, where);
-                            finished?.();
-                        }, false, this.props.ContainingCollectionDoc, batch.end, undefined);
-                    } else {
-                        ScriptCast(layout.onClick)?.script.run({ this: layout, self: Cast(layout.rootDocument, Doc, null) || layout });
-                    }
-                }}
-                icon={icon}
+                position={location}
+                onClick={() => this.markerClick(layout, location)}
+                icon={this.renderMarkerIcon(layout)}
             />;
     }
 
-    _cancelAddrReq = new Map<string, boolean>();
-    _cancelLocReq = new Map<string, boolean>();
-    private get contents() {
+    @computed get contents() {
         this.addressUpdaters.forEach(disposer => disposer());
         this.addressUpdaters = [];
         this.latlngUpdaters.forEach(disposer => disposer());
         this.latlngUpdaters = [];
-        return this.childLayoutPairs.map(({ layout, data }) => {
-            let icon: Opt<google.maps.Icon>, iconUrl: Opt<string>;
-            if ((iconUrl = StrCast(this.props.Document.mapIconUrl, null))) {
-                const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45);
-                const iconHeight = NumCast(layout["mapLocation-iconHeight"], 45);
-                const iconSize = new google.maps.Size(iconWidth, iconHeight);
-                icon = {
-                    size: iconSize,
-                    scaledSize: iconSize,
-                    url: iconUrl
-                };
-            }
+        return this.childLayoutPairs.map(({ layout }) => {
             this.addressUpdaters.push(reaction(
-                () => ({
-                    lat: NumCast(layout["mapLocation-lat"]),
-                    lng: NumCast(layout["mapLocation-lng"])
-                }),
+                () => ({ lat: layout["mapLocation-lat"], lng: layout["mapLocation-lng"] }),
                 ({ lat, lng }) => {
                     if (this._cancelLocReq.get(layout[Id])) {
                         this._cancelLocReq.set(layout[Id], false);
                     }
                     else if (lat !== undefined && lng !== undefined) {
-                        const target = `${base}latlng=${lat},${lng}&key=${process.env.GOOGLE_MAPS_GEO!}`;
+                        const target = `${base}latlng=${NumCast(lat)},${NumCast(lng)}&key=${process.env.GOOGLE_MAPS_GEO!}`;
                         requestPromise.get(target).then(res => {
                             const formatted_address = JSON.parse(res).results[0].formatted_address || "<invalid address>";
                             if (formatted_address !== layout["mapLocation-address"]) {
@@ -132,14 +138,9 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
                     }
                 }
             ));
-            return this.renderMarker(layout, icon);
+            return this.renderMarker(layout);
         });
     }
-
-    private get map() {
-        return (this.mapRef.current as any).map;
-    }
-
     render() {
         const { childLayoutPairs } = this;
         const { Document } = this.props;
@@ -156,16 +157,15 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
             onWheel={e => e.stopPropagation()}
             onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
             <GeoMap
-                ref={this.mapRef}
+                //ref={this.mapRef}
                 google={this.props.google}
                 zoom={center.zoom || 10}
                 initialCenter={center}
                 center={center}
-                onDragend={() => {
-                    const { center } = this.map;
-                    Document[this.props.fieldKey + "-mapCenter-lat"] = center.lat();
-                    Document[this.props.fieldKey + "-mapCenter-lng"] = center.lng();
-                }}
+                onDragend={undoBatch(action((e: any, map: any) => {
+                    Document[this.props.fieldKey + "-mapCenter-lat"] = map.center.lat();
+                    Document[this.props.fieldKey + "-mapCenter-lng"] = map.center.lng();
+                }))}
             >
                 {this.contents}
             </GeoMap>
@@ -174,8 +174,5 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
 
 }
 
-const LoadingContainer = () => {
-    return <div className={"loadingWrapper"}><img className={"loadingGif"} src={"/assets/loading.gif"} /></div>;
-};
-
+const LoadingContainer = () => <div className={"loadingWrapper"}><img className={"loadingGif"} src={"/assets/loading.gif"} /></div>;
 export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS!, LoadingContainer })(CollectionMapView) as any;
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5b6478e66..0ef7ece9c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -586,7 +586,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
         const docTemplate = docLayoutTemplate || creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) });
 
-        fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate));
+        fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate);
         docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined);
     }
 
-- 
cgit v1.2.3-70-g09d2


From 3494d987b72b1e60bf1ded41443391952578e323 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 14 Apr 2020 13:32:33 -0400
Subject: fixed maps to support drag/drop, and fixed to work with docs that
 just have an address.

---
 .../views/collections/CollectionMapView.scss       |  6 ++-
 src/client/views/collections/CollectionMapView.tsx | 59 ++++++++++++++--------
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 3 files changed, 44 insertions(+), 23 deletions(-)

(limited to 'src/client/views/collections/CollectionMapView.tsx')

diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss
index 4956ad946..cb58b1750 100644
--- a/src/client/views/collections/CollectionMapView.scss
+++ b/src/client/views/collections/CollectionMapView.scss
@@ -1,6 +1,10 @@
-.collectionMapView-contents {
+.collectionMapView {
     width: 100%;
     height: 100%;
+    .collectionMapView-contents {
+        width: 100%;
+        height: 100%;
+    }
 }
 
 .loadingWrapper {
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index fced52275..a80f0557f 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -40,10 +40,25 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
 
     getLocation = (doc: Opt<Doc>, fieldKey: string): Opt<LocationData> => {
         if (doc) {
-            const lat: Opt<number> = Cast(doc[fieldKey + "-lat"], "number", null);
-            const lng: Opt<number> = Cast(doc[fieldKey + "-lng"], "number", null);
-            const zoom: Opt<number> = Cast(doc[fieldKey + "-zoom"], "number", null);
-            return lat !== undefined && lng !== undefined ? ({ lat, lng, zoom }) : undefined;
+            const lat: Opt<number> = Cast(doc[fieldKey + "-lat"], "number", null) || (Cast(doc[fieldKey + "-lat"], "string", null) && Number(Cast(doc[fieldKey + "-lat"], "string", null))) || undefined;
+            const lng: Opt<number> = Cast(doc[fieldKey + "-lng"], "number", null) || (Cast(doc[fieldKey + "-lng"], "string", null) && Number(Cast(doc[fieldKey + "-lng"], "string", null))) || undefined;
+            const zoom: Opt<number> = Cast(doc[fieldKey + "-zoom"], "number", null) || (Cast(doc[fieldKey + "-zoom"], "string", null) && Number(Cast(doc[fieldKey + "-zoom"], "string", null))) || undefined;
+            const address: Opt<string> = Cast(doc[fieldKey + "-address"], "string", null);
+            if (lat !== undefined && lng !== undefined) {
+                return ({ lat, lng, zoom });
+            } else if (address) {
+                setTimeout(() => {
+                    const target = `${base}address=${address.replace(/\s+/g, "+")}&key=${process.env.GOOGLE_MAPS_GEO!}`;
+                    requestPromise.get(target).then(action((res: any) => {
+                        const { lat, lng } = JSON.parse(res).results[0].geometry.location;
+                        if (doc[fieldKey + "-lat"] !== lat || doc[fieldKey + "-lng"] !== lng) {
+                            Doc.SetInPlace(doc, fieldKey + "-lat", lat, true);
+                            Doc.SetInPlace(doc, fieldKey + "-lng", lng, true);
+                        }
+                    }));
+                });
+                return ({ lat: 35.1592238, lng: -98.444512, zoom: 15 });
+            }
         }
         return undefined;
     }
@@ -152,23 +167,25 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
             }
         }
         TraceMobx();
-        return <div className={"collectionMapView-contents"}
-            style={{ pointerEvents: this.props.active() ? undefined : "none" }}
-            onWheel={e => e.stopPropagation()}
-            onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
-            <GeoMap
-                //ref={this.mapRef}
-                google={this.props.google}
-                zoom={center.zoom || 10}
-                initialCenter={center}
-                center={center}
-                onDragend={undoBatch(action((e: any, map: any) => {
-                    Document[this.props.fieldKey + "-mapCenter-lat"] = map.center.lat();
-                    Document[this.props.fieldKey + "-mapCenter-lng"] = map.center.lng();
-                }))}
-            >
-                {this.contents}
-            </GeoMap>
+        return <div className="collectionMapView" ref={this.createDashEventsTarget}>
+            <div className={"collectionMapView-contents"}
+                style={{ pointerEvents: this.props.active() ? undefined : "none" }}
+                onWheel={e => e.stopPropagation()}
+                onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
+                <GeoMap
+                    //ref={this.mapRef}
+                    google={this.props.google}
+                    zoom={center.zoom || 10}
+                    initialCenter={center}
+                    center={center}
+                    onDragend={undoBatch(action((e: any, map: any) => {
+                        Document[this.props.fieldKey + "-mapCenter-lat"] = map.center.lat();
+                        Document[this.props.fieldKey + "-mapCenter-lng"] = map.center.lng();
+                    }))}
+                >
+                    {this.contents}
+                </GeoMap>
+            </div>
         </div>;
     }
 
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 9dd3d640b..454c3a5f2 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -309,7 +309,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this.hideMarquee();
     }
 
-    getCollection = (selected: Doc[], asTemplate: boolean, isBackground: boolean = false) => {
+    getCollection = (selected: Doc[], asTemplate: boolean, isBackground?: boolean) => {
         const bounds = this.Bounds;
         // const inkData = this.ink ? this.ink.inkData : undefined;
         const creator = asTemplate ? Docs.Create.StackingDocument : Docs.Create.FreeformDocument;
-- 
cgit v1.2.3-70-g09d2


From 3d4a7582dcd0df151f9571fdeb24507acefe49e1 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Tue, 14 Apr 2020 13:48:25 -0700
Subject: map view cleanup

---
 .../views/collections/CollectionMapView.scss       |   7 +-
 src/client/views/collections/CollectionMapView.tsx | 142 ++++++++++++---------
 2 files changed, 87 insertions(+), 62 deletions(-)

(limited to 'src/client/views/collections/CollectionMapView.tsx')

diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss
index cb58b1750..2e642be6b 100644
--- a/src/client/views/collections/CollectionMapView.scss
+++ b/src/client/views/collections/CollectionMapView.scss
@@ -1,6 +1,7 @@
 .collectionMapView {
     width: 100%;
     height: 100%;
+
     .collectionMapView-contents {
         width: 100%;
         height: 100%;
@@ -10,7 +11,7 @@
 .loadingWrapper {
     width: 100%;
     height: 100%;
-    background-color: yellow;
+    background-color: pink;
     display: flex;
     flex-direction: column;
     justify-content: center;
@@ -20,7 +21,7 @@
     .loadingGif {
         align-self: center;
         justify-self: center;
-        width: 20%;
-        height: 20%
+        width: 50px;
+        height: 50px;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index a80f0557f..b6772c5a2 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -4,14 +4,13 @@ import { Doc, Opt, DocListCast } from "../../../new_fields/Doc";
 import { documentSchema } from "../../../new_fields/documentSchemas";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, NumCast, ScriptCast, StrCast, BoolCast } from "../../../new_fields/Types";
-import { TraceMobx } from "../../../new_fields/util";
+import { Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
 import "./CollectionMapView.scss";
 import { CollectionSubView } from "./CollectionSubView";
 import React = require("react");
 import { DocumentManager } from "../../util/DocumentManager";
 import { UndoManager, undoBatch } from "../../util/UndoManager";
-import { IReactionDisposer, reaction, action, computed } from "mobx";
+import { IReactionDisposer, reaction, computed, runInAction } from "mobx";
 import requestPromise = require("request-promise");
 
 type MapSchema = makeInterface<[typeof documentSchema]>;
@@ -23,21 +22,31 @@ export type LocationData = google.maps.LatLngLiteral & {
     zoom?: number;
 };
 
-const base = "https://maps.googleapis.com/maps/api/geocode/json?";
+// Nowhere, Oklahoma
+const defaultLocation = { lat: 35.1592238, lng: -98.444512, zoom: 15 };
+
+const query = async (data: string | google.maps.LatLngLiteral) => {
+    const contents = typeof data === "string" ? `address=${data.replace(/\s+/g, "+")}` : `latlng=${data.lat},${data.lng}`;
+    const target = `https://maps.googleapis.com/maps/api/geocode/json?${contents}&key=${process.env.GOOGLE_MAPS_GEO}`;
+    return JSON.parse(await requestPromise.get(target));
+};
 
 @observer
 class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> & { google: any }>(MapSchema) {
 
-    // private mapRef = React.createRef<GeoMap>();
-    // private get map() {
-    //     return (this.mapRef.current as any).map;
-    // }
-
     private _cancelAddrReq = new Map<string, boolean>();
     private _cancelLocReq = new Map<string, boolean>();
     private addressUpdaters: IReactionDisposer[] = [];
     private latlngUpdaters: IReactionDisposer[] = [];
 
+    /**
+     * Note that all the uses of runInAction below are not included
+     * as a way to update observables (documents handle this already
+     * in their property setters), but rather to create a single bulk
+     * update and thus prevent uneeded invocations of the location-
+     * and address–updating reactions. 
+     */
+
     getLocation = (doc: Opt<Doc>, fieldKey: string): Opt<LocationData> => {
         if (doc) {
             const lat: Opt<number> = Cast(doc[fieldKey + "-lat"], "number", null) || (Cast(doc[fieldKey + "-lat"], "string", null) && Number(Cast(doc[fieldKey + "-lat"], "string", null))) || undefined;
@@ -48,27 +57,31 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
                 return ({ lat, lng, zoom });
             } else if (address) {
                 setTimeout(() => {
-                    const target = `${base}address=${address.replace(/\s+/g, "+")}&key=${process.env.GOOGLE_MAPS_GEO!}`;
-                    requestPromise.get(target).then(action((res: any) => {
-                        const { lat, lng } = JSON.parse(res).results[0].geometry.location;
-                        if (doc[fieldKey + "-lat"] !== lat || doc[fieldKey + "-lng"] !== lng) {
-                            Doc.SetInPlace(doc, fieldKey + "-lat", lat, true);
-                            Doc.SetInPlace(doc, fieldKey + "-lng", lng, true);
+                    query(address).then(({ results }) => {
+                        if (results?.length) {
+                            const { lat, lng } = results[0].geometry.location;
+                            if (doc[fieldKey + "-lat"] !== lat || doc[fieldKey + "-lng"] !== lng) {
+                                runInAction(() => {
+                                    Doc.SetInPlace(doc, fieldKey + "-lat", lat, true);
+                                    Doc.SetInPlace(doc, fieldKey + "-lng", lng, true);
+                                });
+                            }
                         }
-                    }));
+                    });
                 });
-                return ({ lat: 35.1592238, lng: -98.444512, zoom: 15 });
+                return defaultLocation;
             }
         }
         return undefined;
     }
 
-    markerClick = action(async (layout: Doc, location: LocationData) => {
-        //this.map.panTo(location);
+    private markerClick = async (layout: Doc, { lat, lng, zoom }: LocationData) => {
         const batch = UndoManager.StartBatch("marker click");
-        this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = location.lat;
-        this.layoutDoc[this.props.fieldKey + "-mapCenter-lng"] = location.lng;
-        location.zoom && (this.layoutDoc[this.props.fieldKey + "-mapCenter-zoom"] = location.zoom);
+        runInAction(() => {
+            this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = lat;
+            this.layoutDoc[this.props.fieldKey + "-mapCenter-lng"] = lng;
+            zoom && (this.layoutDoc[this.props.fieldKey + "-mapCenter-zoom"] = zoom);
+        });
         if (layout.isLinkButton && DocListCast(layout.links).length) {
             await DocumentManager.Instance.FollowLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => {
                 this.props.addDocTab(doc, where);
@@ -78,7 +91,7 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
             ScriptCast(layout.onClick)?.script.run({ this: layout, self: Cast(layout.rootDocument, Doc, null) || layout });
             batch.end();
         }
-    });
+    }
 
     renderMarkerIcon(layout: Doc) {
         const iconUrl = StrCast(this.props.Document.mapIconUrl, null);
@@ -93,6 +106,7 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
             };
         }
     }
+
     renderMarker(layout: Doc) {
         const location = this.getLocation(layout, "mapLocation");
         return !location ? (null) :
@@ -116,14 +130,14 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
                 ({ lat, lng }) => {
                     if (this._cancelLocReq.get(layout[Id])) {
                         this._cancelLocReq.set(layout[Id], false);
-                    }
-                    else if (lat !== undefined && lng !== undefined) {
-                        const target = `${base}latlng=${NumCast(lat)},${NumCast(lng)}&key=${process.env.GOOGLE_MAPS_GEO!}`;
-                        requestPromise.get(target).then(res => {
-                            const formatted_address = JSON.parse(res).results[0].formatted_address || "<invalid address>";
-                            if (formatted_address !== layout["mapLocation-address"]) {
-                                this._cancelAddrReq.set(layout[Id], true);
-                                Doc.SetInPlace(layout, "mapLocation-address", formatted_address, true);
+                    } else if (lat !== undefined && lng !== undefined) {
+                        query({ lat: NumCast(lat), lng: NumCast(lng) }).then(({ results }) => {
+                            if (results?.length) {
+                                const { formatted_address } = results[0];
+                                if (formatted_address !== layout["mapLocation-address"]) {
+                                    this._cancelAddrReq.set(layout[Id], true);
+                                    Doc.SetInPlace(layout, "mapLocation-address", formatted_address, true);
+                                }
                             }
                         });
                     }
@@ -134,54 +148,58 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
                 ({ address }) => {
                     if (this._cancelAddrReq.get(layout[Id])) {
                         this._cancelAddrReq.set(layout[Id], false);
-                    }
-                    else if (address?.length) {
-                        const target = `${base}address=${address.replace(/\s+/g, "+")}&key=${process.env.GOOGLE_MAPS_GEO!}`;
-                        requestPromise.get(target).then(action((res: any) => {
-                            const { geometry, formatted_address } = JSON.parse(res).results[0];
-                            const { lat, lng } = geometry.location;
-                            if (layout["mapLocation-lat"] !== lat || layout["mapLocation-lng"] !== lng) {
-                                this._cancelLocReq.set(layout[Id], true);
-                                Doc.SetInPlace(layout, "mapLocation-lat", lat, true);
-                                Doc.SetInPlace(layout, "mapLocation-lng", lng, true);
-                            }
-                            if (formatted_address !== address) {
-                                this._cancelAddrReq.set(layout[Id], true);
-                                Doc.SetInPlace(layout, "mapLocation-address", formatted_address, true);
+                    } else if (address?.length) {
+                        query(address).then(({ results }) => {
+                            if (results?.length) {
+                                const { geometry, formatted_address } = results[0];
+                                const { lat, lng } = geometry.location;
+                                runInAction(() => {
+                                    if (layout["mapLocation-lat"] !== lat || layout["mapLocation-lng"] !== lng) {
+                                        this._cancelLocReq.set(layout[Id], true);
+                                        Doc.SetInPlace(layout, "mapLocation-lat", lat, true);
+                                        Doc.SetInPlace(layout, "mapLocation-lng", lng, true);
+                                    }
+                                    if (formatted_address !== address) {
+                                        this._cancelAddrReq.set(layout[Id], true);
+                                        Doc.SetInPlace(layout, "mapLocation-address", formatted_address, true);
+                                    }
+                                });
                             }
-                        }));
+                        });
                     }
                 }
             ));
             return this.renderMarker(layout);
         });
     }
+
     render() {
         const { childLayoutPairs } = this;
-        const { Document } = this.props;
-        let center = this.getLocation(Document, this.props.fieldKey + "-mapCenter");
+        const { Document, fieldKey, active, google } = this.props;
+        let center = this.getLocation(Document, fieldKey + "-mapCenter");
         if (center === undefined) {
             center = childLayoutPairs.map(pair => this.getLocation(pair.layout, "mapLocation")).find(layout => layout);
             if (center === undefined) {
-                center = { lat: 35.1592238, lng: -98.444512, zoom: 15 }; // nowhere, OK
+                center = defaultLocation;
             }
         }
-        TraceMobx();
         return <div className="collectionMapView" ref={this.createDashEventsTarget}>
             <div className={"collectionMapView-contents"}
-                style={{ pointerEvents: this.props.active() ? undefined : "none" }}
+                style={{ pointerEvents: active() ? undefined : "none" }}
                 onWheel={e => e.stopPropagation()}
                 onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
                 <GeoMap
-                    //ref={this.mapRef}
-                    google={this.props.google}
+                    google={google}
                     zoom={center.zoom || 10}
                     initialCenter={center}
                     center={center}
-                    onDragend={undoBatch(action((e: any, map: any) => {
-                        Document[this.props.fieldKey + "-mapCenter-lat"] = map.center.lat();
-                        Document[this.props.fieldKey + "-mapCenter-lng"] = map.center.lng();
-                    }))}
+                    onDragend={undoBatch((_props: MapProps, map: google.maps.Map) => {
+                        const { lat, lng } = map.getCenter();
+                        runInAction(() => {
+                            Document[fieldKey + "-mapCenter-lat"] = lat();
+                            Document[fieldKey + "-mapCenter-lng"] = lng();
+                        });
+                    })}
                 >
                     {this.contents}
                 </GeoMap>
@@ -191,5 +209,11 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
 
 }
 
-const LoadingContainer = () => <div className={"loadingWrapper"}><img className={"loadingGif"} src={"/assets/loading.gif"} /></div>;
-export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS!, LoadingContainer })(CollectionMapView) as any;
\ No newline at end of file
+export default GoogleApiWrapper({
+    apiKey: process.env.GOOGLE_MAPS!,
+    LoadingContainer: () => (
+        <div className={"loadingWrapper"}>
+            <img className={"loadingGif"} src={"/assets/loading.gif"} />
+        </div>
+    )
+})(CollectionMapView) as any;
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2

