@atomiqlab/react-native-mapbox-navigation 1.1.0
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/CHANGELOG.md +18 -0
- package/LICENSE +21 -0
- package/QUICKSTART.md +46 -0
- package/README.md +131 -0
- package/android/build.gradle +64 -0
- package/android/src/main/AndroidManifest.xml +8 -0
- package/android/src/main/java/expo/modules/mapboxnavigation/MapboxNavigationActivity.kt +421 -0
- package/android/src/main/java/expo/modules/mapboxnavigation/MapboxNavigationEventBridge.kt +18 -0
- package/android/src/main/java/expo/modules/mapboxnavigation/MapboxNavigationModule.kt +296 -0
- package/android/src/main/java/expo/modules/mapboxnavigation/MapboxNavigationViewManager.kt +143 -0
- package/app.plugin.js +154 -0
- package/docs/PUBLISHING.md +97 -0
- package/docs/TROUBLESHOOTING.md +35 -0
- package/docs/USAGE.md +100 -0
- package/expo-module.config.json +9 -0
- package/ios/ExpoMapboxNavigationNative.podspec +31 -0
- package/ios/MapboxNavigationModule.swift +613 -0
- package/ios/MapboxNavigationView.swift +298 -0
- package/package.json +75 -0
- package/scripts/verify-release.mjs +115 -0
- package/src/MapboxNavigation.types.ts +136 -0
- package/src/index.tsx +204 -0
package/src/index.tsx
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { requireNativeModule, requireNativeViewManager } from "expo-modules-core";
|
|
2
|
+
import { ViewProps } from "react-native";
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
ArrivalEvent,
|
|
6
|
+
BannerInstruction,
|
|
7
|
+
LocationUpdate,
|
|
8
|
+
MapboxNavigationModule as MapboxNavigationModuleType,
|
|
9
|
+
MapboxNavigationViewProps,
|
|
10
|
+
NavigationSettings,
|
|
11
|
+
NavigationError,
|
|
12
|
+
NavigationOptions,
|
|
13
|
+
RouteProgress,
|
|
14
|
+
Subscription,
|
|
15
|
+
} from "./MapboxNavigation.types";
|
|
16
|
+
|
|
17
|
+
// Get the native module (use requireNativeModule instead of deprecated NativeModulesProxy)
|
|
18
|
+
const MapboxNavigationModule = requireNativeModule<MapboxNavigationModuleType>(
|
|
19
|
+
"MapboxNavigationModule",
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
// The native module already acts as an EventEmitter; don't wrap with `new EventEmitter`.
|
|
23
|
+
const emitter = MapboxNavigationModule as unknown as {
|
|
24
|
+
addListener: (
|
|
25
|
+
eventName: string,
|
|
26
|
+
listener: (...args: any[]) => void,
|
|
27
|
+
) => Subscription;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const VALID_DISTANCE_UNITS = new Set(["metric", "imperial"]);
|
|
31
|
+
const VALID_CAMERA_MODES = new Set(["following", "overview"]);
|
|
32
|
+
const VALID_UI_THEMES = new Set(["system", "light", "dark", "day", "night"]);
|
|
33
|
+
|
|
34
|
+
function assertCoordinate(
|
|
35
|
+
coordinate: { latitude: number; longitude: number },
|
|
36
|
+
fieldName: string,
|
|
37
|
+
) {
|
|
38
|
+
if (!Number.isFinite(coordinate.latitude) || !Number.isFinite(coordinate.longitude)) {
|
|
39
|
+
throw new Error(`${fieldName} must contain finite latitude/longitude numbers.`);
|
|
40
|
+
}
|
|
41
|
+
if (coordinate.latitude < -90 || coordinate.latitude > 90) {
|
|
42
|
+
throw new Error(`${fieldName}.latitude must be between -90 and 90.`);
|
|
43
|
+
}
|
|
44
|
+
if (coordinate.longitude < -180 || coordinate.longitude > 180) {
|
|
45
|
+
throw new Error(`${fieldName}.longitude must be between -180 and 180.`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizeNavigationOptions(options: NavigationOptions): NavigationOptions {
|
|
50
|
+
if (!options || typeof options !== "object") {
|
|
51
|
+
throw new Error("startNavigation options must be an object.");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
assertCoordinate(options.destination, "destination");
|
|
55
|
+
if (options.startOrigin) {
|
|
56
|
+
assertCoordinate(options.startOrigin, "startOrigin");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (options.waypoints?.length) {
|
|
60
|
+
options.waypoints.forEach((waypoint, index) => {
|
|
61
|
+
assertCoordinate(waypoint, `waypoints[${index}]`);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (options.distanceUnit && !VALID_DISTANCE_UNITS.has(options.distanceUnit)) {
|
|
66
|
+
throw new Error("distanceUnit must be 'metric' or 'imperial'.");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (options.cameraMode && !VALID_CAMERA_MODES.has(options.cameraMode)) {
|
|
70
|
+
throw new Error("cameraMode must be 'following' or 'overview'.");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (options.uiTheme && !VALID_UI_THEMES.has(options.uiTheme)) {
|
|
74
|
+
throw new Error("uiTheme must be one of: system, light, dark, day, night.");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (options.voiceVolume != null) {
|
|
78
|
+
if (!Number.isFinite(options.voiceVolume) || options.voiceVolume < 0 || options.voiceVolume > 1) {
|
|
79
|
+
throw new Error("voiceVolume must be a finite number between 0 and 1.");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (options.cameraPitch != null) {
|
|
84
|
+
if (!Number.isFinite(options.cameraPitch) || options.cameraPitch < 0 || options.cameraPitch > 85) {
|
|
85
|
+
throw new Error("cameraPitch must be a finite number between 0 and 85.");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (options.cameraZoom != null) {
|
|
90
|
+
if (!Number.isFinite(options.cameraZoom) || options.cameraZoom < 1 || options.cameraZoom > 22) {
|
|
91
|
+
throw new Error("cameraZoom must be a finite number between 1 and 22.");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const trimmedLanguage = options.language?.trim();
|
|
96
|
+
if (trimmedLanguage != null && trimmedLanguage.length === 0) {
|
|
97
|
+
throw new Error("language cannot be an empty string.");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
...options,
|
|
102
|
+
language: trimmedLanguage || options.language,
|
|
103
|
+
mapStyleUri: options.mapStyleUri?.trim() || options.mapStyleUri,
|
|
104
|
+
mapStyleUriDay: options.mapStyleUriDay?.trim() || options.mapStyleUriDay,
|
|
105
|
+
mapStyleUriNight: options.mapStyleUriNight?.trim() || options.mapStyleUriNight,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Module API
|
|
110
|
+
export async function startNavigation(
|
|
111
|
+
options: NavigationOptions,
|
|
112
|
+
): Promise<void> {
|
|
113
|
+
const normalizedOptions = normalizeNavigationOptions(options);
|
|
114
|
+
return await MapboxNavigationModule.startNavigation(normalizedOptions);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function stopNavigation(): Promise<void> {
|
|
118
|
+
return await MapboxNavigationModule.stopNavigation();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export async function setMuted(muted: boolean): Promise<void> {
|
|
122
|
+
return await MapboxNavigationModule.setMuted(muted);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export async function setVoiceVolume(volume: number): Promise<void> {
|
|
126
|
+
return await MapboxNavigationModule.setVoiceVolume(volume);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function setDistanceUnit(unit: "metric" | "imperial"): Promise<void> {
|
|
130
|
+
return await MapboxNavigationModule.setDistanceUnit(unit);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export async function setLanguage(language: string): Promise<void> {
|
|
134
|
+
return await MapboxNavigationModule.setLanguage(language);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export async function isNavigating(): Promise<boolean> {
|
|
138
|
+
return await MapboxNavigationModule.isNavigating();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export async function getNavigationSettings(): Promise<NavigationSettings> {
|
|
142
|
+
return await MapboxNavigationModule.getNavigationSettings();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Event listeners
|
|
146
|
+
export function addLocationChangeListener(
|
|
147
|
+
listener: (location: LocationUpdate) => void,
|
|
148
|
+
): Subscription {
|
|
149
|
+
return emitter.addListener("onLocationChange", listener);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function addRouteProgressChangeListener(
|
|
153
|
+
listener: (progress: RouteProgress) => void,
|
|
154
|
+
): Subscription {
|
|
155
|
+
return emitter.addListener("onRouteProgressChange", listener);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function addArriveListener(listener: (point: ArrivalEvent) => void): Subscription {
|
|
159
|
+
return emitter.addListener("onArrive", listener);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function addCancelNavigationListener(listener: () => void): Subscription {
|
|
163
|
+
return emitter.addListener("onCancelNavigation", listener);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function addErrorListener(listener: (error: NavigationError) => void): Subscription {
|
|
167
|
+
return emitter.addListener("onError", listener);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function addBannerInstructionListener(
|
|
171
|
+
listener: (instruction: BannerInstruction) => void,
|
|
172
|
+
): Subscription {
|
|
173
|
+
return emitter.addListener("onBannerInstruction", listener);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// React component wrapper
|
|
177
|
+
export function MapboxNavigationView(
|
|
178
|
+
props: MapboxNavigationViewProps & ViewProps,
|
|
179
|
+
) {
|
|
180
|
+
const NativeView = requireNativeViewManager("MapboxNavigationView");
|
|
181
|
+
return <NativeView {...props} />;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Export types
|
|
185
|
+
export * from "./MapboxNavigation.types";
|
|
186
|
+
|
|
187
|
+
// Default export
|
|
188
|
+
export default {
|
|
189
|
+
startNavigation,
|
|
190
|
+
stopNavigation,
|
|
191
|
+
setMuted,
|
|
192
|
+
setVoiceVolume,
|
|
193
|
+
setDistanceUnit,
|
|
194
|
+
setLanguage,
|
|
195
|
+
isNavigating,
|
|
196
|
+
getNavigationSettings,
|
|
197
|
+
addLocationChangeListener,
|
|
198
|
+
addRouteProgressChangeListener,
|
|
199
|
+
addArriveListener,
|
|
200
|
+
addCancelNavigationListener,
|
|
201
|
+
addErrorListener,
|
|
202
|
+
addBannerInstructionListener,
|
|
203
|
+
MapboxNavigationView,
|
|
204
|
+
};
|