aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBob Zeleznik <zzzman@gmail.com>2020-04-14 17:37:25 -0400
committerBob Zeleznik <zzzman@gmail.com>2020-04-14 17:37:25 -0400
commitad863eaee3af92c3bfec6dc72bc5d9ee5eec31d4 (patch)
tree4a81511a39e2c240568646cf8cad099482ead372 /src
parenta9613d73947ee9b000c695b8f3d7685feed96a27 (diff)
parent3d4a7582dcd0df151f9571fdeb24507acefe49e1 (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
Diffstat (limited to 'src')
-rw-r--r--src/client/views/collections/CollectionMapView.scss7
-rw-r--r--src/client/views/collections/CollectionMapView.tsx142
2 files changed, 87 insertions, 62 deletions
diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss
index 7e4f9a0f4..870b7fda8 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%;
@@ -13,7 +14,7 @@
.loadingWrapper {
width: 100%;
height: 100%;
- background-color: yellow;
+ background-color: pink;
display: flex;
flex-direction: column;
justify-content: center;
@@ -23,7 +24,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