From 348e794e7ff341a4358be6ed9515fc6ae723092e Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sun, 12 Apr 2020 21:36:26 -0700 Subject: rough pass at collection map view --- src/client/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 & { google: any }>(MapDocument) { + + render() { + const { childLayoutPairs, props } = this; + const { Document } = props; + const center: LocationData = { lat: NumCast(Document.mapCenterLat), lng: NumCast(Document.mapCenterLng) }; + if (!center.lat) { + center.lat = childLayoutPairs.length ? NumCast(childLayoutPairs[0].layout.locationLat, 0) : 0; + center.lng = childLayoutPairs.length ? NumCast(childLayoutPairs[0].layout.locationLng, 0) : 0; + } + return ( +
+ + {childLayoutPairs.map(({ layout }) => { + const location: LocationData = { + lat: NumCast(childLayoutPairs[0].layout.locationLat, 0), + lng: NumCast(childLayoutPairs[0].layout.locationLng, 0) + }; + const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45)); + return ( + { + Document.mapCenterLat = location.lat; + Document.mapCenterLng = location.lng; + }} + icon={{ + size: iconSize, + scaledSize: iconSize, + url: StrCast(Document.mapIconUrl, "https://www.pinclipart.com/picdir/middle/359-3598915_map-marker-icon-location-icon-png-clipart.png") + }} + /> + ); + })} + +
+ ); + } + +} + +declare var process: { + env: { + GOOGLE_MAPS_API_KEY: string; + } +}; + +export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS_API_KEY })(CollectionMapView) as any; \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 8143af6ed24b4d3b02e8be306e1dd6fba1e206ce Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sun, 12 Apr 2020 21:41:06 -0700 Subject: clean up --- src/client/views/collections/CollectionMapView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/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 lng: NumCast(childLayoutPairs[0].layout.locationLng, 0) }; const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45)); + return ( icon={{ size: iconSize, scaledSize: iconSize, - url: StrCast(Document.mapIconUrl, "https://www.pinclipart.com/picdir/middle/359-3598915_map-marker-icon-location-icon-png-clipart.png") + url: StrCast(Document.mapIconUrl, null) }} /> ); -- cgit v1.2.3-70-g09d2 From e3a5dc3ce59b1d8f2dde63395505061b304d64c0 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 13 Apr 2020 00:03:13 -0700 Subject: completed migration to webpack-transferred environment variables, rather than a server route --- src/Utils.ts | 5 ----- src/client/Network.ts | 13 ------------- src/client/cognitive_services/CognitiveServices.ts | 2 +- src/client/views/collections/CollectionMapView.tsx | 8 +------- src/server/ApiManagers/UtilManager.ts | 13 ------------- webpack.config.js | 3 +-- 6 files changed, 3 insertions(+), 41 deletions(-) (limited to 'src/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 { - const response = await fetch(prepend(`/environment/${target.toUpperCase()}`)); - return response.text(); - } - export function CopyText(text: string) { const textArea = document.createElement("textarea"); textArea.value = text; diff --git a/src/client/Network.ts b/src/client/Network.ts index bd0e6e61a..6982ecf19 100644 --- a/src/client/Network.ts +++ b/src/client/Network.ts @@ -4,23 +4,10 @@ import { Upload } from "../server/SharedMediaTypes"; export namespace Networking { - const EnvVarCache = new Map(); - export async function FetchFromServer(relativeRoute: string) { return (await fetch(relativeRoute)).text(); } - export async function FetchEnvironmentVariable(varNameLiteral: string) { - let resolved = EnvVarCache.get(varNameLiteral); - if (!resolved) { - resolved = await FetchFromServer(`/environment/${varNameLiteral}`); - if (resolved !== undefined) { - EnvVarCache.set(varNameLiteral, resolved); - } - } - return resolved; - } - export async function PostToServer(relativeRoute: string, body?: any) { const options = { uri: Utils.prepend(relativeRoute), diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 3133bf4b1..8c63ae906 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -45,7 +45,7 @@ export enum Confidence { export namespace CognitiveServices { const ExecuteQuery = async (service: Service, manager: APIManager, data: D): Promise => { - const apiKey = await Utils.getApiKey(service); + const apiKey = process.env[service.toUpperCase()]; if (!apiKey) { console.log(`No API key found for ${service}: ensure index.ts has access to a .env file in your root directory.`); return undefined; diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 6ab152836..a99d5be50 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -64,10 +64,4 @@ class CollectionMapView extends CollectionSubView } -declare var process: { - env: { - GOOGLE_MAPS_API_KEY: string; - } -}; - -export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS_API_KEY })(CollectionMapView) as any; \ No newline at end of file +export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS_API_KEY! })(CollectionMapView) as any; \ No newline at end of file diff --git a/src/server/ApiManagers/UtilManager.ts b/src/server/ApiManagers/UtilManager.ts index ad8119bf4..aec523cd0 100644 --- a/src/server/ApiManagers/UtilManager.ts +++ b/src/server/ApiManagers/UtilManager.ts @@ -14,19 +14,6 @@ export default class UtilManager extends ApiManager { protected initialize(register: Registration): void { - register({ - method: Method.GET, - subscription: new RouteSubscriber("environment").add("key"), - secureHandler: ({ req, res }) => { - const { key } = req.params; - const value = process.env[key]; - if (!value) { - console.log(red(`process.env.${key} is not defined.`)); - } - return res.send(value); - } - }); - // register({ // method: Method.POST, // subscription: "/IBMAnalysis", diff --git a/webpack.config.js b/webpack.config.js index 9225093be..c8ef269d4 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -20,8 +20,7 @@ const env = require('dotenv').config().parsed; if (env) { plugins.push(new webpack.DefinePlugin(Object.keys(env).reduce((prev, next) => { if (next.startsWith("DASH_")) { - const resolved = next.replace("DASH_", ""); - prev[`process.env.${resolved}`] = JSON.stringify(env[next]); + prev[`process.env.${next.replace("DASH_", "")}`] = JSON.stringify(env[next]); } return prev; }, {}))); -- cgit v1.2.3-70-g09d2 From bf622aa5b5cd9d3256ee8e4f26245b9858cccfba Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 13 Apr 2020 00:56:21 -0700 Subject: final env cleanup --- src/client/views/collections/CollectionMapView.tsx | 20 +++++++++++-------- src/server/server_Initialization.ts | 1 - webpack.config.js | 23 ++++++++++++++-------- 3 files changed, 27 insertions(+), 17 deletions(-) (limited to 'src/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 lat: NumCast(childLayoutPairs[0].layout.locationLat, 0), lng: NumCast(childLayoutPairs[0].layout.locationLng, 0) }; - const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45)); - + let icon: Opt, iconUrl: Opt; + if ((iconUrl = StrCast(Document.mapIconUrl, null))) { + const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45)); + icon = { + size: iconSize, + scaledSize: iconSize, + url: iconUrl + }; + } return ( Document.mapCenterLat = location.lat; Document.mapCenterLng = location.lng; }} - icon={{ - size: iconSize, - scaledSize: iconSize, - url: StrCast(Document.mapIconUrl, null) - }} + icon={icon} /> ); })} @@ -64,4 +68,4 @@ class CollectionMapView extends CollectionSubView } -export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS_API_KEY! })(CollectionMapView) as any; \ No newline at end of file +export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS! })(CollectionMapView) as any; \ No newline at end of file diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts index 1150118f7..add607761 100644 --- a/src/server/server_Initialization.ts +++ b/src/server/server_Initialization.ts @@ -20,7 +20,6 @@ import * as request from 'request'; import RouteSubscriber from './RouteSubscriber'; import { publicDirectory } from '.'; import { logPort, } from './ActionUtilities'; -import { Utils } from '../Utils'; import { blue, yellow } from 'colors'; import * as cors from "cors"; diff --git a/webpack.config.js b/webpack.config.js index c8ef269d4..6265883fd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -16,16 +16,23 @@ const plugins = [ new webpack.HotModuleReplacementPlugin(), ]; -const env = require('dotenv').config().parsed; -if (env) { - plugins.push(new webpack.DefinePlugin(Object.keys(env).reduce((prev, next) => { - if (next.startsWith("DASH_")) { - prev[`process.env.${next.replace("DASH_", "")}`] = JSON.stringify(env[next]); - } - return prev; - }, {}))); +const dotenv = require('dotenv'); + +function transferEnvironmentVariables() { + const prefix = "_CLIENT_"; + const env = dotenv.config().parsed; + if (env) { + plugins.push(new webpack.DefinePlugin(Object.keys(env).reduce((mapping, envKey) => { + if (envKey.startsWith(prefix)) { + mapping[`process.env.${envKey.replace(prefix, "")}`] = JSON.stringify(env[envKey]); + } + return mapping; + }, {}))); + } } +transferEnvironmentVariables(); + module.exports = { mode: 'development', entry: { -- cgit v1.2.3-70-g09d2 From 87268612b9b765d74f1a5317b0894be0ceb1dfd1 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik 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 center.lng = childLayoutPairs.length ? NumCast(childLayoutPairs[0].layout.locationLng, 0) : 0; } return ( -
+
(this.props.active() && e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > { 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 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 & { google: any }>(MapDocument) { + getLocation = (doc: Opt, fieldKey: string, defaultLocation?: LocationData) => { + if (doc) { + let lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null); + let lng: Opt = 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) { + const location = this.getLocation(layout, "location"); + return location.lat === undefined || location.lng === undefined ? (null) : + { + 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 ( -
(this.props.active() && e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > + TraceMobx(); + return center.lat === undefined || center.lng === undefined ? (null) : +
e.stopPropagation()} + onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > {childLayoutPairs.map(({ layout }) => { - const location: LocationData = { - lat: NumCast(childLayoutPairs[0].layout.locationLat, 0), - lng: NumCast(childLayoutPairs[0].layout.locationLng, 0) - }; let icon: Opt, iconUrl: Opt; if ((iconUrl = StrCast(Document.mapIconUrl, null))) { const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45)); @@ -48,22 +92,10 @@ class CollectionMapView extends CollectionSubView url: iconUrl }; } - return ( - { - Document.mapCenterLat = location.lat; - Document.mapCenterLng = location.lng; - }} - icon={icon} - /> - ); + return this.renderMarker(layout, icon); })} -
- ); +
; } } -- cgit v1.2.3-70-g09d2 From 479964e7c13f7b9b1102b36575ad8f4cc8dc00c2 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik 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 & { google: any }>(MapDocument) { - getLocation = (doc: Opt, fieldKey: string, defaultLocation?: LocationData) => { + getLocation = (doc: Opt, fieldKey: string) => { if (doc) { let lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null); let lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null); + let zoom: Opt = 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) { - const location = this.getLocation(layout, "location"); - return location.lat === undefined || location.lng === undefined ? (null) : + const location = this.getLocation(layout, "mapLocation"); + return !location ? (null) : { - 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 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) : -
e.stopPropagation()} - onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > - - {childLayoutPairs.map(({ layout }) => { - let icon: Opt, iconUrl: Opt; - if ((iconUrl = StrCast(Document.mapIconUrl, null))) { - const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45)); - icon = { - size: iconSize, - scaledSize: iconSize, - url: iconUrl - }; - } - return this.renderMarker(layout, icon); - })} - -
; + return
e.stopPropagation()} + onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > + 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, iconUrl: Opt; + 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); + })} + +
; } } -- cgit v1.2.3-70-g09d2 From 14b3f6b3580b1987bdf9f8f865a79087e801d367 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik 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 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 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 & { google: any }>(MapDocument) { +class CollectionMapView extends CollectionSubView & { google: any }>(MapSchema) { getLocation = (doc: Opt, fieldKey: string) => { if (doc) { let lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null); let lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null); - let zoom: Opt = Cast(doc[fieldKey + "-zoom"], "number", null); + const zoom: Opt = 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 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 {childLayoutPairs.map(({ layout }) => { let icon: Opt, iconUrl: Opt; 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 } -export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS! })(CollectionMapView) as any; \ No newline at end of file +const LoadingContainer = () => { + return
; +}; + +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 - - - - - - - - - + {Object.values(CollectionViewType).map(type => ["invalid", "docking"].includes(type) ? (null) : ( + + ))}
-- cgit v1.2.3-70-g09d2 From 8a537327fb685fa9267e25e550ec0aa7a3ae86cc Mon Sep 17 00:00:00 2001 From: Sam Wilkins 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 & { google: any }>(MapSchema) { - getLocation = (doc: Opt, fieldKey: string) => { + private mapRef = React.createRef(); + private addressUpdaters: IReactionDisposer[] = []; + private latlngUpdaters: IReactionDisposer[] = []; + + getLocation = (doc: Opt, fieldKey: string): Opt => { if (doc) { - let lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null); - let lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null); + const lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null); + const lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null); const zoom: Opt = 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) { const location = this.getLocation(layout, "mapLocation"); return !location ? (null) : @@ -43,6 +50,7 @@ class CollectionMapView extends CollectionSubView & 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 & 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, iconUrl: Opt; + 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 || ""; + }); + } + } 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 & onWheel={e => e.stopPropagation()} onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > 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, iconUrl: Opt; - 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} ; } 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 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 & { google: any }>(MapSchema) { - private mapRef = React.createRef(); + private mapRef = React.createRef(); private addressUpdaters: IReactionDisposer[] = []; private latlngUpdaters: IReactionDisposer[] = []; @@ -51,7 +51,6 @@ class CollectionMapView extends CollectionSubView & 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 & />; } + _cancelAddrReq = new Map(); + _cancelLocReq = new Map(); 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, iconUrl: Opt; if ((iconUrl = StrCast(this.props.Document.mapIconUrl, null))) { const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45); @@ -92,38 +93,42 @@ class CollectionMapView extends CollectionSubView & 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 || ""; - }); - } - } 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 || ""; + 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 & style={{ pointerEvents: this.props.active() ? undefined : "none" }} onWheel={e => e.stopPropagation()} onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > - & }} > {this.contents} - + ; } -- cgit v1.2.3-70-g09d2 From 26893de4b384678041523a0bbf71f895ad1cd2b7 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik 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 & { google: any }>(MapSchema) { - private mapRef = React.createRef(); + // private mapRef = React.createRef(); + // private get map() { + // return (this.mapRef.current as any).map; + // } + + private _cancelAddrReq = new Map(); + private _cancelLocReq = new Map(); private addressUpdaters: IReactionDisposer[] = []; private latlngUpdaters: IReactionDisposer[] = []; @@ -42,62 +48,62 @@ class CollectionMapView extends CollectionSubView & return undefined; } - renderMarker(layout: Doc, icon: Opt) { + 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) : { - 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(); - _cancelLocReq = new Map(); - 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, iconUrl: Opt; - 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 || ""; if (formatted_address !== layout["mapLocation-address"]) { @@ -132,14 +138,9 @@ class CollectionMapView extends CollectionSubView & } } )); - 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 & onWheel={e => e.stopPropagation()} onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > { - 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} @@ -174,8 +174,5 @@ class CollectionMapView extends CollectionSubView & } -const LoadingContainer = () => { - return
; -}; - +const LoadingContainer = () =>
; 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(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 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 & getLocation = (doc: Opt, fieldKey: string): Opt => { if (doc) { - const lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null); - const lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null); - const zoom: Opt = Cast(doc[fieldKey + "-zoom"], "number", null); - return lat !== undefined && lng !== undefined ? ({ lat, lng, zoom }) : undefined; + const lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null) || (Cast(doc[fieldKey + "-lat"], "string", null) && Number(Cast(doc[fieldKey + "-lat"], "string", null))) || undefined; + const lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null) || (Cast(doc[fieldKey + "-lng"], "string", null) && Number(Cast(doc[fieldKey + "-lng"], "string", null))) || undefined; + const zoom: Opt = Cast(doc[fieldKey + "-zoom"], "number", null) || (Cast(doc[fieldKey + "-zoom"], "string", null) && Number(Cast(doc[fieldKey + "-zoom"], "string", null))) || undefined; + const address: Opt = 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 & } } TraceMobx(); - return
e.stopPropagation()} - onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > - { - Document[this.props.fieldKey + "-mapCenter-lat"] = map.center.lat(); - Document[this.props.fieldKey + "-mapCenter-lng"] = map.center.lng(); - }))} - > - {this.contents} - + return
+
e.stopPropagation()} + onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > + { + Document[this.props.fieldKey + "-mapCenter-lat"] = map.center.lat(); + Document[this.props.fieldKey + "-mapCenter-lng"] = map.center.lng(); + }))} + > + {this.contents} + +
; } 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 { + 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 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 & { google: any }>(MapSchema) { - // private mapRef = React.createRef(); - // private get map() { - // return (this.mapRef.current as any).map; - // } - private _cancelAddrReq = new Map(); private _cancelLocReq = new Map(); 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, fieldKey: string): Opt => { if (doc) { const lat: Opt = 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 & 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 & 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 & }; } } + renderMarker(layout: Doc) { const location = this.getLocation(layout, "mapLocation"); return !location ? (null) : @@ -116,14 +130,14 @@ class CollectionMapView extends CollectionSubView & ({ 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 || ""; - 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 & ({ 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
e.stopPropagation()} onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > { - 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} @@ -191,5 +209,11 @@ class CollectionMapView extends CollectionSubView & } -const LoadingContainer = () =>
; -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: () => ( +
+ +
+ ) +})(CollectionMapView) as any; \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 53570be88cded2ec15338857b039564b16545d47 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 14 Apr 2020 16:21:51 -0700 Subject: factored out and handled edge case, also basic error checking on request --- src/client/views/collections/CollectionMapView.tsx | 93 ++++++++++++---------- 1 file changed, 50 insertions(+), 43 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 b6772c5a2..64946e59e 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -1,6 +1,6 @@ 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 { Doc, Opt, DocListCast, FieldResult } from "../../../new_fields/Doc"; import { documentSchema } from "../../../new_fields/documentSchemas"; import { Id } from "../../../new_fields/FieldSymbols"; import { makeInterface } from "../../../new_fields/Schema"; @@ -28,7 +28,11 @@ 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)); + try { + return JSON.parse(await requestPromise.get(target)); + } catch { + return undefined; + } }; @observer @@ -36,6 +40,7 @@ class CollectionMapView extends CollectionSubView & private _cancelAddrReq = new Map(); private _cancelLocReq = new Map(); + private _initialLookupPending = new Map(); private addressUpdaters: IReactionDisposer[] = []; private latlngUpdaters: IReactionDisposer[] = []; @@ -47,7 +52,7 @@ class CollectionMapView extends CollectionSubView & * and address–updating reactions. */ - getLocation = (doc: Opt, fieldKey: string): Opt => { + private getLocation = (doc: Opt, fieldKey: string): Opt => { if (doc) { const lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null) || (Cast(doc[fieldKey + "-lat"], "string", null) && Number(Cast(doc[fieldKey + "-lat"], "string", null))) || undefined; const lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null) || (Cast(doc[fieldKey + "-lng"], "string", null) && Number(Cast(doc[fieldKey + "-lng"], "string", null))) || undefined; @@ -56,19 +61,13 @@ class CollectionMapView extends CollectionSubView & if (lat !== undefined && lng !== undefined) { return ({ lat, lng, zoom }); } else if (address) { - setTimeout(() => { - 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); - }); - } - } + const id = doc[Id]; + if (!this._initialLookupPending.get(id)) { + this._initialLookupPending.set(id, true); + setTimeout(() => { + this.respondToAddressChange(address, doc).then(() => this._initialLookupPending.delete(id)); }); - }); + } return defaultLocation; } } @@ -93,7 +92,7 @@ class CollectionMapView extends CollectionSubView & } } - renderMarkerIcon(layout: Doc) { + private renderMarkerIcon = (layout: Doc) => { const iconUrl = StrCast(this.props.Document.mapIconUrl, null); if (iconUrl) { const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45); @@ -107,7 +106,7 @@ class CollectionMapView extends CollectionSubView & } } - renderMarker(layout: Doc) { + private renderMarker = (layout: Doc) => { const location = this.getLocation(layout, "mapLocation"); return !location ? (null) : & />; } + private respondToAddressChange = async (newAddress: string, doc: Doc) => { + const { results } = await query(newAddress); + if (!results?.length) { + return; + } + const { geometry, formatted_address } = results[0]; + const { lat, lng } = geometry.location; + runInAction(() => { + if (doc["mapLocation-lat"] !== lat || doc["mapLocation-lng"] !== lng) { + this._cancelLocReq.set(doc[Id], true); + Doc.SetInPlace(doc, "mapLocation-lat", lat, true); + Doc.SetInPlace(doc, "mapLocation-lng", lng, true); + } + if (formatted_address !== newAddress) { + this._cancelAddrReq.set(doc[Id], true); + Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true); + } + }); + } + + private respondToLocationChange = async (newLat: FieldResult, newLng: FieldResult, doc: Doc) => { + const { results } = await query({ lat: NumCast(newLat), lng: NumCast(newLng) }); + if (!results?.length) { + return; + } + const { formatted_address } = results[0]; + if (formatted_address !== doc["mapLocation-address"]) { + this._cancelAddrReq.set(doc[Id], true); + Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true); + } + } + @computed get contents() { this.addressUpdaters.forEach(disposer => disposer()); this.addressUpdaters = []; @@ -131,15 +162,7 @@ class CollectionMapView extends CollectionSubView & if (this._cancelLocReq.get(layout[Id])) { this._cancelLocReq.set(layout[Id], false); } 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); - } - } - }); + this.respondToLocationChange(lat, lng, layout); } } )); @@ -149,23 +172,7 @@ class CollectionMapView extends CollectionSubView & if (this._cancelAddrReq.get(layout[Id])) { this._cancelAddrReq.set(layout[Id], false); } 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); - } - }); - } - }); + this.respondToAddressChange(address, layout); } } )); -- cgit v1.2.3-70-g09d2 From 142cb372c6dec9fa641786784cbdd6dca6896d33 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 15 Apr 2020 00:42:07 -0700 Subject: added robust error checking for lat, lng and address values, reverts to previous on invalid entry or zero results --- src/client/views/collections/CollectionMapView.tsx | 63 +++++++++++++++------- 1 file changed, 44 insertions(+), 19 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 64946e59e..5b8b6e75a 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -12,6 +12,7 @@ import { DocumentManager } from "../../util/DocumentManager"; import { UndoManager, undoBatch } from "../../util/UndoManager"; import { IReactionDisposer, reaction, computed, runInAction } from "mobx"; import requestPromise = require("request-promise"); +import { emptyFunction } from "../../../Utils"; type MapSchema = makeInterface<[typeof documentSchema]>; const MapSchema = makeInterface(documentSchema); @@ -119,11 +120,11 @@ class CollectionMapView extends CollectionSubView & } private respondToAddressChange = async (newAddress: string, doc: Doc) => { - const { results } = await query(newAddress); - if (!results?.length) { - return; + const response = await query(newAddress); + if (!response || response.status === "ZERO_RESULTS") { + return false; } - const { geometry, formatted_address } = results[0]; + const { geometry, formatted_address } = response.results[0]; const { lat, lng } = geometry.location; runInAction(() => { if (doc["mapLocation-lat"] !== lat || doc["mapLocation-lng"] !== lng) { @@ -136,18 +137,20 @@ class CollectionMapView extends CollectionSubView & Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true); } }); + return true; } private respondToLocationChange = async (newLat: FieldResult, newLng: FieldResult, doc: Doc) => { - const { results } = await query({ lat: NumCast(newLat), lng: NumCast(newLng) }); - if (!results?.length) { - return; + const response = await query({ lat: NumCast(newLat), lng: NumCast(newLng) }); + if (!response || response.status === "ZERO_RESULTS") { + return false; } - const { formatted_address } = results[0]; + const { formatted_address } = response.results[0]; if (formatted_address !== doc["mapLocation-address"]) { this._cancelAddrReq.set(doc[Id], true); Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true); } + return true; } @computed get contents() { @@ -156,23 +159,45 @@ class CollectionMapView extends CollectionSubView & this.latlngUpdaters.forEach(disposer => disposer()); this.latlngUpdaters = []; return this.childLayoutPairs.map(({ layout }) => { + const id = layout[Id]; this.addressUpdaters.push(reaction( () => ({ 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) { - this.respondToLocationChange(lat, lng, layout); + emptyFunction, + { + equals: (previous, { lat, lng }) => { + if (this._cancelLocReq.get(id)) { + this._cancelLocReq.set(id, false); + } else if (lat !== undefined && lng !== undefined) { + this.respondToLocationChange(lat, lng, layout).then(success => { + if (!success) { + this._cancelLocReq.set(id, true); + runInAction(() => { + layout["mapLocation-lat"] = previous.lat; + layout["mapLocation-lng"] = previous.lng; + }); + } + }); + } + return previous === { lat, lng }; } } )); this.latlngUpdaters.push(reaction( - () => ({ address: Cast(layout["mapLocation-address"], "string", null) }), - ({ address }) => { - if (this._cancelAddrReq.get(layout[Id])) { - this._cancelAddrReq.set(layout[Id], false); - } else if (address?.length) { - this.respondToAddressChange(address, layout); + () => Cast(layout["mapLocation-address"], "string", null), + emptyFunction, + { + equals: (previous, address) => { + if (this._cancelAddrReq.get(id)) { + this._cancelAddrReq.set(id, false); + } else if (address?.length) { + this.respondToAddressChange(address, layout).then(success => { + if (!success) { + this._cancelAddrReq.set(id, true); + layout["mapLocation-address"] = previous; + } + }); + } + return previous === address; } } )); -- cgit v1.2.3-70-g09d2 From 15f3a7898268b8fc6eded456515726a2c40b6b46 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 15 Apr 2020 01:22:18 -0700 Subject: using interesting mobx paradigm to manage value interception --- src/client/views/collections/CollectionMapView.tsx | 49 ++++++++++------------ 1 file changed, 21 insertions(+), 28 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 5b8b6e75a..4fbccac33 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -10,7 +10,7 @@ import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); import { DocumentManager } from "../../util/DocumentManager"; import { UndoManager, undoBatch } from "../../util/UndoManager"; -import { IReactionDisposer, reaction, computed, runInAction } from "mobx"; +import { IReactionDisposer, reaction, computed, runInAction, Lambda } from "mobx"; import requestPromise = require("request-promise"); import { emptyFunction } from "../../../Utils"; @@ -42,8 +42,8 @@ class CollectionMapView extends CollectionSubView & private _cancelAddrReq = new Map(); private _cancelLocReq = new Map(); private _initialLookupPending = new Map(); - private addressUpdaters: IReactionDisposer[] = []; - private latlngUpdaters: IReactionDisposer[] = []; + private addressUpdaters: Lambda[] = []; + private latlngUpdaters: Lambda[] = []; /** * Note that all the uses of runInAction below are not included @@ -153,18 +153,17 @@ class CollectionMapView extends CollectionSubView & return true; } - @computed get contents() { + @computed get reactiveContents() { this.addressUpdaters.forEach(disposer => disposer()); this.addressUpdaters = []; this.latlngUpdaters.forEach(disposer => disposer()); this.latlngUpdaters = []; return this.childLayoutPairs.map(({ layout }) => { const id = layout[Id]; - this.addressUpdaters.push(reaction( - () => ({ lat: layout["mapLocation-lat"], lng: layout["mapLocation-lng"] }), - emptyFunction, - { - equals: (previous, { lat, lng }) => { + this.addressUpdaters.push( + computed(() => ({ lat: layout["mapLocation-lat"], lng: layout["mapLocation-lng"] })) + .observe(({ oldValue, newValue }) => { + const { lat, lng } = newValue; if (this._cancelLocReq.get(id)) { this._cancelLocReq.set(id, false); } else if (lat !== undefined && lng !== undefined) { @@ -172,35 +171,29 @@ class CollectionMapView extends CollectionSubView & if (!success) { this._cancelLocReq.set(id, true); runInAction(() => { - layout["mapLocation-lat"] = previous.lat; - layout["mapLocation-lng"] = previous.lng; + layout["mapLocation-lat"] = oldValue ? oldValue.lat : undefined; + layout["mapLocation-lng"] = oldValue ? oldValue.lng : undefined; }); } }); } - return previous === { lat, lng }; - } - } - )); - this.latlngUpdaters.push(reaction( - () => Cast(layout["mapLocation-address"], "string", null), - emptyFunction, - { - equals: (previous, address) => { + }) + ); + this.latlngUpdaters.push( + computed(() => Cast(layout["mapLocation-address"], "string", null)) + .observe(({ oldValue, newValue }) => { if (this._cancelAddrReq.get(id)) { this._cancelAddrReq.set(id, false); - } else if (address?.length) { - this.respondToAddressChange(address, layout).then(success => { + } else if (newValue?.length) { + this.respondToAddressChange(newValue, layout).then(success => { if (!success) { this._cancelAddrReq.set(id, true); - layout["mapLocation-address"] = previous; + layout["mapLocation-address"] = oldValue; } }); } - return previous === address; - } - } - )); + }) + ); return this.renderMarker(layout); }); } @@ -233,7 +226,7 @@ class CollectionMapView extends CollectionSubView & }); })} > - {this.contents} + {this.reactiveContents}
; -- cgit v1.2.3-70-g09d2 From 86885c0e97322ae99f331e594a5c67cf04cb4ec2 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 15 Apr 2020 12:33:21 -0400 Subject: extended getLocation to work with the map itself in addition to makers so that document titles can be used to indicate locations. --- src/client/views/collections/CollectionMapView.tsx | 35 +++++++++++----------- 1 file changed, 17 insertions(+), 18 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 4fbccac33..583594057 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -1,18 +1,17 @@ import { GoogleApiWrapper, Map as GeoMap, MapProps, Marker } from "google-maps-react"; +import { computed, Lambda, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, Opt, DocListCast, FieldResult } from "../../../new_fields/Doc"; +import { Doc, DocListCast, FieldResult, Opt } 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 { DocumentManager } from "../../util/DocumentManager"; +import { undoBatch, UndoManager } from "../../util/UndoManager"; 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, computed, runInAction, Lambda } from "mobx"; import requestPromise = require("request-promise"); -import { emptyFunction } from "../../../Utils"; type MapSchema = makeInterface<[typeof documentSchema]>; const MapSchema = makeInterface(documentSchema); @@ -58,15 +57,15 @@ class CollectionMapView extends CollectionSubView & const lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null) || (Cast(doc[fieldKey + "-lat"], "string", null) && Number(Cast(doc[fieldKey + "-lat"], "string", null))) || undefined; const lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null) || (Cast(doc[fieldKey + "-lng"], "string", null) && Number(Cast(doc[fieldKey + "-lng"], "string", null))) || undefined; const zoom: Opt = Cast(doc[fieldKey + "-zoom"], "number", null) || (Cast(doc[fieldKey + "-zoom"], "string", null) && Number(Cast(doc[fieldKey + "-zoom"], "string", null))) || undefined; - const address: Opt = Cast(doc[fieldKey + "-address"], "string", null); + const address: Opt = Cast(doc[fieldKey + "-address"], "string", (lat === undefined || lng === undefined ? Cast(doc.title, "string", null) : null)); if (lat !== undefined && lng !== undefined) { return ({ lat, lng, zoom }); } else if (address) { const id = doc[Id]; if (!this._initialLookupPending.get(id)) { - this._initialLookupPending.set(id, true); + this._initialLookupPending.set(id, true); `` setTimeout(() => { - this.respondToAddressChange(address, doc).then(() => this._initialLookupPending.delete(id)); + this.respondToAddressChange(address, fieldKey, doc).then(() => this._initialLookupPending.delete(id)); }); } return defaultLocation; @@ -119,7 +118,7 @@ class CollectionMapView extends CollectionSubView & />; } - private respondToAddressChange = async (newAddress: string, doc: Doc) => { + private respondToAddressChange = async (newAddress: string, fieldKey: string, doc: Doc) => { const response = await query(newAddress); if (!response || response.status === "ZERO_RESULTS") { return false; @@ -127,28 +126,28 @@ class CollectionMapView extends CollectionSubView & const { geometry, formatted_address } = response.results[0]; const { lat, lng } = geometry.location; runInAction(() => { - if (doc["mapLocation-lat"] !== lat || doc["mapLocation-lng"] !== lng) { + if (doc[fieldKey + "-lat"] !== lat || doc[fieldKey + "-lng"] !== lng) { this._cancelLocReq.set(doc[Id], true); - Doc.SetInPlace(doc, "mapLocation-lat", lat, true); - Doc.SetInPlace(doc, "mapLocation-lng", lng, true); + Doc.SetInPlace(doc, fieldKey + "-lat", lat, true); + Doc.SetInPlace(doc, fieldKey + "-lng", lng, true); } if (formatted_address !== newAddress) { this._cancelAddrReq.set(doc[Id], true); - Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true); + Doc.SetInPlace(doc, fieldKey + "-address", formatted_address, true); } }); return true; } - private respondToLocationChange = async (newLat: FieldResult, newLng: FieldResult, doc: Doc) => { + private respondToLocationChange = async (newLat: FieldResult, newLng: FieldResult, fieldKey: string, doc: Doc) => { const response = await query({ lat: NumCast(newLat), lng: NumCast(newLng) }); if (!response || response.status === "ZERO_RESULTS") { return false; } const { formatted_address } = response.results[0]; - if (formatted_address !== doc["mapLocation-address"]) { + if (formatted_address !== doc[fieldKey + "-address"]) { this._cancelAddrReq.set(doc[Id], true); - Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true); + Doc.SetInPlace(doc, fieldKey + "-address", formatted_address, true); } return true; } @@ -167,7 +166,7 @@ class CollectionMapView extends CollectionSubView & if (this._cancelLocReq.get(id)) { this._cancelLocReq.set(id, false); } else if (lat !== undefined && lng !== undefined) { - this.respondToLocationChange(lat, lng, layout).then(success => { + this.respondToLocationChange(lat, lng, "mapLocation", layout).then(success => { if (!success) { this._cancelLocReq.set(id, true); runInAction(() => { @@ -185,7 +184,7 @@ class CollectionMapView extends CollectionSubView & if (this._cancelAddrReq.get(id)) { this._cancelAddrReq.set(id, false); } else if (newValue?.length) { - this.respondToAddressChange(newValue, layout).then(success => { + this.respondToAddressChange(newValue, "mapLocation", layout).then(success => { if (!success) { this._cancelAddrReq.set(id, true); layout["mapLocation-address"] = oldValue; -- cgit v1.2.3-70-g09d2 From 531aab6465459557055c0c02e35b1029192263b4 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 15 Apr 2020 10:19:58 -0700 Subject: final cleanup --- src/client/views/collections/CollectionMapView.tsx | 83 +++++++++++----------- 1 file changed, 42 insertions(+), 41 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 4fbccac33..062419b74 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -1,6 +1,6 @@ import { GoogleApiWrapper, Map as GeoMap, MapProps, Marker } from "google-maps-react"; import { observer } from "mobx-react"; -import { Doc, Opt, DocListCast, FieldResult } from "../../../new_fields/Doc"; +import { Doc, Opt, DocListCast, FieldResult, Field } from "../../../new_fields/Doc"; import { documentSchema } from "../../../new_fields/documentSchemas"; import { Id } from "../../../new_fields/FieldSymbols"; import { makeInterface } from "../../../new_fields/Schema"; @@ -10,9 +10,8 @@ import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); import { DocumentManager } from "../../util/DocumentManager"; import { UndoManager, undoBatch } from "../../util/UndoManager"; -import { IReactionDisposer, reaction, computed, runInAction, Lambda } from "mobx"; +import { computed, runInAction, Lambda } from "mobx"; import requestPromise = require("request-promise"); -import { emptyFunction } from "../../../Utils"; type MapSchema = makeInterface<[typeof documentSchema]>; const MapSchema = makeInterface(documentSchema); @@ -23,8 +22,14 @@ export type LocationData = google.maps.LatLngLiteral & { zoom?: number; }; +interface DocLatLng { + lat: FieldResult; + lng: FieldResult; +} + // Nowhere, Oklahoma const defaultLocation = { lat: 35.1592238, lng: -98.444512, zoom: 15 }; +const noResults = "ZERO_RESULTS"; const query = async (data: string | google.maps.LatLngLiteral) => { const contents = typeof data === "string" ? `address=${data.replace(/\s+/g, "+")}` : `latlng=${data.lat},${data.lng}`; @@ -42,8 +47,7 @@ class CollectionMapView extends CollectionSubView & private _cancelAddrReq = new Map(); private _cancelLocReq = new Map(); private _initialLookupPending = new Map(); - private addressUpdaters: Lambda[] = []; - private latlngUpdaters: Lambda[] = []; + private responders: { location: Lambda, address: Lambda }[] = []; /** * Note that all the uses of runInAction below are not included @@ -66,7 +70,7 @@ class CollectionMapView extends CollectionSubView & if (!this._initialLookupPending.get(id)) { this._initialLookupPending.set(id, true); setTimeout(() => { - this.respondToAddressChange(address, doc).then(() => this._initialLookupPending.delete(id)); + this.respondToAddressChange(doc, address).then(() => this._initialLookupPending.delete(id)); }); } return defaultLocation; @@ -119,30 +123,45 @@ class CollectionMapView extends CollectionSubView & />; } - private respondToAddressChange = async (newAddress: string, doc: Doc) => { + private respondToAddressChange = async (doc: Doc, newAddress: string, oldAddress?: string) => { + if (newAddress === oldAddress) { + return false; + } const response = await query(newAddress); - if (!response || response.status === "ZERO_RESULTS") { + const id = doc[Id]; + if (!response || response.status === noResults) { + this._cancelAddrReq.set(id, true); + doc["mapLocation-address"] = oldAddress; return false; } const { geometry, formatted_address } = response.results[0]; const { lat, lng } = geometry.location; runInAction(() => { if (doc["mapLocation-lat"] !== lat || doc["mapLocation-lng"] !== lng) { - this._cancelLocReq.set(doc[Id], true); + this._cancelLocReq.set(id, true); Doc.SetInPlace(doc, "mapLocation-lat", lat, true); Doc.SetInPlace(doc, "mapLocation-lng", lng, true); } if (formatted_address !== newAddress) { - this._cancelAddrReq.set(doc[Id], true); + this._cancelAddrReq.set(id, true); Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true); } }); return true; } - private respondToLocationChange = async (newLat: FieldResult, newLng: FieldResult, doc: Doc) => { - const response = await query({ lat: NumCast(newLat), lng: NumCast(newLng) }); - if (!response || response.status === "ZERO_RESULTS") { + private respondToLocationChange = async (doc: Doc, newLatLng: DocLatLng, oldLatLng: Opt) => { + if (newLatLng === oldLatLng) { + return false; + } + const response = await query({ lat: NumCast(newLatLng.lat), lng: NumCast(newLatLng.lng) }); + const id = doc[Id]; + if (!response || response.status === noResults) { + this._cancelLocReq.set(id, true); + runInAction(() => { + doc["mapLocation-lat"] = oldLatLng?.lat; + doc["mapLocation-lng"] = oldLatLng?.lng; + }); return false; } const { formatted_address } = response.results[0]; @@ -154,46 +173,28 @@ class CollectionMapView extends CollectionSubView & } @computed get reactiveContents() { - this.addressUpdaters.forEach(disposer => disposer()); - this.addressUpdaters = []; - this.latlngUpdaters.forEach(disposer => disposer()); - this.latlngUpdaters = []; + this.responders.forEach(({ location, address }) => { location(); address(); }); + this.responders = []; return this.childLayoutPairs.map(({ layout }) => { const id = layout[Id]; - this.addressUpdaters.push( - computed(() => ({ lat: layout["mapLocation-lat"], lng: layout["mapLocation-lng"] })) + this.responders.push({ + location: computed(() => ({ lat: layout["mapLocation-lat"], lng: layout["mapLocation-lng"] })) .observe(({ oldValue, newValue }) => { - const { lat, lng } = newValue; if (this._cancelLocReq.get(id)) { this._cancelLocReq.set(id, false); - } else if (lat !== undefined && lng !== undefined) { - this.respondToLocationChange(lat, lng, layout).then(success => { - if (!success) { - this._cancelLocReq.set(id, true); - runInAction(() => { - layout["mapLocation-lat"] = oldValue ? oldValue.lat : undefined; - layout["mapLocation-lng"] = oldValue ? oldValue.lng : undefined; - }); - } - }); + } else if (newValue.lat !== undefined && newValue.lng !== undefined) { + this.respondToLocationChange(layout, newValue, oldValue); } - }) - ); - this.latlngUpdaters.push( - computed(() => Cast(layout["mapLocation-address"], "string", null)) + }), + address: computed(() => Cast(layout["mapLocation-address"], "string", null)) .observe(({ oldValue, newValue }) => { if (this._cancelAddrReq.get(id)) { this._cancelAddrReq.set(id, false); } else if (newValue?.length) { - this.respondToAddressChange(newValue, layout).then(success => { - if (!success) { - this._cancelAddrReq.set(id, true); - layout["mapLocation-address"] = oldValue; - } - }); + this.respondToAddressChange(layout, newValue, oldValue); } }) - ); + }); return this.renderMarker(layout); }); } -- cgit v1.2.3-70-g09d2 From 9a2d00da67b0436d28dd64f8a25aa8a2b1a5140d Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 15 Apr 2020 10:40:07 -0700 Subject: syntax --- 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 1d8ad2458..b5043d1c5 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -207,7 +207,7 @@ class CollectionMapView extends CollectionSubView & 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, fieldKey)).find(layout => layout); + center = childLayoutPairs.map(({ layout }) => this.getLocation(layout, fieldKey)).find(layout => layout); if (center === undefined) { center = defaultLocation; } -- cgit v1.2.3-70-g09d2 From da6d058cd842209bdfd0f618e1cc19c99139135b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 15 Apr 2020 14:14:57 -0400 Subject: improved title support for map entries using @ to reset address. added lockedPosition support for maps... kinda. --- src/client/views/collections/CollectionMapView.tsx | 43 ++++++++++++++-------- 1 file changed, 28 insertions(+), 15 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 583594057..f533a3874 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -1,5 +1,5 @@ import { GoogleApiWrapper, Map as GeoMap, MapProps, Marker } from "google-maps-react"; -import { computed, Lambda, runInAction } from "mobx"; +import { computed, Lambda, runInAction, action } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, FieldResult, Opt } from "../../../new_fields/Doc"; import { documentSchema } from "../../../new_fields/documentSchemas"; @@ -54,22 +54,22 @@ class CollectionMapView extends CollectionSubView & private getLocation = (doc: Opt, fieldKey: string): Opt => { if (doc) { - const lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null) || (Cast(doc[fieldKey + "-lat"], "string", null) && Number(Cast(doc[fieldKey + "-lat"], "string", null))) || undefined; - const lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null) || (Cast(doc[fieldKey + "-lng"], "string", null) && Number(Cast(doc[fieldKey + "-lng"], "string", null))) || undefined; - const zoom: Opt = Cast(doc[fieldKey + "-zoom"], "number", null) || (Cast(doc[fieldKey + "-zoom"], "string", null) && Number(Cast(doc[fieldKey + "-zoom"], "string", null))) || undefined; - const address: Opt = Cast(doc[fieldKey + "-address"], "string", (lat === undefined || lng === undefined ? Cast(doc.title, "string", null) : null)); - if (lat !== undefined && lng !== undefined) { - return ({ lat, lng, zoom }); - } else if (address) { + const titleLoc = StrCast(doc.title).startsWith("@") ? StrCast(doc.title).substring(1) : undefined; + const lat = Cast(doc[fieldKey + "-lat"], "number", null) || (Cast(doc[fieldKey + "-lat"], "string", null) && Number(Cast(doc[fieldKey + "-lat"], "string", null))) || undefined; + const lng = Cast(doc[fieldKey + "-lng"], "number", null) || (Cast(doc[fieldKey + "-lng"], "string", null) && Number(Cast(doc[fieldKey + "-lng"], "string", null))) || undefined; + const zoom = Cast(doc[fieldKey + "-zoom"], "number", null) || (Cast(doc[fieldKey + "-zoom"], "string", null) && Number(Cast(doc[fieldKey + "-zoom"], "string", null))) || undefined; + const address = titleLoc || StrCast(doc[fieldKey + "-address"], StrCast(doc.title)); + if (titleLoc || (address && (lat === undefined || lng === undefined))) { const id = doc[Id]; if (!this._initialLookupPending.get(id)) { - this._initialLookupPending.set(id, true); `` + this._initialLookupPending.set(id, true); setTimeout(() => { + titleLoc && Doc.SetInPlace(doc, "title", titleLoc, true); this.respondToAddressChange(address, fieldKey, doc).then(() => this._initialLookupPending.delete(id)); }); } - return defaultLocation; } + return (lat === undefined || lng === undefined) ? defaultLocation : { lat, lng, zoom }; } return undefined; } @@ -217,12 +217,25 @@ class CollectionMapView extends CollectionSubView & zoom={center.zoom || 10} initialCenter={center} center={center} + onIdle={(_props: any, map: any) => { + if (this.layoutDoc.lockedTransform) { + map.setZoom(center?.zoom || 10); + } else { + center?.zoom !== map.getZoom() && undoBatch(action(() => { + Document[fieldKey + "-mapCenter-zoom"] = map.getZoom(); + }))(); + } + }} onDragend={undoBatch((_props: MapProps, map: google.maps.Map) => { - const { lat, lng } = map.getCenter(); - runInAction(() => { - Document[fieldKey + "-mapCenter-lat"] = lat(); - Document[fieldKey + "-mapCenter-lng"] = lng(); - }); + if (this.layoutDoc.lockedTransform) { + center?.lat && center.lng && map.setCenter(center); + } else { + const { lat, lng } = map.getCenter(); + runInAction(() => { + Document[fieldKey + "-mapCenter-lat"] = lat(); + Document[fieldKey + "-mapCenter-lng"] = lng(); + }); + } })} > {this.reactiveContents} -- cgit v1.2.3-70-g09d2 From 4d1782cbb359de2b672786df24985fbcf4be238a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 15 Apr 2020 14:49:57 -0400 Subject: fixed bug just added with initial location of map --- src/client/views/collections/CollectionMapView.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 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 3051b1ae7..2cb25fd9a 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -57,13 +57,13 @@ class CollectionMapView extends CollectionSubView & * and address–updating reactions. */ - private getLocation = (doc: Opt, fieldKey: string): Opt => { + private getLocation = (doc: Opt, fieldKey: string, returnDefault: boolean = true): Opt => { if (doc) { const titleLoc = StrCast(doc.title).startsWith("@") ? StrCast(doc.title).substring(1) : undefined; const lat = Cast(doc[`${fieldKey}-lat`], "number", null) || (Cast(doc[`${fieldKey}-lat`], "string", null) && Number(Cast(doc[`${fieldKey}-lat`], "string", null))) || undefined; const lng = Cast(doc[`${fieldKey}-lng`], "number", null) || (Cast(doc[`${fieldKey}-lng`], "string", null) && Number(Cast(doc[`${fieldKey}-lng`], "string", null))) || undefined; const zoom = Cast(doc[`${fieldKey}-zoom`], "number", null) || (Cast(doc[`${fieldKey}-zoom`], "string", null) && Number(Cast(doc[`${fieldKey}-zoom`], "string", null))) || undefined; - const address = titleLoc || StrCast(doc[`${fieldKey}-address`], StrCast(doc.title)); + const address = titleLoc || StrCast(doc[`${fieldKey}-address`], StrCast(doc.title).replace(/^-/, "")); if (titleLoc || (address && (lat === undefined || lng === undefined))) { const id = doc[Id]; if (!this._initialLookupPending.get(id)) { @@ -74,7 +74,7 @@ class CollectionMapView extends CollectionSubView & }); } } - return (lat === undefined || lng === undefined) ? defaultLocation : { lat, lng, zoom }; + return (lat === undefined || lng === undefined) ? (returnDefault ? defaultLocation : undefined) : { lat, lng, zoom }; } return undefined; } @@ -205,9 +205,9 @@ class CollectionMapView extends CollectionSubView & render() { const { childLayoutPairs } = this; const { Document, fieldKey, active, google } = this.props; - let center = this.getLocation(Document, `${fieldKey}-mapCenter`); + let center = this.getLocation(Document, `${fieldKey}-mapCenter`, false); if (center === undefined) { - center = childLayoutPairs.map(({ layout }) => this.getLocation(layout, fieldKey)).find(layout => layout); + center = childLayoutPairs.map(({ layout }) => this.getLocation(layout, fieldKey, false)).find(layout => layout); if (center === undefined) { center = defaultLocation; } -- cgit v1.2.3-70-g09d2 From 7d3b2880bebf034607b946360c3e08338cf1d115 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 15 Apr 2020 14:04:13 -0700 Subject: fixed fieldKey errors: --- src/client/views/collections/CollectionMapView.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 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 2cb25fd9a..c80555a2e 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -99,8 +99,9 @@ class CollectionMapView extends CollectionSubView & } private renderMarkerIcon = (layout: Doc) => { - const { Document, fieldKey } = this.props; - const iconUrl = StrCast(Document.mapIconUrl, null); + const { Document } = this.props; + const fieldKey = Doc.LayoutFieldKey(layout); + const iconUrl = StrCast(layout.mapIconUrl, StrCast(Document.mapIconUrl)); if (iconUrl) { const iconWidth = NumCast(layout[`${fieldKey}-iconWidth`], 45); const iconHeight = NumCast(layout[`${fieldKey}-iconHeight`], 45); @@ -114,7 +115,7 @@ class CollectionMapView extends CollectionSubView & } private renderMarker = (layout: Doc) => { - const location = this.getLocation(layout, this.props.fieldKey); + const location = this.getLocation(layout, Doc.LayoutFieldKey(layout)); return !location ? (null) : & } @computed get reactiveContents() { - const { fieldKey } = this.props; this.responders.forEach(({ location, address }) => { location(); address(); }); this.responders = []; return this.childLayoutPairs.map(({ layout }) => { + const fieldKey = Doc.LayoutFieldKey(layout); const id = layout[Id]; this.responders.push({ location: computed(() => ({ lat: layout[`${fieldKey}-lat`], lng: layout[`${fieldKey}-lng`] })) @@ -207,7 +208,7 @@ class CollectionMapView extends CollectionSubView & const { Document, fieldKey, active, google } = this.props; let center = this.getLocation(Document, `${fieldKey}-mapCenter`, false); if (center === undefined) { - center = childLayoutPairs.map(({ layout }) => this.getLocation(layout, fieldKey, false)).find(layout => layout); + center = childLayoutPairs.map(({ layout }) => this.getLocation(layout, Doc.LayoutFieldKey(layout), false)).find(layout => layout); if (center === undefined) { center = defaultLocation; } -- cgit v1.2.3-70-g09d2 From 35a31253ed3c7f4d31cebc2d1f2d6b1b4084108c Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 15 Apr 2020 22:27:38 -0700 Subject: cleanup --- src/client/views/collections/CollectionMapView.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 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 c80555a2e..7b7828d7d 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -208,10 +208,8 @@ class CollectionMapView extends CollectionSubView & const { Document, fieldKey, active, google } = this.props; let center = this.getLocation(Document, `${fieldKey}-mapCenter`, false); if (center === undefined) { - center = childLayoutPairs.map(({ layout }) => this.getLocation(layout, Doc.LayoutFieldKey(layout), false)).find(layout => layout); - if (center === undefined) { - center = defaultLocation; - } + const childLocations = childLayoutPairs.map(({ layout }) => this.getLocation(layout, Doc.LayoutFieldKey(layout), false)); + center = childLocations.find(location => location) || defaultLocation; } return
& center={center} onIdle={(_props?: MapProps, map?: google.maps.Map) => { if (this.layoutDoc.lockedTransform) { - map?.setZoom(center?.zoom || 10); // reset zoom (probably can tell the map to disallow zooming somehow) + // reset zoom (ideally, we could probably can tell the map to disallow zooming somehow instead) + map?.setZoom(center?.zoom || 10); + map?.setCenter({ lat: center?.lat!, lng: center?.lng! }); } else { const zoom = map?.getZoom(); - center?.zoom !== zoom && undoBatch(action(() => { + (center?.zoom !== zoom) && undoBatch(action(() => { Document[`${fieldKey}-mapCenter-zoom`] = zoom; }))(); } }} onDragend={(_props?: MapProps, map?: google.maps.Map) => { if (this.layoutDoc.lockedTransform) { - map?.setCenter({ lat: center?.lat!, lng: center?.lng! }); // reset the drag (probably can tell the map to disallow dragging somehow) + // reset the drag (ideally, we could probably can tell the map to disallow dragging somehow instead) + map?.setCenter({ lat: center?.lat!, lng: center?.lng! }); } else { undoBatch(action(({ lat, lng }) => { Document[`${fieldKey}-mapCenter-lat`] = lat(); -- cgit v1.2.3-70-g09d2