react-native-google-places-autocomplete 2.5.5 → 2.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -286,11 +286,15 @@ interface GooglePlaceData {
286
286
  interface Point {
287
287
  lat: number;
288
288
  lng: number;
289
+ latitude: number;
290
+ longitude: number;
289
291
  }
290
292
 
291
293
  interface AddressComponent {
292
294
  long_name: string;
293
295
  short_name: string;
296
+ longText: string;
297
+ shortText: string;
294
298
  types: PlaceType[];
295
299
  }
296
300
 
@@ -323,6 +327,11 @@ interface GooglePlaceDetail {
323
327
  url: string;
324
328
  utc_offset: number;
325
329
  vicinity: string;
330
+ // New Places API parameters
331
+ addressComponents: AddressComponent[];
332
+ adrFormatAddress: string;
333
+ formattedAddress: string;
334
+ location: Point;
326
335
  }
327
336
 
328
337
  /** @see https://developers.google.com/places/web-service/autocomplete */
@@ -431,6 +440,8 @@ interface GooglePlacesAutocompleteProps {
431
440
  /** text input props */
432
441
  textInputProps?: TextInputProps | Object;
433
442
  timeout?: number;
443
+ isNewPlacesAPI?: boolean;
444
+ fields?: string;
434
445
  }
435
446
 
436
447
  export type GooglePlacesAutocompleteRef = {
@@ -2,6 +2,7 @@
2
2
  import debounce from 'lodash.debounce';
3
3
  import PropTypes from 'prop-types';
4
4
  import Qs from 'qs';
5
+ import { v4 as uuidv4 } from 'uuid';
5
6
  import React, {
6
7
  forwardRef,
7
8
  useMemo,
@@ -9,6 +10,7 @@ import React, {
9
10
  useImperativeHandle,
10
11
  useRef,
11
12
  useState,
13
+ useCallback,
12
14
  } from 'react';
13
15
  import {
14
16
  ActivityIndicator,
@@ -84,36 +86,46 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
84
86
  }
85
87
  };
86
88
 
87
- const buildRowsFromResults = (results, text) => {
88
- let res = [];
89
- const shouldDisplayPredefinedPlaces = text
90
- ? results.length === 0 && text.length === 0
91
- : results.length === 0;
92
- if (
93
- shouldDisplayPredefinedPlaces ||
94
- props.predefinedPlacesAlwaysVisible === true
95
- ) {
96
- res = [
97
- ...props.predefinedPlaces.filter((place) => place?.description.length),
98
- ];
99
-
100
- if (props.currentLocation === true && hasNavigator()) {
101
- res.unshift({
102
- description: props.currentLocationLabel,
103
- isCurrentLocation: true,
104
- });
89
+ const buildRowsFromResults = useCallback(
90
+ (results, text) => {
91
+ let res = [];
92
+ const shouldDisplayPredefinedPlaces = text
93
+ ? results.length === 0 && text.length === 0
94
+ : results.length === 0;
95
+ if (
96
+ shouldDisplayPredefinedPlaces ||
97
+ props.predefinedPlacesAlwaysVisible === true
98
+ ) {
99
+ res = [
100
+ ...props.predefinedPlaces.filter(
101
+ (place) => place?.description.length,
102
+ ),
103
+ ];
104
+
105
+ if (props.currentLocation === true && hasNavigator()) {
106
+ res.unshift({
107
+ description: props.currentLocationLabel,
108
+ isCurrentLocation: true,
109
+ });
110
+ }
105
111
  }
106
- }
107
112
 
108
- res = res.map((place) => ({
109
- ...place,
110
- isPredefinedPlace: true,
111
- }));
113
+ res = res.map((place) => ({
114
+ ...place,
115
+ isPredefinedPlace: true,
116
+ }));
112
117
 
113
- return [...res, ...results];
114
- };
118
+ return [...res, ...results];
119
+ },
120
+ [
121
+ props.currentLocation,
122
+ props.currentLocationLabel,
123
+ props.predefinedPlaces,
124
+ props.predefinedPlacesAlwaysVisible,
125
+ ],
126
+ );
115
127
 
116
- const getRequestUrl = (requestUrl) => {
128
+ const getRequestUrl = useCallback((requestUrl) => {
117
129
  if (requestUrl) {
118
130
  if (requestUrl.useOnPlatform === 'all') {
119
131
  return requestUrl.url;
@@ -127,7 +139,7 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
127
139
  } else {
128
140
  return 'https://maps.googleapis.com/maps/api';
129
141
  }
130
- };
142
+ }, []);
131
143
 
132
144
  const getRequestHeaders = (requestUrl) => {
133
145
  return requestUrl?.headers || {};
@@ -144,10 +156,14 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
144
156
  const [listViewDisplayed, setListViewDisplayed] = useState(
145
157
  props.listViewDisplayed === 'auto' ? false : props.listViewDisplayed,
146
158
  );
147
- const [url] = useState(getRequestUrl(props.requestUrl));
159
+ const [url, setUrl] = useState(getRequestUrl(props.requestUrl));
148
160
  const [listLoaderDisplayed, setListLoaderDisplayed] = useState(false);
149
161
 
150
162
  const inputRef = useRef();
163
+ const [sessionToken, setSessionToken] = useState(uuidv4());
164
+ useEffect(() => {
165
+ setUrl(getRequestUrl(props.requestUrl));
166
+ }, [getRequestUrl, props.requestUrl]);
151
167
 
152
168
  useEffect(() => {
153
169
  // This will load the search results after the query object ref gets changed
@@ -157,10 +173,11 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
157
173
  };
158
174
  // eslint-disable-next-line react-hooks/exhaustive-deps
159
175
  }, [props.query]);
176
+
160
177
  useEffect(() => {
161
178
  // Update dataSource if props.predefinedPlaces changed
162
179
  setDataSource(buildRowsFromResults([]));
163
- }, [props.predefinedPlaces]);
180
+ }, [buildRowsFromResults, props.predefinedPlaces]);
164
181
 
165
182
  useImperativeHandle(ref, () => ({
166
183
  setAddressText: (address) => {
@@ -265,10 +282,14 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
265
282
 
266
283
  if (request.status === 200) {
267
284
  const responseJSON = JSON.parse(request.responseText);
268
-
269
- if (responseJSON.status === 'OK') {
285
+ if (
286
+ responseJSON.status === 'OK' ||
287
+ (props.isNewPlacesAPI && responseJSON.id)
288
+ ) {
270
289
  // if (_isMounted === true) {
271
- const details = responseJSON.result;
290
+ const details = props.isNewPlacesAPI
291
+ ? responseJSON
292
+ : responseJSON.result;
272
293
  _disableRowLoaders();
273
294
  _onBlur();
274
295
 
@@ -306,16 +327,29 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
306
327
  }
307
328
  };
308
329
 
309
- request.open(
310
- 'GET',
311
- `${url}/place/details/json?` +
312
- Qs.stringify({
313
- key: props.query.key,
314
- placeid: rowData.place_id,
315
- language: props.query.language,
316
- ...props.GooglePlacesDetailsQuery,
317
- }),
318
- );
330
+ if (props.isNewPlacesAPI) {
331
+ request.open(
332
+ 'GET',
333
+ `${url}/v1/places/${rowData.place_id}?` +
334
+ Qs.stringify({
335
+ key: props.query.key,
336
+ sessionToken,
337
+ fields: props.fields,
338
+ }),
339
+ );
340
+ setSessionToken(uuidv4());
341
+ } else {
342
+ request.open(
343
+ 'GET',
344
+ `${url}/place/details/json?` +
345
+ Qs.stringify({
346
+ key: props.query.key,
347
+ placeid: rowData.place_id,
348
+ language: props.query.language,
349
+ ...props.GooglePlacesDetailsQuery,
350
+ }),
351
+ );
352
+ }
319
353
 
320
354
  request.withCredentials = requestShouldUseWithCredentials();
321
355
  setRequestHeaders(request, getRequestHeaders(props.requestUrl));
@@ -403,6 +437,29 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
403
437
  return results;
404
438
  };
405
439
 
440
+ const _filterResultsByPlacePredictions = (unfilteredResults) => {
441
+ const results = [];
442
+ for (let i = 0; i < unfilteredResults.length; i++) {
443
+ if (unfilteredResults[i].placePrediction) {
444
+ results.push({
445
+ description: unfilteredResults[i].placePrediction.text?.text,
446
+ place_id: unfilteredResults[i].placePrediction.placeId,
447
+ reference: unfilteredResults[i].placePrediction.placeId,
448
+ structured_formatting: {
449
+ main_text:
450
+ unfilteredResults[i].placePrediction.structuredFormat?.mainText
451
+ ?.text,
452
+ secondary_text:
453
+ unfilteredResults[i].placePrediction.structuredFormat
454
+ ?.secondaryText?.text,
455
+ },
456
+ types: unfilteredResults[i].placePrediction.types ?? [],
457
+ });
458
+ }
459
+ }
460
+ return results;
461
+ };
462
+
406
463
  const _requestNearby = (latitude, longitude) => {
407
464
  _abortRequests();
408
465
 
@@ -508,6 +565,7 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
508
565
  setListLoaderDisplayed(false);
509
566
  if (request.status === 200) {
510
567
  const responseJSON = JSON.parse(request.responseText);
568
+
511
569
  if (typeof responseJSON.predictions !== 'undefined') {
512
570
  // if (_isMounted === true) {
513
571
  const results =
@@ -522,6 +580,14 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
522
580
  setDataSource(buildRowsFromResults(results, text));
523
581
  // }
524
582
  }
583
+ if (typeof responseJSON.suggestions !== 'undefined') {
584
+ const results = _filterResultsByPlacePredictions(
585
+ responseJSON.suggestions,
586
+ );
587
+
588
+ _results = results;
589
+ setDataSource(buildRowsFromResults(results, text));
590
+ }
525
591
  if (typeof responseJSON.error_message !== 'undefined') {
526
592
  if (!props.onFail)
527
593
  console.warn(
@@ -540,18 +606,39 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
540
606
  setStateText(props.preProcess(text));
541
607
  }
542
608
 
543
- request.open(
544
- 'GET',
545
- `${url}/place/autocomplete/json?input=` +
546
- encodeURIComponent(text) +
547
- '&' +
548
- Qs.stringify(props.query),
549
- );
609
+ if (props.isNewPlacesAPI) {
610
+ const keyQueryParam = props.query.key
611
+ ? '?' +
612
+ Qs.stringify({
613
+ key: props.query.key,
614
+ })
615
+ : '';
616
+ request.open('POST', `${url}/v1/places:autocomplete${keyQueryParam}`);
617
+ } else {
618
+ request.open(
619
+ 'GET',
620
+ `${url}/place/autocomplete/json?input=` +
621
+ encodeURIComponent(text) +
622
+ '&' +
623
+ Qs.stringify(props.query),
624
+ );
625
+ }
550
626
 
551
627
  request.withCredentials = requestShouldUseWithCredentials();
552
628
  setRequestHeaders(request, getRequestHeaders(props.requestUrl));
553
629
 
554
- request.send();
630
+ if (props.isNewPlacesAPI) {
631
+ const { key, locationbias, types, ...rest } = props.query;
632
+ request.send(
633
+ JSON.stringify({
634
+ input: text,
635
+ sessionToken,
636
+ ...rest,
637
+ }),
638
+ );
639
+ } else {
640
+ request.send();
641
+ }
555
642
  } else {
556
643
  _results = [];
557
644
  setDataSource(buildRowsFromResults([]));
@@ -561,6 +648,7 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
561
648
  // eslint-disable-next-line react-hooks/exhaustive-deps
562
649
  const debounceData = useMemo(() => debounce(_request, props.debounce), [
563
650
  props.query,
651
+ url,
564
652
  ]);
565
653
 
566
654
  const _onChangeText = (text) => {
@@ -888,8 +976,8 @@ GooglePlacesAutocomplete.propTypes = {
888
976
  inbetweenCompo: PropTypes.object,
889
977
  isRowScrollable: PropTypes.bool,
890
978
  keyboardShouldPersistTaps: PropTypes.oneOf(['never', 'always', 'handled']),
891
- listEmptyComponent: PropTypes.func,
892
- listLoaderComponent: PropTypes.func,
979
+ listEmptyComponent: PropTypes.element,
980
+ listLoaderComponent: PropTypes.element,
893
981
  listHoverColor: PropTypes.string,
894
982
  listUnderlayColor: PropTypes.string,
895
983
  // Must write it this way: https://stackoverflow.com/a/54290946/7180620
@@ -925,6 +1013,8 @@ GooglePlacesAutocomplete.propTypes = {
925
1013
  textInputHide: PropTypes.bool,
926
1014
  textInputProps: PropTypes.object,
927
1015
  timeout: PropTypes.number,
1016
+ isNewPlacesAPI: PropTypes.bool,
1017
+ fields: PropTypes.string,
928
1018
  };
929
1019
 
930
1020
  GooglePlacesAutocomplete.defaultProps = {
@@ -969,6 +1059,8 @@ GooglePlacesAutocomplete.defaultProps = {
969
1059
  textInputHide: false,
970
1060
  textInputProps: {},
971
1061
  timeout: 20000,
1062
+ isNewPlacesAPI: false,
1063
+ fields: '*',
972
1064
  };
973
1065
 
974
1066
  GooglePlacesAutocomplete.displayName = 'GooglePlacesAutocomplete';
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "react-native-google-places-autocomplete",
3
- "version": "2.5.5",
3
+ "version": "2.5.7",
4
4
  "description": "Customizable Google Places autocomplete component for iOS and Android React-Native apps",
5
5
  "main": "GooglePlacesAutocomplete.js",
6
6
  "types": "GooglePlacesAutocomplete.d.ts",
7
7
  "scripts": {
8
8
  "lint": "eslint . && prettier --write .",
9
- "release": "npx release-it"
9
+ "release": "npx release-it",
10
+ "prettier": "prettier --write ."
10
11
  },
11
12
  "author": "Farid from Safi",
12
13
  "license": "MIT",
@@ -30,7 +31,8 @@
30
31
  "dependencies": {
31
32
  "lodash.debounce": "^4.0.8",
32
33
  "prop-types": "^15.7.2",
33
- "qs": "~6.9.1"
34
+ "qs": "~6.9.1",
35
+ "uuid": "^10.0.0"
34
36
  },
35
37
  "devDependencies": {
36
38
  "@react-native-community/eslint-config": "2.0.0",