diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e214ea4..3d5b6d65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,9 @@ way to update this template, but currently, we follow a pattern: --- ## Upcoming version - +* [change] Use sessionTokens and fields for Autocomplete calls to Google Maps. + This is a reaction to pricing change of Google Maps APIs. + [#867](https://github.com/sharetribe/flex-template-web/pull/867) * [change] Change TransactionPage state management in loadData. [#863](https://github.com/sharetribe/flex-template-web/pull/863), [#865](https://github.com/sharetribe/flex-template-web/pull/865) & [#866](https://github.com/sharetribe/flex-template-web/pull/866) * [fix] Fix submit button state on contact details page. diff --git a/src/components/LocationAutocompleteInput/LocationAutocompleteInput.js b/src/components/LocationAutocompleteInput/LocationAutocompleteInput.js index a501ed13..ba198de7 100644 --- a/src/components/LocationAutocompleteInput/LocationAutocompleteInput.js +++ b/src/components/LocationAutocompleteInput/LocationAutocompleteInput.js @@ -152,6 +152,10 @@ class LocationAutocompleteInput extends Component { // Ref to the input element. this.input = null; + // Current sessionToken used to combine autocomplete calls with place details call + // This reduces Google Maps pricing. + this.autocompleteSessionToken = null; + this.changeHighlight = this.changeHighlight.bind(this); this.selectItem = this.selectItem.bind(this); this.selectItemIfNoneSelected = this.selectItemIfNoneSelected.bind(this); @@ -266,7 +270,11 @@ class LocationAutocompleteInput extends Component { selectedPlace: null, }); - getPlaceDetails(placeId) + this.autocompleteSessionToken = + this.autocompleteSessionToken || new window.google.maps.places.AutocompleteSessionToken(); + const sessionToken = this.autocompleteSessionToken; + + getPlaceDetails(placeId, sessionToken) .then(place => { this.props.input.onChange({ search: prediction.description, @@ -274,6 +282,7 @@ class LocationAutocompleteInput extends Component { selectedPlaceId: placeId, selectedPlace: place, }); + this.autocompleteSessionToken = null; }) .catch(e => { // eslint-disable-next-line no-console @@ -298,7 +307,12 @@ class LocationAutocompleteInput extends Component { throw new Error('Google Maps API must be loaded for LocationAutocompleteInput'); } const onChange = this.props.input.onChange; - getPlacePredictions(search) + + this.autocompleteSessionToken = + this.autocompleteSessionToken || new window.google.maps.places.AutocompleteSessionToken(); + const sessionToken = this.autocompleteSessionToken; + + getPlacePredictions(search, sessionToken) .then(results => { const { search: currentSearch } = currentValue(this.props); diff --git a/src/util/googleMaps.js b/src/util/googleMaps.js index 4611eecc..e64daf75 100644 --- a/src/util/googleMaps.js +++ b/src/util/googleMaps.js @@ -36,17 +36,21 @@ const placeCountry = place => { * * @param {String} placeId - ID for a place received from the * autocomplete service + * @param {String} sessionToken - token to tie different autocomplete character searches together + * with getPlaceDetails call * * @return {Promise} Promise that * resolves to the detailed place, rejects if the request failed */ -export const getPlaceDetails = placeId => +export const getPlaceDetails = (placeId, sessionToken) => new Promise((resolve, reject) => { const serviceStatus = window.google.maps.places.PlacesServiceStatus; const el = document.createElement('div'); const service = new window.google.maps.places.PlacesService(el); + const fields = ['address_component', 'formatted_address', 'geometry', 'place_id']; + const sessionTokenMaybe = sessionToken ? { sessionToken } : {}; - service.getDetails({ placeId }, (place, status) => { + service.getDetails({ placeId, fields, ...sessionTokenMaybe }, (place, status) => { if (status !== serviceStatus.OK) { reject( new Error(`Could not get details for place id "${placeId}", error status was "${status}"`) @@ -71,16 +75,19 @@ const predictionSuccessful = status => { * Get place predictions for the given search * * @param {String} search - place name or address to search + * @param {String} sessionToken - token to tie different autocomplete character searches together + * with getPlaceDetails call * * @return {Promise<{ search, predictions[] }>} - Promise of an object * with the original search query and an array of * `google.maps.places.AutocompletePrediction` objects */ -export const getPlacePredictions = search => +export const getPlacePredictions = (search, sessionToken) => new Promise((resolve, reject) => { const service = new window.google.maps.places.AutocompleteService(); + const sessionTokenMaybe = sessionToken ? { sessionToken } : {}; - service.getPlacePredictions({ input: search }, (predictions, status) => { + service.getPlacePredictions({ input: search, ...sessionTokenMaybe }, (predictions, status) => { if (!predictionSuccessful(status)) { reject(new Error(`Prediction service status not OK: ${status}`)); } else {