@maplibre/maplibre-react-native 11.0.0-alpha.15 → 11.0.0-alpha.17

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.
Files changed (124) hide show
  1. package/android/src/main/java/org/maplibre/reactnative/components/camera/MLRNCameraManager.kt +3 -5
  2. package/android/src/main/java/org/maplibre/reactnative/components/location/LocationComponentManager.kt +154 -0
  3. package/android/src/main/java/org/maplibre/reactnative/components/location/MLRNNativeUserLocation.kt +70 -0
  4. package/android/src/main/java/org/maplibre/reactnative/components/location/MLRNNativeUserLocationManager.kt +50 -0
  5. package/android/src/main/java/org/maplibre/reactnative/modules/MLRNLocationModule.kt +20 -4
  6. package/ios/components/camera/MLRNCameraComponentView.h +0 -8
  7. package/ios/components/map-view/MLRNMapView.m +1 -1
  8. package/ios/components/map-view/MLRNMapViewComponentView.h +0 -5
  9. package/ios/components/map-view/MLRNMapViewModule.mm +11 -6
  10. package/ios/components/user-location/MLRNNativeUserLocation.h +1 -1
  11. package/ios/components/user-location/MLRNNativeUserLocation.m +6 -5
  12. package/ios/components/user-location/MLRNNativeUserLocationComponentView.h +10 -0
  13. package/ios/components/user-location/MLRNNativeUserLocationComponentView.mm +51 -0
  14. package/ios/modules/location/MLRNLocation.m +17 -14
  15. package/ios/modules/logging/MLRNLogging.h +0 -5
  16. package/lib/commonjs/components/annotations/Annotation.js +2 -1
  17. package/lib/commonjs/components/annotations/Annotation.js.map +1 -1
  18. package/lib/commonjs/components/sources/ShapeSource.js +1 -0
  19. package/lib/commonjs/components/sources/ShapeSource.js.map +1 -1
  20. package/lib/commonjs/components/user-location/NativeUserLocation.js +3 -4
  21. package/lib/commonjs/components/user-location/NativeUserLocation.js.map +1 -1
  22. package/lib/commonjs/components/user-location/UserLocation.js +22 -132
  23. package/lib/commonjs/components/user-location/UserLocation.js.map +1 -1
  24. package/lib/commonjs/components/user-location/UserLocationNativeComponent.ts +19 -0
  25. package/lib/commonjs/components/user-location/UserLocationPuck.js +34 -22
  26. package/lib/commonjs/components/user-location/UserLocationPuck.js.map +1 -1
  27. package/lib/commonjs/components/user-location/UserLocationPuckHeading.js +3 -2
  28. package/lib/commonjs/components/user-location/UserLocationPuckHeading.js.map +1 -1
  29. package/lib/commonjs/hooks/useCurrentPosition.js +34 -0
  30. package/lib/commonjs/hooks/useCurrentPosition.js.map +1 -0
  31. package/lib/commonjs/index.js +12 -11
  32. package/lib/commonjs/index.js.map +1 -1
  33. package/lib/commonjs/utils/animated/Animated.js +1 -7
  34. package/lib/commonjs/utils/animated/Animated.js.map +1 -1
  35. package/lib/module/components/annotations/Annotation.js +3 -2
  36. package/lib/module/components/annotations/Annotation.js.map +1 -1
  37. package/lib/module/components/sources/ShapeSource.js +1 -0
  38. package/lib/module/components/sources/ShapeSource.js.map +1 -1
  39. package/lib/module/components/user-location/NativeUserLocation.js +2 -4
  40. package/lib/module/components/user-location/NativeUserLocation.js.map +1 -1
  41. package/lib/module/components/user-location/UserLocation.js +22 -132
  42. package/lib/module/components/user-location/UserLocation.js.map +1 -1
  43. package/lib/module/components/user-location/UserLocationNativeComponent.ts +19 -0
  44. package/lib/module/components/user-location/UserLocationPuck.js +34 -22
  45. package/lib/module/components/user-location/UserLocationPuck.js.map +1 -1
  46. package/lib/module/components/user-location/UserLocationPuckHeading.js +3 -2
  47. package/lib/module/components/user-location/UserLocationPuckHeading.js.map +1 -1
  48. package/lib/module/hooks/useCurrentPosition.js +30 -0
  49. package/lib/module/hooks/useCurrentPosition.js.map +1 -0
  50. package/lib/module/index.js +3 -2
  51. package/lib/module/index.js.map +1 -1
  52. package/lib/module/utils/animated/Animated.js +0 -6
  53. package/lib/module/utils/animated/Animated.js.map +1 -1
  54. package/lib/typescript/commonjs/src/components/annotations/Annotation.d.ts +3 -2
  55. package/lib/typescript/commonjs/src/components/annotations/Annotation.d.ts.map +1 -1
  56. package/lib/typescript/commonjs/src/components/sources/ShapeSource.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/src/components/user-location/NativeUserLocation.d.ts +12 -13
  58. package/lib/typescript/commonjs/src/components/user-location/NativeUserLocation.d.ts.map +1 -1
  59. package/lib/typescript/commonjs/src/components/user-location/UserLocation.d.ts +13 -53
  60. package/lib/typescript/commonjs/src/components/user-location/UserLocation.d.ts.map +1 -1
  61. package/lib/typescript/commonjs/src/components/user-location/UserLocationNativeComponent.d.ts +8 -0
  62. package/lib/typescript/commonjs/src/components/user-location/UserLocationNativeComponent.d.ts.map +1 -0
  63. package/lib/typescript/commonjs/src/components/user-location/UserLocationPuck.d.ts +2 -1
  64. package/lib/typescript/commonjs/src/components/user-location/UserLocationPuck.d.ts.map +1 -1
  65. package/lib/typescript/commonjs/src/components/user-location/UserLocationPuckHeading.d.ts.map +1 -1
  66. package/lib/typescript/commonjs/src/hooks/useCurrentPosition.d.ts +18 -0
  67. package/lib/typescript/commonjs/src/hooks/useCurrentPosition.d.ts.map +1 -0
  68. package/lib/typescript/commonjs/src/index.d.ts +3 -3
  69. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  70. package/lib/typescript/commonjs/src/modules/location/LocationManager.d.ts +3 -3
  71. package/lib/typescript/commonjs/src/modules/location/LocationManager.d.ts.map +1 -1
  72. package/lib/typescript/commonjs/src/modules/location/NativeLocationModule.d.ts +3 -3
  73. package/lib/typescript/commonjs/src/modules/location/NativeLocationModule.d.ts.map +1 -1
  74. package/lib/typescript/commonjs/src/utils/animated/Animated.d.ts +1 -13
  75. package/lib/typescript/commonjs/src/utils/animated/Animated.d.ts.map +1 -1
  76. package/lib/typescript/module/src/components/annotations/Annotation.d.ts +3 -2
  77. package/lib/typescript/module/src/components/annotations/Annotation.d.ts.map +1 -1
  78. package/lib/typescript/module/src/components/sources/ShapeSource.d.ts.map +1 -1
  79. package/lib/typescript/module/src/components/user-location/NativeUserLocation.d.ts +12 -13
  80. package/lib/typescript/module/src/components/user-location/NativeUserLocation.d.ts.map +1 -1
  81. package/lib/typescript/module/src/components/user-location/UserLocation.d.ts +13 -53
  82. package/lib/typescript/module/src/components/user-location/UserLocation.d.ts.map +1 -1
  83. package/lib/typescript/module/src/components/user-location/UserLocationNativeComponent.d.ts +8 -0
  84. package/lib/typescript/module/src/components/user-location/UserLocationNativeComponent.d.ts.map +1 -0
  85. package/lib/typescript/module/src/components/user-location/UserLocationPuck.d.ts +2 -1
  86. package/lib/typescript/module/src/components/user-location/UserLocationPuck.d.ts.map +1 -1
  87. package/lib/typescript/module/src/components/user-location/UserLocationPuckHeading.d.ts.map +1 -1
  88. package/lib/typescript/module/src/hooks/useCurrentPosition.d.ts +18 -0
  89. package/lib/typescript/module/src/hooks/useCurrentPosition.d.ts.map +1 -0
  90. package/lib/typescript/module/src/index.d.ts +3 -3
  91. package/lib/typescript/module/src/index.d.ts.map +1 -1
  92. package/lib/typescript/module/src/modules/location/LocationManager.d.ts +3 -3
  93. package/lib/typescript/module/src/modules/location/LocationManager.d.ts.map +1 -1
  94. package/lib/typescript/module/src/modules/location/NativeLocationModule.d.ts +3 -3
  95. package/lib/typescript/module/src/modules/location/NativeLocationModule.d.ts.map +1 -1
  96. package/lib/typescript/module/src/utils/animated/Animated.d.ts +1 -13
  97. package/lib/typescript/module/src/utils/animated/Animated.d.ts.map +1 -1
  98. package/package.json +3 -2
  99. package/src/components/annotations/Annotation.tsx +7 -5
  100. package/src/components/sources/ShapeSource.tsx +1 -0
  101. package/src/components/user-location/NativeUserLocation.tsx +15 -19
  102. package/src/components/user-location/UserLocation.tsx +65 -249
  103. package/src/components/user-location/UserLocationNativeComponent.ts +19 -0
  104. package/src/components/user-location/UserLocationPuck.tsx +47 -28
  105. package/src/components/user-location/UserLocationPuckHeading.tsx +2 -1
  106. package/src/hooks/useCurrentPosition.ts +53 -0
  107. package/src/index.ts +5 -6
  108. package/src/modules/location/LocationManager.ts +3 -3
  109. package/src/modules/location/NativeLocationModule.ts +3 -3
  110. package/src/utils/animated/Animated.ts +1 -28
  111. package/android/src/main/java/org/maplibre/reactnative/components/location/LocationComponentManager.java +0 -165
  112. package/android/src/main/java/org/maplibre/reactnative/components/location/MLRNNativeUserLocation.java +0 -76
  113. package/android/src/main/java/org/maplibre/reactnative/components/location/MLRNNativeUserLocationManager.java +0 -40
  114. package/ios/components/user-location/MLRNNativeUserLocationManager.h +0 -5
  115. package/ios/components/user-location/MLRNNativeUserLocationManager.m +0 -21
  116. package/lib/commonjs/modules/location/requestAndroidLocationPermissions.js +0 -14
  117. package/lib/commonjs/modules/location/requestAndroidLocationPermissions.js.map +0 -1
  118. package/lib/module/modules/location/requestAndroidLocationPermissions.js +0 -11
  119. package/lib/module/modules/location/requestAndroidLocationPermissions.js.map +0 -1
  120. package/lib/typescript/commonjs/src/modules/location/requestAndroidLocationPermissions.d.ts +0 -5
  121. package/lib/typescript/commonjs/src/modules/location/requestAndroidLocationPermissions.d.ts.map +0 -1
  122. package/lib/typescript/module/src/modules/location/requestAndroidLocationPermissions.d.ts +0 -5
  123. package/lib/typescript/module/src/modules/location/requestAndroidLocationPermissions.d.ts.map +0 -1
  124. package/src/modules/location/requestAndroidLocationPermissions.ts +0 -8
@@ -1,32 +1,28 @@
1
- import { requireNativeComponent } from "react-native";
1
+ import UserLocationNativeComponent from "./UserLocationNativeComponent";
2
+ import type { BaseProps } from "../../types/BaseProps";
2
3
 
3
- const NATIVE_MODULE_NAME = "MLRNNativeUserLocation";
4
-
5
- interface NativeUserLocationProps {
4
+ interface NativeUserLocationProps extends BaseProps {
6
5
  /**
7
- * Android render mode.
6
+ * Rendering mode
8
7
  *
9
- * - normal: just a circle
10
- * - compass: triangle with heading
11
- * - gps: large arrow
8
+ * - "default": Renders only a puck
9
+ * - "heading": Renders a puck with triangle indicating device heading based on compass
10
+ * - "course": Android renders an arrow indicating device heading based on GPS course, iOS behaves like mode="heading"
12
11
  *
13
- * @platform android
12
+ * @default "default"
14
13
  */
15
- androidRenderMode?: "normal" | "compass" | "gps";
14
+ mode?: "default" | "heading" | "course";
15
+
16
16
  /**
17
- * iOS only. A Boolean value indicating whether the user location annotation may display a permanent heading indicator.
17
+ * Limit the maximum frames per second for location updates on Android
18
18
  *
19
- * @platform ios
20
- */
21
- iosShowsUserHeadingIndicator?: boolean;
22
- /**
23
- * Android only. Set max FPS at which location animators can output updates. Use this setting to limit animation rate of the location puck on higher zoom levels to decrease the stress on the device's CPU which can directly improve battery life, without sacrificing UX.
19
+ * Use this setting to limit animation rate of the location puck to decrease the stress on the device's CPU which could improve battery life.
20
+ *
21
+ * @platform android
24
22
  */
25
23
  androidPreferredFramesPerSecond?: number;
26
24
  }
27
25
 
28
26
  export const NativeUserLocation = (props: NativeUserLocationProps) => {
29
- return <MLRNNativeUserLocation {...props} />;
27
+ return <UserLocationNativeComponent {...props} />;
30
28
  };
31
-
32
- const MLRNNativeUserLocation = requireNativeComponent(NATIVE_MODULE_NAME);
@@ -1,270 +1,86 @@
1
- import {
2
- forwardRef,
3
- memo,
4
- type ReactNode,
5
- useEffect,
6
- useImperativeHandle,
7
- useRef,
8
- useState,
9
- } from "react";
1
+ import { memo, type ReactNode, useMemo } from "react";
10
2
 
11
- import { NativeUserLocation } from "./NativeUserLocation";
12
3
  import { UserLocationPuck } from "./UserLocationPuck";
13
- import {
14
- type GeolocationPosition,
15
- LocationManager,
16
- } from "../../modules/location/LocationManager";
4
+ import { useCurrentPosition } from "../../hooks/useCurrentPosition";
17
5
  import { Annotation } from "../annotations/Annotation";
18
6
 
19
- const USER_LOCATION_SOURCE_ID = "mlrn-user-location";
20
-
21
- interface UserLocationProps {
22
- /**
23
- * Whether location icon is animated between updates
24
- */
25
- animated?: boolean;
26
- /**
27
- * Which render mode to use.
28
- * Can either be `normal` or `native`
29
- */
30
- renderMode?: "normal" | "native";
7
+ export interface UserLocationProps {
31
8
  /**
32
- * native/android only render mode
33
- *
34
- * - normal: just a circle
35
- * - compass: triangle with heading
36
- * - gps: large arrow
37
- *
38
- * @platform android
9
+ * Children to render inside the UserLocation Annotation, e.g. CircleLayer, SymbolLayer
39
10
  */
40
- androidRenderMode?: "normal" | "compass" | "gps";
41
- /**
42
- * Whether location icon is visible
43
- */
44
- visible?: boolean;
11
+ children?: ReactNode;
12
+
45
13
  /**
46
- * Callback that is triggered on location icon press
14
+ * Whether the UserLocation Annotation is animated between updates
47
15
  */
48
- onPress?: () => void;
16
+ animated?: boolean;
17
+
49
18
  /**
50
- * Callback that is triggered on location update
19
+ * Render a circle which indicates the accuracy of the location
51
20
  */
52
- onUpdate?: (location: GeolocationPosition) => void;
21
+ accuracy?: boolean;
22
+
53
23
  /**
54
- * Show or hide small arrow which indicates direction the device is pointing relative to north.
24
+ * Render an arrow which indicates direction the device is pointing relative to north
55
25
  */
56
- showsUserHeadingIndicator?: boolean;
26
+ heading?: boolean;
27
+
57
28
  /**
58
- * Minimum amount of movement before GPS location is updated in meters
29
+ * Minimum delta in meters for location updates
59
30
  */
60
31
  minDisplacement?: number;
32
+
61
33
  /**
62
- * Android only. Set max FPS at which location animators can output updates. Use this setting to limit animation rate of the location puck on higher zoom levels to decrease the stress on the device's CPU which can directly improve battery life, without sacrificing UX.
63
- *
64
- * @platform android
65
- */
66
- androidPreferredFramesPerSecond?: number;
67
- /**
68
- * Custom location icon of type mapbox-gl-native components
69
- *
70
- * NOTE: Forking maintainer does not understand the above comment.
34
+ * Event triggered on pressing the UserLocation Annotation
71
35
  */
72
- children?: ReactNode;
73
- }
74
-
75
- interface UserLocationState {
76
- shouldShowUserLocation: boolean;
77
- coordinates?: number[];
78
- heading?: number;
79
- }
80
-
81
- export enum UserLocationRenderMode {
82
- Native = "native",
83
- Normal = "normal",
84
- }
85
-
86
- export interface UserLocationRef {
87
- setLocationManager: (props: { running: boolean }) => Promise<void>;
88
- needsLocationManagerRunning: () => boolean;
89
- _onLocationUpdate: (location: GeolocationPosition | undefined) => void;
36
+ onPress?: () => void;
90
37
  }
91
38
 
92
39
  export const UserLocation = memo(
93
- forwardRef<UserLocationRef, UserLocationProps>(
94
- (
95
- {
96
- animated = true,
97
- visible = true,
98
- showsUserHeadingIndicator = false,
99
- minDisplacement = 0,
100
- renderMode = "normal",
101
- androidRenderMode,
102
- androidPreferredFramesPerSecond,
103
- children,
104
- onUpdate,
105
- onPress,
106
- }: UserLocationProps,
107
- ref,
108
- ) => {
109
- const _isMounted = useRef<boolean | null>(null);
110
- const locationManagerRunning = useRef<boolean>(false);
111
-
112
- const [userLocationState, setUserLocationState] =
113
- useState<UserLocationState>({
114
- shouldShowUserLocation: false,
115
- });
116
-
117
- useImperativeHandle(
118
- ref,
119
- (): UserLocationRef => ({
120
- /**
121
- * Whether to start or stop listening to the LocationManager
122
- *
123
- * Notice, that listening will start automatically when
124
- * either `onUpdate` or `visible` are set
125
- *
126
- * @async
127
- * @param {{running: boolean}} running - Object with key `running` and `boolean` value
128
- * @return {Promise<void>}
129
- */
130
- setLocationManager,
131
- /**
132
- *
133
- * If LocationManager should be running
134
- *
135
- * @return {boolean}
136
- */
137
- needsLocationManagerRunning,
138
- _onLocationUpdate,
139
- }),
140
- );
141
-
142
- useEffect(() => {
143
- _isMounted.current = true;
144
-
145
- setLocationManager({
146
- running: needsLocationManagerRunning(),
147
- }).then(() => {
148
- if (renderMode === UserLocationRenderMode.Native) {
149
- return;
150
- }
151
-
152
- LocationManager.setMinDisplacement(minDisplacement);
153
- });
154
-
155
- return (): void => {
156
- _isMounted.current = false;
157
- setLocationManager({ running: false });
158
- };
159
- // eslint-disable-next-line react-hooks/exhaustive-deps
160
- }, []);
161
-
162
- useEffect(() => {
163
- LocationManager.setMinDisplacement(minDisplacement);
164
- }, [minDisplacement]);
165
-
166
- useEffect(() => {
167
- if (!_isMounted.current) {
168
- return;
169
- }
170
-
171
- setLocationManager({
172
- running: needsLocationManagerRunning(),
173
- });
174
- });
175
-
176
- async function setLocationManager({
177
- running,
178
- }: {
179
- running: boolean;
180
- }): Promise<void> {
181
- if (locationManagerRunning.current !== running) {
182
- locationManagerRunning.current = running;
183
-
184
- if (running) {
185
- LocationManager.addListener(_onLocationUpdate);
186
- const location = await LocationManager.getCurrentPosition();
187
- _onLocationUpdate(location);
188
- } else {
189
- LocationManager.removeListener(_onLocationUpdate);
190
- }
191
- }
192
- }
193
-
194
- function needsLocationManagerRunning(): boolean {
195
- return !!(
196
- !!onUpdate ||
197
- (renderMode === UserLocationRenderMode.Normal && visible)
198
- );
199
- }
200
-
201
- function _onLocationUpdate(
202
- location: GeolocationPosition | undefined,
203
- ): void {
204
- if (!_isMounted.current || !location) {
205
- return;
206
- }
207
-
208
- let coordinates;
209
- let heading;
210
-
211
- if (location && location.coords) {
212
- const { longitude, latitude } = location.coords;
213
- heading = location.coords.heading;
214
- coordinates = [longitude, latitude];
215
- }
216
-
217
- setUserLocationState({
218
- ...userLocationState,
219
- coordinates,
220
- heading,
221
- });
222
-
223
- if (onUpdate) {
224
- onUpdate(location);
225
- }
226
- }
227
-
228
- if (!visible) {
229
- return null;
230
- }
231
-
232
- if (renderMode === UserLocationRenderMode.Native) {
233
- const props = {
234
- androidRenderMode,
235
- iosShowsUserHeadingIndicator: showsUserHeadingIndicator,
236
- androidPreferredFramesPerSecond,
237
- };
238
-
239
- return <NativeUserLocation {...props} />;
240
- }
241
-
242
- if (!userLocationState.coordinates) {
243
- return null;
244
- }
245
-
246
- return (
247
- <Annotation
248
- animated={animated}
249
- id={USER_LOCATION_SOURCE_ID}
250
- onPress={onPress}
251
- coordinates={userLocationState.coordinates}
252
- style={{
253
- iconRotate: userLocationState.heading,
254
- }}
255
- >
256
- {children || (
257
- <UserLocationPuck
258
- sourceID={USER_LOCATION_SOURCE_ID}
259
- heading={
260
- showsUserHeadingIndicator
261
- ? userLocationState.heading
262
- : undefined
263
- }
264
- />
265
- )}
266
- </Annotation>
267
- );
268
- },
269
- ),
40
+ ({
41
+ animated = true,
42
+ accuracy = false,
43
+ heading = false,
44
+ minDisplacement,
45
+ children,
46
+ onPress,
47
+ }: UserLocationProps) => {
48
+ const currentPosition = useCurrentPosition({ minDisplacement });
49
+
50
+ const coordinates = useMemo(() => {
51
+ return currentPosition?.coords
52
+ ? [currentPosition.coords.longitude, currentPosition.coords.latitude]
53
+ : undefined;
54
+ }, [currentPosition?.coords]);
55
+
56
+ if (!coordinates || !currentPosition) {
57
+ return null;
58
+ }
59
+
60
+ return (
61
+ <Annotation
62
+ animated={animated}
63
+ id="mlrn-user-location"
64
+ testID="mlrn-user-location"
65
+ onPress={onPress}
66
+ coordinates={coordinates}
67
+ style={{
68
+ iconRotate: currentPosition.coords.heading ?? undefined,
69
+ }}
70
+ >
71
+ {children || (
72
+ <UserLocationPuck
73
+ testID="mlrn-user-location-puck"
74
+ sourceID="mlrn-user-location"
75
+ accuracy={accuracy ? currentPosition.coords.accuracy : undefined}
76
+ heading={
77
+ heading
78
+ ? (currentPosition.coords.heading ?? undefined)
79
+ : undefined
80
+ }
81
+ />
82
+ )}
83
+ </Annotation>
84
+ );
85
+ },
270
86
  );
@@ -0,0 +1,19 @@
1
+ import {
2
+ codegenNativeComponent,
3
+ type CodegenTypes,
4
+ type HostComponent,
5
+ type ViewProps,
6
+ } from "react-native";
7
+
8
+ export interface NativeProps extends ViewProps {
9
+ mode?: CodegenTypes.WithDefault<"default" | "heading" | "course", "default">;
10
+
11
+ androidPreferredFramesPerSecond?: CodegenTypes.WithDefault<
12
+ CodegenTypes.Int32,
13
+ -1
14
+ >;
15
+ }
16
+
17
+ export default codegenNativeComponent<NativeProps>(
18
+ "MLRNNativeUserLocation",
19
+ ) as HostComponent<NativeProps>;
@@ -7,12 +7,12 @@ import { CircleLayer } from "../layers/CircleLayer";
7
7
 
8
8
  const blue = "#33B5E5";
9
9
 
10
- const layerStyles: Record<"pulse" | "white" | "blue", CircleLayerStyle> = {
11
- pulse: {
12
- circleRadius: 15,
10
+ const layerStyles = {
11
+ accuracy: {
13
12
  circleColor: blue,
14
13
  circleOpacity: 0.2,
15
14
  circlePitchAlignment: "map",
15
+ circleRadiusTransition: { duration: 300, delay: 0 },
16
16
  },
17
17
  white: {
18
18
  circleRadius: 9,
@@ -24,39 +24,58 @@ const layerStyles: Record<"pulse" | "white" | "blue", CircleLayerStyle> = {
24
24
  circleColor: blue,
25
25
  circlePitchAlignment: "map",
26
26
  },
27
- };
27
+ } satisfies Record<"accuracy" | "white" | "blue", CircleLayerStyle>;
28
28
 
29
29
  interface UserLocationPuckProps extends BaseProps {
30
30
  sourceID: string;
31
+ accuracy?: number;
31
32
  heading?: number;
32
33
  belowLayerID?: string;
33
34
  }
34
35
 
35
36
  export const UserLocationPuck = memo(
36
- ({ sourceID, heading }: UserLocationPuckProps) => (
37
- <>
38
- <CircleLayer
39
- id="mlrn-user-location-puck-pulse"
40
- sourceID={sourceID}
41
- style={layerStyles.pulse}
42
- />
43
- <CircleLayer
44
- id="mlrn-user-location-puck-white"
45
- sourceID={sourceID}
46
- style={layerStyles.white}
47
- />
48
- <CircleLayer
49
- id="mlrn-user-location-puck-blue"
50
- sourceID={sourceID}
51
- style={layerStyles.blue}
52
- />
53
- {typeof heading === "number" && (
54
- <UserLocationPuckHeading
37
+ ({ sourceID, accuracy, heading }: UserLocationPuckProps) => {
38
+ return (
39
+ <>
40
+ {typeof accuracy === "number" && (
41
+ <CircleLayer
42
+ id="mlrn-user-location-puck-accuracy"
43
+ testID="mlrn-user-location-puck-accuracy"
44
+ sourceID={sourceID}
45
+ style={{
46
+ ...layerStyles.accuracy,
47
+ circleRadius: [
48
+ "interpolate",
49
+ ["exponential", 2],
50
+ ["zoom"],
51
+ 0,
52
+ layerStyles.white.circleRadius,
53
+ 22,
54
+ layerStyles.white.circleRadius + accuracy * 100,
55
+ ],
56
+ }}
57
+ />
58
+ )}
59
+ <CircleLayer
60
+ id="mlrn-user-location-puck-white"
61
+ testID="mlrn-user-location-puck-white"
55
62
  sourceID={sourceID}
56
- belowLayerID="mlrn-user-location-puck-white"
57
- heading={heading}
63
+ style={layerStyles.white}
58
64
  />
59
- )}
60
- </>
61
- ),
65
+ <CircleLayer
66
+ id="mlrn-user-location-puck-blue"
67
+ testID="mlrn-user-location-puck-blue"
68
+ sourceID={sourceID}
69
+ style={layerStyles.blue}
70
+ />
71
+ {typeof heading === "number" && (
72
+ <UserLocationPuckHeading
73
+ sourceID={sourceID}
74
+ belowLayerID="mlrn-user-location-puck-white"
75
+ heading={heading}
76
+ />
77
+ )}
78
+ </>
79
+ );
80
+ },
62
81
  );
@@ -22,11 +22,12 @@ export const UserLocationPuckHeading = memo(
22
22
  ({ sourceID, belowLayerID, heading }: UserLocationPuckHeadingProps) => (
23
23
  <SymbolLayer
24
24
  id="mlrn-user-location-puck-heading"
25
+ testID="mlrn-user-location-puck-heading"
25
26
  sourceID={sourceID}
26
27
  belowLayerID={belowLayerID}
27
28
  style={{
28
- iconRotate: heading,
29
29
  ...layerStyle,
30
+ iconRotate: heading,
30
31
  }}
31
32
  />
32
33
  ),
@@ -0,0 +1,53 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+
3
+ import {
4
+ type GeolocationPosition,
5
+ LocationManager,
6
+ } from "../modules/location/LocationManager";
7
+
8
+ interface UseCurrentPositionOptions {
9
+ /**
10
+ * Enable or disable position updates
11
+ *
12
+ * @default true
13
+ */
14
+ enabled?: boolean;
15
+
16
+ /**
17
+ * Minimum displacement in meters to trigger position update
18
+ *
19
+ * @default 0
20
+ */
21
+ minDisplacement?: number;
22
+ }
23
+
24
+ export function useCurrentPosition({
25
+ enabled = true,
26
+ minDisplacement,
27
+ }: UseCurrentPositionOptions = {}) {
28
+ const [currentPosition, setCurrentPosition] = useState<GeolocationPosition>();
29
+
30
+ useEffect(() => {
31
+ if (minDisplacement !== undefined) {
32
+ LocationManager.setMinDisplacement(minDisplacement);
33
+ }
34
+ }, [minDisplacement]);
35
+
36
+ const handleUpdate = useCallback((position: GeolocationPosition) => {
37
+ setCurrentPosition(position);
38
+ }, []);
39
+
40
+ useEffect(() => {
41
+ if (enabled) {
42
+ LocationManager.addListener(handleUpdate);
43
+ }
44
+
45
+ return () => {
46
+ if (enabled) {
47
+ LocationManager.removeListener(handleUpdate);
48
+ }
49
+ };
50
+ }, [enabled, handleUpdate]);
51
+
52
+ return currentPosition;
53
+ }
package/src/index.ts CHANGED
@@ -28,11 +28,11 @@ export { PointAnnotation } from "./components/annotations/PointAnnotation";
28
28
  export type { PointAnnotationRef } from "./components/annotations/PointAnnotation";
29
29
  export { Annotation } from "./components/annotations/Annotation";
30
30
  export { Callout } from "./components/annotations/Callout";
31
- export {
32
- UserLocation,
33
- UserLocationRenderMode,
34
- } from "./components/user-location/UserLocation";
35
- export type { UserLocationRef } from "./components/user-location/UserLocation";
31
+
32
+ export { UserLocation } from "./components/user-location/UserLocation";
33
+ export { NativeUserLocation } from "./components/user-location/NativeUserLocation";
34
+ export { useCurrentPosition } from "./hooks/useCurrentPosition";
35
+
36
36
  export { VectorSource } from "./components/sources/VectorSource";
37
37
  export { ShapeSource } from "./components/sources/ShapeSource";
38
38
  export type { ShapeSourceRef } from "./components/sources/ShapeSource";
@@ -53,7 +53,6 @@ export {
53
53
  LocationManager,
54
54
  type GeolocationPosition,
55
55
  } from "./modules/location/LocationManager";
56
- export { requestAndroidLocationPermissions } from "./modules/location/requestAndroidLocationPermissions";
57
56
 
58
57
  export { OfflineManager } from "./modules/offline/OfflineManager";
59
58
  export type { OfflinePackError } from "./modules/offline/OfflineManager";
@@ -26,7 +26,7 @@ interface GeolocationCoordinates {
26
26
  /**
27
27
  * Altitude in meters
28
28
  */
29
- altitude: number;
29
+ altitude: number | null;
30
30
 
31
31
  /**
32
32
  * Accuracy for altitude in meters
@@ -36,12 +36,12 @@ interface GeolocationCoordinates {
36
36
  /**
37
37
  * Direction in which the device is traveling in degrees, relative to north
38
38
  */
39
- heading: number;
39
+ heading: number | null;
40
40
 
41
41
  /**
42
42
  * Instantaneous speed of the device in meters per second
43
43
  */
44
- speed: number;
44
+ speed: number | null;
45
45
  }
46
46
 
47
47
  export interface GeolocationPosition {
@@ -5,10 +5,10 @@ type NativeGeolocationCoordinates = {
5
5
  longitude: CodegenTypes.Double;
6
6
  latitude: CodegenTypes.Double;
7
7
  accuracy: CodegenTypes.Double;
8
- altitude: CodegenTypes.Double;
8
+ altitude: CodegenTypes.Double | null;
9
9
  altitudeAccuracy: CodegenTypes.Double | null;
10
- heading: CodegenTypes.Double;
11
- speed: CodegenTypes.Double;
10
+ heading: CodegenTypes.Double | null;
11
+ speed: CodegenTypes.Double | null;
12
12
  };
13
13
 
14
14
  type NativeGeolocationPosition = {
@@ -1,8 +1,3 @@
1
- import {
2
- type ForwardRefExoticComponent,
3
- type MemoExoticComponent,
4
- type RefAttributes,
5
- } from "react";
6
1
  import { Animated as RNAnimated } from "react-native";
7
2
 
8
3
  import { AnimatedCoordinatesArray } from "./AnimatedCoordinatesArray";
@@ -17,11 +12,7 @@ import { LineLayer } from "../../components/layers/LineLayer";
17
12
  import { RasterLayer } from "../../components/layers/RasterLayer";
18
13
  import { SymbolLayer } from "../../components/layers/SymbolLayer";
19
14
  import { ImageSource } from "../../components/sources/ImageSource";
20
- import {
21
- ShapeSource,
22
- type ShapeSourceProps,
23
- type ShapeSourceRef,
24
- } from "../../components/sources/ShapeSource";
15
+ import { ShapeSource } from "../../components/sources/ShapeSource";
25
16
 
26
17
  export const Animated = {
27
18
  // sources
@@ -43,21 +34,3 @@ export const Animated = {
43
34
  Shape: AnimatedShape,
44
35
  ExtractCoordinateFromArray: AnimatedExtractCoordinateFromArray,
45
36
  };
46
-
47
- type ShapeSourcePropsWithRef = ShapeSourceProps & RefAttributes<ShapeSourceRef>;
48
-
49
- type BaseShapeSourceComponent =
50
- ForwardRefExoticComponent<ShapeSourcePropsWithRef>;
51
-
52
- type AnimatedShapeSourceType =
53
- RNAnimated.AnimatedComponent<BaseShapeSourceComponent> &
54
- MemoExoticComponent<BaseShapeSourceComponent>;
55
-
56
- /**
57
- * Manual typing is required for AnimatedShapeSource because the
58
- * following error:
59
- * `Type instantiation is excessively deep and possibly infinite.ts(2589)`
60
- */
61
- export const AnimatedShapeSource = RNAnimated.createAnimatedComponent(
62
- ShapeSource,
63
- ) as AnimatedShapeSourceType;