@doorstepai/dropoff-sdk 2.0.7 → 2.0.9
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/android/build.gradle +1 -1
- package/android/src/main/java/com/doorstepai/dropoffsdk/DoorstepAIModule.kt +168 -7
- package/ios/DoorstepAI.mm +25 -1
- package/ios/DoorstepAI.swift +147 -3
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64/DoorstepDropoffSDK.framework/DoorstepDropoffSDK +0 -0
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64/DoorstepDropoffSDK.framework/Info.plist +0 -0
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/arm64-apple-ios.abi.json +5436 -1479
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +93 -9
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/arm64-apple-ios.swiftinterface +93 -9
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64/DoorstepDropoffSDK.framework/_CodeSignature/CodeResources +11 -11
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64/dSYMs/DoorstepDropoffSDK.framework.dSYM/Contents/Resources/DWARF/DoorstepDropoffSDK +0 -0
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64/dSYMs/DoorstepDropoffSDK.framework.dSYM/Contents/Resources/Relocations/aarch64/DoorstepDropoffSDK.yml +2560 -2106
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/DoorstepDropoffSDK.framework/DoorstepDropoffSDK +0 -0
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/DoorstepDropoffSDK.framework/Info.plist +0 -0
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +5436 -1479
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +93 -9
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +93 -9
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +5436 -1479
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +93 -9
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +93 -9
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/DoorstepDropoffSDK.framework/_CodeSignature/CodeResources +21 -21
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/DoorstepDropoffSDK.framework.dSYM/Contents/Resources/DWARF/DoorstepDropoffSDK +0 -0
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/DoorstepDropoffSDK.framework.dSYM/Contents/Resources/Relocations/aarch64/DoorstepDropoffSDK.yml +2560 -2106
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/DoorstepDropoffSDK.framework.dSYM/Contents/Resources/Relocations/x86_64/DoorstepDropoffSDK.yml +2551 -2096
- package/lib/module/DoorstepAI.js +107 -12
- package/lib/module/DoorstepAI.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/DoorstepAI.d.ts +75 -2
- package/lib/typescript/src/DoorstepAI.d.ts.map +1 -1
- package/lib/typescript/src/RootDoorstepAI.d.ts +1 -1
- package/lib/typescript/src/RootDoorstepAI.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/DoorstepAI.tsx +199 -18
- package/src/index.tsx +4 -0
package/android/build.gradle
CHANGED
|
@@ -86,7 +86,7 @@ def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
|
86
86
|
dependencies {
|
|
87
87
|
implementation "com.facebook.react:react-android"
|
|
88
88
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
89
|
-
implementation("ai.doorstep.com:doorstepai-dropoff-sdk:2.0.
|
|
89
|
+
implementation("ai.doorstep.com:doorstepai-dropoff-sdk:2.0.13")
|
|
90
90
|
api "org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1"
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -11,9 +11,19 @@ import com.doorstepai.sdks.tracking.AddressType
|
|
|
11
11
|
import com.doorstepai.sdks.tracking.DropoffType
|
|
12
12
|
import com.doorstepai.sdks.tracking.LatLngObject
|
|
13
13
|
import com.facebook.react.bridge.ReadableMap
|
|
14
|
+
import com.facebook.react.bridge.ReadableArray
|
|
15
|
+
import com.facebook.react.bridge.WritableMap
|
|
16
|
+
import com.facebook.react.bridge.Arguments
|
|
17
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
18
|
+
import com.doorstepai.sdks.tracking.DeliveryStop
|
|
19
|
+
import com.doorstepai.sdks.tracking.RouteGeofenceOptions
|
|
14
20
|
import kotlinx.coroutines.Dispatchers
|
|
15
21
|
import kotlinx.coroutines.CoroutineScope
|
|
22
|
+
import kotlinx.coroutines.SupervisorJob
|
|
23
|
+
import kotlinx.coroutines.Job
|
|
16
24
|
import kotlinx.coroutines.launch
|
|
25
|
+
import kotlinx.coroutines.flow.launchIn
|
|
26
|
+
import kotlinx.coroutines.flow.onEach
|
|
17
27
|
|
|
18
28
|
|
|
19
29
|
class DoorstepAIModule(private val reactContext: ReactApplicationContext) :
|
|
@@ -45,6 +55,8 @@ class DoorstepAIModule(private val reactContext: ReactApplicationContext) :
|
|
|
45
55
|
timeoutSeconds: Double?,
|
|
46
56
|
autoStopAfterDropoffSeconds: Double?,
|
|
47
57
|
manualForeground: Boolean?,
|
|
58
|
+
customerId: String?,
|
|
59
|
+
driverId: String?,
|
|
48
60
|
promise: Promise
|
|
49
61
|
) {
|
|
50
62
|
try {
|
|
@@ -53,7 +65,9 @@ class DoorstepAIModule(private val reactContext: ReactApplicationContext) :
|
|
|
53
65
|
deliveryId = deliveryId,
|
|
54
66
|
timeoutSeconds = timeoutSeconds,
|
|
55
67
|
manualForeground = manualForeground ?: false,
|
|
56
|
-
autoStopAfterDropoffSeconds = autoStopAfterDropoffSeconds
|
|
68
|
+
autoStopAfterDropoffSeconds = autoStopAfterDropoffSeconds,
|
|
69
|
+
customerId = customerId,
|
|
70
|
+
driverId = driverId
|
|
57
71
|
) { result ->
|
|
58
72
|
result.onSuccess { sessionId ->
|
|
59
73
|
promise.resolve(sessionId)
|
|
@@ -71,10 +85,12 @@ class DoorstepAIModule(private val reactContext: ReactApplicationContext) :
|
|
|
71
85
|
plusCode: String,
|
|
72
86
|
deliveryId: String,
|
|
73
87
|
timeoutSeconds: Double?,
|
|
88
|
+
customerId: String?,
|
|
89
|
+
driverId: String?,
|
|
74
90
|
promise: Promise
|
|
75
91
|
) {
|
|
76
92
|
try {
|
|
77
|
-
DoorstepAI.startDeliveryByPlusCode(plusCode, deliveryId, timeoutSeconds) { result ->
|
|
93
|
+
DoorstepAI.startDeliveryByPlusCode(plusCode, deliveryId, timeoutSeconds, customerId, driverId) { result ->
|
|
78
94
|
result.onSuccess { sessionId ->
|
|
79
95
|
promise.resolve(sessionId)
|
|
80
96
|
}.onFailure { error ->
|
|
@@ -94,6 +110,8 @@ class DoorstepAIModule(private val reactContext: ReactApplicationContext) :
|
|
|
94
110
|
timeoutSeconds: Double?,
|
|
95
111
|
autoStopAfterDropoffSeconds: Double?,
|
|
96
112
|
manualForeground: Boolean?,
|
|
113
|
+
customerId: String?,
|
|
114
|
+
driverId: String?,
|
|
97
115
|
promise: Promise
|
|
98
116
|
) {
|
|
99
117
|
try {
|
|
@@ -104,7 +122,9 @@ class DoorstepAIModule(private val reactContext: ReactApplicationContext) :
|
|
|
104
122
|
coordinates = coords,
|
|
105
123
|
timeoutSeconds = timeoutSeconds,
|
|
106
124
|
manualForeground = manualForeground ?: false,
|
|
107
|
-
autoStopAfterDropoffSeconds = autoStopAfterDropoffSeconds
|
|
125
|
+
autoStopAfterDropoffSeconds = autoStopAfterDropoffSeconds,
|
|
126
|
+
customerId = customerId,
|
|
127
|
+
driverId = driverId
|
|
108
128
|
) { result ->
|
|
109
129
|
result.onSuccess { message ->
|
|
110
130
|
promise.resolve(message)
|
|
@@ -124,10 +144,12 @@ class DoorstepAIModule(private val reactContext: ReactApplicationContext) :
|
|
|
124
144
|
subUnit: String,
|
|
125
145
|
deliveryId: String,
|
|
126
146
|
timeoutSeconds: Double?,
|
|
147
|
+
customerId: String?,
|
|
148
|
+
driverId: String?,
|
|
127
149
|
promise: Promise
|
|
128
150
|
) {
|
|
129
151
|
try {
|
|
130
|
-
DoorstepAI.startDeliveryByLatLng(latitude, longitude, subUnit, deliveryId, timeoutSeconds) { result ->
|
|
152
|
+
DoorstepAI.startDeliveryByLatLng(latitude, longitude, subUnit, deliveryId, timeoutSeconds, customerId, driverId) { result ->
|
|
131
153
|
result.onSuccess { message ->
|
|
132
154
|
promise.resolve(message)
|
|
133
155
|
}.onFailure { error ->
|
|
@@ -147,6 +169,8 @@ class DoorstepAIModule(private val reactContext: ReactApplicationContext) :
|
|
|
147
169
|
timeoutSeconds: Double?,
|
|
148
170
|
autoStopAfterDropoffSeconds: Double?,
|
|
149
171
|
manualForeground: Boolean?,
|
|
172
|
+
customerId: String?,
|
|
173
|
+
driverId: String?,
|
|
150
174
|
promise: Promise
|
|
151
175
|
) {
|
|
152
176
|
try {
|
|
@@ -173,7 +197,9 @@ class DoorstepAIModule(private val reactContext: ReactApplicationContext) :
|
|
|
173
197
|
coordinates = coords,
|
|
174
198
|
timeoutSeconds = timeoutSeconds,
|
|
175
199
|
manualForeground = manualForeground ?: false,
|
|
176
|
-
autoStopAfterDropoffSeconds = autoStopAfterDropoffSeconds
|
|
200
|
+
autoStopAfterDropoffSeconds = autoStopAfterDropoffSeconds,
|
|
201
|
+
customerId = customerId,
|
|
202
|
+
driverId = driverId
|
|
177
203
|
) { result ->
|
|
178
204
|
result.onSuccess { sessionId ->
|
|
179
205
|
promise.resolve(sessionId)
|
|
@@ -187,9 +213,9 @@ class DoorstepAIModule(private val reactContext: ReactApplicationContext) :
|
|
|
187
213
|
}
|
|
188
214
|
|
|
189
215
|
@ReactMethod
|
|
190
|
-
fun newEvent(eventName: String, deliveryId: String, promise: Promise) {
|
|
216
|
+
fun newEvent(eventName: String, deliveryId: String, timestamp: Double?, promise: Promise) {
|
|
191
217
|
try {
|
|
192
|
-
DoorstepAI.newEvent(eventName, deliveryId) { /* Callback
|
|
218
|
+
DoorstepAI.newEvent(eventName, deliveryId, timestamp) { /* Callback not needed here */ }
|
|
193
219
|
promise.resolve("Event $eventName triggered for $deliveryId")
|
|
194
220
|
} catch (e: Exception) {
|
|
195
221
|
promise.reject("EVENT_CREATION_ERROR", e.message ?: "Failed to save event")
|
|
@@ -273,7 +299,142 @@ class DoorstepAIModule(private val reactContext: ReactApplicationContext) :
|
|
|
273
299
|
return LatLngObject(lat = lat, lng = lng)
|
|
274
300
|
}
|
|
275
301
|
|
|
302
|
+
// ---- Route geofencing ----
|
|
303
|
+
|
|
304
|
+
private val moduleScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
|
305
|
+
private var geofenceJob: Job? = null
|
|
306
|
+
|
|
307
|
+
@ReactMethod
|
|
308
|
+
fun startRouteGeofencing(stops: ReadableArray, options: ReadableMap?, promise: Promise) {
|
|
309
|
+
try {
|
|
310
|
+
val parsed = parseStops(stops)
|
|
311
|
+
if (parsed.isEmpty()) {
|
|
312
|
+
promise.reject(
|
|
313
|
+
"ROUTE_GEOFENCING_ERROR",
|
|
314
|
+
"startRouteGeofencing requires at least one valid stop (deliveryId + latitude + longitude)"
|
|
315
|
+
)
|
|
316
|
+
return
|
|
317
|
+
}
|
|
318
|
+
DoorstepAI.startRouteGeofencing(parsed, parseRouteOptions(options)) { result ->
|
|
319
|
+
result.onSuccess { promise.resolve(true) }
|
|
320
|
+
.onFailure { promise.reject("ROUTE_GEOFENCING_ERROR", it.message ?: "Failed to start route geofencing") }
|
|
321
|
+
}
|
|
322
|
+
} catch (e: Exception) {
|
|
323
|
+
promise.reject("ROUTE_GEOFENCING_ERROR", e.message ?: "Failed to start route geofencing")
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
@ReactMethod
|
|
328
|
+
fun stopRouteGeofencing(promise: Promise) {
|
|
329
|
+
try {
|
|
330
|
+
DoorstepAI.stopRouteGeofencing()
|
|
331
|
+
promise.resolve(true)
|
|
332
|
+
} catch (e: Exception) {
|
|
333
|
+
promise.reject("ROUTE_GEOFENCING_ERROR", e.message ?: "Failed to stop route geofencing")
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
@ReactMethod
|
|
338
|
+
fun resumeRouteGeofencingIfNeeded(promise: Promise) {
|
|
339
|
+
try {
|
|
340
|
+
DoorstepAI.resumeRouteGeofencingIfNeeded()
|
|
341
|
+
promise.resolve(true)
|
|
342
|
+
} catch (e: Exception) {
|
|
343
|
+
promise.reject("ROUTE_GEOFENCING_ERROR", e.message ?: "Failed to resume route geofencing")
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
@ReactMethod
|
|
348
|
+
fun getMonitoredStops(promise: Promise) {
|
|
349
|
+
try {
|
|
350
|
+
val arr = Arguments.createArray()
|
|
351
|
+
DoorstepAI.monitoredStops.forEach { arr.pushMap(serializeStop(it)) }
|
|
352
|
+
promise.resolve(arr)
|
|
353
|
+
} catch (e: Exception) {
|
|
354
|
+
promise.reject("ROUTE_GEOFENCING_ERROR", e.message ?: "Failed to get monitored stops")
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// ---- Geofence-session event stream (NativeEventEmitter) ----
|
|
359
|
+
|
|
360
|
+
@ReactMethod
|
|
361
|
+
fun addListener(eventName: String) {
|
|
362
|
+
// On the first JS subscription, bridge the SDK's geofenceSessionEvents Flow
|
|
363
|
+
// to JS. A single collector serves the module lifetime (cancelled in invalidate()).
|
|
364
|
+
if (geofenceJob == null) {
|
|
365
|
+
geofenceJob = DoorstepAI.geofenceSessionEvents
|
|
366
|
+
.onEach { ev ->
|
|
367
|
+
val map = Arguments.createMap().apply {
|
|
368
|
+
putString("deliveryId", ev.deliveryId)
|
|
369
|
+
putString("type", ev.type.name) // "STARTED" | "STOPPED"
|
|
370
|
+
putString("reason", ev.reason)
|
|
371
|
+
}
|
|
372
|
+
reactContext
|
|
373
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
374
|
+
.emit(GEOFENCE_EVENT, map)
|
|
375
|
+
}
|
|
376
|
+
.launchIn(moduleScope)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
@ReactMethod
|
|
381
|
+
fun removeListeners(count: Int) {
|
|
382
|
+
// Required stub for NativeEventEmitter on Android. The single Flow collector
|
|
383
|
+
// is torn down in invalidate(); no per-listener teardown is needed.
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
override fun invalidate() {
|
|
387
|
+
geofenceJob?.cancel()
|
|
388
|
+
geofenceJob = null
|
|
389
|
+
super.invalidate()
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private fun parseStops(arr: ReadableArray): List<DeliveryStop> {
|
|
393
|
+
val out = mutableListOf<DeliveryStop>()
|
|
394
|
+
for (i in 0 until arr.size()) {
|
|
395
|
+
val m = arr.getMap(i) ?: continue
|
|
396
|
+
val deliveryId = if (m.hasKey("deliveryId")) m.getString("deliveryId") else null
|
|
397
|
+
if (deliveryId.isNullOrEmpty()) continue
|
|
398
|
+
if (!m.hasKey("latitude") || !m.hasKey("longitude")) continue
|
|
399
|
+
out.add(
|
|
400
|
+
DeliveryStop(
|
|
401
|
+
deliveryId = deliveryId,
|
|
402
|
+
address = if (m.hasKey("address")) m.getString("address") ?: "" else "",
|
|
403
|
+
latitude = m.getDouble("latitude"),
|
|
404
|
+
longitude = m.getDouble("longitude"),
|
|
405
|
+
radiusMeters = if (m.hasKey("radiusMeters") && !m.isNull("radiusMeters")) m.getDouble("radiusMeters") else null,
|
|
406
|
+
customerId = if (m.hasKey("customerId")) m.getString("customerId") else null,
|
|
407
|
+
driverId = if (m.hasKey("driverId")) m.getString("driverId") else null
|
|
408
|
+
)
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
return out
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
private fun parseRouteOptions(m: ReadableMap?): RouteGeofenceOptions {
|
|
415
|
+
if (m == null) return RouteGeofenceOptions()
|
|
416
|
+
return RouteGeofenceOptions(
|
|
417
|
+
defaultRadiusMeters = if (m.hasKey("defaultRadiusMeters") && !m.isNull("defaultRadiusMeters")) m.getDouble("defaultRadiusMeters") else 250.0,
|
|
418
|
+
timeoutSeconds = if (m.hasKey("timeoutSeconds") && !m.isNull("timeoutSeconds")) m.getDouble("timeoutSeconds") else null,
|
|
419
|
+
autoStopAfterDropoffSeconds = if (m.hasKey("autoStopAfterDropoffSeconds") && !m.isNull("autoStopAfterDropoffSeconds")) m.getDouble("autoStopAfterDropoffSeconds") else null,
|
|
420
|
+
manualForeground = if (m.hasKey("manualForeground") && !m.isNull("manualForeground")) m.getBoolean("manualForeground") else false
|
|
421
|
+
)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
private fun serializeStop(s: DeliveryStop): WritableMap {
|
|
425
|
+
val m = Arguments.createMap()
|
|
426
|
+
m.putString("deliveryId", s.deliveryId)
|
|
427
|
+
m.putString("address", s.address)
|
|
428
|
+
m.putDouble("latitude", s.latitude)
|
|
429
|
+
m.putDouble("longitude", s.longitude)
|
|
430
|
+
s.radiusMeters?.let { m.putDouble("radiusMeters", it) }
|
|
431
|
+
s.customerId?.let { m.putString("customerId", it) }
|
|
432
|
+
s.driverId?.let { m.putString("driverId", it) }
|
|
433
|
+
return m
|
|
434
|
+
}
|
|
435
|
+
|
|
276
436
|
companion object {
|
|
277
437
|
const val NAME = "DoorstepAI"
|
|
438
|
+
private const val GEOFENCE_EVENT = "DoorstepGeofenceSessionEvent"
|
|
278
439
|
}
|
|
279
440
|
}
|
package/ios/DoorstepAI.mm
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#import <React/RCTBridgeModule.h>
|
|
2
|
+
#import <React/RCTEventEmitter.h>
|
|
2
3
|
|
|
3
|
-
@interface RCT_EXTERN_MODULE(DoorstepAI,
|
|
4
|
+
@interface RCT_EXTERN_MODULE(DoorstepAI, RCTEventEmitter)
|
|
4
5
|
|
|
5
6
|
RCT_EXTERN_METHOD(setApiKey:(nonnull NSString *)apiKey)
|
|
6
7
|
RCT_EXTERN_METHOD(requestAllPermissions:(BOOL)requestAlwaysLocation)
|
|
@@ -16,12 +17,16 @@ RCT_EXTERN_METHOD(startDeliveryByPlaceID:(nonnull NSString *)placeID
|
|
|
16
17
|
deliveryId:(nonnull NSString *)deliveryId
|
|
17
18
|
timeoutSeconds:(id)timeoutSeconds
|
|
18
19
|
autoStopAfterDropoffSeconds:(id)autoStopAfterDropoffSeconds
|
|
20
|
+
customerId:(nullable NSString *)customerId
|
|
21
|
+
driverId:(nullable NSString *)driverId
|
|
19
22
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
20
23
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
21
24
|
RCT_EXTERN_METHOD(startDeliveryByPlusCode:(nonnull NSString *)plusCode
|
|
22
25
|
deliveryId:(nonnull NSString *)deliveryId
|
|
23
26
|
timeoutSeconds:(id)timeoutSeconds
|
|
24
27
|
autoStopAfterDropoffSeconds:(id)autoStopAfterDropoffSeconds
|
|
28
|
+
customerId:(nullable NSString *)customerId
|
|
29
|
+
driverId:(nullable NSString *)driverId
|
|
25
30
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
26
31
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
27
32
|
RCT_EXTERN_METHOD(startDeliveryByAddress:(nonnull NSDictionary *)address
|
|
@@ -29,6 +34,8 @@ RCT_EXTERN_METHOD(startDeliveryByAddress:(nonnull NSDictionary *)address
|
|
|
29
34
|
coordinates:(nullable NSDictionary *)coordinates
|
|
30
35
|
timeoutSeconds:(id)timeoutSeconds
|
|
31
36
|
autoStopAfterDropoffSeconds:(id)autoStopAfterDropoffSeconds
|
|
37
|
+
customerId:(nullable NSString *)customerId
|
|
38
|
+
driverId:(nullable NSString *)driverId
|
|
32
39
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
33
40
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
34
41
|
RCT_EXTERN_METHOD(startDeliveryByAddressString:(nonnull NSString *)address
|
|
@@ -36,6 +43,8 @@ RCT_EXTERN_METHOD(startDeliveryByAddressString:(nonnull NSString *)address
|
|
|
36
43
|
coordinates:(nullable NSDictionary *)coordinates
|
|
37
44
|
timeoutSeconds:(id)timeoutSeconds
|
|
38
45
|
autoStopAfterDropoffSeconds:(id)autoStopAfterDropoffSeconds
|
|
46
|
+
customerId:(nullable NSString *)customerId
|
|
47
|
+
driverId:(nullable NSString *)driverId
|
|
39
48
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
40
49
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
41
50
|
RCT_EXTERN_METHOD(startDeliveryByLatLng:(nonnull NSNumber *)latitude
|
|
@@ -44,10 +53,13 @@ RCT_EXTERN_METHOD(startDeliveryByLatLng:(nonnull NSNumber *)latitude
|
|
|
44
53
|
deliveryId:(nonnull NSString *)deliveryId
|
|
45
54
|
timeoutSeconds:(id)timeoutSeconds
|
|
46
55
|
autoStopAfterDropoffSeconds:(id)autoStopAfterDropoffSeconds
|
|
56
|
+
customerId:(nullable NSString *)customerId
|
|
57
|
+
driverId:(nullable NSString *)driverId
|
|
47
58
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
48
59
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
49
60
|
RCT_EXTERN_METHOD(newEvent:(nonnull NSString *)eventName
|
|
50
61
|
deliveryId:(nonnull NSString *)deliveryId
|
|
62
|
+
timestamp:(id)timestamp
|
|
51
63
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
52
64
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
53
65
|
RCT_EXTERN_METHOD(markDropoff:(nonnull NSString *)deliveryId
|
|
@@ -58,4 +70,16 @@ RCT_EXTERN_METHOD(stopDelivery:(nonnull NSString *)deliveryId
|
|
|
58
70
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
59
71
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
60
72
|
|
|
73
|
+
// ---- Route geofencing ----
|
|
74
|
+
RCT_EXTERN_METHOD(startRouteGeofencing:(nonnull NSArray *)stops
|
|
75
|
+
options:(nullable NSDictionary *)options
|
|
76
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
77
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
78
|
+
RCT_EXTERN_METHOD(stopRouteGeofencing:(RCTPromiseResolveBlock)resolve
|
|
79
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
80
|
+
RCT_EXTERN_METHOD(resumeRouteGeofencingIfNeeded:(RCTPromiseResolveBlock)resolve
|
|
81
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
82
|
+
RCT_EXTERN_METHOD(getMonitoredStops:(RCTPromiseResolveBlock)resolve
|
|
83
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
84
|
+
|
|
61
85
|
@end
|
package/ios/DoorstepAI.swift
CHANGED
|
@@ -3,12 +3,48 @@ import React
|
|
|
3
3
|
import DoorstepDropoffSDK
|
|
4
4
|
|
|
5
5
|
@objc(DoorstepAI)
|
|
6
|
-
class DoorstepAIBridge:
|
|
6
|
+
class DoorstepAIBridge: RCTEventEmitter {
|
|
7
7
|
@objc
|
|
8
|
-
static func requiresMainQueueSetup() -> Bool {
|
|
8
|
+
override static func requiresMainQueueSetup() -> Bool {
|
|
9
9
|
return true
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
// MARK: - Geofence session events (RCTEventEmitter)
|
|
13
|
+
|
|
14
|
+
private static let geofenceEventName = "DoorstepGeofenceSessionEvent"
|
|
15
|
+
private var geofenceObservers: [NSObjectProtocol] = []
|
|
16
|
+
|
|
17
|
+
override func supportedEvents() -> [String]! {
|
|
18
|
+
return [DoorstepAIBridge.geofenceEventName]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// Called by RN when the first JS listener subscribes: bridge the SDK's
|
|
22
|
+
/// NotificationCenter geofence notifications to `sendEvent`.
|
|
23
|
+
override func startObserving() {
|
|
24
|
+
let center = NotificationCenter.default
|
|
25
|
+
let started = center.addObserver(
|
|
26
|
+
forName: DoorstepAI.geofenceSessionStartedNotification, object: nil, queue: .main
|
|
27
|
+
) { [weak self] note in self?.emitGeofenceEvent(type: "STARTED", userInfo: note.userInfo) }
|
|
28
|
+
let stopped = center.addObserver(
|
|
29
|
+
forName: DoorstepAI.geofenceSessionStoppedNotification, object: nil, queue: .main
|
|
30
|
+
) { [weak self] note in self?.emitGeofenceEvent(type: "STOPPED", userInfo: note.userInfo) }
|
|
31
|
+
geofenceObservers = [started, stopped]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// Called by RN when the last JS listener unsubscribes.
|
|
35
|
+
override func stopObserving() {
|
|
36
|
+
let center = NotificationCenter.default
|
|
37
|
+
geofenceObservers.forEach { center.removeObserver($0) }
|
|
38
|
+
geofenceObservers.removeAll()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private func emitGeofenceEvent(type: String, userInfo: [AnyHashable: Any]?) {
|
|
42
|
+
let deliveryId = (userInfo?["deliveryId"] as? String) ?? ""
|
|
43
|
+
let reason = (userInfo?["reason"] as? String) ?? ""
|
|
44
|
+
sendEvent(withName: DoorstepAIBridge.geofenceEventName,
|
|
45
|
+
body: ["deliveryId": deliveryId, "type": type, "reason": reason])
|
|
46
|
+
}
|
|
47
|
+
|
|
12
48
|
@objc
|
|
13
49
|
func enableDevMode(_ apiKey: NSString,
|
|
14
50
|
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
@@ -53,6 +89,8 @@ class DoorstepAIBridge: NSObject {
|
|
|
53
89
|
deliveryId: NSString,
|
|
54
90
|
timeoutSeconds: Any?,
|
|
55
91
|
autoStopAfterDropoffSeconds: Any?,
|
|
92
|
+
customerId: NSString?,
|
|
93
|
+
driverId: NSString?,
|
|
56
94
|
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
57
95
|
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
58
96
|
// Use Swift's concurrency support to call the async method.
|
|
@@ -63,6 +101,8 @@ class DoorstepAIBridge: NSObject {
|
|
|
63
101
|
try await DoorstepAI.startDeliveryByPlaceID(
|
|
64
102
|
placeID: placeID as String,
|
|
65
103
|
deliveryId: deliveryId as String,
|
|
104
|
+
customerId: customerId as String?,
|
|
105
|
+
driverId: driverId as String?,
|
|
66
106
|
timeoutSeconds: timeout,
|
|
67
107
|
autoStopAfterDropoffSeconds: autoStop
|
|
68
108
|
)
|
|
@@ -78,6 +118,8 @@ class DoorstepAIBridge: NSObject {
|
|
|
78
118
|
deliveryId: NSString,
|
|
79
119
|
timeoutSeconds: Any?,
|
|
80
120
|
autoStopAfterDropoffSeconds: Any?,
|
|
121
|
+
customerId: NSString?,
|
|
122
|
+
driverId: NSString?,
|
|
81
123
|
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
82
124
|
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
83
125
|
Task {
|
|
@@ -87,6 +129,8 @@ class DoorstepAIBridge: NSObject {
|
|
|
87
129
|
try await DoorstepAI.startDeliveryByPlusCode(
|
|
88
130
|
plusCode: plusCode as String,
|
|
89
131
|
deliveryId: deliveryId as String,
|
|
132
|
+
customerId: customerId as String?,
|
|
133
|
+
driverId: driverId as String?,
|
|
90
134
|
timeoutSeconds: timeout,
|
|
91
135
|
autoStopAfterDropoffSeconds: autoStop
|
|
92
136
|
)
|
|
@@ -103,6 +147,8 @@ class DoorstepAIBridge: NSObject {
|
|
|
103
147
|
coordinates: NSDictionary?,
|
|
104
148
|
timeoutSeconds: Any?,
|
|
105
149
|
autoStopAfterDropoffSeconds: Any?,
|
|
150
|
+
customerId: NSString?,
|
|
151
|
+
driverId: NSString?,
|
|
106
152
|
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
107
153
|
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
108
154
|
Task {
|
|
@@ -113,6 +159,8 @@ class DoorstepAIBridge: NSObject {
|
|
|
113
159
|
try await DoorstepAI.startDeliveryByAddressString(
|
|
114
160
|
address: address as String,
|
|
115
161
|
deliveryId: deliveryId as String,
|
|
162
|
+
customerId: customerId as String?,
|
|
163
|
+
driverId: driverId as String?,
|
|
116
164
|
coordinates: coords,
|
|
117
165
|
timeoutSeconds: timeout,
|
|
118
166
|
autoStopAfterDropoffSeconds: autoStop
|
|
@@ -131,6 +179,8 @@ class DoorstepAIBridge: NSObject {
|
|
|
131
179
|
deliveryId: NSString,
|
|
132
180
|
timeoutSeconds: Any?,
|
|
133
181
|
autoStopAfterDropoffSeconds: Any?,
|
|
182
|
+
customerId: NSString?,
|
|
183
|
+
driverId: NSString?,
|
|
134
184
|
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
135
185
|
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
136
186
|
Task {
|
|
@@ -142,6 +192,8 @@ class DoorstepAIBridge: NSObject {
|
|
|
142
192
|
longitude: longitude.doubleValue,
|
|
143
193
|
subUnit: subUnit as String,
|
|
144
194
|
deliveryId: deliveryId as String,
|
|
195
|
+
customerId: customerId as String?,
|
|
196
|
+
driverId: driverId as String?,
|
|
145
197
|
timeoutSeconds: timeout,
|
|
146
198
|
autoStopAfterDropoffSeconds: autoStop
|
|
147
199
|
)
|
|
@@ -158,6 +210,8 @@ class DoorstepAIBridge: NSObject {
|
|
|
158
210
|
coordinates: NSDictionary?,
|
|
159
211
|
timeoutSeconds: Any?,
|
|
160
212
|
autoStopAfterDropoffSeconds: Any?,
|
|
213
|
+
customerId: NSString?,
|
|
214
|
+
driverId: NSString?,
|
|
161
215
|
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
162
216
|
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
163
217
|
Task {
|
|
@@ -185,6 +239,8 @@ class DoorstepAIBridge: NSObject {
|
|
|
185
239
|
try await DoorstepAI.startDeliveryByAddressType(
|
|
186
240
|
address: addressStruct,
|
|
187
241
|
deliveryId: deliveryId as String,
|
|
242
|
+
customerId: customerId as String?,
|
|
243
|
+
driverId: driverId as String?,
|
|
188
244
|
coordinates: coords,
|
|
189
245
|
timeoutSeconds: timeout,
|
|
190
246
|
autoStopAfterDropoffSeconds: autoStop
|
|
@@ -201,11 +257,13 @@ class DoorstepAIBridge: NSObject {
|
|
|
201
257
|
@objc
|
|
202
258
|
func newEvent(_ eventName: NSString,
|
|
203
259
|
deliveryId: NSString,
|
|
260
|
+
timestamp: Any?,
|
|
204
261
|
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
205
262
|
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
206
263
|
Task {
|
|
207
264
|
do {
|
|
208
|
-
|
|
265
|
+
let ts = (timestamp as? NSNumber)?.doubleValue
|
|
266
|
+
try await DoorstepAI.newEvent(eventName: eventName as String, deliveryId: deliveryId as String, timestamp: ts)
|
|
209
267
|
resolve(nil)
|
|
210
268
|
} catch {
|
|
211
269
|
reject("E_NEW_EVENT", "Failed to send event: \(error.localizedDescription)", error)
|
|
@@ -250,6 +308,92 @@ class DoorstepAIBridge: NSObject {
|
|
|
250
308
|
}
|
|
251
309
|
}
|
|
252
310
|
|
|
311
|
+
// MARK: - Route geofencing
|
|
312
|
+
|
|
313
|
+
@objc
|
|
314
|
+
func startRouteGeofencing(_ stops: NSArray,
|
|
315
|
+
options: NSDictionary?,
|
|
316
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
317
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
318
|
+
let parsed: [DeliveryStop] = (stops as? [[String: Any]] ?? []).compactMap { parseDeliveryStop($0) }
|
|
319
|
+
if parsed.isEmpty {
|
|
320
|
+
reject("E_ROUTE_GEOFENCING", "startRouteGeofencing requires at least one valid stop (deliveryId + latitude + longitude)", nil)
|
|
321
|
+
return
|
|
322
|
+
}
|
|
323
|
+
let opts = parseRouteOptions(options)
|
|
324
|
+
DispatchQueue.main.async {
|
|
325
|
+
DoorstepAI.startRouteGeofencing(stops: parsed, options: opts)
|
|
326
|
+
resolve(nil)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
@objc
|
|
331
|
+
func stopRouteGeofencing(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
332
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
333
|
+
DispatchQueue.main.async {
|
|
334
|
+
DoorstepAI.stopRouteGeofencing()
|
|
335
|
+
resolve(nil)
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
@objc
|
|
340
|
+
func resumeRouteGeofencingIfNeeded(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
341
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
342
|
+
DispatchQueue.main.async {
|
|
343
|
+
DoorstepAI.resumeRouteGeofencingIfNeeded()
|
|
344
|
+
resolve(nil)
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
@objc
|
|
349
|
+
func getMonitoredStops(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
350
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
351
|
+
DispatchQueue.main.async {
|
|
352
|
+
resolve(DoorstepAI.monitoredStops.map { self.serializeStop($0) })
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
private func parseDeliveryStop(_ raw: [String: Any]) -> DeliveryStop? {
|
|
357
|
+
guard let deliveryId = raw["deliveryId"] as? String,
|
|
358
|
+
let latitude = (raw["latitude"] as? NSNumber)?.doubleValue,
|
|
359
|
+
let longitude = (raw["longitude"] as? NSNumber)?.doubleValue else {
|
|
360
|
+
return nil
|
|
361
|
+
}
|
|
362
|
+
return DeliveryStop(
|
|
363
|
+
deliveryId: deliveryId,
|
|
364
|
+
address: (raw["address"] as? String) ?? "",
|
|
365
|
+
latitude: latitude,
|
|
366
|
+
longitude: longitude,
|
|
367
|
+
radiusMeters: (raw["radiusMeters"] as? NSNumber)?.doubleValue,
|
|
368
|
+
customerId: raw["customerId"] as? String,
|
|
369
|
+
driverId: raw["driverId"] as? String
|
|
370
|
+
)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private func parseRouteOptions(_ raw: NSDictionary?) -> RouteGeofenceOptions {
|
|
374
|
+
guard let o = raw as? [String: Any] else { return RouteGeofenceOptions() }
|
|
375
|
+
// iOS RouteGeofenceOptions has no `manualForeground` (OS owns background region
|
|
376
|
+
// monitoring); a JS-supplied manualForeground is intentionally ignored here.
|
|
377
|
+
return RouteGeofenceOptions(
|
|
378
|
+
defaultRadiusMeters: (o["defaultRadiusMeters"] as? NSNumber)?.doubleValue ?? 250,
|
|
379
|
+
timeoutSeconds: (o["timeoutSeconds"] as? NSNumber)?.doubleValue,
|
|
380
|
+
autoStopAfterDropoffSeconds: (o["autoStopAfterDropoffSeconds"] as? NSNumber)?.doubleValue
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
private func serializeStop(_ s: DeliveryStop) -> [String: Any] {
|
|
385
|
+
var m: [String: Any] = [
|
|
386
|
+
"deliveryId": s.deliveryId,
|
|
387
|
+
"address": s.address,
|
|
388
|
+
"latitude": s.latitude,
|
|
389
|
+
"longitude": s.longitude
|
|
390
|
+
]
|
|
391
|
+
if let r = s.radiusMeters { m["radiusMeters"] = r }
|
|
392
|
+
if let c = s.customerId { m["customerId"] = c }
|
|
393
|
+
if let d = s.driverId { m["driverId"] = d }
|
|
394
|
+
return m
|
|
395
|
+
}
|
|
396
|
+
|
|
253
397
|
private func parseCoordinates(_ raw: NSDictionary?) -> LatLngObject? {
|
|
254
398
|
guard let coordinates = raw as? [String: Any],
|
|
255
399
|
let lat = coordinates["lat"] as? Double,
|