react-native-radar 3.8.1 → 3.8.2

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.
@@ -18,7 +18,7 @@ android {
18
18
  minSdkVersion 16
19
19
  targetSdkVersion 31
20
20
  versionCode 1
21
- versionName '3.8.1'
21
+ versionName '3.8.2'
22
22
  }
23
23
  lintOptions {
24
24
  abortOnError false
@@ -45,5 +45,5 @@ repositories {
45
45
 
46
46
  dependencies {
47
47
  api 'com.facebook.react:react-native:+'
48
- api 'io.radar:sdk:3.8.4'
48
+ api 'io.radar:sdk:3.8.5'
49
49
  }
@@ -148,6 +148,24 @@ public class RNRadarModule extends ReactContextBaseJavaModule implements Permiss
148
148
  Radar.setAnonymousTrackingEnabled(enabled);
149
149
  }
150
150
 
151
+ @ReactMethod
152
+ public void getHost(final Promise promise) {
153
+ if (promise == null) {
154
+ return;
155
+ }
156
+
157
+ promise.resolve(Radar.getHost());
158
+ }
159
+
160
+ @ReactMethod
161
+ public void getPublishableKey(final Promise promise) {
162
+ if (promise == null) {
163
+ return;
164
+ }
165
+
166
+ promise.resolve(Radar.getPublishableKey());
167
+ }
168
+
151
169
  @ReactMethod
152
170
  public void getPermissionsStatus(final Promise promise) {
153
171
  if (promise == null) {
package/ios/RNRadar.h CHANGED
@@ -1,4 +1,5 @@
1
1
  #import <RadarSDK/RadarSDK.h>
2
+ #import <RadarSDK/RadarSettings.h>
2
3
  #import <React/RCTBridgeModule.h>
3
4
  #import <React/RCTEventEmitter.h>
4
5
 
package/ios/RNRadar.m CHANGED
@@ -139,6 +139,14 @@ RCT_EXPORT_METHOD(setAnonymousTrackingEnabled:(BOOL)enabled) {
139
139
  [Radar setAnonymousTrackingEnabled:enabled];
140
140
  }
141
141
 
142
+ RCT_EXPORT_METHOD(getHost:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
143
+ resolve([RadarSettings host]);
144
+ }
145
+
146
+ RCT_EXPORT_METHOD(getPublishableKey:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
147
+ resolve([RadarSettings publishableKey]);
148
+ }
149
+
142
150
  RCT_REMAP_METHOD(getPermissionsStatus, getPermissionsStatusWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
143
151
  CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
144
152
  NSString *statusStr;
@@ -729,19 +737,16 @@ RCT_EXPORT_METHOD(autocomplete:(NSDictionary *)optionsDict resolve:(RCTPromiseRe
729
737
  }
730
738
 
731
739
  NSDictionary *nearDict = optionsDict[@"near"];
732
- if (nearDict == nil || ![nearDict isKindOfClass:[NSDictionary class]]) {
733
- if (reject) {
734
- reject([Radar stringForStatus:RadarStatusErrorBadRequest], [Radar stringForStatus:RadarStatusErrorBadRequest], nil);
735
- }
736
-
737
- return;
740
+ CLLocation *near = nil;
741
+ if (nearDict && [nearDict isKindOfClass:[NSDictionary class]]) {
742
+ double latitude = [RCTConvert double:nearDict[@"latitude"]];
743
+ double longitude = [RCTConvert double:nearDict[@"longitude"]];
744
+ NSDate *timestamp = [NSDate new];
745
+ near = [[CLLocation alloc] initWithCoordinate:CLLocationCoordinate2DMake(latitude, longitude) altitude:-1 horizontalAccuracy:5 verticalAccuracy:-1 timestamp:timestamp];
738
746
  }
739
747
 
748
+
740
749
  NSString *query = optionsDict[@"query"];
741
- double latitude = [RCTConvert double:nearDict[@"latitude"]];
742
- double longitude = [RCTConvert double:nearDict[@"longitude"]];
743
- NSDate *timestamp = [NSDate new];
744
- CLLocation *near = [[CLLocation alloc] initWithCoordinate:CLLocationCoordinate2DMake(latitude, longitude) altitude:-1 horizontalAccuracy:5 verticalAccuracy:-1 timestamp:timestamp];
745
750
  NSNumber *limitNumber = optionsDict[@"limit"];
746
751
  int limit;
747
752
  if (limitNumber != nil && [limitNumber isKindOfClass:[NSNumber class]]) {
package/js/helpers.js ADDED
@@ -0,0 +1,15 @@
1
+ import { NativeModules, Platform } from 'react-native';
2
+
3
+ if (!NativeModules.RNRadar && (Platform.OS === 'ios' || Platform.OS === 'android')) {
4
+ throw new Error('NativeModules.RNRadar is undefined');
5
+ }
6
+
7
+ const getHost = () => (
8
+ NativeModules.RNRadar.getHost()
9
+ );
10
+
11
+ const getPublishableKey = () => (
12
+ NativeModules.RNRadar.getPublishableKey()
13
+ );
14
+
15
+ export { getHost, getPublishableKey };
package/js/index.js CHANGED
@@ -1,9 +1,12 @@
1
1
  import { Platform } from 'react-native';
2
2
 
3
3
  let module = {};
4
- if (Platform.OS === 'web')
5
- module = require('./index.web').default;
6
- else
4
+ if (Platform.OS === 'web') {
5
+ module = require('./index.web').default;
6
+ } else {
7
7
  module = require('./index.native').default;
8
-
8
+ }
9
9
  export default module;
10
+
11
+ export { default as Autocomplete } from './ui/autocomplete';
12
+ export { default as Map } from './ui/map';
@@ -0,0 +1,157 @@
1
+ ## Example usage
2
+
3
+ We provide UI elements for autocomplete & maps to make building easy.
4
+
5
+ ### Adding an address autocomplete
6
+
7
+ Adding an address search autocomplete is straightforward. Our `<Autocomplete>` element is comprised of a TextInput and Flatlist with the results.
8
+
9
+ The example below provides optional `location` and `onSelect` props to the component. Providing a location will improve autocomplete result quality. Without it, the API utilizes the IP address location to rank results.
10
+
11
+ ```
12
+ import { View } from 'react-native';
13
+ import { useState, useEffect } from 'react';
14
+ import Radar, { Autocomplete } from 'react-native-radar';
15
+
16
+ export default function App() {
17
+ const [location, setLocation] = useState(null);
18
+
19
+ useEffect(() => {
20
+ Radar.initialize('prj_live_pk_...');
21
+
22
+ Radar.trackOnce().then((result) => {
23
+ setLocation({
24
+ latitude: result.location.latitude,
25
+ longitude: result.location.longitude,
26
+ });
27
+ });
28
+ }, []);
29
+
30
+ const onSelect = (selectedAddress) => {
31
+ // Do something with the selected address
32
+ }
33
+
34
+ return (
35
+ <View style={{ marginTop: 50}}>
36
+ <Autocomplete location={location} onSelect={onSelect} />
37
+ </View>
38
+ );
39
+ }
40
+
41
+ ```
42
+
43
+ ### Adding a map
44
+
45
+ If you're using the Map element, you'll need to install [Maplibre React Native](https://github.com/maplibre/maplibre-react-native), which `react-native-radar` has an optional peer dependency.
46
+ ```
47
+ npm install @maplibre/maplibre-react-native
48
+ ```
49
+
50
+ Then make sure to complete required [platform specific installation steps](https://github.com/maplibre/maplibre-react-native/blob/main/docs/GettingStarted.md#review-platform-specific-info) as well.
51
+
52
+
53
+ We've taken care of linking the map tile server to the map, so all you need to do is make sure you've initialized the Radar SDK and use `<Map>`. Here's a minimal example:
54
+
55
+ ```
56
+ import {View} from 'react-native';
57
+ import Radar, { Map } from 'react-native-radar';
58
+ import MapLibreGL from '@maplibre/maplibre-react-native';
59
+
60
+
61
+ // A quirk of Map Libre requires us to set their deprecated access token to null
62
+ MapLibreGL.setAccessToken(null);
63
+
64
+ export default function App() {
65
+
66
+ useEffect(() => {
67
+ Radar.initialize('prj_live_pk_...');
68
+ }, []);
69
+
70
+ return (
71
+ <View style={{ width: '100%', height: '95%'}}>
72
+ <Map />
73
+ </View>
74
+ );
75
+ }
76
+ ```
77
+
78
+ And here's how you might add a custom pin to the map and control the camera:
79
+ ```
80
+ // ... rest of your file
81
+
82
+ const [cameraConfig, setCameraConfig] = useState({
83
+ triggerKey: Date.now(),
84
+ centerCoordinate: [-73.9911, 40.7342],
85
+ animationMode: 'flyTo',
86
+ animationDuration: 600,
87
+ zoomLevel: 12,
88
+ });
89
+
90
+ const onRegionDidChange = (event) => {
91
+ // handle region change
92
+ }
93
+
94
+ const mapOptions = {
95
+ onRegionDidChange: onRegionDidChange,
96
+ }
97
+
98
+ const onSelect = (selectedAddress) => {
99
+ // Do something with the selected address
100
+ }
101
+
102
+ const pointsCollection = {
103
+ type: "FeatureCollection",
104
+ features: [{
105
+ type: "Feature",
106
+ properties: {
107
+ _id: '123',
108
+ },
109
+ geometry: {
110
+ type: "Point",
111
+ coordinates: [-73.9911, 40.7342]
112
+ }
113
+ }]
114
+ };
115
+
116
+ const onPressIcon = (event) => {
117
+ // do something with the symbol, such as scrolling to the geofence
118
+ // associated with the icon in the list
119
+ }
120
+
121
+ return (
122
+ <View style={{ width: '100%', marginTop: '10%', height: '90%'}}>
123
+ <Map mapOptions={mapOptions}>
124
+ <MapLibreGL.Camera
125
+ {...cameraConfig}
126
+ />
127
+ <MapLibreGL.Images
128
+ images={{
129
+ icon: require('./assets/marker.png'),
130
+ }}
131
+ />
132
+ <MapLibreGL.ShapeSource
133
+ id="points"
134
+ shape={pointsCollection}
135
+ onPress={onPressIcon}
136
+ >
137
+
138
+ <MapLibreGL.SymbolLayer
139
+ id="symbol"
140
+ style={{
141
+ iconImage: 'icon',
142
+ iconSize: [
143
+ 'interpolate',
144
+ ['linear'],
145
+ ['zoom'],
146
+ 0, 0.2, // Adjust the icon size for zoom level 0
147
+ 12, 0.4, // Adjust the icon size for zoom level 12
148
+ 22, 0.8, // Adjust the icon size for zoom level 22
149
+ ],
150
+ iconAllowOverlap: true,
151
+ }}
152
+ />
153
+ </MapLibreGL.ShapeSource>
154
+ </Map>
155
+ </View>
156
+ );
157
+ ```
@@ -0,0 +1,312 @@
1
+ // Autocomplete.js
2
+ import React, { useState, useCallback, useRef, useEffect } from 'react';
3
+ import {
4
+ View,
5
+ TextInput,
6
+ FlatList,
7
+ TouchableOpacity,
8
+ Text,
9
+ StyleSheet,
10
+ Image,
11
+ Modal,
12
+ KeyboardAvoidingView,
13
+ Animated,
14
+ Dimensions,
15
+ Easing,
16
+ Keyboard,
17
+ SafeAreaView,
18
+ Pressable,
19
+ } from 'react-native';
20
+ import Radar from '../index.native';
21
+ import {
22
+ BACK_ICON,
23
+ SEARCH_ICON,
24
+ RADAR_LOGO,
25
+ MARKER_ICON,
26
+ CLOSE_ICON,
27
+ } from "./images";
28
+ import { default as defaultStyles } from './styles';
29
+
30
+ const defaultAutocompleteOptions = {
31
+ debounceMS: 200,
32
+ threshold: 3,
33
+ limit: 8,
34
+ placeholder: "Search address",
35
+ showPin: true,
36
+ };
37
+
38
+ const autocompleteUI = ({ options = {}, onSelect, location, style = {} }) => {
39
+ const [query, setQuery] = useState("");
40
+ const [results, setResults] = useState([]);
41
+ const [isOpen, setIsOpen] = useState(false);
42
+ const animationValue = useRef(new Animated.Value(0)).current; // animation value
43
+ const timerRef = useRef(null);
44
+ const textInputRef = useRef(null);
45
+
46
+ const config = { ...defaultAutocompleteOptions, ...options };
47
+
48
+ const fetchResults = useCallback(
49
+ async (searchQuery) => {
50
+ if (searchQuery.length < config.threshold) return;
51
+
52
+ const params = { query: searchQuery, limit: config.limit };
53
+
54
+ if (location && location.latitude && location.longitude) {
55
+ params.near = location;
56
+ }
57
+
58
+ try {
59
+ const result = await Radar.autocomplete(params);
60
+ setResults(result.addresses);
61
+ setIsOpen(true);
62
+ } catch (error) {
63
+ if (config.onError && typeof config.onError === "function") {
64
+ config.onError(error);
65
+ }
66
+ }
67
+ },
68
+ [config]
69
+ );
70
+
71
+ const handleInput = useCallback(
72
+ (text) => {
73
+ setQuery(text);
74
+
75
+ // Clear the existing timer
76
+ if (timerRef.current) {
77
+ clearTimeout(timerRef.current);
78
+ }
79
+
80
+ if (text.length < config.threshold) {
81
+ return;
82
+ }
83
+
84
+ // Set the new timer
85
+ timerRef.current = setTimeout(() => {
86
+ fetchResults(text);
87
+ }, config.debounceMS);
88
+ },
89
+ [config, fetchResults]
90
+ );
91
+
92
+ const handleSelect = (item) => {
93
+ setQuery(item.formattedAddress);
94
+ setIsOpen(false);
95
+
96
+ if (typeof onSelect === "function") {
97
+ onSelect(item);
98
+ }
99
+ };
100
+
101
+ const renderFooter = () => {
102
+ if (results.length === 0) return null;
103
+
104
+ return (
105
+ <View style={styles.footerContainer}>
106
+ <View style={{ flexDirection: "row", alignItems: "center" }}>
107
+ <Text style={styles.footerText}>Powered by</Text>
108
+ <Image
109
+ source={RADAR_LOGO}
110
+ resizeMode="contain"
111
+ style={defaultStyles.logo}
112
+ />
113
+ </View>
114
+ </View>
115
+ );
116
+ };
117
+
118
+ const renderItem = ({ item }) => (
119
+ <Pressable
120
+ style={({ pressed }) => [
121
+ {
122
+ ...styles.resultItem,
123
+ backgroundColor: pressed
124
+ ? styles.resultItem.pressedBackgroundColor
125
+ : styles.resultItem.backgroundColor,
126
+ },
127
+ ]}
128
+ onPress={() => handleSelect(item)}
129
+ >
130
+ <View style={styles.addressContainer}>
131
+ <View style={styles.pinIconContainer}>
132
+ {config.showPin ? (
133
+ <Image source={MARKER_ICON} style={styles.pinIcon} />
134
+ ) : null}
135
+ </View>
136
+ <View style={styles.addressTextContainer}>
137
+ <Text numberOfLines={1} style={styles.addressText}>
138
+ {item.addressLabel || item?.placeLabel}
139
+ </Text>
140
+ {item?.formattedAddress.length > 0 && (
141
+ <Text numberOfLines={1} style={styles.addressSubtext}>
142
+ {item?.formattedAddress?.replace(
143
+ `${item?.addressLabel || item?.placeLabel}, `,
144
+ ""
145
+ )}
146
+ </Text>
147
+ )}
148
+ </View>
149
+ </View>
150
+ </Pressable>
151
+ );
152
+
153
+ const styles = {
154
+ ...defaultStyles,
155
+ container: StyleSheet.compose(defaultStyles.container, style.container),
156
+ input: StyleSheet.compose(defaultStyles.input, style.input),
157
+ inputContainer: StyleSheet.compose(
158
+ defaultStyles.inputContainer,
159
+ style.inputContainer
160
+ ),
161
+ modalInputContainer: StyleSheet.compose(
162
+ defaultStyles.modalInputContainer,
163
+ style.modalInputContainer
164
+ ),
165
+ resultList: StyleSheet.compose(defaultStyles.resultList, style.resultList),
166
+ resultItem: StyleSheet.compose(defaultStyles.resultItem, style.resultItem),
167
+ addressContainer: StyleSheet.compose(
168
+ defaultStyles.addressContainer,
169
+ style.addressContainer
170
+ ),
171
+ pinIconContainer: StyleSheet.compose(
172
+ defaultStyles.pinIconContainer,
173
+ style.pinIconContainer
174
+ ),
175
+ pinIcon: StyleSheet.compose(defaultStyles.pinIcon, style.pinIcon),
176
+ addressTextContainer: StyleSheet.compose(
177
+ defaultStyles.addressTextContainer,
178
+ style.addressTextContainer
179
+ ),
180
+ addressText: StyleSheet.compose(
181
+ defaultStyles.addressText,
182
+ style.addressText
183
+ ),
184
+ addressSubtext: StyleSheet.compose(
185
+ defaultStyles.addressSubtext,
186
+ style.addressSubtext
187
+ ),
188
+ footerContainer: StyleSheet.compose(
189
+ defaultStyles.footerContainer,
190
+ style.footerContainer
191
+ ),
192
+ footerText: StyleSheet.compose(defaultStyles.footerText, style.footerText),
193
+ };
194
+
195
+ useEffect(() => {
196
+ Animated.timing(animationValue, {
197
+ toValue: isOpen ? 1 : 0,
198
+ duration: 300,
199
+ easing: Easing.inOut(Easing.ease),
200
+ useNativeDriver: false,
201
+ }).start();
202
+ }, [isOpen]);
203
+
204
+ const screenHeight = Dimensions.get("window").height;
205
+
206
+ const inputHeight = animationValue.interpolate({
207
+ inputRange: [0, 1],
208
+ outputRange: [40, screenHeight],
209
+ });
210
+
211
+ const modalOpacity = animationValue.interpolate({
212
+ inputRange: [0, 0.5, 1],
213
+ outputRange: [0, 0, 1],
214
+ });
215
+
216
+ return (
217
+ <View style={styles.container}>
218
+ <Animated.View style={{ height: inputHeight }}>
219
+ <TouchableOpacity
220
+ style={styles.inputContainer}
221
+ onPress={() => {
222
+ setIsOpen(true);
223
+ // Set the focus on the other textinput after it opens
224
+ setTimeout(() => {
225
+ textInputRef.current.focus();
226
+ }, 100);
227
+ }}
228
+ >
229
+ <Image source={SEARCH_ICON} style={styles.inputIcon} />
230
+ <TextInput
231
+ style={styles.input}
232
+ onFocus={() => {
233
+ setIsOpen(true);
234
+ setTimeout(() => {
235
+ textInputRef.current.focus();
236
+ }, 100);
237
+ }}
238
+ value={query}
239
+ returnKeyType="done"
240
+ placeholder={config.placeholder}
241
+ placeholderTextColor="#acbdc8"
242
+ />
243
+ </TouchableOpacity>
244
+ </Animated.View>
245
+ <Modal
246
+ animationType="slide"
247
+ transparent={false}
248
+ visible={isOpen}
249
+ onRequestClose={() => setIsOpen(false)}
250
+ >
251
+ <Animated.View style={{ flex: 1, opacity: modalOpacity }}>
252
+ <SafeAreaView>
253
+ <KeyboardAvoidingView
254
+ behavior={Platform.OS === "ios" ? "padding" : "height"}
255
+ keyboardVerticalOffset={50}
256
+ style={styles.container}
257
+ >
258
+ <View style={styles.modalInputContainer}>
259
+ <TouchableOpacity
260
+ onPress={() => {
261
+ setIsOpen(false);
262
+ }}
263
+ >
264
+ <Image source={BACK_ICON} style={styles.inputIcon} />
265
+ </TouchableOpacity>
266
+ <TextInput
267
+ ref={textInputRef}
268
+ style={styles.input}
269
+ onChangeText={handleInput}
270
+ value={query}
271
+ placeholder={config.placeholder}
272
+ returnKeyType="done"
273
+ onSubmitEditing={() => {
274
+ setIsOpen(false);
275
+ }}
276
+ placeholderTextColor="#acbdc8"
277
+ />
278
+ <TouchableOpacity
279
+ onPress={() => {
280
+ setQuery("");
281
+ }}
282
+ >
283
+ <Image source={CLOSE_ICON} style={styles.closeIcon} />
284
+ </TouchableOpacity>
285
+ </View>
286
+ {results.length > 0 && (
287
+ <View style={styles.resultListWrapper}>
288
+ <FlatList
289
+ style={styles.resultList}
290
+ data={results}
291
+ onScroll={() => {
292
+ textInputRef.current.blur();
293
+ Keyboard.dismiss();
294
+ }}
295
+ keyboardShouldPersistTaps="handled"
296
+ renderItem={renderItem}
297
+ keyExtractor={(item) =>
298
+ item.formattedAddress + item.postalCode
299
+ }
300
+ />
301
+ {renderFooter()}
302
+ </View>
303
+ )}
304
+ </KeyboardAvoidingView>
305
+ </SafeAreaView>
306
+ </Animated.View>
307
+ </Modal>
308
+ </View>
309
+ );
310
+ };
311
+
312
+ export default autocompleteUI;
package/js/ui/back.png ADDED
Binary file
Binary file
@@ -0,0 +1,5 @@
1
+ export const BACK_ICON = require('./back.png');
2
+ export const SEARCH_ICON = require('./search.png');
3
+ export const RADAR_LOGO = require('./radar-logo.png');
4
+ export const MARKER_ICON = require('./marker.png');
5
+ export const CLOSE_ICON = require('./close.png');
Binary file
package/js/ui/map.jsx ADDED
@@ -0,0 +1,105 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { View, Image } from 'react-native';
3
+ import Radar from '../index.native';
4
+ import { getHost, getPublishableKey } from '../helpers';
5
+ import styles from './styles';
6
+
7
+ let MapLibreGL;
8
+ try {
9
+ MapLibreGL = require('@maplibre/maplibre-react-native');
10
+ } catch (e) {
11
+ MapLibreGL = null;
12
+ }
13
+
14
+ const DEFAULT_STYLE = 'radar-default-v1';
15
+
16
+ const createStyleURL = async (style = DEFAULT_STYLE) => {
17
+ const host = await getHost();
18
+ const publishableKey = await getPublishableKey();
19
+ return `${host}/maps/styles/${style}?publishableKey=${publishableKey}`;
20
+ };
21
+
22
+ const RadarMap = ({ mapOptions, children }) => {
23
+ const [styleURL, setStyleURL] = useState(null);
24
+ const [userLocation, setUserLocation] = useState(null);
25
+
26
+ useEffect(() => {
27
+ createStyleURL(mapOptions?.mapStyle || DEFAULT_STYLE).then((result) => {
28
+ setStyleURL(result);
29
+ });
30
+ }, [mapOptions]);
31
+
32
+ useEffect(() => {
33
+ Radar.getLocation().then((result) => {
34
+ setUserLocation({
35
+ latitude: result.location.latitude,
36
+ longitude: result.location.longitude,
37
+ });
38
+ });
39
+ }, [mapOptions]);
40
+
41
+ if (!styleURL) {
42
+ return null;
43
+ }
44
+
45
+ if (!MapLibreGL) {
46
+ return null;
47
+ }
48
+
49
+ const geoJSONUserLocation = {
50
+ type: 'FeatureCollection',
51
+ features: userLocation?.longitude !== undefined ? [
52
+ {
53
+ type: 'Feature',
54
+ geometry: {
55
+ type: 'Point',
56
+ coordinates: [userLocation.longitude, userLocation.latitude],
57
+ },
58
+ },
59
+ ] : [],
60
+ };
61
+
62
+ const userLocationMapIndicator = (
63
+ <MapLibreGL.ShapeSource
64
+ id="user-location"
65
+ shape={geoJSONUserLocation}
66
+ >
67
+ <MapLibreGL.CircleLayer
68
+ id="user-location-inner"
69
+ style={styles.userLocation.pulse}
70
+ />
71
+ <MapLibreGL.CircleLayer
72
+ id="user-location-middle"
73
+ style={styles.userLocation.background}
74
+ />
75
+ <MapLibreGL.CircleLayer
76
+ id="user-location-outer"
77
+ style={styles.userLocation.foreground}
78
+ />
79
+
80
+ </MapLibreGL.ShapeSource>
81
+ );
82
+
83
+ return (
84
+ <View style={styles.mapContainer}>
85
+ <MapLibreGL.MapView
86
+ style={styles.map}
87
+ pitchEnabled={false}
88
+ compassEnabled={false}
89
+ logoEnabled={false}
90
+ attributionEnabled
91
+ onRegionDidChange={mapOptions?.onRegionDidChange ? mapOptions.onRegionDidChange : null}
92
+ styleURL={styleURL}
93
+ >
94
+ {userLocationMapIndicator}
95
+ {children}
96
+ </MapLibreGL.MapView>
97
+ <Image
98
+ source={require('./map-logo.png')}
99
+ style={styles.mapLogo}
100
+ />
101
+ </View>
102
+ );
103
+ };
104
+
105
+ export default RadarMap;
Binary file
Binary file
Binary file
@@ -0,0 +1,144 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ const styles = StyleSheet.create({
4
+ container: {
5
+ width: '100%',
6
+ height: '100%',
7
+ alignItems: 'center',
8
+ paddingTop: 8,
9
+ },
10
+ inputContainer: {
11
+ flexDirection: 'row',
12
+ alignItems: 'center',
13
+ marginHorizontal: 16,
14
+ backgroundColor: 'white',
15
+ borderRadius: 5,
16
+ borderColor: '#DBE5EB',
17
+ borderWidth: 1,
18
+ width: '95%', // only difference between this and the modalInputContainer
19
+ },
20
+ modalInputContainer: {
21
+ flexDirection: 'row',
22
+ alignItems: 'center',
23
+ marginHorizontal: 16,
24
+ backgroundColor: 'white',
25
+ borderRadius: 5,
26
+ borderColor: '#DBE5EB',
27
+ borderWidth: 1,
28
+ },
29
+ inputIcon: {
30
+ marginLeft: 10,
31
+ height: 18,
32
+ width: 18,
33
+ backgroundColor: 'white',
34
+ },
35
+ closeIcon: {
36
+ marginRight: 10,
37
+ height: 18,
38
+ width: 18,
39
+ backgroundColor: 'white',
40
+ },
41
+ input: {
42
+ flex: 1,
43
+ backgroundColor: 'white',
44
+ height: 40,
45
+ fontSize: 14,
46
+ paddingHorizontal: 8,
47
+ borderRadius: 5,
48
+ },
49
+ resultListWrapper: {
50
+ width: '100%',
51
+ marginBottom: 30,
52
+ backgroundColor: 'white',
53
+ borderRadius: 5,
54
+ paddingVertical: 8,
55
+ },
56
+ resultList: {
57
+ width: '100%',
58
+ },
59
+ resultItem: {
60
+ paddingRight: 16,
61
+ paddingVertical: 8,
62
+ paddingHorizontal: 16,
63
+ fontSize: 12,
64
+ backgroundColor: 'white',
65
+ pressedBackgroundColor: '#F6FAFC',
66
+ },
67
+ addressContainer: {
68
+ flexDirection: 'row',
69
+ alignItems: 'center',
70
+ },
71
+ pinIconContainer: {
72
+ width: 28,
73
+ marginRight: 8,
74
+ },
75
+ pinIcon: {
76
+ height: 26,
77
+ width: 26,
78
+ },
79
+ addressTextContainer: {
80
+ flex: 1,
81
+ },
82
+ addressText: {
83
+ fontSize: 14,
84
+ lineHeight: 24,
85
+ color: '#000',
86
+ fontWeight: '600',
87
+ },
88
+ addressSubtext: {
89
+ fontSize: 14,
90
+ color: '#5A6872',
91
+ },
92
+ footerContainer: {
93
+ flexDirection: 'row',
94
+ alignItems: 'center',
95
+ paddingVertical: 10,
96
+ marginRight: 16,
97
+ alignSelf: 'flex-end',
98
+ },
99
+ footerText: {
100
+ marginTop: 2,
101
+ marginRight: 4,
102
+ fontSize: 10,
103
+ color: '#5A6872',
104
+ },
105
+ logo: {
106
+ width: 50,
107
+ height: 15,
108
+ resizeMode: 'contain',
109
+ },
110
+ mapContainer: {
111
+ flex: 1,
112
+ },
113
+ map: {
114
+ flex: 1,
115
+ },
116
+ mapLogo: {
117
+ position: 'absolute',
118
+ bottom: -10,
119
+ left: 5,
120
+ width: 50,
121
+ height: 50,
122
+ resizeMode: 'contain',
123
+ },
124
+ userLocation: {
125
+ pulse: {
126
+ circleRadius: 15,
127
+ circleColor: '#000257',
128
+ circleOpacity: 0.2,
129
+ circlePitchAlignment: 'map',
130
+ },
131
+ background: {
132
+ circleRadius: 9,
133
+ circleColor: '#fff',
134
+ circlePitchAlignment: 'map',
135
+ },
136
+ foreground: {
137
+ circleRadius: 6,
138
+ circleColor: '#000257',
139
+ circlePitchAlignment: 'map',
140
+ },
141
+ },
142
+ });
143
+
144
+ export default styles;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "React Native module for Radar, the leading geofencing and location tracking platform",
4
4
  "homepage": "https://radar.com",
5
5
  "license": "Apache-2.0",
6
- "version": "3.8.1",
6
+ "version": "3.8.2",
7
7
  "main": "js/index.js",
8
8
  "files": [
9
9
  "android",
@@ -25,7 +25,13 @@
25
25
  },
26
26
  "peerDependencies": {
27
27
  "react": ">= 16.8.6",
28
- "react-native": ">= 0.60.0"
28
+ "react-native": ">= 0.60.0",
29
+ "@maplibre/maplibre-react-native": "^9.0.1"
30
+ },
31
+ "peerDependenciesMeta": {
32
+ "@maplibre/maplibre-react-native": {
33
+ "optional": true
34
+ }
29
35
  },
30
36
  "devDependencies": {
31
37
  "@babel/core": "^7.2.2",