diff --git a/src/components/Map/Map.example.js b/src/components/Map/Map.example.js index 2b9da873..1a6e7684 100644 --- a/src/components/Map/Map.example.js +++ b/src/components/Map/Map.example.js @@ -13,6 +13,7 @@ export const WithMarker = { ), props: { center: new LatLng(60.16502999999999, 24.940064399999983), + obfuscatedCenter: new LatLng(60.16502999999999, 24.940064399999983), address: 'Sharetribe', zoom: 22, }, @@ -26,6 +27,7 @@ export const WithObfuscatedLocation = { ), props: { center: new LatLng(60.16502999999999, 24.940064399999983), + obfuscatedCenter: new LatLng(60.16502999999999, 24.940064399999983), address: 'Sharetribe', zoom: 14, coordinatesConfig: { diff --git a/src/components/Map/Map.js b/src/components/Map/Map.js index 3e16a6d9..0dfe43f9 100644 --- a/src/components/Map/Map.js +++ b/src/components/Map/Map.js @@ -3,7 +3,6 @@ import { string, number, object } from 'prop-types'; import { withGoogleMap, GoogleMap, Marker, Circle } from 'react-google-maps'; import classNames from 'classnames'; import { propTypes } from '../../util/types'; -import { obfuscatedCoordinates } from '../../util/maps'; import config from '../../config'; import CustomMarker from './images/marker-32x32.png'; @@ -70,12 +69,23 @@ export class Map extends Component { mapRootClassName, address, center, + obfuscatedCenter, zoom, coordinatesConfig, } = this.props; const classes = classNames(rootClassName || css.root, className); const mapClasses = mapRootClassName || css.mapRoot; - const location = coordinatesConfig.fuzzy ? obfuscatedCoordinates(center) : center; + + if (coordinatesConfig.fuzzy && !obfuscatedCenter) { + throw new Error( + 'Map: obfuscatedCenter prop is required when config.coordinates.fuzzy === true' + ); + } + if (!coordinatesConfig.fuzzy && !center) { + throw new Error('Map: center prop is required when config.coordinates.fuzzy === false'); + } + + const location = coordinatesConfig.fuzzy ? obfuscatedCenter : center; const centerLocationForGoogleMap = { lat: location.lat, lng: location.lng }; return ( @@ -95,6 +105,7 @@ Map.defaultProps = { className: null, rootClassName: null, mapRootClassName: null, + address: '', zoom: config.coordinates.fuzzy ? config.coordinates.fuzzyDefaultZoomLevel : 11, coordinatesConfig: config.coordinates, }; @@ -103,8 +114,9 @@ Map.propTypes = { className: string, rootClassName: string, mapRootClassName: string, - address: string.isRequired, - center: propTypes.latlng.isRequired, + address: string, + center: propTypes.latlng, + obfuscatedCenter: propTypes.latlng, zoom: number, coordinatesConfig: object, }; diff --git a/src/components/SearchMap/SearchMap.js b/src/components/SearchMap/SearchMap.js index cdeb45f1..b4c6447b 100644 --- a/src/components/SearchMap/SearchMap.js +++ b/src/components/SearchMap/SearchMap.js @@ -71,14 +71,15 @@ const reducedToArray = mapListings => { const withCoordinatesObfuscated = listings => { return listings.map(listing => { - const { attributes, ...rest } = listing; - const attrs = { - ...attributes, - geolocation: obfuscatedCoordinates(attributes.geolocation), - }; + const { id, attributes, ...rest } = listing; + const geolocation = obfuscatedCoordinates(attributes.geolocation, id ? id.uuid : null); return { + id, ...rest, - attributes: attrs, + attributes: { + ...attributes, + geolocation, + }, }; }); }; diff --git a/src/config.js b/src/config.js index 552ec11d..aa1472b5 100644 --- a/src/config.js +++ b/src/config.js @@ -241,7 +241,7 @@ const facebookAppId = null; const coordinates = { // If true, obfuscate the coordinates of the listings that are shown // on a map. - fuzzy: process.env.REACT_APP_FUZZY_COORDINATES || true, + fuzzy: process.env.REACT_APP_FUZZY_COORDINATES || false, fuzzyDefaultZoomLevel: 14, diff --git a/src/containers/ListingPage/ListingPage.js b/src/containers/ListingPage/ListingPage.js index 61e719ea..bc043942 100644 --- a/src/containers/ListingPage/ListingPage.js +++ b/src/containers/ListingPage/ListingPage.js @@ -575,7 +575,11 @@ export class ListingPageComponent extends Component { - +

diff --git a/src/containers/ListingPage/SectionMapMaybe.js b/src/containers/ListingPage/SectionMapMaybe.js index 0d0b6a0a..f5499cd8 100644 --- a/src/containers/ListingPage/SectionMapMaybe.js +++ b/src/containers/ListingPage/SectionMapMaybe.js @@ -1,36 +1,52 @@ import React from 'react'; -import { object, string } from 'prop-types'; +import { string } from 'prop-types'; import { FormattedMessage } from 'react-intl'; import classNames from 'classnames'; import { propTypes } from '../../util/types'; +import { obfuscatedCoordinates } from '../../util/maps'; import { Map } from '../../components'; +import config from '../../config'; import css from './ListingPage.css'; const SectionMapMaybe = props => { - const { className, rootClassName, geolocation, publicData } = props; + const { className, rootClassName, geolocation, publicData, listingId } = props; + + if (!geolocation) { + return null; + } + const address = publicData.location ? publicData.location.address : ''; const classes = classNames(rootClassName || css.locationContainer, className); - return geolocation ? ( + const mapProps = config.coordinates.fuzzy + ? { obfuscatedCenter: obfuscatedCoordinates(geolocation, listingId ? listingId.uuid : null) } + : { address, center: geolocation }; + + return (

- +
- ) : null; + ); }; -SectionMapMaybe.defaultProps = { className: null, rootClassName: null }; +SectionMapMaybe.defaultProps = { + rootClassName: null, + className: null, + geolocation: null, + listingId: null, +}; SectionMapMaybe.propTypes = { - className: string, rootClassName: string, - geolocation: propTypes.latlng.isRequired, - publicData: object.isRequired, + className: string, + geolocation: propTypes.latlng, + listingId: propTypes.uuid, }; export default SectionMapMaybe; diff --git a/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap b/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap index 924f07f7..15e4d027 100644 --- a/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap +++ b/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap @@ -334,6 +334,11 @@ exports[`ListingPage matches snapshot 1`] = ` "lng": 60, } } + listingId={ + UUID { + "uuid": "listing1", + } + } publicData={Object {}} rootClassName={null} /> diff --git a/src/containers/SearchPage/__snapshots__/SearchPage.test.js.snap b/src/containers/SearchPage/__snapshots__/SearchPage.test.js.snap index 9129bd02..479088a3 100644 --- a/src/containers/SearchPage/__snapshots__/SearchPage.test.js.snap +++ b/src/containers/SearchPage/__snapshots__/SearchPage.test.js.snap @@ -124,7 +124,7 @@ exports[`SearchPageComponent matches snapshot 1`] = ` "strokeWeight": 0.5, }, "circleRadius": 500, - "fuzzy": true, + "fuzzy": false, "fuzzyDefaultZoomLevel": 14, } } diff --git a/src/util/maps.js b/src/util/maps.js index 6b986184..a5440366 100644 --- a/src/util/maps.js +++ b/src/util/maps.js @@ -1,19 +1,34 @@ -import { random } from 'lodash'; +import { random, memoize, round } from 'lodash'; import { types as sdkTypes } from './sdkLoader'; const { LatLng } = sdkTypes; +const obfuscatedCoordinatesImpl = latlng => { + const { lat, lng } = latlng; + const threshold = 0.01; + const newLat = round(lat + random(-1 * threshold, threshold), 5); + const newLng = round(lng + random(-1 * threshold, threshold), 5); + return new LatLng(newLat, newLng); +}; + +const obfuscationKeyGetter = (latlng, cacheKey) => cacheKey; + +const memoizedObfuscatedCoordinatesImpl = memoize(obfuscatedCoordinatesImpl, obfuscationKeyGetter); + /** * Make the given coordinates randomly a little bit different. * * @param {LatLng} latlng coordinates + * @param {String?} cacheKey if given, the results are memoized and + * the same coordinates are returned for the same key as long as the + * cache isn't cleared (e.g. with page refresh). This results in + * e.g. same listings always getting the same obfuscated coordinates + * if the listing id is used as the cache key. * * @return {LatLng} obfuscated coordinates */ -export const obfuscatedCoordinates = latlng => { - const { lat, lng } = latlng; - const threshold = 0.01; - const newLat = lat + random(-1 * threshold, threshold); - const newLng = lng + random(-1 * threshold, threshold); - return new LatLng(newLat, newLng); +export const obfuscatedCoordinates = (latlng, cacheKey = null) => { + return cacheKey + ? memoizedObfuscatedCoordinatesImpl(latlng, cacheKey) + : obfuscatedCoordinatesImpl(latlng); };