react-native-pointr 10.7.1 → 10.9.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/API_REFERENCE.md +132 -0
- package/CHANGELOG.md +18 -1
- package/EXTENDING.md +12 -2
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/pointr/PTRBridgeKeys.kt +45 -3
- package/android/src/main/java/com/pointr/PTRNativeLibrary.kt +224 -0
- package/ios/PTRBridgeKeys.swift +41 -3
- package/ios/PTRNativeLibrary-Bridging.m +42 -0
- package/ios/PTRNativeLibrary.swift +262 -9
- package/package.json +1 -1
- package/react-native-pointr.podspec +1 -1
- package/src/NativePointrModule.ts +29 -0
- package/src/api/PointrSdk.ts +163 -2
- package/src/constants/bridgeKeys.ts +51 -3
- package/src/constants/index.ts +14 -0
- package/src/hooks/index.ts +9 -0
- package/src/hooks/usePointrData.ts +94 -0
- package/src/hooks/usePointrDataEvents.ts +74 -0
- package/src/hooks/usePointrSite.ts +73 -0
- package/src/index.tsx +17 -0
- package/src/managers/PTRDataManager.ts +57 -0
- package/src/managers/PTRSiteManager.ts +143 -0
- package/src/types/events.ts +73 -0
package/API_REFERENCE.md
CHANGED
|
@@ -354,6 +354,138 @@ const clientName = await pointrSdk.getClientName();
|
|
|
354
354
|
|
|
355
355
|
---
|
|
356
356
|
|
|
357
|
+
The Site Manager and Data Manager methods that follow resolve sites and buildings by either identifier, defaulting to the **internal** one — whereas the older `getPois`, `getBuildings`, and `getSiteByExternalId` above always take an **external** identifier.
|
|
358
|
+
|
|
359
|
+
#### `getSite(siteId, isExternalIdentifier?)`
|
|
360
|
+
|
|
361
|
+
Returns a `Promise<PTRSite | null>`, or `null` if the site is not found.
|
|
362
|
+
|
|
363
|
+
**Parameters:**
|
|
364
|
+
- `siteId` (`string`): Site identifier.
|
|
365
|
+
- `isExternalIdentifier` (`boolean`, default `false`): When `false`, `siteId` is the internal identifier; when `true`, the external identifier.
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
const site = await pointrSdk.getSite('<SITE_ID>');
|
|
369
|
+
const byExternal = await pointrSdk.getSite('<SITE_EXTERNAL_ID>', true);
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
#### `getSiteBuildings(siteId, isExternalIdentifier?)`
|
|
375
|
+
|
|
376
|
+
Returns a `Promise<PTRBuilding[]>` with all buildings for the given site. Rejects with `SITE_NOT_FOUND` if the site does not exist.
|
|
377
|
+
|
|
378
|
+
**Parameters:**
|
|
379
|
+
- `siteId` (`string`): Site identifier.
|
|
380
|
+
- `isExternalIdentifier` (`boolean`, default `false`): Resolve `siteId` as internal (`false`) or external (`true`).
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
const buildings = await pointrSdk.getSiteBuildings('<SITE_ID>');
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
#### `getBuilding(siteId, buildingId, isExternalIdentifier?)`
|
|
389
|
+
|
|
390
|
+
Returns a `Promise<PTRBuilding | null>`, or `null` if the building is not found in that site. Building identifiers are only unique within a site, so the lookup is always scoped by `siteId`.
|
|
391
|
+
|
|
392
|
+
Rejects with `NOT_READY` if the SDK is not running, or `ERROR` if the lookup fails.
|
|
393
|
+
|
|
394
|
+
**Parameters:**
|
|
395
|
+
- `siteId` (`string`): Site identifier.
|
|
396
|
+
- `buildingId` (`string`): Building identifier.
|
|
397
|
+
- `isExternalIdentifier` (`boolean`, default `false`): When `false`, **both** ids are internal; when `true`, **both** are external.
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
const building = await pointrSdk.getBuilding('<SITE_ID>', '<BUILDING_ID>');
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
#### `getLevelByExternalIdentifier(buildingExternalIdentifier, levelExternalIdentifier)`
|
|
406
|
+
|
|
407
|
+
Returns a `Promise<PTRLevel | null>`, or `null` if the level is not found in that building.
|
|
408
|
+
|
|
409
|
+
Level external identifiers are only unique **within a building**, so the building's external identifier is required to scope the lookup. Both native SDKs expose this lookup by external identifier only, so there is no `isExternalIdentifier` flag — pass external identifiers for both arguments.
|
|
410
|
+
|
|
411
|
+
**Parameters:**
|
|
412
|
+
- `buildingExternalIdentifier` (`string`): External identifier of the building containing the level.
|
|
413
|
+
- `levelExternalIdentifier` (`string`): External identifier of the level.
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
const level = await pointrSdk.getLevelByExternalIdentifier(
|
|
417
|
+
'<BUILDING_EXTERNAL_ID>',
|
|
418
|
+
'<LEVEL_EXTERNAL_ID>'
|
|
419
|
+
);
|
|
420
|
+
// level?.index — 0 is ground level
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
#### `getMapUrl(siteId, isExternalIdentifier?)`
|
|
426
|
+
|
|
427
|
+
Returns a `Promise<string | null>` with the site's map data URL, or `null` if unavailable. Rejects with `SITE_NOT_FOUND` if the site does not exist, or `ERROR` if the URL cannot be retrieved.
|
|
428
|
+
|
|
429
|
+
**Parameters:**
|
|
430
|
+
- `siteId` (`string`): Site identifier.
|
|
431
|
+
- `isExternalIdentifier` (`boolean`, default `false`): Resolve `siteId` as internal (`false`) or external (`true`).
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
#### `getStyleJsonUrl()`
|
|
436
|
+
|
|
437
|
+
Returns a `Promise<string | null>` with the map style JSON URL, or `null` if unavailable.
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
#### `loadDataForSite(siteId, shouldRespectCachePolicy?, isExternalIdentifier?)`
|
|
442
|
+
|
|
443
|
+
Starts data management for a site if the data is not already present. Returns a `Promise<void>`.
|
|
444
|
+
|
|
445
|
+
This resolves as soon as data management has been **triggered**, not when it completes — subscribe to `onDataManagerCompleteAll` (which reports failures) or `onDataManagerReady` to observe the outcome.
|
|
446
|
+
|
|
447
|
+
**Parameters:**
|
|
448
|
+
- `siteId` (`string`): Site identifier.
|
|
449
|
+
- `shouldRespectCachePolicy` (`boolean`, default `true`): When `true`, waits until the cache expires if data is already present. When `false`, ignores the cache and triggers an update immediately.
|
|
450
|
+
- `isExternalIdentifier` (`boolean`, default `false`): Resolve `siteId` as internal (`false`) or external (`true`).
|
|
451
|
+
|
|
452
|
+
Note that if the data is already present no event may fire at all, so check `isSiteContentReady` first and guard with a timeout:
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
if (await pointrSdk.isSiteContentReady(siteId)) return; // nothing to download
|
|
456
|
+
|
|
457
|
+
let subscription: { remove: () => void } | null = null;
|
|
458
|
+
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
459
|
+
const cleanup = () => {
|
|
460
|
+
subscription?.remove();
|
|
461
|
+
if (timeout != null) clearTimeout(timeout);
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
subscription = pointrSdk.onDataManagerCompleteAll((event) => {
|
|
465
|
+
if (event.site.identifier !== siteId) return;
|
|
466
|
+
cleanup();
|
|
467
|
+
console.log(event.isSuccessful ? 'loaded' : event.errors.join(', '));
|
|
468
|
+
});
|
|
469
|
+
timeout = setTimeout(() => {
|
|
470
|
+
cleanup();
|
|
471
|
+
console.log('timed out');
|
|
472
|
+
}, 60000);
|
|
473
|
+
|
|
474
|
+
await pointrSdk.loadDataForSite(siteId);
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
#### `isSiteContentReady(siteId, isExternalIdentifier?)`
|
|
480
|
+
|
|
481
|
+
Returns a `Promise<boolean>` — whether all data needed to display the site is available. This does not mean the data is the latest, only that there is valid data to show.
|
|
482
|
+
|
|
483
|
+
**Parameters:**
|
|
484
|
+
- `siteId` (`string`): Site identifier.
|
|
485
|
+
- `isExternalIdentifier` (`boolean`, default `false`): Resolve `siteId` as internal (`false`) or external (`true`).
|
|
486
|
+
|
|
487
|
+
---
|
|
488
|
+
|
|
357
489
|
#### `isWayfindingReady(siteId)`
|
|
358
490
|
|
|
359
491
|
Returns a `Promise<boolean>` indicating whether wayfinding data is ready for the given site.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [10.9.0] - 2026-09-01
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Mobile SDK 10.9.0 integration.
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## [10.8.0] - 2026-08-18
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- Mobile SDK 10.8.0 integration.
|
|
17
|
+
|
|
18
|
+
|
|
7
19
|
## [10.7.1] - 2026-08-05
|
|
8
20
|
|
|
9
21
|
### Changed
|
|
@@ -39,6 +51,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
39
51
|
### Added
|
|
40
52
|
- **`PointrSdk` methods** exposing previously native-only capabilities: `shouldRequestPermissionsAtStartup`, `requestPermissions`, `getPois`, `searchPois`, `getSites`, `getBuildings`, `getSiteByExternalId`, `getClientName`, `isWayfindingReady`, `calculateDistance`, `isMyCarMarked`.
|
|
41
53
|
- **Shared bridge key constants** — every string crossing the JS ↔ native boundary for a map widget action (action types, `action` and `sdkConfig` payload keys, view manager command names) is now declared in one constants file per platform: `src/constants/bridgeKeys.ts`, `android/src/main/java/com/pointr/PTRBridgeKeys.kt` and `ios/PTRBridgeKeys.swift`. No literals remain in the action serializer or in either native reader, so a key can no longer be renamed on one side only. `PTRActionParamKeys`, `PTRSdkConfigKeys` and `PTRCommandNames` are exported from the package.
|
|
54
|
+
- **SiteManager** exposed through `PointrSdk`: `getSite`, `getSiteBuildings`, `getBuilding`, `getLevelByExternalIdentifier`, `getMapUrl`, `getStyleJsonUrl`. Site and building lookups accept an `isExternalIdentifier` flag to resolve by internal (default) or external identifier.
|
|
55
|
+
- **Level external identifiers** — `getLevelByExternalIdentifier(buildingExternalIdentifier, levelExternalIdentifier)` wraps the level lookup added in native SDK 10.6.0. Levels are only unique within a building, so the building's external identifier scopes the lookup. Exposed by external identifier only, matching the native SDKs.
|
|
56
|
+
- **DataManager** exposed through `PointrSdk`: `loadDataForSite`, `isSiteContentReady`, plus the five data-management events (`onDataManagerStart`, `onDataManagerCompleteAll`, `onDataManagerBeginProcessing`, `onDataManagerEndProcessing`, `onDataManagerReady`).
|
|
57
|
+
- **Hooks**: `usePointrSite`, `usePointrData`, and one hook per data-manager event.
|
|
58
|
+
- **Bridge key constants for the native → JS path** — the event payload fields (`PTREventPayloadKeys`) and the site / building / level object fields (`PTRModelKeys`) join the existing groups in the three `PTRBridgeKeys` files, so the data-manager events and the level lookup no longer write raw literals on either native side. iOS event names moved to `PTRNativeLibrary.EventNames`, matching Android's companion object and `PTREvents` in TypeScript.
|
|
59
|
+
|
|
42
60
|
|
|
43
61
|
### Changed
|
|
44
62
|
- **Actions serialize through `toPayload()`** — `PTRAction` subclasses now build their JSON payload from `PTRActionParamKeys` instead of the component spreading their class fields, so a field rename can no longer silently change the wire format.
|
|
@@ -48,7 +66,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
48
66
|
- **`markMyCar` / `showMyCar` via the declarative `action` prop on Android** — the completion event read an argument slot the declarative path never pushed, throwing inside the callback. Argument reads are now type-checked with defaults.
|
|
49
67
|
- **`personaId` in the `sdkConfig` prop on iOS** — the key was written by JS but never read, so the persona was ignored when the widget initialised the SDK.
|
|
50
68
|
|
|
51
|
-
|
|
52
69
|
## [10.3.0] - 2026-06-30
|
|
53
70
|
|
|
54
71
|
### Changed
|
package/EXTENDING.md
CHANGED
|
@@ -360,7 +360,7 @@ Ensure both iOS and Android implementations return the same data structure:
|
|
|
360
360
|
|
|
361
361
|
### 7. Never Hard-code Bridge Strings
|
|
362
362
|
|
|
363
|
-
|
|
363
|
+
Data crosses the bridge as JSON, so a key that one side writes and the other
|
|
364
364
|
reads is matched by string only — rename it on one side and the other side
|
|
365
365
|
silently reads an empty value, with no compile error anywhere. Every such string
|
|
366
366
|
lives in a constants file, one per language, and the three are kept identical:
|
|
@@ -371,12 +371,22 @@ lives in a constants file, one per language, and the three are kept identical:
|
|
|
371
371
|
| Android | `android/src/main/java/com/pointr/PTRBridgeKeys.kt` |
|
|
372
372
|
| iOS | `ios/PTRBridgeKeys.swift` |
|
|
373
373
|
|
|
374
|
-
They cover four
|
|
374
|
+
They cover six groups — the first four on the JS -> native path, the last two on
|
|
375
|
+
the native -> JS path:
|
|
375
376
|
|
|
376
377
|
- **Action types** — the `type` discriminator of the `action` prop
|
|
377
378
|
- **Action param keys** — every other field of the `action` prop payload
|
|
378
379
|
- **SDK config keys** — the fields of the `sdkConfig` prop
|
|
379
380
|
- **Command names** — the view manager commands `executeMapAction` dispatches
|
|
381
|
+
- **Event payload keys** — the fields of the payloads native emits to JS; the
|
|
382
|
+
interfaces in `src/types/events.ts` are the TypeScript side of this contract
|
|
383
|
+
- **Model keys** — the fields of the site / building / level objects native
|
|
384
|
+
serializes, matching `src/types/PTRSite.ts`
|
|
385
|
+
|
|
386
|
+
Event *names* are the one exception: each side declares them where it emits or
|
|
387
|
+
subscribes — `PTREvents` in `src/constants/index.ts`, `PTRNativeLibrary`'s
|
|
388
|
+
companion object on Android and `PTRNativeLibrary.EventNames` on iOS. They must
|
|
389
|
+
still match across all three.
|
|
380
390
|
|
|
381
391
|
When adding an action or a parameter:
|
|
382
392
|
|
package/android/build.gradle
CHANGED
|
@@ -93,7 +93,7 @@ dependencies {
|
|
|
93
93
|
//noinspection GradleDynamicVersion
|
|
94
94
|
implementation "com.facebook.react:react-android:0.82.1"
|
|
95
95
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
96
|
-
implementation("com.pointrlabs:pointr:10.
|
|
96
|
+
implementation("com.pointrlabs:pointr:10.9.0")
|
|
97
97
|
implementation ("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version")
|
|
98
98
|
implementation 'com.google.android.material:material:1.12.0'
|
|
99
99
|
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
|
|
@@ -3,9 +3,10 @@ package com.pointr
|
|
|
3
3
|
import androidx.annotation.StringDef
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Every string that crosses the JS <-> native boundary
|
|
7
|
-
*
|
|
8
|
-
* keys
|
|
6
|
+
* Every string that crosses the JS <-> native boundary: action type
|
|
7
|
+
* discriminators, `action` / `sdkConfig` payload keys and view manager command
|
|
8
|
+
* names on the JS -> native path, plus the payload keys of the events native
|
|
9
|
+
* emits back to JS.
|
|
9
10
|
*
|
|
10
11
|
* Nothing here may be hard-coded anywhere else — a key renamed on one side only
|
|
11
12
|
* fails silently (`optString` returns an empty string), so all three sides read
|
|
@@ -115,6 +116,47 @@ object PTRBridgeKeys {
|
|
|
115
116
|
/** Highlight a category */
|
|
116
117
|
const val HIGHLIGHT_CATEGORY = "highlightCategory"
|
|
117
118
|
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Field names of the event payloads emitted to JS through the event
|
|
122
|
+
* emitter. Native writes them, JS reads them back through the typed
|
|
123
|
+
* interfaces in `src/types/events.ts`.
|
|
124
|
+
*
|
|
125
|
+
* Event names are not listed here: they live in `PTREvents` on the JS side
|
|
126
|
+
* and in [PTRNativeLibrary]'s companion object on this side.
|
|
127
|
+
*/
|
|
128
|
+
object EventPayloadKeys {
|
|
129
|
+
/** Site the event refers to, serialized by [ModelKeys] */
|
|
130
|
+
const val SITE = "site"
|
|
131
|
+
/** Whether the data came from the server (online) or a local bundle */
|
|
132
|
+
const val IS_ONLINE_DATA = "isOnlineData"
|
|
133
|
+
/** Whether the reported operation succeeded */
|
|
134
|
+
const val IS_SUCCESSFUL = "isSuccessful"
|
|
135
|
+
/** Error messages collected during the operation */
|
|
136
|
+
const val ERRORS = "errors"
|
|
137
|
+
/** Data type being processed */
|
|
138
|
+
const val DATA_TYPE = "dataType"
|
|
139
|
+
/** Numeric value of `dataType` */
|
|
140
|
+
const val DATA_TYPE_VALUE = "value"
|
|
141
|
+
/** Human-readable name of `dataType` */
|
|
142
|
+
const val DATA_TYPE_NAME = "name"
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Field names shared by the site, building and level objects serialized for
|
|
147
|
+
* JS — both as promise results and inside event payloads. They match the
|
|
148
|
+
* interfaces in `src/types/PTRSite.ts`.
|
|
149
|
+
*/
|
|
150
|
+
object ModelKeys {
|
|
151
|
+
/** Internal identifier */
|
|
152
|
+
const val IDENTIFIER = "identifier"
|
|
153
|
+
/** External (customer-facing) identifier */
|
|
154
|
+
const val EXTERNAL_IDENTIFIER = "externalIdentifier"
|
|
155
|
+
/** Human-readable name */
|
|
156
|
+
const val NAME = "name"
|
|
157
|
+
/** Zero-based level index */
|
|
158
|
+
const val INDEX = "index"
|
|
159
|
+
}
|
|
118
160
|
}
|
|
119
161
|
|
|
120
162
|
/** Restricts a String parameter to the [PTRBridgeKeys.ActionTypes] values. */
|
|
@@ -12,16 +12,22 @@ import com.facebook.react.bridge.Promise
|
|
|
12
12
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
13
13
|
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
14
14
|
import com.facebook.react.bridge.ReactMethod
|
|
15
|
+
import com.facebook.react.bridge.WritableArray
|
|
15
16
|
import com.facebook.react.bridge.WritableMap
|
|
16
17
|
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
18
|
+
import com.pointr.PTRBridgeKeys.EventPayloadKeys
|
|
19
|
+
import com.pointr.PTRBridgeKeys.ModelKeys
|
|
17
20
|
import com.pointrlabs.core.geofence.GeofenceEvent
|
|
18
21
|
import com.pointrlabs.core.geofence.GeofenceEventType
|
|
19
22
|
import com.pointrlabs.core.geofence.GeofenceType
|
|
23
|
+
import com.pointrlabs.core.management.DataManager
|
|
20
24
|
import com.pointrlabs.core.management.GeofenceManager
|
|
21
25
|
import com.pointrlabs.core.management.Pointr
|
|
22
26
|
import com.pointrlabs.core.management.PositioningManager
|
|
23
27
|
import com.pointrlabs.core.management.interfaces.PointrListener
|
|
24
28
|
import com.pointrlabs.core.management.models.Building
|
|
29
|
+
import com.pointrlabs.core.management.models.DataType
|
|
30
|
+
import com.pointrlabs.core.management.models.ErrorMessage
|
|
25
31
|
import com.pointrlabs.core.management.models.PTRParams
|
|
26
32
|
import com.pointrlabs.core.management.models.Site
|
|
27
33
|
import com.pointrlabs.core.nativecore.wrappers.Plog
|
|
@@ -43,6 +49,7 @@ class PTRNativeLibrary(reactContext: ReactApplicationContext) :
|
|
|
43
49
|
|
|
44
50
|
private val positionManagerListener: PositioningManager.Listener
|
|
45
51
|
private val geofenceListener : GeofenceManager.Listener
|
|
52
|
+
private val dataManagerListener: DataManager.Listener
|
|
46
53
|
private var listenerCount = 0
|
|
47
54
|
|
|
48
55
|
init {
|
|
@@ -58,9 +65,75 @@ class PTRNativeLibrary(reactContext: ReactApplicationContext) :
|
|
|
58
65
|
sendEventToJs(reactContext, OnGeofenceEvent, params)
|
|
59
66
|
}
|
|
60
67
|
}
|
|
68
|
+
dataManagerListener = object : DataManager.Listener {
|
|
69
|
+
override fun onDataManagerStartDataManagementForSite(site: Site, isOnlineData: Boolean) {
|
|
70
|
+
sendEventToJs(reactContext, OnDataManagerStartDataManagementForSite, Arguments.createMap().apply {
|
|
71
|
+
putMap(EventPayloadKeys.SITE, site.toWritableMap())
|
|
72
|
+
putBoolean(EventPayloadKeys.IS_ONLINE_DATA, isOnlineData)
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
override fun onDataManagerCompleteAllForSite(
|
|
77
|
+
site: Site,
|
|
78
|
+
isSuccessful: Boolean,
|
|
79
|
+
isOnlineData: Boolean,
|
|
80
|
+
errors: List<ErrorMessage?>?
|
|
81
|
+
) {
|
|
82
|
+
sendEventToJs(reactContext, OnDataManagerCompleteAllForSite, Arguments.createMap().apply {
|
|
83
|
+
putMap(EventPayloadKeys.SITE, site.toWritableMap())
|
|
84
|
+
putBoolean(EventPayloadKeys.IS_SUCCESSFUL, isSuccessful)
|
|
85
|
+
putBoolean(EventPayloadKeys.IS_ONLINE_DATA, isOnlineData)
|
|
86
|
+
putArray(EventPayloadKeys.ERRORS, errorsArray(errors))
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
override fun onDataManagerBeginProcessingDataForSite(site: Site, dataType: DataType?, isOnlineData: Boolean) {
|
|
91
|
+
sendEventToJs(reactContext, OnDataManagerBeginProcessingDataForSite, Arguments.createMap().apply {
|
|
92
|
+
putMap(EventPayloadKeys.SITE, site.toWritableMap())
|
|
93
|
+
putMap(EventPayloadKeys.DATA_TYPE, dataTypeMap(dataType))
|
|
94
|
+
putBoolean(EventPayloadKeys.IS_ONLINE_DATA, isOnlineData)
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
override fun onDataManagerEndProcessingDataForSite(
|
|
99
|
+
site: Site,
|
|
100
|
+
dataType: DataType?,
|
|
101
|
+
isOnlineData: Boolean,
|
|
102
|
+
isSuccessful: Boolean,
|
|
103
|
+
errors: List<ErrorMessage?>?
|
|
104
|
+
) {
|
|
105
|
+
sendEventToJs(reactContext, OnDataManagerEndProcessingDataForSite, Arguments.createMap().apply {
|
|
106
|
+
putMap(EventPayloadKeys.SITE, site.toWritableMap())
|
|
107
|
+
putMap(EventPayloadKeys.DATA_TYPE, dataTypeMap(dataType))
|
|
108
|
+
putBoolean(EventPayloadKeys.IS_ONLINE_DATA, isOnlineData)
|
|
109
|
+
putBoolean(EventPayloadKeys.IS_SUCCESSFUL, isSuccessful)
|
|
110
|
+
putArray(EventPayloadKeys.ERRORS, errorsArray(errors))
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
override fun onDataManagerReadyForSite(site: Site) {
|
|
115
|
+
sendEventToJs(reactContext, OnDataManagerReadyForSite, Arguments.createMap().apply {
|
|
116
|
+
putMap(EventPayloadKeys.SITE, site.toWritableMap())
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
}
|
|
61
120
|
setPointrListeners()
|
|
62
121
|
}
|
|
63
122
|
|
|
123
|
+
private fun dataTypeMap(dataType: DataType?): WritableMap? {
|
|
124
|
+
val type = dataType ?: return null
|
|
125
|
+
return Arguments.createMap().apply {
|
|
126
|
+
putInt(EventPayloadKeys.DATA_TYPE_VALUE, type.value)
|
|
127
|
+
putString(EventPayloadKeys.DATA_TYPE_NAME, type.name)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private fun errorsArray(errors: List<ErrorMessage?>?): WritableArray {
|
|
132
|
+
val arr = Arguments.createArray()
|
|
133
|
+
errors?.filterNotNull()?.forEach { arr.pushString(it.message) }
|
|
134
|
+
return arr
|
|
135
|
+
}
|
|
136
|
+
|
|
64
137
|
|
|
65
138
|
override fun getName(): String {
|
|
66
139
|
return NAME
|
|
@@ -198,6 +271,149 @@ class PTRNativeLibrary(reactContext: ReactApplicationContext) :
|
|
|
198
271
|
}
|
|
199
272
|
}
|
|
200
273
|
|
|
274
|
+
// ─── Data Manager ────────────────────────────────────────────────────────
|
|
275
|
+
|
|
276
|
+
@ReactMethod
|
|
277
|
+
fun loadDataForSite(
|
|
278
|
+
siteId: String,
|
|
279
|
+
shouldRespectCachePolicy: Boolean,
|
|
280
|
+
isExternalIdentifier: Boolean,
|
|
281
|
+
promise: Promise
|
|
282
|
+
) {
|
|
283
|
+
try {
|
|
284
|
+
val site = resolveSite(siteId, isExternalIdentifier)
|
|
285
|
+
?: return promise.reject("SITE_NOT_FOUND", "Site not found: $siteId")
|
|
286
|
+
Pointr.getPointr()?.dataManager?.loadDataForSite(site, shouldRespectCachePolicy)
|
|
287
|
+
promise.resolve(null)
|
|
288
|
+
} catch (e: Exception) {
|
|
289
|
+
promise.reject("ERROR", "Failed to load data for site: ${e.message}", e)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
@ReactMethod
|
|
294
|
+
fun isSiteContentReady(siteId: String, isExternalIdentifier: Boolean, promise: Promise) {
|
|
295
|
+
try {
|
|
296
|
+
val site = resolveSite(siteId, isExternalIdentifier)
|
|
297
|
+
?: return promise.reject("SITE_NOT_FOUND", "Site not found: $siteId")
|
|
298
|
+
val ready = Pointr.getPointr()?.dataManager?.isSiteContentReady(site) ?: false
|
|
299
|
+
promise.resolve(ready)
|
|
300
|
+
} catch (e: Exception) {
|
|
301
|
+
promise.reject("ERROR", "Failed to check site content: ${e.message}", e)
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
private fun resolveSite(siteId: String, isExternalIdentifier: Boolean): Site? {
|
|
306
|
+
val siteManager = Pointr.getPointr()?.siteManager ?: return null
|
|
307
|
+
return if (isExternalIdentifier) {
|
|
308
|
+
siteManager.getSiteByExternalIdentifier(siteId)
|
|
309
|
+
} else {
|
|
310
|
+
siteManager.getSite(siteId)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// ─── Site Manager ────────────────────────────────────────────────────────
|
|
315
|
+
|
|
316
|
+
@ReactMethod
|
|
317
|
+
fun getSite(siteId: String, isExternalIdentifier: Boolean, promise: Promise) {
|
|
318
|
+
try {
|
|
319
|
+
val site = resolveSite(siteId, isExternalIdentifier)
|
|
320
|
+
promise.resolve(site?.toWritableMap())
|
|
321
|
+
} catch (e: Exception) {
|
|
322
|
+
promise.reject("ERROR", "Failed to get site: ${e.message}", e)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
@ReactMethod
|
|
327
|
+
fun getSiteBuildings(siteId: String, isExternalIdentifier: Boolean, promise: Promise) {
|
|
328
|
+
try {
|
|
329
|
+
val site = resolveSite(siteId, isExternalIdentifier)
|
|
330
|
+
?: return promise.reject("SITE_NOT_FOUND", "Site not found: $siteId")
|
|
331
|
+
val buildings = Pointr.getPointr()?.siteManager?.getBuildings(site.identifier) ?: emptyList()
|
|
332
|
+
val arr = Arguments.createArray()
|
|
333
|
+
buildings.forEach { b -> arr.pushMap(b.toWritableMap()) }
|
|
334
|
+
promise.resolve(arr)
|
|
335
|
+
} catch (e: Exception) {
|
|
336
|
+
promise.reject("ERROR", "Failed to get buildings: ${e.message}", e)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
@ReactMethod
|
|
341
|
+
fun getBuilding(
|
|
342
|
+
siteId: String,
|
|
343
|
+
buildingId: String,
|
|
344
|
+
isExternalIdentifier: Boolean,
|
|
345
|
+
promise: Promise
|
|
346
|
+
) {
|
|
347
|
+
try {
|
|
348
|
+
val siteManager = Pointr.getPointr()?.siteManager
|
|
349
|
+
?: return promise.reject("NOT_READY", "Site manager is not available")
|
|
350
|
+
val building = if (isExternalIdentifier) {
|
|
351
|
+
siteManager.getBuildingByExternalIdentifier(siteId, buildingId)
|
|
352
|
+
} else {
|
|
353
|
+
siteManager.getBuilding(siteId, buildingId)
|
|
354
|
+
}
|
|
355
|
+
promise.resolve(building?.toWritableMap())
|
|
356
|
+
} catch (e: Exception) {
|
|
357
|
+
promise.reject("ERROR", "Failed to get building: ${e.message}", e)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
@ReactMethod
|
|
362
|
+
fun getLevelByExternalIdentifier(
|
|
363
|
+
buildingExternalIdentifier: String,
|
|
364
|
+
levelExternalIdentifier: String,
|
|
365
|
+
promise: Promise
|
|
366
|
+
) {
|
|
367
|
+
try {
|
|
368
|
+
val siteManager = Pointr.getPointr()?.siteManager
|
|
369
|
+
?: return promise.reject("NOT_READY", "Site manager is not available")
|
|
370
|
+
// Named arguments on purpose: PointrKit takes (level, building) while
|
|
371
|
+
// this SDK takes (building, level), and both are plain strings.
|
|
372
|
+
val level = siteManager.getLevelByExternalIdentifier(
|
|
373
|
+
buildingExternalIdentifier = buildingExternalIdentifier,
|
|
374
|
+
levelExternalIdentifier = levelExternalIdentifier
|
|
375
|
+
)
|
|
376
|
+
promise.resolve(level?.let { lvl ->
|
|
377
|
+
Arguments.createMap().apply {
|
|
378
|
+
putString(ModelKeys.IDENTIFIER, lvl.identifier)
|
|
379
|
+
putString(ModelKeys.EXTERNAL_IDENTIFIER, lvl.externalIdentifier)
|
|
380
|
+
putString(ModelKeys.NAME, lvl.name ?: "")
|
|
381
|
+
putInt(ModelKeys.INDEX, lvl.index)
|
|
382
|
+
}
|
|
383
|
+
})
|
|
384
|
+
} catch (e: Exception) {
|
|
385
|
+
promise.reject("ERROR", "Failed to get level: ${e.message}", e)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
@ReactMethod
|
|
390
|
+
fun getMapUrl(siteId: String, isExternalIdentifier: Boolean, promise: Promise) {
|
|
391
|
+
try {
|
|
392
|
+
val site = resolveSite(siteId, isExternalIdentifier)
|
|
393
|
+
?: return promise.reject("SITE_NOT_FOUND", "Site not found: $siteId")
|
|
394
|
+
val siteManager = Pointr.getPointr()?.siteManager
|
|
395
|
+
?: return promise.reject("NOT_READY", "Site manager is not available")
|
|
396
|
+
siteManager.getMapUrl(site.identifier) { mapUrl, error ->
|
|
397
|
+
if (error != null) {
|
|
398
|
+
promise.reject("ERROR", error)
|
|
399
|
+
} else {
|
|
400
|
+
promise.resolve(mapUrl)
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
} catch (e: Exception) {
|
|
404
|
+
promise.reject("ERROR", "Failed to get map url: ${e.message}", e)
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
@ReactMethod
|
|
409
|
+
fun getStyleJsonUrl(promise: Promise) {
|
|
410
|
+
try {
|
|
411
|
+
promise.resolve(Pointr.getPointr()?.siteManager?.styleJsonUrl)
|
|
412
|
+
} catch (e: Exception) {
|
|
413
|
+
promise.reject("ERROR", "Failed to get style json url: ${e.message}", e)
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
201
417
|
// ─── Sites & Buildings ───────────────────────────────────────────────────
|
|
202
418
|
|
|
203
419
|
@ReactMethod
|
|
@@ -358,6 +574,12 @@ class PTRNativeLibrary(reactContext: ReactApplicationContext) :
|
|
|
358
574
|
|
|
359
575
|
const val OnGeofenceEvent = "OnGeofenceEvent"
|
|
360
576
|
|
|
577
|
+
const val OnDataManagerStartDataManagementForSite = "OnDataManagerStartDataManagementForSite"
|
|
578
|
+
const val OnDataManagerCompleteAllForSite = "OnDataManagerCompleteAllForSite"
|
|
579
|
+
const val OnDataManagerBeginProcessingDataForSite = "OnDataManagerBeginProcessingDataForSite"
|
|
580
|
+
const val OnDataManagerEndProcessingDataForSite = "OnDataManagerEndProcessingDataForSite"
|
|
581
|
+
const val OnDataManagerReadyForSite = "OnDataManagerReadyForSite"
|
|
582
|
+
|
|
361
583
|
var shouldRequestPermissionsAtStartup = true
|
|
362
584
|
|
|
363
585
|
fun sendEventToJs(
|
|
@@ -597,6 +819,7 @@ class PTRNativeLibrary(reactContext: ReactApplicationContext) :
|
|
|
597
819
|
if (pointr.state != Pointr.State.RUNNING) return
|
|
598
820
|
pointr.positioningManager?.removeListener(positionManagerListener)
|
|
599
821
|
pointr.geofenceManager?.removeListener(geofenceListener)
|
|
822
|
+
pointr.dataManager?.removeListener(dataManagerListener)
|
|
600
823
|
}
|
|
601
824
|
|
|
602
825
|
private fun setPointrListeners() {
|
|
@@ -604,6 +827,7 @@ class PTRNativeLibrary(reactContext: ReactApplicationContext) :
|
|
|
604
827
|
if (pointr.state != Pointr.State.RUNNING) return
|
|
605
828
|
pointr.positioningManager?.addListener(positionManagerListener)
|
|
606
829
|
pointr.geofenceManager?.addListener(geofenceListener)
|
|
830
|
+
pointr.dataManager?.addListener(dataManagerListener)
|
|
607
831
|
if (shouldRequestPermissionsAtStartup) {
|
|
608
832
|
requestPermissions()
|
|
609
833
|
} else {
|
package/ios/PTRBridgeKeys.swift
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
|
|
3
|
-
/// Every string that crosses the JS <-> native boundary
|
|
4
|
-
///
|
|
5
|
-
/// keys
|
|
3
|
+
/// Every string that crosses the JS <-> native boundary: action type
|
|
4
|
+
/// discriminators, `action` / `sdkConfig` payload keys and view manager command
|
|
5
|
+
/// names on the JS -> native path, plus the payload keys of the events native
|
|
6
|
+
/// emits back to JS.
|
|
6
7
|
///
|
|
7
8
|
/// Nothing here may be hard-coded anywhere else — a key renamed on one side only
|
|
8
9
|
/// fails silently (the cast yields `nil` and the parameter defaults to empty), so
|
|
@@ -107,4 +108,41 @@ enum PTRBridgeKeys {
|
|
|
107
108
|
/// Highlight a category
|
|
108
109
|
static let highlightCategory = "highlightCategory"
|
|
109
110
|
}
|
|
111
|
+
|
|
112
|
+
/// Field names of the event payloads emitted to JS through the event
|
|
113
|
+
/// emitter. Native writes them, JS reads them back through the typed
|
|
114
|
+
/// interfaces in `src/types/events.ts`.
|
|
115
|
+
///
|
|
116
|
+
/// Event names are not listed here: they live in `PTREvents` on the JS side
|
|
117
|
+
/// and in `PTRNativeLibrary.EventNames` on this side.
|
|
118
|
+
enum EventPayloadKeys {
|
|
119
|
+
/// Site the event refers to, serialized by `ModelKeys`
|
|
120
|
+
static let site = "site"
|
|
121
|
+
/// Whether the data came from the server (online) or a local bundle
|
|
122
|
+
static let isOnlineData = "isOnlineData"
|
|
123
|
+
/// Whether the reported operation succeeded
|
|
124
|
+
static let isSuccessful = "isSuccessful"
|
|
125
|
+
/// Error messages collected during the operation
|
|
126
|
+
static let errors = "errors"
|
|
127
|
+
/// Data type being processed
|
|
128
|
+
static let dataType = "dataType"
|
|
129
|
+
/// Numeric value of `dataType`
|
|
130
|
+
static let dataTypeValue = "value"
|
|
131
|
+
/// Human-readable name of `dataType`
|
|
132
|
+
static let dataTypeName = "name"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/// Field names shared by the site, building and level objects serialized for
|
|
136
|
+
/// JS — both as promise results and inside event payloads. They match the
|
|
137
|
+
/// interfaces in `src/types/PTRSite.ts`.
|
|
138
|
+
enum ModelKeys {
|
|
139
|
+
/// Internal identifier
|
|
140
|
+
static let identifier = "identifier"
|
|
141
|
+
/// External (customer-facing) identifier
|
|
142
|
+
static let externalIdentifier = "externalIdentifier"
|
|
143
|
+
/// Human-readable name
|
|
144
|
+
static let name = "name"
|
|
145
|
+
/// Zero-based level index
|
|
146
|
+
static let index = "index"
|
|
147
|
+
}
|
|
110
148
|
}
|
|
@@ -35,6 +35,48 @@ RCT_EXTERN_METHOD(getPois:(NSString *)siteId
|
|
|
35
35
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
36
36
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
37
37
|
|
|
38
|
+
// Data Manager
|
|
39
|
+
RCT_EXTERN_METHOD(loadDataForSite:(NSString *)siteId
|
|
40
|
+
shouldRespectCachePolicy:(BOOL)shouldRespectCachePolicy
|
|
41
|
+
isExternalIdentifier:(BOOL)isExternalIdentifier
|
|
42
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
43
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
44
|
+
|
|
45
|
+
RCT_EXTERN_METHOD(isSiteContentReady:(NSString *)siteId
|
|
46
|
+
isExternalIdentifier:(BOOL)isExternalIdentifier
|
|
47
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
48
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
49
|
+
|
|
50
|
+
// Site Manager
|
|
51
|
+
RCT_EXTERN_METHOD(getSite:(NSString *)siteId
|
|
52
|
+
isExternalIdentifier:(BOOL)isExternalIdentifier
|
|
53
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
54
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
55
|
+
|
|
56
|
+
RCT_EXTERN_METHOD(getSiteBuildings:(NSString *)siteId
|
|
57
|
+
isExternalIdentifier:(BOOL)isExternalIdentifier
|
|
58
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
59
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
60
|
+
|
|
61
|
+
RCT_EXTERN_METHOD(getBuilding:(NSString *)siteId
|
|
62
|
+
buildingId:(NSString *)buildingId
|
|
63
|
+
isExternalIdentifier:(BOOL)isExternalIdentifier
|
|
64
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
65
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
66
|
+
|
|
67
|
+
RCT_EXTERN_METHOD(getLevelByExternalIdentifier:(NSString *)buildingExternalIdentifier
|
|
68
|
+
levelExternalIdentifier:(NSString *)levelExternalIdentifier
|
|
69
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
70
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
71
|
+
|
|
72
|
+
RCT_EXTERN_METHOD(getMapUrl:(NSString *)siteId
|
|
73
|
+
isExternalIdentifier:(BOOL)isExternalIdentifier
|
|
74
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
75
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
76
|
+
|
|
77
|
+
RCT_EXTERN_METHOD(getStyleJsonUrl:(RCTPromiseResolveBlock)resolve
|
|
78
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
79
|
+
|
|
38
80
|
// Sites & Buildings
|
|
39
81
|
RCT_EXTERN_METHOD(getSites:(RCTPromiseResolveBlock)resolve
|
|
40
82
|
rejecter:(RCTPromiseRejectBlock)reject)
|