@doorstepai/dropoff-sdk 2.0.8 → 2.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/android/build.gradle +1 -1
- package/android/src/main/java/com/doorstepai/dropoffsdk/DoorstepAIModule.kt +159 -2
- package/ios/DoorstepAI.mm +20 -2
- package/ios/DoorstepAI.swift +140 -5
- package/ios/DoorstepDropoffSDK.xcframework/Info.plist +5 -5
- 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 +5341 -1609
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +77 -4
- 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 +77 -4
- 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 +2630 -2116
- 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 +5341 -1609
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +77 -4
- 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 +77 -4
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +5341 -1609
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/DoorstepDropoffSDK.framework/Modules/DoorstepDropoffSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +77 -4
- 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 +77 -4
- 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 +2630 -2116
- package/ios/DoorstepDropoffSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/DoorstepDropoffSDK.framework.dSYM/Contents/Resources/Relocations/x86_64/DoorstepDropoffSDK.yml +2616 -2105
- package/lib/module/DoorstepAI.js +102 -5
- package/lib/module/DoorstepAI.js.map +1 -1
- package/lib/module/RootDoorstepAI.js +10 -1
- package/lib/module/RootDoorstepAI.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/DoorstepAI.d.ts +72 -3
- package/lib/typescript/src/DoorstepAI.d.ts.map +1 -1
- package/lib/typescript/src/RootDoorstepAI.d.ts +4 -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 +162 -6
- package/src/RootDoorstepAI.tsx +18 -1
- 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.1.0")
|
|
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) :
|
|
@@ -203,9 +213,9 @@ class DoorstepAIModule(private val reactContext: ReactApplicationContext) :
|
|
|
203
213
|
}
|
|
204
214
|
|
|
205
215
|
@ReactMethod
|
|
206
|
-
fun newEvent(eventName: String, deliveryId: String, promise: Promise) {
|
|
216
|
+
fun newEvent(eventName: String, deliveryId: String, timestamp: Double?, promise: Promise) {
|
|
207
217
|
try {
|
|
208
|
-
DoorstepAI.newEvent(eventName, deliveryId) { /* Callback
|
|
218
|
+
DoorstepAI.newEvent(eventName, deliveryId, timestamp) { /* Callback not needed here */ }
|
|
209
219
|
promise.resolve("Event $eventName triggered for $deliveryId")
|
|
210
220
|
} catch (e: Exception) {
|
|
211
221
|
promise.reject("EVENT_CREATION_ERROR", e.message ?: "Failed to save event")
|
|
@@ -289,7 +299,154 @@ class DoorstepAIModule(private val reactContext: ReactApplicationContext) :
|
|
|
289
299
|
return LatLngObject(lat = lat, lng = lng)
|
|
290
300
|
}
|
|
291
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 updateRouteStops(stops: ReadableArray, promise: Promise) {
|
|
329
|
+
try {
|
|
330
|
+
DoorstepAI.updateRouteStops(parseStops(stops)) { result ->
|
|
331
|
+
result.onSuccess { promise.resolve(true) }
|
|
332
|
+
.onFailure { promise.reject("ROUTE_GEOFENCING_ERROR", it.message ?: "Failed to update route stops") }
|
|
333
|
+
}
|
|
334
|
+
} catch (e: Exception) {
|
|
335
|
+
promise.reject("ROUTE_GEOFENCING_ERROR", e.message ?: "Failed to update route stops")
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
@ReactMethod
|
|
340
|
+
fun stopRouteGeofencing(promise: Promise) {
|
|
341
|
+
try {
|
|
342
|
+
DoorstepAI.stopRouteGeofencing()
|
|
343
|
+
promise.resolve(true)
|
|
344
|
+
} catch (e: Exception) {
|
|
345
|
+
promise.reject("ROUTE_GEOFENCING_ERROR", e.message ?: "Failed to stop route geofencing")
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
@ReactMethod
|
|
350
|
+
fun resumeRouteGeofencingIfNeeded(promise: Promise) {
|
|
351
|
+
try {
|
|
352
|
+
DoorstepAI.resumeRouteGeofencingIfNeeded()
|
|
353
|
+
promise.resolve(true)
|
|
354
|
+
} catch (e: Exception) {
|
|
355
|
+
promise.reject("ROUTE_GEOFENCING_ERROR", e.message ?: "Failed to resume route geofencing")
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
@ReactMethod
|
|
360
|
+
fun getMonitoredStops(promise: Promise) {
|
|
361
|
+
try {
|
|
362
|
+
val arr = Arguments.createArray()
|
|
363
|
+
DoorstepAI.monitoredStops.forEach { arr.pushMap(serializeStop(it)) }
|
|
364
|
+
promise.resolve(arr)
|
|
365
|
+
} catch (e: Exception) {
|
|
366
|
+
promise.reject("ROUTE_GEOFENCING_ERROR", e.message ?: "Failed to get monitored stops")
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// ---- Geofence-session event stream (NativeEventEmitter) ----
|
|
371
|
+
|
|
372
|
+
@ReactMethod
|
|
373
|
+
fun addListener(eventName: String) {
|
|
374
|
+
// On the first JS subscription, bridge the SDK's geofenceSessionEvents Flow
|
|
375
|
+
// to JS. A single collector serves the module lifetime (cancelled in invalidate()).
|
|
376
|
+
if (geofenceJob == null) {
|
|
377
|
+
geofenceJob = DoorstepAI.geofenceSessionEvents
|
|
378
|
+
.onEach { ev ->
|
|
379
|
+
val map = Arguments.createMap().apply {
|
|
380
|
+
putString("deliveryId", ev.deliveryId)
|
|
381
|
+
putString("type", ev.type.name) // "STARTED" | "STOPPED"
|
|
382
|
+
putString("reason", ev.reason)
|
|
383
|
+
}
|
|
384
|
+
reactContext
|
|
385
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
386
|
+
.emit(GEOFENCE_EVENT, map)
|
|
387
|
+
}
|
|
388
|
+
.launchIn(moduleScope)
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
@ReactMethod
|
|
393
|
+
fun removeListeners(count: Int) {
|
|
394
|
+
// Required stub for NativeEventEmitter on Android. The single Flow collector
|
|
395
|
+
// is torn down in invalidate(); no per-listener teardown is needed.
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
override fun invalidate() {
|
|
399
|
+
geofenceJob?.cancel()
|
|
400
|
+
geofenceJob = null
|
|
401
|
+
super.invalidate()
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
private fun parseStops(arr: ReadableArray): List<DeliveryStop> {
|
|
405
|
+
val out = mutableListOf<DeliveryStop>()
|
|
406
|
+
for (i in 0 until arr.size()) {
|
|
407
|
+
val m = arr.getMap(i) ?: continue
|
|
408
|
+
val deliveryId = if (m.hasKey("deliveryId")) m.getString("deliveryId") else null
|
|
409
|
+
if (deliveryId.isNullOrEmpty()) continue
|
|
410
|
+
if (!m.hasKey("latitude") || !m.hasKey("longitude")) continue
|
|
411
|
+
out.add(
|
|
412
|
+
DeliveryStop(
|
|
413
|
+
deliveryId = deliveryId,
|
|
414
|
+
address = if (m.hasKey("address")) m.getString("address") ?: "" else "",
|
|
415
|
+
latitude = m.getDouble("latitude"),
|
|
416
|
+
longitude = m.getDouble("longitude"),
|
|
417
|
+
radiusMeters = if (m.hasKey("radiusMeters") && !m.isNull("radiusMeters")) m.getDouble("radiusMeters") else null,
|
|
418
|
+
customerId = if (m.hasKey("customerId")) m.getString("customerId") else null,
|
|
419
|
+
driverId = if (m.hasKey("driverId")) m.getString("driverId") else null
|
|
420
|
+
)
|
|
421
|
+
)
|
|
422
|
+
}
|
|
423
|
+
return out
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
private fun parseRouteOptions(m: ReadableMap?): RouteGeofenceOptions {
|
|
427
|
+
if (m == null) return RouteGeofenceOptions()
|
|
428
|
+
return RouteGeofenceOptions(
|
|
429
|
+
defaultRadiusMeters = if (m.hasKey("defaultRadiusMeters") && !m.isNull("defaultRadiusMeters")) m.getDouble("defaultRadiusMeters") else 250.0,
|
|
430
|
+
timeoutSeconds = if (m.hasKey("timeoutSeconds") && !m.isNull("timeoutSeconds")) m.getDouble("timeoutSeconds") else null,
|
|
431
|
+
autoStopAfterDropoffSeconds = if (m.hasKey("autoStopAfterDropoffSeconds") && !m.isNull("autoStopAfterDropoffSeconds")) m.getDouble("autoStopAfterDropoffSeconds") else null,
|
|
432
|
+
manualForeground = if (m.hasKey("manualForeground") && !m.isNull("manualForeground")) m.getBoolean("manualForeground") else false
|
|
433
|
+
)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
private fun serializeStop(s: DeliveryStop): WritableMap {
|
|
437
|
+
val m = Arguments.createMap()
|
|
438
|
+
m.putString("deliveryId", s.deliveryId)
|
|
439
|
+
m.putString("address", s.address)
|
|
440
|
+
m.putDouble("latitude", s.latitude)
|
|
441
|
+
m.putDouble("longitude", s.longitude)
|
|
442
|
+
s.radiusMeters?.let { m.putDouble("radiusMeters", it) }
|
|
443
|
+
s.customerId?.let { m.putString("customerId", it) }
|
|
444
|
+
s.driverId?.let { m.putString("driverId", it) }
|
|
445
|
+
return m
|
|
446
|
+
}
|
|
447
|
+
|
|
292
448
|
companion object {
|
|
293
449
|
const val NAME = "DoorstepAI"
|
|
450
|
+
private const val GEOFENCE_EVENT = "DoorstepGeofenceSessionEvent"
|
|
294
451
|
}
|
|
295
452
|
}
|
package/ios/DoorstepAI.mm
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
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
|
-
RCT_EXTERN_METHOD(requestAllPermissions:(BOOL)requestAlwaysLocation
|
|
7
|
+
RCT_EXTERN_METHOD(requestAllPermissions:(BOOL)requestAlwaysLocation
|
|
8
|
+
requestBluetooth:(BOOL)requestBluetooth)
|
|
7
9
|
RCT_EXTERN_METHOD(configureRemoteLogging:(BOOL)enabled
|
|
8
10
|
minLevel:(id)minLevel
|
|
9
11
|
flushIntervalSeconds:(id)flushIntervalSeconds
|
|
@@ -58,6 +60,7 @@ RCT_EXTERN_METHOD(startDeliveryByLatLng:(nonnull NSNumber *)latitude
|
|
|
58
60
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
59
61
|
RCT_EXTERN_METHOD(newEvent:(nonnull NSString *)eventName
|
|
60
62
|
deliveryId:(nonnull NSString *)deliveryId
|
|
63
|
+
timestamp:(id)timestamp
|
|
61
64
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
62
65
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
63
66
|
RCT_EXTERN_METHOD(markDropoff:(nonnull NSString *)deliveryId
|
|
@@ -68,4 +71,19 @@ RCT_EXTERN_METHOD(stopDelivery:(nonnull NSString *)deliveryId
|
|
|
68
71
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
69
72
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
70
73
|
|
|
74
|
+
// ---- Route geofencing ----
|
|
75
|
+
RCT_EXTERN_METHOD(startRouteGeofencing:(nonnull NSArray *)stops
|
|
76
|
+
options:(nullable NSDictionary *)options
|
|
77
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
78
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
79
|
+
RCT_EXTERN_METHOD(updateRouteStops:(nonnull NSArray *)stops
|
|
80
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
81
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
82
|
+
RCT_EXTERN_METHOD(stopRouteGeofencing:(RCTPromiseResolveBlock)resolve
|
|
83
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
84
|
+
RCT_EXTERN_METHOD(resumeRouteGeofencingIfNeeded:(RCTPromiseResolveBlock)resolve
|
|
85
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
86
|
+
RCT_EXTERN_METHOD(getMonitoredStops:(RCTPromiseResolveBlock)resolve
|
|
87
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
88
|
+
|
|
71
89
|
@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,
|
|
@@ -25,8 +61,8 @@ class DoorstepAIBridge: NSObject {
|
|
|
25
61
|
}
|
|
26
62
|
|
|
27
63
|
@objc
|
|
28
|
-
func requestAllPermissions(_ requestAlwaysLocation: Bool) {
|
|
29
|
-
DoorstepAI.requestAllPermissions(requestAlwaysLocation: requestAlwaysLocation)
|
|
64
|
+
func requestAllPermissions(_ requestAlwaysLocation: Bool, requestBluetooth: Bool) {
|
|
65
|
+
DoorstepAI.requestAllPermissions(requestAlwaysLocation: requestAlwaysLocation, requestBluetooth: requestBluetooth)
|
|
30
66
|
}
|
|
31
67
|
|
|
32
68
|
@objc
|
|
@@ -221,11 +257,13 @@ class DoorstepAIBridge: NSObject {
|
|
|
221
257
|
@objc
|
|
222
258
|
func newEvent(_ eventName: NSString,
|
|
223
259
|
deliveryId: NSString,
|
|
260
|
+
timestamp: Any?,
|
|
224
261
|
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
225
262
|
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
226
263
|
Task {
|
|
227
264
|
do {
|
|
228
|
-
|
|
265
|
+
let ts = (timestamp as? NSNumber)?.doubleValue
|
|
266
|
+
try await DoorstepAI.newEvent(eventName: eventName as String, deliveryId: deliveryId as String, timestamp: ts)
|
|
229
267
|
resolve(nil)
|
|
230
268
|
} catch {
|
|
231
269
|
reject("E_NEW_EVENT", "Failed to send event: \(error.localizedDescription)", error)
|
|
@@ -270,6 +308,103 @@ class DoorstepAIBridge: NSObject {
|
|
|
270
308
|
}
|
|
271
309
|
}
|
|
272
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 updateRouteStops(_ stops: NSArray,
|
|
332
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
333
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
334
|
+
let parsed: [DeliveryStop] = (stops as? [[String: Any]] ?? []).compactMap { parseDeliveryStop($0) }
|
|
335
|
+
DispatchQueue.main.async {
|
|
336
|
+
DoorstepAI.updateRouteStops(parsed)
|
|
337
|
+
resolve(nil)
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
@objc
|
|
342
|
+
func stopRouteGeofencing(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
343
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
344
|
+
DispatchQueue.main.async {
|
|
345
|
+
DoorstepAI.stopRouteGeofencing()
|
|
346
|
+
resolve(nil)
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
@objc
|
|
351
|
+
func resumeRouteGeofencingIfNeeded(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
352
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
353
|
+
DispatchQueue.main.async {
|
|
354
|
+
DoorstepAI.resumeRouteGeofencingIfNeeded()
|
|
355
|
+
resolve(nil)
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
@objc
|
|
360
|
+
func getMonitoredStops(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
361
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
362
|
+
DispatchQueue.main.async {
|
|
363
|
+
resolve(DoorstepAI.monitoredStops.map { self.serializeStop($0) })
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
private func parseDeliveryStop(_ raw: [String: Any]) -> DeliveryStop? {
|
|
368
|
+
guard let deliveryId = raw["deliveryId"] as? String,
|
|
369
|
+
let latitude = (raw["latitude"] as? NSNumber)?.doubleValue,
|
|
370
|
+
let longitude = (raw["longitude"] as? NSNumber)?.doubleValue else {
|
|
371
|
+
return nil
|
|
372
|
+
}
|
|
373
|
+
return DeliveryStop(
|
|
374
|
+
deliveryId: deliveryId,
|
|
375
|
+
address: (raw["address"] as? String) ?? "",
|
|
376
|
+
latitude: latitude,
|
|
377
|
+
longitude: longitude,
|
|
378
|
+
radiusMeters: (raw["radiusMeters"] as? NSNumber)?.doubleValue,
|
|
379
|
+
customerId: raw["customerId"] as? String,
|
|
380
|
+
driverId: raw["driverId"] as? String
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
private func parseRouteOptions(_ raw: NSDictionary?) -> RouteGeofenceOptions {
|
|
385
|
+
guard let o = raw as? [String: Any] else { return RouteGeofenceOptions() }
|
|
386
|
+
// iOS RouteGeofenceOptions has no `manualForeground` (OS owns background region
|
|
387
|
+
// monitoring); a JS-supplied manualForeground is intentionally ignored here.
|
|
388
|
+
return RouteGeofenceOptions(
|
|
389
|
+
defaultRadiusMeters: (o["defaultRadiusMeters"] as? NSNumber)?.doubleValue ?? 250,
|
|
390
|
+
timeoutSeconds: (o["timeoutSeconds"] as? NSNumber)?.doubleValue,
|
|
391
|
+
autoStopAfterDropoffSeconds: (o["autoStopAfterDropoffSeconds"] as? NSNumber)?.doubleValue
|
|
392
|
+
)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
private func serializeStop(_ s: DeliveryStop) -> [String: Any] {
|
|
396
|
+
var m: [String: Any] = [
|
|
397
|
+
"deliveryId": s.deliveryId,
|
|
398
|
+
"address": s.address,
|
|
399
|
+
"latitude": s.latitude,
|
|
400
|
+
"longitude": s.longitude
|
|
401
|
+
]
|
|
402
|
+
if let r = s.radiusMeters { m["radiusMeters"] = r }
|
|
403
|
+
if let c = s.customerId { m["customerId"] = c }
|
|
404
|
+
if let d = s.driverId { m["driverId"] = d }
|
|
405
|
+
return m
|
|
406
|
+
}
|
|
407
|
+
|
|
273
408
|
private func parseCoordinates(_ raw: NSDictionary?) -> LatLngObject? {
|
|
274
409
|
guard let coordinates = raw as? [String: Any],
|
|
275
410
|
let lat = coordinates["lat"] as? Double,
|
|
@@ -10,15 +10,18 @@
|
|
|
10
10
|
<key>DebugSymbolsPath</key>
|
|
11
11
|
<string>dSYMs</string>
|
|
12
12
|
<key>LibraryIdentifier</key>
|
|
13
|
-
<string>ios-
|
|
13
|
+
<string>ios-arm64_x86_64-simulator</string>
|
|
14
14
|
<key>LibraryPath</key>
|
|
15
15
|
<string>DoorstepDropoffSDK.framework</string>
|
|
16
16
|
<key>SupportedArchitectures</key>
|
|
17
17
|
<array>
|
|
18
18
|
<string>arm64</string>
|
|
19
|
+
<string>x86_64</string>
|
|
19
20
|
</array>
|
|
20
21
|
<key>SupportedPlatform</key>
|
|
21
22
|
<string>ios</string>
|
|
23
|
+
<key>SupportedPlatformVariant</key>
|
|
24
|
+
<string>simulator</string>
|
|
22
25
|
</dict>
|
|
23
26
|
<dict>
|
|
24
27
|
<key>BinaryPath</key>
|
|
@@ -26,18 +29,15 @@
|
|
|
26
29
|
<key>DebugSymbolsPath</key>
|
|
27
30
|
<string>dSYMs</string>
|
|
28
31
|
<key>LibraryIdentifier</key>
|
|
29
|
-
<string>ios-
|
|
32
|
+
<string>ios-arm64</string>
|
|
30
33
|
<key>LibraryPath</key>
|
|
31
34
|
<string>DoorstepDropoffSDK.framework</string>
|
|
32
35
|
<key>SupportedArchitectures</key>
|
|
33
36
|
<array>
|
|
34
37
|
<string>arm64</string>
|
|
35
|
-
<string>x86_64</string>
|
|
36
38
|
</array>
|
|
37
39
|
<key>SupportedPlatform</key>
|
|
38
40
|
<string>ios</string>
|
|
39
|
-
<key>SupportedPlatformVariant</key>
|
|
40
|
-
<string>simulator</string>
|
|
41
41
|
</dict>
|
|
42
42
|
</array>
|
|
43
43
|
<key>CFBundlePackageType</key>
|