react-native-google-places-autocomplete 2.6.3 → 2.6.5
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.
- package/GooglePlacesAutocomplete.d.ts +19 -8
- package/GooglePlacesAutocomplete.js +828 -972
- package/package.json +38 -15
- package/src/Row.js +107 -0
- package/src/debounce.js +31 -0
- package/src/queryString.js +49 -0
- package/src/results.js +105 -0
- package/src/styles.js +92 -0
- package/src/uuid.js +60 -0
- package/example/App.js +0 -221
- package/example/README.md +0 -64
- package/example/app.json +0 -24
- package/example/babel.config.js +0 -6
- package/example/package.json +0 -23
package/package.json
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-google-places-autocomplete",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.5",
|
|
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
|
+
"sideEffects": false,
|
|
8
|
+
"files": [
|
|
9
|
+
"GooglePlacesAutocomplete.js",
|
|
10
|
+
"GooglePlacesAutocomplete.d.ts",
|
|
11
|
+
"src/",
|
|
12
|
+
"images/",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
7
16
|
"scripts": {
|
|
8
17
|
"lint": "eslint . && prettier --write .",
|
|
9
18
|
"release": "npx release-it",
|
|
10
|
-
"prettier": "prettier --write ."
|
|
19
|
+
"prettier": "prettier --write .",
|
|
20
|
+
"prepare": "husky install",
|
|
21
|
+
"test": "jest",
|
|
22
|
+
"test:watch": "jest --watch",
|
|
23
|
+
"typecheck": "tsc --noEmit"
|
|
11
24
|
},
|
|
12
25
|
"author": "Farid from Safi",
|
|
13
26
|
"license": "MIT",
|
|
@@ -28,29 +41,39 @@
|
|
|
28
41
|
"url": "https://github.com/FaridSafi/react-native-google-places-autocomplete/issues"
|
|
29
42
|
},
|
|
30
43
|
"homepage": "https://github.com/FaridSafi/react-native-google-places-autocomplete#readme",
|
|
31
|
-
"dependencies": {
|
|
32
|
-
"lodash.debounce": "^4.0.8",
|
|
33
|
-
"qs": "~6.9.1",
|
|
34
|
-
"react-native-uuid": "^2.0.3"
|
|
35
|
-
},
|
|
36
44
|
"devDependencies": {
|
|
45
|
+
"@babel/core": "^7.29.0",
|
|
46
|
+
"@babel/preset-env": "^7.29.0",
|
|
47
|
+
"@babel/preset-react": "^7.28.0",
|
|
37
48
|
"@react-native-community/eslint-config": "2.0.0",
|
|
49
|
+
"@react-native/babel-preset": "0.86.3",
|
|
50
|
+
"@react-native/jest-preset": "0.86.3",
|
|
51
|
+
"@testing-library/react-native": "^13.3.3",
|
|
52
|
+
"@types/react": "^19.2.0",
|
|
53
|
+
"babel-jest": "^29.7.0",
|
|
38
54
|
"eslint": "7.10.0",
|
|
39
55
|
"eslint-plugin-prettier": "^3.1.2",
|
|
40
|
-
"husky": "
|
|
56
|
+
"husky": "^8.0.3",
|
|
57
|
+
"jest": "^29.7.0",
|
|
41
58
|
"lint-staged": "10.4.0",
|
|
42
59
|
"prettier": "2.1.2",
|
|
43
|
-
"
|
|
60
|
+
"qs": "^6.14.1",
|
|
61
|
+
"react": "19.2.3",
|
|
62
|
+
"react-native": "0.86.3",
|
|
63
|
+
"react-test-renderer": "19.2.3",
|
|
64
|
+
"typescript": "^5.6.0"
|
|
44
65
|
},
|
|
45
66
|
"peerDependencies": {
|
|
67
|
+
"react": ">=16.8.0",
|
|
46
68
|
"react-native": ">= 0.59"
|
|
47
69
|
},
|
|
48
|
-
"husky": {
|
|
49
|
-
"hooks": {
|
|
50
|
-
"pre-commit": "lint-staged"
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
70
|
"lint-staged": {
|
|
54
|
-
"
|
|
71
|
+
"*.{js,jsx,ts,tsx}": [
|
|
72
|
+
"eslint --fix",
|
|
73
|
+
"prettier --write"
|
|
74
|
+
],
|
|
75
|
+
"*.{json,md,yml,yaml}": [
|
|
76
|
+
"prettier --write"
|
|
77
|
+
]
|
|
55
78
|
}
|
|
56
79
|
}
|
package/src/Row.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import React, { memo, useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
ActivityIndicator,
|
|
4
|
+
Pressable,
|
|
5
|
+
ScrollView,
|
|
6
|
+
Text,
|
|
7
|
+
View,
|
|
8
|
+
} from 'react-native';
|
|
9
|
+
|
|
10
|
+
import { defaultStyles } from './styles';
|
|
11
|
+
|
|
12
|
+
const Loader = () => <ActivityIndicator animating={true} size='small' />;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A single suggestion row.
|
|
16
|
+
*
|
|
17
|
+
* Memoised, and — unlike the previous implementation — it only mounts a
|
|
18
|
+
* horizontal ScrollView when `isRowScrollable` is actually on. A native scroll
|
|
19
|
+
* view per row is expensive, and the list renders one per suggestion.
|
|
20
|
+
*/
|
|
21
|
+
const Row = memo(function Row({
|
|
22
|
+
rowData,
|
|
23
|
+
index,
|
|
24
|
+
isLoading,
|
|
25
|
+
isRowScrollable,
|
|
26
|
+
keyboardShouldPersistTaps,
|
|
27
|
+
listHoverColor,
|
|
28
|
+
listUnderlayColor,
|
|
29
|
+
mergedStyles,
|
|
30
|
+
numberOfLines,
|
|
31
|
+
onBlur,
|
|
32
|
+
onPress,
|
|
33
|
+
renderDescription,
|
|
34
|
+
renderRow,
|
|
35
|
+
suppressDefaultStyles,
|
|
36
|
+
}) {
|
|
37
|
+
const handlePress = useCallback(() => onPress(rowData), [onPress, rowData]);
|
|
38
|
+
|
|
39
|
+
const widthStyle = isRowScrollable
|
|
40
|
+
? defaultStyles.minFullWidth
|
|
41
|
+
: defaultStyles.fullWidth;
|
|
42
|
+
|
|
43
|
+
const pressableStyle = useCallback(
|
|
44
|
+
({ hovered, pressed }) => [
|
|
45
|
+
widthStyle,
|
|
46
|
+
{
|
|
47
|
+
backgroundColor: pressed
|
|
48
|
+
? listUnderlayColor
|
|
49
|
+
: hovered
|
|
50
|
+
? listHoverColor
|
|
51
|
+
: undefined,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
[widthStyle, listUnderlayColor, listHoverColor],
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const content = (
|
|
58
|
+
<Pressable style={pressableStyle} onPress={handlePress} onBlur={onBlur}>
|
|
59
|
+
<View
|
|
60
|
+
style={
|
|
61
|
+
rowData.isPredefinedPlace
|
|
62
|
+
? mergedStyles.specialItemRow
|
|
63
|
+
: mergedStyles.row
|
|
64
|
+
}
|
|
65
|
+
>
|
|
66
|
+
{isLoading ? (
|
|
67
|
+
<View style={mergedStyles.loader}>
|
|
68
|
+
<Loader />
|
|
69
|
+
</View>
|
|
70
|
+
) : null}
|
|
71
|
+
{renderRow ? (
|
|
72
|
+
renderRow(rowData, index)
|
|
73
|
+
) : (
|
|
74
|
+
<Text
|
|
75
|
+
style={
|
|
76
|
+
rowData.isPredefinedPlace
|
|
77
|
+
? mergedStyles.predefinedPlacesDescription
|
|
78
|
+
: mergedStyles.description
|
|
79
|
+
}
|
|
80
|
+
numberOfLines={numberOfLines}
|
|
81
|
+
>
|
|
82
|
+
{renderDescription(rowData)}
|
|
83
|
+
</Text>
|
|
84
|
+
)}
|
|
85
|
+
</View>
|
|
86
|
+
</Pressable>
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
if (!isRowScrollable) {
|
|
90
|
+
return content;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<ScrollView
|
|
95
|
+
contentContainerStyle={defaultStyles.minFullWidth}
|
|
96
|
+
scrollEnabled={true}
|
|
97
|
+
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
|
|
98
|
+
horizontal={true}
|
|
99
|
+
showsHorizontalScrollIndicator={false}
|
|
100
|
+
showsVerticalScrollIndicator={false}
|
|
101
|
+
>
|
|
102
|
+
{content}
|
|
103
|
+
</ScrollView>
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
export default Row;
|
package/src/debounce.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trailing-edge debounce, replacing `lodash.debounce`.
|
|
3
|
+
*
|
|
4
|
+
* A wait of 0 still defers to the next tick, matching lodash's behaviour and
|
|
5
|
+
* the existing default of `debounce={0}`.
|
|
6
|
+
*/
|
|
7
|
+
export const debounce = (fn, wait = 0) => {
|
|
8
|
+
let timeoutId = null;
|
|
9
|
+
|
|
10
|
+
const debounced = function (...args) {
|
|
11
|
+
const context = this;
|
|
12
|
+
|
|
13
|
+
if (timeoutId !== null) {
|
|
14
|
+
clearTimeout(timeoutId);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
timeoutId = setTimeout(() => {
|
|
18
|
+
timeoutId = null;
|
|
19
|
+
fn.apply(context, args);
|
|
20
|
+
}, wait);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
debounced.cancel = () => {
|
|
24
|
+
if (timeoutId !== null) {
|
|
25
|
+
clearTimeout(timeoutId);
|
|
26
|
+
timeoutId = null;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return debounced;
|
|
31
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drop-in replacement for the subset of `qs.stringify` this library uses.
|
|
3
|
+
*
|
|
4
|
+
* Matches `qs` defaults exactly: RFC3986 percent-encoding, `undefined` values
|
|
5
|
+
* omitted, `null` serialised as a bare `key=`, arrays in `indices` format
|
|
6
|
+
* (`a[0]=x`) and nested objects in bracket format (`a[b]=x`).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// encodeURIComponent leaves !'()* alone; RFC3986 (and therefore qs) does not.
|
|
10
|
+
const encode = (value) =>
|
|
11
|
+
encodeURIComponent(String(value)).replace(
|
|
12
|
+
/[!'()*]/g,
|
|
13
|
+
(char) => '%' + char.charCodeAt(0).toString(16).toUpperCase(),
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const append = (parts, key, value) => {
|
|
17
|
+
if (value === undefined) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (value === null) {
|
|
22
|
+
parts.push(`${encode(key)}=`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (Array.isArray(value)) {
|
|
27
|
+
value.forEach((item, index) => append(parts, `${key}[${index}]`, item));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (typeof value === 'object') {
|
|
32
|
+
Object.keys(value).forEach((nestedKey) =>
|
|
33
|
+
append(parts, `${key}[${nestedKey}]`, value[nestedKey]),
|
|
34
|
+
);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
parts.push(`${encode(key)}=${encode(value)}`);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const stringify = (params) => {
|
|
42
|
+
if (!params) {
|
|
43
|
+
return '';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const parts = [];
|
|
47
|
+
Object.keys(params).forEach((key) => append(parts, key, params[key]));
|
|
48
|
+
return parts.join('&');
|
|
49
|
+
};
|
package/src/results.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for turning API responses into the rows the list renders.
|
|
3
|
+
* Kept free of React so they can be unit-tested directly.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Keep only the results carrying at least one of the requested types.
|
|
8
|
+
*
|
|
9
|
+
* Previously written as `types?.indexOf(t) !== -1`, which evaluated to
|
|
10
|
+
* `undefined !== -1` — i.e. `true` — for results with no `types` field, so
|
|
11
|
+
* untyped results slipped through the filter.
|
|
12
|
+
*/
|
|
13
|
+
export const filterResultsByTypes = (unfilteredResults, types) => {
|
|
14
|
+
if (!types || types.length === 0) {
|
|
15
|
+
return unfilteredResults;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return unfilteredResults.filter(
|
|
19
|
+
(result) =>
|
|
20
|
+
Array.isArray(result.types) &&
|
|
21
|
+
result.types.some((type) => types.indexOf(type) !== -1),
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** Map new (v1) Places API suggestions onto the legacy prediction shape. */
|
|
26
|
+
export const filterResultsByPlacePredictions = (unfilteredResults) => {
|
|
27
|
+
const results = [];
|
|
28
|
+
|
|
29
|
+
for (let i = 0; i < unfilteredResults.length; i++) {
|
|
30
|
+
const { placePrediction } = unfilteredResults[i];
|
|
31
|
+
|
|
32
|
+
if (!placePrediction) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
results.push({
|
|
37
|
+
description: placePrediction.text?.text,
|
|
38
|
+
place_id: placePrediction.placeId,
|
|
39
|
+
reference: placePrediction.placeId,
|
|
40
|
+
structured_formatting: {
|
|
41
|
+
main_text: placePrediction.structuredFormat?.mainText?.text,
|
|
42
|
+
secondary_text: placePrediction.structuredFormat?.secondaryText?.text,
|
|
43
|
+
},
|
|
44
|
+
types: placePrediction.types ?? [],
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return results;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Compose the rendered rows: predefined places (and optionally a "current
|
|
53
|
+
* location" entry) followed by the API results.
|
|
54
|
+
*
|
|
55
|
+
* Returns the `results` array untouched when there is nothing to prepend, so
|
|
56
|
+
* FlatList identity checks stay meaningful.
|
|
57
|
+
*/
|
|
58
|
+
export const buildRowsFromResults = (results, text, options) => {
|
|
59
|
+
const {
|
|
60
|
+
predefinedPlaces = [],
|
|
61
|
+
predefinedPlacesAlwaysVisible = false,
|
|
62
|
+
currentLocation = false,
|
|
63
|
+
currentLocationLabel = 'Current location',
|
|
64
|
+
} = options;
|
|
65
|
+
|
|
66
|
+
const shouldDisplayPredefinedPlaces =
|
|
67
|
+
predefinedPlacesAlwaysVisible === true ||
|
|
68
|
+
((!text || text.length === 0) && results.length === 0);
|
|
69
|
+
|
|
70
|
+
if (!shouldDisplayPredefinedPlaces) {
|
|
71
|
+
return results;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const rows = predefinedPlaces
|
|
75
|
+
.filter((place) => place?.description?.length)
|
|
76
|
+
.map((place) => ({ ...place, isPredefinedPlace: true }));
|
|
77
|
+
|
|
78
|
+
if (currentLocation === true) {
|
|
79
|
+
rows.unshift({
|
|
80
|
+
description: currentLocationLabel,
|
|
81
|
+
isCurrentLocation: true,
|
|
82
|
+
isPredefinedPlace: true,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (rows.length === 0) {
|
|
87
|
+
return results;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return [...rows, ...results];
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/** Stable identity for a row, used as the FlatList key and loader target. */
|
|
94
|
+
export const getRowKey = (row) => {
|
|
95
|
+
if (row.isCurrentLocation === true) {
|
|
96
|
+
return 'current-location';
|
|
97
|
+
}
|
|
98
|
+
if (row.place_id) {
|
|
99
|
+
return `place:${row.place_id}`;
|
|
100
|
+
}
|
|
101
|
+
if (row.isPredefinedPlace && row.description) {
|
|
102
|
+
return `predefined:${row.description}`;
|
|
103
|
+
}
|
|
104
|
+
return `row:${row.description ?? ''}`;
|
|
105
|
+
};
|
package/src/styles.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export const defaultStyles = StyleSheet.create({
|
|
4
|
+
container: {
|
|
5
|
+
flex: 1,
|
|
6
|
+
},
|
|
7
|
+
textInputContainer: {
|
|
8
|
+
flexDirection: 'row',
|
|
9
|
+
},
|
|
10
|
+
textInput: {
|
|
11
|
+
backgroundColor: '#FFFFFF',
|
|
12
|
+
height: 44,
|
|
13
|
+
borderRadius: 5,
|
|
14
|
+
paddingVertical: 5,
|
|
15
|
+
paddingHorizontal: 10,
|
|
16
|
+
fontSize: 15,
|
|
17
|
+
flex: 1,
|
|
18
|
+
marginBottom: 5,
|
|
19
|
+
},
|
|
20
|
+
listView: {
|
|
21
|
+
backgroundColor: '#FFFFFF',
|
|
22
|
+
},
|
|
23
|
+
row: {
|
|
24
|
+
backgroundColor: '#FFFFFF',
|
|
25
|
+
padding: 13,
|
|
26
|
+
minHeight: 44,
|
|
27
|
+
flexDirection: 'row',
|
|
28
|
+
},
|
|
29
|
+
loader: {
|
|
30
|
+
flexDirection: 'row',
|
|
31
|
+
justifyContent: 'flex-end',
|
|
32
|
+
height: 20,
|
|
33
|
+
},
|
|
34
|
+
description: {},
|
|
35
|
+
separator: {
|
|
36
|
+
height: StyleSheet.hairlineWidth,
|
|
37
|
+
backgroundColor: '#c8c7cc',
|
|
38
|
+
},
|
|
39
|
+
poweredContainer: {
|
|
40
|
+
justifyContent: 'flex-end',
|
|
41
|
+
alignItems: 'center',
|
|
42
|
+
borderBottomRightRadius: 5,
|
|
43
|
+
borderBottomLeftRadius: 5,
|
|
44
|
+
borderColor: '#c8c7cc',
|
|
45
|
+
borderTopWidth: 0.5,
|
|
46
|
+
},
|
|
47
|
+
powered: {},
|
|
48
|
+
fullWidth: {
|
|
49
|
+
width: '100%',
|
|
50
|
+
},
|
|
51
|
+
minFullWidth: {
|
|
52
|
+
minWidth: '100%',
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const EMPTY = {};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Merge a default style with the consumer override once per style key, so the
|
|
60
|
+
* render path stops allocating a fresh array for every element on every frame.
|
|
61
|
+
*/
|
|
62
|
+
export const mergeStyles = (styles, suppressDefaultStyles) => {
|
|
63
|
+
const userStyles = styles || EMPTY;
|
|
64
|
+
const base = (key) =>
|
|
65
|
+
suppressDefaultStyles ? undefined : defaultStyles[key];
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
container: [base('container'), userStyles.container],
|
|
69
|
+
textInputContainer: [
|
|
70
|
+
base('textInputContainer'),
|
|
71
|
+
userStyles.textInputContainer,
|
|
72
|
+
],
|
|
73
|
+
textInput: [base('textInput'), userStyles.textInput],
|
|
74
|
+
listView: [base('listView'), userStyles.listView],
|
|
75
|
+
row: [base('row'), userStyles.row],
|
|
76
|
+
specialItemRow: [base('row'), userStyles.row, userStyles.specialItemRow],
|
|
77
|
+
loader: [base('loader'), userStyles.loader],
|
|
78
|
+
description: [base('description'), userStyles.description],
|
|
79
|
+
predefinedPlacesDescription: [
|
|
80
|
+
base('description'),
|
|
81
|
+
userStyles.description,
|
|
82
|
+
userStyles.predefinedPlacesDescription,
|
|
83
|
+
],
|
|
84
|
+
separator: [base('separator'), userStyles.separator],
|
|
85
|
+
poweredContainer: [
|
|
86
|
+
base('row'),
|
|
87
|
+
defaultStyles.poweredContainer,
|
|
88
|
+
userStyles.poweredContainer,
|
|
89
|
+
],
|
|
90
|
+
powered: [base('powered'), userStyles.powered],
|
|
91
|
+
};
|
|
92
|
+
};
|
package/src/uuid.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC4122 version 4 UUID generator.
|
|
3
|
+
*
|
|
4
|
+
* Replaces `react-native-uuid` (448 KB installed) for the one thing this
|
|
5
|
+
* library needs it for: Places API session tokens.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const BYTE_TO_HEX = [];
|
|
9
|
+
for (let i = 0; i < 256; i++) {
|
|
10
|
+
BYTE_TO_HEX.push((i + 0x100).toString(16).slice(1));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const randomBytes = (length) => {
|
|
14
|
+
const bytes = new Uint8Array(length);
|
|
15
|
+
|
|
16
|
+
// Available on web, and on Hermes/JSC in recent React Native versions.
|
|
17
|
+
const cryptoObj =
|
|
18
|
+
typeof globalThis !== 'undefined' ? globalThis.crypto : undefined;
|
|
19
|
+
|
|
20
|
+
if (cryptoObj && typeof cryptoObj.getRandomValues === 'function') {
|
|
21
|
+
cryptoObj.getRandomValues(bytes);
|
|
22
|
+
return bytes;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
for (let i = 0; i < length; i++) {
|
|
26
|
+
bytes[i] = (Math.random() * 256) | 0;
|
|
27
|
+
}
|
|
28
|
+
return bytes;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const uuidv4 = () => {
|
|
32
|
+
const bytes = randomBytes(16);
|
|
33
|
+
|
|
34
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
|
|
35
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10xx
|
|
36
|
+
|
|
37
|
+
const hex = BYTE_TO_HEX;
|
|
38
|
+
return (
|
|
39
|
+
hex[bytes[0]] +
|
|
40
|
+
hex[bytes[1]] +
|
|
41
|
+
hex[bytes[2]] +
|
|
42
|
+
hex[bytes[3]] +
|
|
43
|
+
'-' +
|
|
44
|
+
hex[bytes[4]] +
|
|
45
|
+
hex[bytes[5]] +
|
|
46
|
+
'-' +
|
|
47
|
+
hex[bytes[6]] +
|
|
48
|
+
hex[bytes[7]] +
|
|
49
|
+
'-' +
|
|
50
|
+
hex[bytes[8]] +
|
|
51
|
+
hex[bytes[9]] +
|
|
52
|
+
'-' +
|
|
53
|
+
hex[bytes[10]] +
|
|
54
|
+
hex[bytes[11]] +
|
|
55
|
+
hex[bytes[12]] +
|
|
56
|
+
hex[bytes[13]] +
|
|
57
|
+
hex[bytes[14]] +
|
|
58
|
+
hex[bytes[15]]
|
|
59
|
+
);
|
|
60
|
+
};
|