@atomiqlab/react-native-mapbox-navigation 1.1.1 → 1.1.3
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/README.md +379 -0
- package/SECURITY.md +36 -0
- package/android/src/main/java/expo/modules/mapboxnavigation/MapboxNavigationActivity.kt +96 -8
- package/app.plugin.js +64 -4
- package/docs/TROUBLESHOOTING.md +22 -0
- package/docs/imgs/support_me_on_kofi_badge_red.png +0 -0
- package/docs/screenshots/.gitkeep +0 -0
- package/docs/screenshots/android.png +0 -0
- package/docs/screenshots/ios.png +0 -0
- package/ios/MapboxNavigationModule.swift +143 -2
- package/package.json +4 -2
- package/scripts/verify-release.mjs +24 -9
- package/src/MapboxNavigation.types.ts +47 -17
- package/src/index.tsx +113 -25
package/docs/TROUBLESHOOTING.md
CHANGED
|
@@ -33,3 +33,25 @@ If not, verify you are running the latest package version and rebuilt native cod
|
|
|
33
33
|
```bash
|
|
34
34
|
npx expo prebuild --clean
|
|
35
35
|
```
|
|
36
|
+
|
|
37
|
+
## Prebuild Fails With Missing Token Error
|
|
38
|
+
|
|
39
|
+
The config plugin now validates tokens early. Ensure one of these provides a public token:
|
|
40
|
+
|
|
41
|
+
- `EXPO_PUBLIC_MAPBOX_ACCESS_TOKEN`
|
|
42
|
+
- `MAPBOX_PUBLIC_TOKEN`
|
|
43
|
+
- `expo.extra.mapboxPublicToken`
|
|
44
|
+
|
|
45
|
+
And provide downloads token:
|
|
46
|
+
|
|
47
|
+
- `MAPBOX_DOWNLOADS_TOKEN` or `expo.extra.mapboxDownloadsToken`
|
|
48
|
+
|
|
49
|
+
## Route Fetch Errors (401/403/429)
|
|
50
|
+
|
|
51
|
+
Listen to `onError` / `addErrorListener` and check `code`:
|
|
52
|
+
|
|
53
|
+
- `MAPBOX_TOKEN_INVALID` (invalid/expired token)
|
|
54
|
+
- `MAPBOX_TOKEN_FORBIDDEN` (missing scopes)
|
|
55
|
+
- `MAPBOX_RATE_LIMITED` (request throttling)
|
|
56
|
+
|
|
57
|
+
For Android dependency download failures during build, verify `MAPBOX_DOWNLOADS_TOKEN` has `DOWNLOADS:READ` scope.
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
@@ -157,8 +157,23 @@ public class MapboxNavigationModule: Module {
|
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
private func configuredMapboxPublicToken() -> String? {
|
|
161
|
+
guard let raw = Bundle.main.object(forInfoDictionaryKey: "MBXAccessToken") as? String else {
|
|
162
|
+
return nil
|
|
163
|
+
}
|
|
164
|
+
let token = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
165
|
+
guard token.hasPrefix("pk."), token.count > 20 else {
|
|
166
|
+
return nil
|
|
167
|
+
}
|
|
168
|
+
return token
|
|
169
|
+
}
|
|
170
|
+
|
|
160
171
|
private func startNavigation(options: NavigationStartOptions, promise: Promise) {
|
|
161
172
|
guard let destination = options.destination.toCLLocationCoordinate2D() else {
|
|
173
|
+
self.emitErrorAndShowScreen([
|
|
174
|
+
"code": "INVALID_COORDINATES",
|
|
175
|
+
"message": "Invalid coordinates provided"
|
|
176
|
+
])
|
|
162
177
|
promise.reject("INVALID_COORDINATES", "Invalid coordinates provided")
|
|
163
178
|
return
|
|
164
179
|
}
|
|
@@ -184,6 +199,10 @@ public class MapboxNavigationModule: Module {
|
|
|
184
199
|
promise: promise
|
|
185
200
|
)
|
|
186
201
|
case .failure(let error):
|
|
202
|
+
self.emitErrorAndShowScreen([
|
|
203
|
+
"code": "CURRENT_LOCATION_UNAVAILABLE",
|
|
204
|
+
"message": error.localizedDescription
|
|
205
|
+
])
|
|
187
206
|
promise.reject("CURRENT_LOCATION_UNAVAILABLE", error.localizedDescription)
|
|
188
207
|
}
|
|
189
208
|
}
|
|
@@ -195,7 +214,16 @@ public class MapboxNavigationModule: Module {
|
|
|
195
214
|
options: NavigationStartOptions,
|
|
196
215
|
promise: Promise
|
|
197
216
|
) {
|
|
198
|
-
|
|
217
|
+
guard configuredMapboxPublicToken() != nil else {
|
|
218
|
+
let message = "Missing or invalid MBXAccessToken. Add the package plugin to app.json and set EXPO_PUBLIC_MAPBOX_ACCESS_TOKEN before prebuild."
|
|
219
|
+
self.emitErrorAndShowScreen([
|
|
220
|
+
"code": "MISSING_ACCESS_TOKEN",
|
|
221
|
+
"message": message
|
|
222
|
+
])
|
|
223
|
+
promise.reject("MISSING_ACCESS_TOKEN", message)
|
|
224
|
+
return
|
|
225
|
+
}
|
|
226
|
+
|
|
199
227
|
var waypoints = [Waypoint(coordinate: origin)]
|
|
200
228
|
|
|
201
229
|
// Add intermediate waypoints if provided
|
|
@@ -225,6 +253,10 @@ public class MapboxNavigationModule: Module {
|
|
|
225
253
|
switch result {
|
|
226
254
|
case .success(let response):
|
|
227
255
|
guard response.routes?.first != nil else {
|
|
256
|
+
self.emitErrorAndShowScreen([
|
|
257
|
+
"code": "NO_ROUTE",
|
|
258
|
+
"message": "No route found"
|
|
259
|
+
])
|
|
228
260
|
promise.reject("NO_ROUTE", "No route found")
|
|
229
261
|
return
|
|
230
262
|
}
|
|
@@ -272,11 +304,20 @@ public class MapboxNavigationModule: Module {
|
|
|
272
304
|
promise.resolve(nil)
|
|
273
305
|
}
|
|
274
306
|
} else {
|
|
307
|
+
self.emitErrorAndShowScreen([
|
|
308
|
+
"code": "NO_ROOT_VC",
|
|
309
|
+
"message": "Could not find root view controller"
|
|
310
|
+
])
|
|
275
311
|
promise.reject("NO_ROOT_VC", "Could not find root view controller")
|
|
276
312
|
}
|
|
277
313
|
|
|
278
314
|
case .failure(let error):
|
|
279
|
-
|
|
315
|
+
let (code, message) = self.mapDirectionsError(error)
|
|
316
|
+
self.emitErrorAndShowScreen([
|
|
317
|
+
"code": code,
|
|
318
|
+
"message": message
|
|
319
|
+
])
|
|
320
|
+
promise.reject(code, message)
|
|
280
321
|
}
|
|
281
322
|
}
|
|
282
323
|
}
|
|
@@ -292,6 +333,42 @@ public class MapboxNavigationModule: Module {
|
|
|
292
333
|
}
|
|
293
334
|
}
|
|
294
335
|
|
|
336
|
+
private func emitErrorAndShowScreen(_ payload: [String: Any]) {
|
|
337
|
+
sendEvent("onError", payload)
|
|
338
|
+
|
|
339
|
+
guard let navVC = navigationViewController else {
|
|
340
|
+
return
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if navVC.presentingViewController == nil {
|
|
344
|
+
navigationViewController = nil
|
|
345
|
+
isCurrentlyNavigating = false
|
|
346
|
+
return
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
navVC.dismiss(animated: true) {
|
|
350
|
+
self.navigationViewController = nil
|
|
351
|
+
self.isCurrentlyNavigating = false
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
private func mapDirectionsError(_ error: Error) -> (String, String) {
|
|
356
|
+
let message = error.localizedDescription
|
|
357
|
+
let lowered = message.lowercased()
|
|
358
|
+
|
|
359
|
+
if lowered.contains("401") || lowered.contains("unauthorized") {
|
|
360
|
+
return ("MAPBOX_TOKEN_INVALID", "Route fetch failed: unauthorized. Check EXPO_PUBLIC_MAPBOX_ACCESS_TOKEN and token scopes.")
|
|
361
|
+
}
|
|
362
|
+
if lowered.contains("403") || lowered.contains("forbidden") {
|
|
363
|
+
return ("MAPBOX_TOKEN_FORBIDDEN", "Route fetch failed: access forbidden. Verify token scopes and account permissions.")
|
|
364
|
+
}
|
|
365
|
+
if lowered.contains("429") || lowered.contains("rate") {
|
|
366
|
+
return ("MAPBOX_RATE_LIMITED", "Route fetch failed: rate limited by Mapbox.")
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return ("ROUTE_ERROR", message)
|
|
370
|
+
}
|
|
371
|
+
|
|
295
372
|
private func stopNavigation(promise: Promise) {
|
|
296
373
|
guard let navVC = navigationViewController else {
|
|
297
374
|
promise.resolve(nil)
|
|
@@ -611,3 +688,67 @@ private final class CurrentLocationResolver: NSObject, CLLocationManagerDelegate
|
|
|
611
688
|
completion(result)
|
|
612
689
|
}
|
|
613
690
|
}
|
|
691
|
+
|
|
692
|
+
private final class NavigationErrorViewController: UIViewController {
|
|
693
|
+
private let message: String
|
|
694
|
+
|
|
695
|
+
init(message: String) {
|
|
696
|
+
self.message = message
|
|
697
|
+
super.init(nibName: nil, bundle: nil)
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
required init?(coder: NSCoder) {
|
|
701
|
+
nil
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
override func viewDidLoad() {
|
|
705
|
+
super.viewDidLoad()
|
|
706
|
+
|
|
707
|
+
view.backgroundColor = UIColor(red: 11 / 255, green: 16 / 255, blue: 32 / 255, alpha: 1)
|
|
708
|
+
|
|
709
|
+
let titleLabel = UILabel()
|
|
710
|
+
titleLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
711
|
+
titleLabel.text = "Navigation Error"
|
|
712
|
+
titleLabel.textColor = .white
|
|
713
|
+
titleLabel.font = UIFont.systemFont(ofSize: 28, weight: .bold)
|
|
714
|
+
titleLabel.textAlignment = .center
|
|
715
|
+
titleLabel.numberOfLines = 0
|
|
716
|
+
|
|
717
|
+
let messageLabel = UILabel()
|
|
718
|
+
messageLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
719
|
+
messageLabel.text = message
|
|
720
|
+
messageLabel.textColor = UIColor(red: 214 / 255, green: 228 / 255, blue: 255 / 255, alpha: 1)
|
|
721
|
+
messageLabel.font = UIFont.systemFont(ofSize: 16, weight: .regular)
|
|
722
|
+
messageLabel.textAlignment = .center
|
|
723
|
+
messageLabel.numberOfLines = 0
|
|
724
|
+
|
|
725
|
+
let closeButton = UIButton(type: .system)
|
|
726
|
+
closeButton.translatesAutoresizingMaskIntoConstraints = false
|
|
727
|
+
closeButton.setTitle("Back", for: .normal)
|
|
728
|
+
closeButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
|
|
729
|
+
closeButton.backgroundColor = UIColor.white.withAlphaComponent(0.15)
|
|
730
|
+
closeButton.setTitleColor(.white, for: .normal)
|
|
731
|
+
closeButton.layer.cornerRadius = 10
|
|
732
|
+
closeButton.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
|
|
733
|
+
closeButton.addTarget(self, action: #selector(closeTapped), for: .touchUpInside)
|
|
734
|
+
|
|
735
|
+
let stack = UIStackView(arrangedSubviews: [titleLabel, messageLabel, closeButton])
|
|
736
|
+
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
737
|
+
stack.axis = .vertical
|
|
738
|
+
stack.alignment = .fill
|
|
739
|
+
stack.spacing = 20
|
|
740
|
+
|
|
741
|
+
view.addSubview(stack)
|
|
742
|
+
|
|
743
|
+
NSLayoutConstraint.activate([
|
|
744
|
+
stack.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 24),
|
|
745
|
+
stack.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -24),
|
|
746
|
+
stack.centerYAnchor.constraint(equalTo: view.centerYAnchor),
|
|
747
|
+
])
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
@objc
|
|
751
|
+
private func closeTapped() {
|
|
752
|
+
dismiss(animated: true)
|
|
753
|
+
}
|
|
754
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atomiqlab/react-native-mapbox-navigation",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "Native Mapbox turn-by-turn navigation for Expo and React Native (iOS + Android)",
|
|
5
5
|
"main": "src/index.tsx",
|
|
6
6
|
"types": "src/index.tsx",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"README.md",
|
|
28
28
|
"QUICKSTART.md",
|
|
29
29
|
"CHANGELOG.md",
|
|
30
|
+
"SECURITY.md",
|
|
30
31
|
"docs",
|
|
31
32
|
"scripts"
|
|
32
33
|
],
|
|
@@ -70,6 +71,7 @@
|
|
|
70
71
|
"react-native": "*"
|
|
71
72
|
},
|
|
72
73
|
"devDependencies": {
|
|
73
|
-
"expo-module-scripts": "^3.0.0"
|
|
74
|
+
"expo-module-scripts": "^3.0.0",
|
|
75
|
+
"typescript": "^5.9.3"
|
|
74
76
|
}
|
|
75
77
|
}
|
|
@@ -5,9 +5,9 @@ import { fileURLToPath } from "node:url";
|
|
|
5
5
|
|
|
6
6
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
7
|
const moduleDir = path.resolve(path.dirname(__filename), "..");
|
|
8
|
-
const repoRoot =
|
|
9
|
-
const androidDir = path.join(
|
|
10
|
-
const envFilePath = path.join(
|
|
8
|
+
const repoRoot = moduleDir;
|
|
9
|
+
const androidDir = path.join(moduleDir, "android");
|
|
10
|
+
const envFilePath = path.join(moduleDir, ".env");
|
|
11
11
|
|
|
12
12
|
function loadDotEnv(filePath) {
|
|
13
13
|
if (!existsSync(filePath)) {
|
|
@@ -49,7 +49,8 @@ function run(command, cwd = moduleDir, options = {}) {
|
|
|
49
49
|
env: {
|
|
50
50
|
...process.env,
|
|
51
51
|
GRADLE_USER_HOME:
|
|
52
|
-
process.env.GRADLE_USER_HOME ||
|
|
52
|
+
process.env.GRADLE_USER_HOME ||
|
|
53
|
+
"/tmp/gradle-user-home-react-native-mapbox-navigation",
|
|
53
54
|
},
|
|
54
55
|
});
|
|
55
56
|
if (output?.length) {
|
|
@@ -72,7 +73,9 @@ function run(command, cwd = moduleDir, options = {}) {
|
|
|
72
73
|
combined.includes("SocketException: Operation not permitted");
|
|
73
74
|
|
|
74
75
|
if (optionalOnOfflineGradle && skippableGradleEnvironmentIssue) {
|
|
75
|
-
console.warn(
|
|
76
|
+
console.warn(
|
|
77
|
+
`\nSkipping optional Gradle check due to restricted/offline environment: ${command}`,
|
|
78
|
+
);
|
|
76
79
|
return;
|
|
77
80
|
}
|
|
78
81
|
throw error;
|
|
@@ -95,7 +98,14 @@ function runWithFallback(commands, cwd, options = {}) {
|
|
|
95
98
|
|
|
96
99
|
loadDotEnv(envFilePath);
|
|
97
100
|
|
|
98
|
-
|
|
101
|
+
const tsconfigPath = path.join(moduleDir, "tsconfig.json");
|
|
102
|
+
if (existsSync(tsconfigPath)) {
|
|
103
|
+
run(`npx --no-install tsc -p ${tsconfigPath} --noEmit`, moduleDir);
|
|
104
|
+
} else {
|
|
105
|
+
console.log(
|
|
106
|
+
"\n> Skipping TypeScript check (tsconfig.json not found in package root).",
|
|
107
|
+
);
|
|
108
|
+
}
|
|
99
109
|
|
|
100
110
|
if (existsSync(path.join(androidDir, "gradlew"))) {
|
|
101
111
|
runWithFallback(
|
|
@@ -104,12 +114,17 @@ if (existsSync(path.join(androidDir, "gradlew"))) {
|
|
|
104
114
|
"./gradlew :mapbox-navigation-native:compileDebugKotlin",
|
|
105
115
|
],
|
|
106
116
|
androidDir,
|
|
107
|
-
{ optionalOnOfflineGradle: true }
|
|
117
|
+
{ optionalOnOfflineGradle: true },
|
|
108
118
|
);
|
|
109
119
|
} else {
|
|
110
|
-
console.log(
|
|
120
|
+
console.log(
|
|
121
|
+
"\n> Skipping Android compile check (android/gradlew not found).",
|
|
122
|
+
);
|
|
111
123
|
}
|
|
112
124
|
|
|
113
|
-
run(
|
|
125
|
+
run(
|
|
126
|
+
"npm pack --dry-run --cache /tmp/npm-cache-react-native-mapbox-navigation",
|
|
127
|
+
moduleDir,
|
|
128
|
+
);
|
|
114
129
|
|
|
115
130
|
console.log("\nRelease verification completed.");
|
|
@@ -1,36 +1,66 @@
|
|
|
1
|
+
/** Geographic coordinate in WGS84 format. */
|
|
1
2
|
export type Coordinate = {
|
|
3
|
+
/** Latitude in range -90..90 */
|
|
2
4
|
latitude: number;
|
|
5
|
+
/** Longitude in range -180..180 */
|
|
3
6
|
longitude: number;
|
|
4
7
|
};
|
|
5
8
|
|
|
9
|
+
/** A route point that can optionally include a display name. */
|
|
6
10
|
export type Waypoint = Coordinate & {
|
|
11
|
+
/** Optional label for UI/debug output. */
|
|
7
12
|
name?: string;
|
|
8
13
|
};
|
|
9
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Options passed to `startNavigation`.
|
|
17
|
+
*/
|
|
10
18
|
export type NavigationOptions = {
|
|
19
|
+
/** Optional route origin. If omitted, native layer may resolve current location. */
|
|
11
20
|
startOrigin?: Coordinate;
|
|
21
|
+
/** Final destination waypoint (required). */
|
|
12
22
|
destination: Waypoint;
|
|
23
|
+
/** Optional intermediate waypoints. */
|
|
13
24
|
waypoints?: Waypoint[];
|
|
25
|
+
/** Enable route simulation for testing without physical movement. */
|
|
14
26
|
shouldSimulateRoute?: boolean;
|
|
27
|
+
/** UI theme selection for native UI. */
|
|
15
28
|
uiTheme?: 'system' | 'light' | 'dark' | 'day' | 'night';
|
|
29
|
+
/** Request alternative routes when available. */
|
|
16
30
|
routeAlternatives?: boolean;
|
|
31
|
+
/** Distance unit for guidance. */
|
|
17
32
|
distanceUnit?: 'metric' | 'imperial';
|
|
33
|
+
/** Guidance language (for example `en`, `fr`). */
|
|
18
34
|
language?: string;
|
|
35
|
+
/** Start muted. */
|
|
19
36
|
mute?: boolean;
|
|
37
|
+
/** Voice volume range `0..1`. */
|
|
20
38
|
voiceVolume?: number;
|
|
39
|
+
/** Camera pitch range `0..85`. */
|
|
21
40
|
cameraPitch?: number;
|
|
41
|
+
/** Camera zoom range `1..22`. */
|
|
22
42
|
cameraZoom?: number;
|
|
43
|
+
/** Camera behavior mode. */
|
|
23
44
|
cameraMode?: 'following' | 'overview';
|
|
45
|
+
/** One style URI used for both day/night fallback. */
|
|
24
46
|
mapStyleUri?: string;
|
|
47
|
+
/** Day style URI override. */
|
|
25
48
|
mapStyleUriDay?: string;
|
|
49
|
+
/** Night style URI override. */
|
|
26
50
|
mapStyleUriNight?: string;
|
|
51
|
+
/** Show speed limit panel when supported. */
|
|
27
52
|
showsSpeedLimits?: boolean;
|
|
53
|
+
/** Show current road name label. */
|
|
28
54
|
showsWayNameLabel?: boolean;
|
|
55
|
+
/** Show trip progress summary. */
|
|
29
56
|
showsTripProgress?: boolean;
|
|
57
|
+
/** Show maneuver/instruction view. */
|
|
30
58
|
showsManeuverView?: boolean;
|
|
59
|
+
/** Show default action buttons in native UI. */
|
|
31
60
|
showsActionButtons?: boolean;
|
|
32
61
|
};
|
|
33
62
|
|
|
63
|
+
/** Runtime settings/state returned by `getNavigationSettings()`. */
|
|
34
64
|
export type NavigationSettings = {
|
|
35
65
|
isNavigating: boolean;
|
|
36
66
|
mute: boolean;
|
|
@@ -39,6 +69,7 @@ export type NavigationSettings = {
|
|
|
39
69
|
language: string;
|
|
40
70
|
};
|
|
41
71
|
|
|
72
|
+
/** Location update payload emitted by native layer. */
|
|
42
73
|
export type LocationUpdate = {
|
|
43
74
|
latitude: number;
|
|
44
75
|
longitude: number;
|
|
@@ -48,6 +79,7 @@ export type LocationUpdate = {
|
|
|
48
79
|
accuracy?: number;
|
|
49
80
|
};
|
|
50
81
|
|
|
82
|
+
/** Route progress payload emitted by native layer. */
|
|
51
83
|
export type RouteProgress = {
|
|
52
84
|
distanceTraveled: number;
|
|
53
85
|
distanceRemaining: number;
|
|
@@ -55,52 +87,45 @@ export type RouteProgress = {
|
|
|
55
87
|
fractionTraveled: number;
|
|
56
88
|
};
|
|
57
89
|
|
|
90
|
+
/** Arrival event payload. */
|
|
58
91
|
export type ArrivalEvent = {
|
|
59
92
|
index?: number;
|
|
60
93
|
name?: string;
|
|
61
94
|
};
|
|
62
95
|
|
|
96
|
+
/** Error payload emitted by native layer. */
|
|
63
97
|
export type NavigationError = {
|
|
98
|
+
/** Machine-readable error code. */
|
|
64
99
|
code: string;
|
|
100
|
+
/** Developer-readable error details. */
|
|
65
101
|
message: string;
|
|
66
102
|
};
|
|
67
103
|
|
|
104
|
+
/** Banner instruction payload emitted during guidance. */
|
|
68
105
|
export type BannerInstruction = {
|
|
69
106
|
primaryText: string;
|
|
70
107
|
secondaryText?: string;
|
|
71
108
|
stepDistanceRemaining?: number;
|
|
72
109
|
};
|
|
73
110
|
|
|
111
|
+
/** Event subscription handle. */
|
|
74
112
|
export type Subscription = {
|
|
75
113
|
remove: () => void;
|
|
76
114
|
};
|
|
77
115
|
|
|
116
|
+
/** Native module interface bridged from iOS/Android. */
|
|
78
117
|
export interface MapboxNavigationModule {
|
|
79
|
-
// Start navigation with options
|
|
80
118
|
startNavigation(options: NavigationOptions): Promise<void>;
|
|
81
|
-
|
|
82
|
-
// Stop/cancel navigation
|
|
83
119
|
stopNavigation(): Promise<void>;
|
|
84
|
-
|
|
85
|
-
// Mute/unmute voice guidance
|
|
86
120
|
setMuted(muted: boolean): Promise<void>;
|
|
87
|
-
|
|
88
|
-
// Set voice volume (0.0 - 1.0)
|
|
89
121
|
setVoiceVolume(volume: number): Promise<void>;
|
|
90
|
-
|
|
91
|
-
// Set distance unit used by spoken and visual instructions
|
|
92
122
|
setDistanceUnit(unit: 'metric' | 'imperial'): Promise<void>;
|
|
93
|
-
|
|
94
|
-
// Set route instruction language (BCP-47, e.g. 'en', 'fr')
|
|
95
123
|
setLanguage(language: string): Promise<void>;
|
|
96
|
-
|
|
97
|
-
// Check if navigation is active
|
|
98
124
|
isNavigating(): Promise<boolean>;
|
|
99
|
-
|
|
100
|
-
// Get current native navigation settings
|
|
101
125
|
getNavigationSettings(): Promise<NavigationSettings>;
|
|
102
126
|
}
|
|
103
127
|
|
|
128
|
+
/** Props for embedded `MapboxNavigationView`. */
|
|
104
129
|
export interface MapboxNavigationViewProps {
|
|
105
130
|
style?: any;
|
|
106
131
|
startOrigin?: Coordinate;
|
|
@@ -125,12 +150,17 @@ export interface MapboxNavigationViewProps {
|
|
|
125
150
|
showsTripProgress?: boolean;
|
|
126
151
|
showsManeuverView?: boolean;
|
|
127
152
|
showsActionButtons?: boolean;
|
|
128
|
-
|
|
129
|
-
|
|
153
|
+
|
|
154
|
+
/** Callback for location changes. */
|
|
130
155
|
onLocationChange?: (location: LocationUpdate) => void;
|
|
156
|
+
/** Callback for route progress changes. */
|
|
131
157
|
onRouteProgressChange?: (progress: RouteProgress) => void;
|
|
158
|
+
/** Callback when arrival is detected. */
|
|
132
159
|
onArrive?: (point: ArrivalEvent) => void;
|
|
160
|
+
/** Callback when navigation is canceled by user. */
|
|
133
161
|
onCancelNavigation?: () => void;
|
|
162
|
+
/** Callback for native errors. */
|
|
134
163
|
onError?: (error: NavigationError) => void;
|
|
164
|
+
/** Callback for banner instruction updates. */
|
|
135
165
|
onBannerInstruction?: (instruction: BannerInstruction) => void;
|
|
136
166
|
}
|