@jacques_gordon/expo-mapbox-navigation 2.0.7 → 2.0.8

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.
@@ -8,6 +8,7 @@ class ExpoMapboxNavigationModule : Module() {
8
8
  Name("ExpoMapboxNavigation")
9
9
 
10
10
  View(ExpoMapboxNavigationView::class) {
11
+ // Events must be declared here — they correspond to EventDispatcher fields in the View
11
12
  Events(
12
13
  "onRouteProgressChanged",
13
14
  "onRoutesReady",
@@ -17,70 +18,42 @@ class ExpoMapboxNavigationModule : Module() {
17
18
  "onArrival"
18
19
  )
19
20
 
20
- // Coordinates: Array of {latitude, longitude} objects
21
21
  Prop("coordinates") { view: ExpoMapboxNavigationView, coordinates: List<Map<String, Double>> ->
22
22
  view.setCoordinates(coordinates)
23
23
  }
24
-
25
- // Waypoint indices
26
24
  Prop("waypointIndices") { view: ExpoMapboxNavigationView, indices: List<Int>? ->
27
25
  view.setWaypointIndices(indices)
28
26
  }
29
-
30
- // Language/locale
31
27
  Prop("language") { view: ExpoMapboxNavigationView, language: String? ->
32
28
  view.setLanguage(language)
33
29
  }
34
-
35
- // ─────────────────────────────────────────────────────────────
36
- // voiceUnits prop — fixes Issue #31 (metric/imperial selection)
37
- // Accepts: "metric" | "imperial" | null (auto from locale)
38
- // ─────────────────────────────────────────────────────────────
39
30
  Prop("voiceUnits") { view: ExpoMapboxNavigationView, units: String? ->
40
31
  view.setVoiceUnits(units)
41
32
  }
42
-
43
- // Navigation profile
44
33
  Prop("navigationProfile") { view: ExpoMapboxNavigationView, profile: String? ->
45
34
  view.setNavigationProfile(profile)
46
35
  }
47
-
48
- // Excluded road types
49
36
  Prop("excludeTypes") { view: ExpoMapboxNavigationView, types: List<String>? ->
50
37
  view.setExcludeTypes(types)
51
38
  }
52
-
53
- // Map style URL
54
39
  Prop("mapStyle") { view: ExpoMapboxNavigationView, style: String? ->
55
40
  view.setMapStyle(style)
56
41
  }
57
-
58
- // Mute navigation audio
59
42
  Prop("mute") { view: ExpoMapboxNavigationView, mute: Boolean ->
60
43
  view.setMute(mute)
61
44
  }
62
-
63
- // Vehicle height restriction (meters)
64
45
  Prop("maxHeight") { view: ExpoMapboxNavigationView, height: Double? ->
65
46
  view.setMaxHeight(height)
66
47
  }
67
-
68
- // Vehicle width restriction (meters)
69
48
  Prop("maxWidth") { view: ExpoMapboxNavigationView, width: Double? ->
70
49
  view.setMaxWidth(width)
71
50
  }
72
-
73
- // Map matching mode
74
51
  Prop("useMapMatching") { view: ExpoMapboxNavigationView, use: Boolean ->
75
52
  view.setUseMapMatching(use)
76
53
  }
77
-
78
- // Custom raster tile URL
79
54
  Prop("customRasterTileUrl") { view: ExpoMapboxNavigationView, url: String? ->
80
55
  view.setCustomRasterTileUrl(url)
81
56
  }
82
-
83
- // Layer ID above which custom raster goes
84
57
  Prop("customRasterAboveLayerId") { view: ExpoMapboxNavigationView, layerId: String? ->
85
58
  view.setCustomRasterAboveLayerId(layerId)
86
59
  }
@@ -7,8 +7,7 @@ import com.mapbox.api.directions.v5.models.RouteOptions
7
7
  import com.mapbox.geojson.Point
8
8
  import com.mapbox.maps.MapView
9
9
  import com.mapbox.maps.Style
10
- import com.mapbox.maps.plugin.animation.camera // ← correct import for .camera extension
11
- import com.mapbox.maps.plugin.locationcomponent.location
10
+ import com.mapbox.maps.plugin.animation.camera
12
11
  import com.mapbox.navigation.base.extensions.applyDefaultNavigationOptions
13
12
  import com.mapbox.navigation.base.options.NavigationOptions
14
13
  import com.mapbox.navigation.base.route.NavigationRoute
@@ -28,6 +27,7 @@ import com.mapbox.navigation.ui.maps.route.line.api.MapboxRouteLineView
28
27
  import com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineApiOptions
29
28
  import com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineViewOptions
30
29
  import expo.modules.kotlin.AppContext
30
+ import expo.modules.kotlin.viewevent.EventDispatcher
31
31
  import expo.modules.kotlin.views.ExpoView
32
32
  import java.util.Locale
33
33
 
@@ -36,6 +36,16 @@ private const val TAG = "ExpoMapboxNavigation"
36
36
  class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
37
37
  ExpoView(context, appContext) {
38
38
 
39
+ // ── EventDispatchers — the correct Expo Modules API for View events ───────
40
+ // Each event declared here must match the Events(...) list in ExpoMapboxNavigationModule
41
+ private val onRouteProgressChanged by EventDispatcher()
42
+ private val onRoutesReady by EventDispatcher()
43
+ private val onNavigationFinished by EventDispatcher()
44
+ private val onNavigationCancelled by EventDispatcher()
45
+ private val onRoutesFailed by EventDispatcher()
46
+ private val onArrival by EventDispatcher()
47
+
48
+ // ── Map & Navigation ──────────────────────────────────────────────────────
39
49
  private val mapView: MapView = MapView(context)
40
50
  private var navigationCamera: NavigationCamera? = null
41
51
  private var viewportDataSource: MapboxNavigationViewportDataSource? = null
@@ -43,7 +53,7 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
43
53
  private var routeLineView: MapboxRouteLineView? = null
44
54
  private var mapboxNavigation: MapboxNavigation? = null
45
55
 
46
- // Props
56
+ // ── Props ─────────────────────────────────────────────────────────────────
47
57
  private var coordinates: List<Map<String, Double>> = emptyList()
48
58
  private var waypointIndices: List<Int>? = null
49
59
  private var language: String? = null
@@ -68,22 +78,17 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
68
78
  val navigationOptions = NavigationOptions.Builder(context).build()
69
79
  mapboxNavigation = MapboxNavigationProvider.create(navigationOptions)
70
80
 
71
- mapView.mapboxMap.loadStyle(mapStyle ?: Style.MAPBOX_STREETS) { style ->
72
- // Route line components
81
+ mapView.mapboxMap.loadStyle(mapStyle ?: Style.MAPBOX_STREETS) { _ ->
73
82
  routeLineApi = MapboxRouteLineApi(MapboxRouteLineApiOptions.Builder().build())
74
83
  routeLineView = MapboxRouteLineView(
75
84
  MapboxRouteLineViewOptions.Builder(context).build()
76
85
  )
77
-
78
- // NavigationCamera — exactly as in official Mapbox example:
79
- // NavigationCamera(mapboxMap, mapView.camera, viewportDataSource)
80
86
  viewportDataSource = MapboxNavigationViewportDataSource(mapView.mapboxMap)
81
87
  navigationCamera = NavigationCamera(
82
88
  mapView.mapboxMap,
83
- mapView.camera, // ← mapView.camera (Maps plugin extension)
89
+ mapView.camera,
84
90
  viewportDataSource!!
85
91
  )
86
-
87
92
  registerObservers()
88
93
  }
89
94
  }
@@ -91,7 +96,6 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
91
96
  private fun registerObservers() {
92
97
  val nav = mapboxNavigation ?: return
93
98
 
94
- // Routes observer
95
99
  nav.registerRoutesObserver(object : RoutesObserver {
96
100
  override fun onRoutesChanged(result: RoutesUpdatedResult) {
97
101
  val routes = result.navigationRoutes
@@ -101,14 +105,11 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
101
105
  routeLineView?.renderRouteDrawData(style, value)
102
106
  }
103
107
  }
104
- // Update viewport data source with the route
105
108
  viewportDataSource?.onRouteChanged(routes.first())
106
109
  viewportDataSource?.evaluate()
107
-
108
- // Issue #43 fix: safe camera following transition
109
110
  safeCameraFollowing()
110
111
 
111
- emitEvent("onRoutesReady", mapOf(
112
+ onRoutesReady(mapOf(
112
113
  "routeCount" to routes.size,
113
114
  "distanceMeters" to (routes.first().directionsRoute.distance() ?: 0.0),
114
115
  "durationSeconds" to (routes.first().directionsRoute.duration() ?: 0.0)
@@ -125,13 +126,11 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
125
126
  }
126
127
  })
127
128
 
128
- // Route progress observer
129
129
  nav.registerRouteProgressObserver(RouteProgressObserver { routeProgress ->
130
- // Update viewport data source with progress
131
130
  viewportDataSource?.onRouteProgressChanged(routeProgress)
132
131
  viewportDataSource?.evaluate()
133
132
 
134
- emitEvent("onRouteProgressChanged", mapOf(
133
+ onRouteProgressChanged(mapOf(
135
134
  "distanceRemaining" to routeProgress.distanceRemaining,
136
135
  "durationRemaining" to routeProgress.durationRemaining,
137
136
  "distanceTraveled" to routeProgress.distanceTraveled,
@@ -143,7 +142,7 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
143
142
  })
144
143
  }
145
144
 
146
- // ── Issue #43: safe camera transition ────────────────────────────────────
145
+ // ── Issue #43: safe camera ────────────────────────────────────────────────
147
146
  private fun safeCameraFollowing() {
148
147
  try {
149
148
  navigationCamera?.requestNavigationCameraToFollowing(
@@ -163,8 +162,7 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
163
162
  "imperial" -> "imperial"
164
163
  else -> {
165
164
  val locale = language?.let { Locale.forLanguageTag(it) } ?: Locale.getDefault()
166
- val imperialCountries = setOf("US", "GB", "LR", "MM")
167
- if (locale.country in imperialCountries) "imperial" else "metric"
165
+ if (locale.country in setOf("US", "GB", "LR", "MM")) "imperial" else "metric"
168
166
  }
169
167
  }
170
168
  }
@@ -183,7 +181,7 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
183
181
  val builder = RouteOptions.builder()
184
182
  .applyDefaultNavigationOptions()
185
183
  .language(locale.toLanguageTag())
186
- .voiceUnits(resolveVoiceUnits()) // Issue #31 fix
184
+ .voiceUnits(resolveVoiceUnits())
187
185
  .coordinatesList(points)
188
186
 
189
187
  waypointIndices?.let { builder.waypointIndicesList(it) }
@@ -196,7 +194,6 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
196
194
  maxHeight?.let { builder.maxHeight(it) }
197
195
  maxWidth?.let { builder.maxWidth(it) }
198
196
 
199
- // Exact Nav SDK v3 callback signature from official Mapbox docs
200
197
  nav.requestRoutes(
201
198
  builder.build(),
202
199
  object : NavigationRouterCallback {
@@ -205,7 +202,7 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
205
202
  @RouterOrigin routerOrigin: String
206
203
  ) {
207
204
  if (routes.isEmpty()) {
208
- emitEvent("onRoutesFailed", mapOf("message" to "No routes returned"))
205
+ onRoutesFailed(mapOf("message" to "No routes returned"))
209
206
  return
210
207
  }
211
208
  nav.setNavigationRoutes(routes)
@@ -218,7 +215,7 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
218
215
  ) {
219
216
  val msg = reasons.firstOrNull()?.message ?: "Unknown error"
220
217
  Log.e(TAG, "Route request failed: $msg")
221
- emitEvent("onRoutesFailed", mapOf("message" to msg))
218
+ onRoutesFailed(mapOf("message" to msg))
222
219
  }
223
220
 
224
221
  override fun onCanceled(
@@ -231,17 +228,6 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
231
228
  )
232
229
  }
233
230
 
234
- // ── Expo event emitter (SDK 53 compatible) ────────────────────────────────
235
- private fun emitEvent(name: String, data: Map<String, Any>) {
236
- try {
237
- // Expo modules core SDK 53: use appContext.eventEmitter
238
- val emitter = appContext.eventEmitter
239
- emitter?.emit(name, data)
240
- } catch (e: Exception) {
241
- Log.e(TAG, "Failed to emit event $name: ${e.message}")
242
- }
243
- }
244
-
245
231
  // ── Prop setters ──────────────────────────────────────────────────────────
246
232
  fun setCoordinates(coords: List<Map<String, Double>>) {
247
233
  coordinates = coords
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jacques_gordon/expo-mapbox-navigation",
3
- "version": "2.0.7",
3
+ "version": "2.0.8",
4
4
  "description": "Expo module for Mapbox Navigation SDK with 16KB page size support, NDK27, and Mapbox Maps v11.11.0+",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",