Skip to content

Commit

Permalink
Merge pull request #900 from sharetribe/reorganise-maps-config
Browse files Browse the repository at this point in the history
Reorganize maps configuration
  • Loading branch information
lyyder authored Aug 24, 2018
2 parents 28078d0 + 8926411 commit 8b70a91
Show file tree
Hide file tree
Showing 17 changed files with 260 additions and 224 deletions.
1 change: 1 addition & 0 deletions .env-template
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ REACT_APP_ENV=production
#

REACT_APP_AVAILABILITY_ENABLED=true
REACT_APP_DEFAULT_SEARCHES_ENABLED=true
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ way to update this template, but currently, we follow a pattern:
* [fix] Fix window resize redirecting to search page with reusable map component
[#905](https://github.com/sharetribe/flex-template-web/pull/905)

* [change] Maps configuration has been restructured. The new
configuration is agnostic of the maps provider in use and works with
both Google Maps as well as Mapbox.

The fuzzy location circle has less configuration, but otherwise all
the previous settings can be set also in the new configuration. See
`config.js` for details.

The default location searches are now enabled in the
`.env-template`. For old installations, the
`REACT_APP_DEFAULT_SEARCHES_ENABLED` env var should be set to
`true`. The default searches can then be configured in
`src/default-location-searches.js`.

[#900](https://github.com/sharetribe/flex-template-web/pull/900)

## v1.4.0 2018-08-17
* [change] Put availability calendar behind a feature flag
[#902](https://github.com/sharetribe/flex-template-web/pull/902)
Expand Down
1 change: 1 addition & 0 deletions docs/env.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ them have defaults that work for development environment. For production deploys
| BASIC_AUTH_PASSWORD | Set to enable HTTP Basic Auth |
| REACT_APP_GOOGLE_ANALYTICS_ID | See: [Google Analytics](./analytics.md) |
| REACT_APP_AVAILABILITY_ENABLED | Enables availability calendar for listings. |
| REACT_APP_DEFAULT_SEARCHES_ENABLED | Enables default search suggestions in location autocomplete search input. |

## Defining configuration

Expand Down
33 changes: 3 additions & 30 deletions src/components/LocationAutocompleteInput/GeocoderGoogleMaps.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,12 @@
import React from 'react';
// import { types as sdkTypes } from '../../util/sdkLoader';
import classNames from 'classnames';
import { getPlacePredictions, getPlaceDetails, locationBounds } from '../../util/googleMaps';
import { userLocation } from '../../util/maps';
import css from './LocationAutocompleteInput.css';
import config from '../../config';

// const { LatLng: SDKLatLng, LatLngBounds: SDKLatLngBounds } = sdkTypes;
import css from './LocationAutocompleteInput.css';

export const CURRENT_LOCATION_ID = 'current-location';
const CURRENT_LOCATION_BOUNDS_DISTANCE = 1000; // meters

// A list of default predictions that can be shown when the user
// focuses on the autocomplete input without typing a search. This can
// be used to reduce typing and Geocoding API calls for common
// searches.
export const defaultPredictions = [
// Examples:
// Current user location from the browser geolocation API
// {
// id: CURRENT_LOCATION_ID,
// predictionPlace: {},
// },
// Helsinki
// {
// id: 'default-helsinki',
// predictionPlace: {
// address: 'Helsinki, Finland',
// origin: new SDKLatLng(60.16985, 24.93837),
// bounds: new SDKLatLngBounds(
// new SDKLatLng(60.29783, 25.25448),
// new SDKLatLng(59.92248, 24.78287)
// ),
// },
// },
];

// When displaying data from the Google Maps Places API, and
// attribution is required next to the results.
Expand Down Expand Up @@ -117,7 +90,7 @@ class GeocoderGoogleMaps {
return {
address: '',
origin: latlng,
bounds: locationBounds(latlng, CURRENT_LOCATION_BOUNDS_DISTANCE),
bounds: locationBounds(latlng, config.maps.search.currentLocationBoundsDistance),
};
});
}
Expand Down
28 changes: 1 addition & 27 deletions src/components/LocationAutocompleteInput/GeocoderMapbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import config from '../../config';
const { LatLng: SDKLatLng, LatLngBounds: SDKLatLngBounds } = sdkTypes;

export const CURRENT_LOCATION_ID = 'current-location';
const CURRENT_LOCATION_BOUNDS_DISTANCE = 1000; // meters

const locationBounds = (latlng, distance) => {
const bounds = new window.mapboxgl.LngLat(latlng.lng, latlng.lat).toBounds(distance);
Expand Down Expand Up @@ -34,31 +33,6 @@ const placeBounds = prediction => {
return null;
};

// A list of default predictions that can be shown when the user
// focuses on the autocomplete input without typing a search. This can
// be used to reduce typing and Geocoding API calls for common
// searches.
export const defaultPredictions = [
// Examples:
// Current user location from the browser geolocation API
// {
// id: CURRENT_LOCATION_ID,
// predictionPlace: {},
// },
// Helsinki
// {
// id: 'default-helsinki',
// predictionPlace: {
// address: 'Helsinki, Finland',
// origin: new SDKLatLng(60.16985, 24.93837),
// bounds: new SDKLatLngBounds(
// new SDKLatLng(60.29783, 25.25448),
// new SDKLatLng(59.92248, 24.78287)
// ),
// },
// },
];

export const GeocoderAttribution = () => null;

/**
Expand Down Expand Up @@ -140,7 +114,7 @@ class GeocoderMapbox {
return {
address: '',
origin: latlng,
bounds: locationBounds(latlng, CURRENT_LOCATION_BOUNDS_DISTANCE),
bounds: locationBounds(latlng, config.maps.search.currentLocationBoundsDistance),
};
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ import { IconSpinner } from '../../components';
import { propTypes } from '../../util/types';
import IconHourGlass from './IconHourGlass';
import IconCurrentLocation from './IconCurrentLocation';
import Geocoder, {
GeocoderAttribution,
defaultPredictions,
CURRENT_LOCATION_ID,
} from './GeocoderGoogleMaps';
// import Geocoder, { GeocoderAttribution, defaultPredictions, CURRENT_LOCATION_ID } from './GeocoderMapbox';
import Geocoder, { GeocoderAttribution, CURRENT_LOCATION_ID } from './GeocoderGoogleMaps';
// import Geocoder, { GeocoderAttribution, CURRENT_LOCATION_ID } from './GeocoderMapbox';
import config from '../../config';

import css from './LocationAutocompleteInput.css';

// A list of default predictions that can be shown when the user
// focuses on the autocomplete input without typing a search. This can
// be used to reduce typing and Geocoding API calls for common
// searches.
export const defaultPredictions = (config.maps.search.suggestCurrentLocation
? [{ id: CURRENT_LOCATION_ID, predictionPlace: {} }]
: []
).concat(config.maps.search.defaults);

const DEBOUNCE_WAIT_TIME = 200;
const KEY_CODE_ARROW_UP = 38;
const KEY_CODE_ARROW_DOWN = 40;
Expand Down
27 changes: 17 additions & 10 deletions src/components/Map/DynamicGoogleMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import config from '../../config';
* It handles some of the google map initialization states.
*/
const DynamicGoogleMap = withGoogleMap(props => {
const { center, zoom, address, coordinatesConfig } = props;
const { center, zoom, address, mapsConfig } = props;

const { markerURI, anchorX, anchorY, width, height } = coordinatesConfig.customMarker || {};
const markerIcon = coordinatesConfig.customMarker
const { enabled, url, anchorX, anchorY, width, height } = mapsConfig.customMarker;
const markerIcon = enabled
? {
icon: {
url: markerURI,
url,

// The origin for this image is (0, 0).
origin: new window.google.maps.Point(0, 0),
Expand All @@ -27,8 +27,15 @@ const DynamicGoogleMap = withGoogleMap(props => {
const marker = <Marker position={center} {...markerIcon} title={address} />;

const circleProps = {
options: coordinatesConfig.circleOptions,
radius: coordinatesConfig.coordinateOffset,
options: {
fillColor: mapsConfig.fuzzy.circleColor,
fillOpacity: 0.2,
strokeColor: mapsConfig.fuzzy.circleColor,
strokeOpacity: 0.5,
strokeWeight: 1,
clickable: false,
},
radius: mapsConfig.fuzzy.offset,
center,
};

Expand Down Expand Up @@ -59,16 +66,16 @@ const DynamicGoogleMap = withGoogleMap(props => {
},
}}
>
{coordinatesConfig.fuzzy ? circle : marker}
{mapsConfig.fuzzy.enabled ? circle : marker}
</GoogleMap>
);
});

DynamicGoogleMap.defaultProps = {
address: '',
center: null,
zoom: config.coordinates.fuzzy ? config.coordinates.fuzzyDefaultZoomLevel : 11,
coordinatesConfig: config.coordinates,
zoom: config.maps.fuzzy.enabled ? config.maps.fuzzy.defaultZoomLevel : 11,
mapsConfig: config.maps,
};

DynamicGoogleMap.propTypes = {
Expand All @@ -78,7 +85,7 @@ DynamicGoogleMap.propTypes = {
lng: number.isRequired,
}).isRequired,
zoom: number,
coordinatesConfig: object,
mapsConfig: object,
};

export default DynamicGoogleMap;
48 changes: 22 additions & 26 deletions src/components/Map/DynamicMapboxMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,21 @@ import uniqueId from 'lodash/uniqueId';
import { circlePolyline } from '../../util/maps';
import config from '../../config';

const mapMarker = coordinatesConfig => {
const { customMarker } = coordinatesConfig;
if (customMarker) {
const mapMarker = mapsConfig => {
const { enabled, url, width, height } = mapsConfig.customMarker;
if (enabled) {
const element = document.createElement('div');
element.style.backgroundImage = `url(${customMarker.markerURI})`;
element.style.width = `${customMarker.width}px`;
element.style.height = `${customMarker.height}px`;
element.style.backgroundImage = `url(${url})`;
element.style.width = `${width}px`;
element.style.height = `${height}px`;
return new window.mapboxgl.Marker({ element });
} else {
return new window.mapboxgl.Marker();
}
};

const circleLayer = (center, coordinatesConfig, layerId) => {
const { fillColor, fillOpacity } = coordinatesConfig.circleOptions;
const path = circlePolyline(center, coordinatesConfig.coordinateOffset).map(([lat, lng]) => [
lng,
lat,
]);
const circleLayer = (center, mapsConfig, layerId) => {
const path = circlePolyline(center, mapsConfig.fuzzy.offset).map(([lat, lng]) => [lng, lat]);
return {
id: layerId,
type: 'fill',
Expand All @@ -37,8 +33,8 @@ const circleLayer = (center, coordinatesConfig, layerId) => {
},
},
paint: {
'fill-color': fillColor,
'fill-opacity': fillOpacity,
'fill-color': mapsConfig.fuzzy.circleColor,
'fill-opacity': 0.2,
},
};
};
Expand All @@ -59,7 +55,7 @@ class DynamicMapboxMap extends Component {
this.updateFuzzyCirclelayer = this.updateFuzzyCirclelayer.bind(this);
}
componentDidMount() {
const { center, zoom, coordinatesConfig } = this.props;
const { center, zoom, mapsConfig } = this.props;
const position = [center.lng, center.lat];

this.map = new window.mapboxgl.Map({
Expand All @@ -71,12 +67,12 @@ class DynamicMapboxMap extends Component {
});
this.map.addControl(new window.mapboxgl.NavigationControl(), 'top-left');

if (coordinatesConfig.fuzzy) {
if (mapsConfig.fuzzy.enabled) {
this.map.on('load', () => {
this.map.addLayer(circleLayer(center, coordinatesConfig, this.fuzzyLayerId));
this.map.addLayer(circleLayer(center, mapsConfig, this.fuzzyLayerId));
});
} else {
this.centerMarker = mapMarker(coordinatesConfig);
this.centerMarker = mapMarker(mapsConfig);
this.centerMarker.setLngLat(position).addTo(this.map);
}
}
Expand All @@ -92,7 +88,7 @@ class DynamicMapboxMap extends Component {
return;
}

const { center, zoom, coordinatesConfig } = this.props;
const { center, zoom, mapsConfig } = this.props;
const { lat, lng } = center;
const position = [lng, lat];

Expand All @@ -110,30 +106,30 @@ class DynamicMapboxMap extends Component {
}

// fuzzy circle change
if (coordinatesConfig.fuzzy && centerChanged) {
if (mapsConfig.fuzzy.enabled && centerChanged) {
if (this.map.loaded()) {
this.updateFuzzyCirclelayer();
} else {
this.map.on('load', this.updateFuzzyCirclelayer);
}
}

// NOTE: coordinatesConfig changes are not handled
// NOTE: mapsConfig changes are not handled
}
updateFuzzyCirclelayer() {
if (!this.map) {
// map already removed
return;
}
const { center, coordinatesConfig } = this.props;
const { center, mapsConfig } = this.props;
const { lat, lng } = center;
const position = [lng, lat];

this.map.removeLayer(this.fuzzyLayerId);

// We have to use a different layer id to avoid Mapbox errors
this.fuzzyLayerId = generateFuzzyLayerId();
this.map.addLayer(circleLayer(center, coordinatesConfig, this.fuzzyLayerId));
this.map.addLayer(circleLayer(center, mapsConfig, this.fuzzyLayerId));

this.map.setCenter(position);
}
Expand All @@ -150,8 +146,8 @@ class DynamicMapboxMap extends Component {
DynamicMapboxMap.defaultProps = {
address: '',
center: null,
zoom: config.coordinates.fuzzy ? config.coordinates.fuzzyDefaultZoomLevel : 11,
coordinatesConfig: config.coordinates,
zoom: config.maps.fuzzy.enabled ? config.maps.fuzzy.defaultZoomLevel : 11,
mapsConfig: config.maps,
};

DynamicMapboxMap.propTypes = {
Expand All @@ -161,7 +157,7 @@ DynamicMapboxMap.propTypes = {
lng: number.isRequired,
}).isRequired,
zoom: number,
coordinatesConfig: object,
mapsConfig: object,
};

export default DynamicMapboxMap;
18 changes: 12 additions & 6 deletions src/components/Map/Map.example.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ export const WithObfuscatedLocation = {
obfuscatedCenter: new LatLng(60.16502999999999, 24.940064399999983),
address: 'Sharetribe',
zoom: 14,
coordinatesConfig: {
...config.coordinates,
fuzzy: true,
mapsConfig: {
...config.maps,
fuzzy: {
...config.maps.fuzzy,
enabled: true,
},
},
},
};
Expand All @@ -48,9 +51,12 @@ export const WithCircleLocation = {
center: new LatLng(60.16502999999999, 24.940064399999983),
obfuscatedCenter: obfuscatedCoordinates(new LatLng(60.16502999999999, 24.940064399999983)),
address: 'Sharetribe',
coordinatesConfig: {
...config.coordinates,
fuzzy: true,
mapsConfig: {
...config.maps,
fuzzy: {
...config.maps.fuzzy,
enabled: true,
},
},
},
};
Loading

0 comments on commit 8b70a91

Please sign in to comment.