mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-29 05:00:43 +10:00
Improve coordinate obfuscation
Now the obfuscation results are memoized so that the results stay stable for each listing id.
This commit is contained in:
parent
b8b8b94375
commit
505cafdb7a
9 changed files with 84 additions and 29 deletions
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
||||
|
|
|
|||
|
|
@ -575,7 +575,11 @@ export class ListingPageComponent extends Component {
|
|||
</div>
|
||||
|
||||
<SectionRulesMaybe publicData={publicData} />
|
||||
<SectionMapMaybe geolocation={geolocation} publicData={publicData} />
|
||||
<SectionMapMaybe
|
||||
geolocation={geolocation}
|
||||
publicData={publicData}
|
||||
listingId={currentListing.id}
|
||||
/>
|
||||
|
||||
<div className={css.reviewsContainer}>
|
||||
<h2 className={css.reviewsHeading}>
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className={classes}>
|
||||
<h2 className={css.locationTitle}>
|
||||
<FormattedMessage id="ListingPage.locationTitle" />
|
||||
</h2>
|
||||
<div className={css.map}>
|
||||
<Map center={geolocation} address={address} />
|
||||
<Map {...mapProps} />
|
||||
</div>
|
||||
</div>
|
||||
) : 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;
|
||||
|
|
|
|||
|
|
@ -334,6 +334,11 @@ exports[`ListingPage matches snapshot 1`] = `
|
|||
"lng": 60,
|
||||
}
|
||||
}
|
||||
listingId={
|
||||
UUID {
|
||||
"uuid": "listing1",
|
||||
}
|
||||
}
|
||||
publicData={Object {}}
|
||||
rootClassName={null}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ exports[`SearchPageComponent matches snapshot 1`] = `
|
|||
"strokeWeight": 0.5,
|
||||
},
|
||||
"circleRadius": 500,
|
||||
"fuzzy": true,
|
||||
"fuzzy": false,
|
||||
"fuzzyDefaultZoomLevel": 14,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue