react-native-pointr 9.6.0 → 9.8.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 CHANGED
@@ -3,287 +3,535 @@
3
3
  > **Platform Compatibility:** This API reference is identical for both Android and iOS platforms. All methods, parameters, and behaviors are consistent across platforms.
4
4
 
5
5
  ## Table of Contents
6
- - [Native Module API](#native-module-api)
7
- - [Map Widget Component](#map-widget-component)
8
- - [Command Types](#command-types)
9
- - [Utility Functions](#utility-functions)
6
+ - [Quick Start](#quick-start)
7
+ - [PointrSdk Class](#pointrsdk-class)
8
+ - [executeMapCommand](#executemapcommand)
9
+ - [React Hooks](#react-hooks)
10
+ - [PTRMapWidget Component](#ptrmapwidget-component)
11
+ - [Constants & Enums](#constants--enums)
12
+ - [Command Classes](#command-classes)
13
+ - [Types](#types)
10
14
  - [Event Listeners](#event-listeners)
15
+ - [Complete Example](#complete-example)
16
+ - [Legacy API](#legacy-api)
17
+ - [Error Handling](#error-handling)
11
18
 
12
19
  ---
13
20
 
14
- ## Native Module API
21
+ ## Quick Start
15
22
 
16
- ### `PTRNativePointrLibrary`
23
+ The library now exposes two API layers:
17
24
 
18
- The main native module for interacting with the Pointr SDK.
25
+ | Layer | When to use |
26
+ |-------|-------------|
27
+ | **New API** (`PointrSdk`, `usePointrSdk`, `PTRMapWidget` declarative props) | All new integration work |
28
+ | **Legacy API** (`NativeModules.PTRNativePointrLibrary`, `showMapWidget`) | Existing code — still fully supported |
19
29
 
20
- #### Methods
30
+ **Minimal declarative setup (new API):**
21
31
 
22
- ##### `initialize(clientId, licenseKey, baseUrl, logLevel)`
32
+ ```tsx
33
+ import { PTRMapWidget, PTRSiteCommand } from 'react-native-pointr';
34
+ import type { PTRConfiguration } from 'react-native-pointr';
23
35
 
24
- Initializes the Pointr SDK with credentials.
36
+ const SDK_CONFIG: PTRConfiguration = {
37
+ clientId: '<CLIENT_ID>',
38
+ licenseKey: '<LICENSE_KEY>',
39
+ baseUrl: 'https://<your-instance>.pointr.cloud',
40
+ };
41
+
42
+ export default function MapScreen() {
43
+ return (
44
+ <PTRMapWidget
45
+ style={{ flex: 1 }}
46
+ sdkConfig={SDK_CONFIG}
47
+ command={new PTRSiteCommand('<SITE_EXTERNAL_ID>')}
48
+ />
49
+ );
50
+ }
51
+ ```
52
+
53
+ ---
54
+
55
+ ## PointrSdk Class
56
+
57
+ `PointrSdk` is a singleton class that wraps the native module and provides a clean, typed interface for SDK lifecycle management.
58
+
59
+ ### Importing
60
+
61
+ ```typescript
62
+ import { PointrSdk, pointrSdk } from 'react-native-pointr';
63
+
64
+ // Use the pre-created singleton (recommended)
65
+ pointrSdk.initialize({ ... });
66
+
67
+ // Or obtain the singleton instance explicitly
68
+ const sdk = PointrSdk.getInstance();
69
+ ```
70
+
71
+ ### Methods
72
+
73
+ #### `initialize(config)`
74
+
75
+ Initializes the SDK. Must be called before `start()`.
25
76
 
26
77
  **Parameters:**
27
- - `clientId` (string): Your Pointr client identifier
28
- - `licenseKey` (string): Your Pointr license key
29
- - `baseUrl` (string): The Pointr cloud base URL
30
- - `logLevel` (number): Log level (0-4)
31
- - `0`: Verbose
32
- - `1`: Debug
33
- - `2`: Info
34
- - `3`: Warning
35
- - `4`: Error
78
+ - `config` (`PTRConfiguration`): SDK credentials and options
79
+
80
+ **Throws:** `Error` if already initialized or if required fields are missing.
36
81
 
37
- **Example:**
38
82
  ```typescript
39
- import { NativeModules } from 'react-native';
83
+ import { pointrSdk, PTRLogLevel } from 'react-native-pointr';
40
84
 
41
- NativeModules.PTRNativePointrLibrary.initialize(
42
- "client-id",
43
- "license-key",
44
- "https://your-instance.pointr.cloud",
45
- 3
46
- );
85
+ pointrSdk.initialize({
86
+ clientId: '<CLIENT_ID>',
87
+ licenseKey: '<LICENSE_KEY>',
88
+ baseUrl: 'https://<your-instance>.pointr.cloud',
89
+ logLevel: PTRLogLevel.WARNING, // optional, defaults to ERROR
90
+ });
47
91
  ```
48
92
 
49
93
  ---
50
94
 
51
- ##### `start(callback)`
95
+ #### `start(callback?)`
52
96
 
53
- Starts the Pointr SDK.
97
+ Starts the SDK. SDK must be initialized first.
54
98
 
55
99
  **Parameters:**
56
- - `callback` (function): Callback function that receives the Pointr state as a string
100
+ - `callback` (`PTRStateCallback`, optional): Receives the native state string when the SDK is running.
101
+
102
+ **Throws:** `Error` if not initialized.
57
103
 
58
- **Example:**
59
104
  ```typescript
60
- NativeModules.PTRNativePointrLibrary.start((state: string) => {
61
- console.log(`Pointr state: ${state}`);
105
+ pointrSdk.start((state) => {
106
+ console.log('Native state:', state); // e.g. "running"
62
107
  });
63
108
  ```
64
109
 
65
- **Possible States:**
66
- - `"running"` - SDK is running successfully
67
- - `"failed registration"` - Registration failed
68
- - `"failed validation"` - Validation failed
69
- - `"failed invalid deep link url"` - Invalid deep link URL
70
- - `"failed no internet"` - No internet connection
71
- - `"off"` - SDK is off
110
+ ---
111
+
112
+ #### `stop()`
113
+
114
+ Stops the SDK when it is in the `RUNNING` state.
115
+
116
+ ```typescript
117
+ pointrSdk.stop();
118
+ ```
72
119
 
73
120
  ---
74
121
 
75
- ##### `stop()`
122
+ #### `getState()`
76
123
 
77
- Stops the Pointr SDK.
124
+ Returns the current `PTRState` enum value.
78
125
 
79
- **Example:**
80
126
  ```typescript
81
- NativeModules.PTRNativePointrLibrary.stop();
127
+ const state: PTRState = pointrSdk.getState();
82
128
  ```
83
129
 
84
130
  ---
85
131
 
86
- ##### `requestPermissions()`
132
+ #### `isInitialized()`
87
133
 
88
- Requests necessary permissions (location, bluetooth, etc.).
134
+ Returns `true` if the SDK has been initialized (any state other than `NOT_INITIALIZED`).
89
135
 
90
- **Example:**
91
136
  ```typescript
92
- NativeModules.PTRNativePointrLibrary.requestPermissions();
137
+ if (pointrSdk.isInitialized()) { ... }
93
138
  ```
94
139
 
95
140
  ---
96
141
 
97
- ##### `shouldRequestPermissionsAtStartup(shouldRequest)`
142
+ #### `isStarted()`
98
143
 
99
- Sets whether permissions should be requested automatically at startup.
144
+ Returns `true` only if the SDK is in the `RUNNING` state.
100
145
 
101
- **Parameters:**
102
- - `shouldRequest` (boolean): True to request permissions automatically
146
+ ```typescript
147
+ if (pointrSdk.isStarted()) { ... }
148
+ ```
149
+
150
+ ---
151
+
152
+ #### `getConfig()`
153
+
154
+ Returns the `PTRConfiguration` passed to `initialize()`, or `null` if not yet initialized.
155
+
156
+ ```typescript
157
+ const config = pointrSdk.getConfig();
158
+ ```
159
+
160
+ ---
161
+
162
+ #### `getCurrentLocation()`
163
+
164
+ Returns a `Promise<any | null>` with the last known calculated position, or `null` if unavailable.
165
+
166
+ The resolved object contains the raw native payload with these fields:
167
+
168
+ | Field | Type | Description |
169
+ |-------|------|-------------|
170
+ | `latitude` | number | Latitude |
171
+ | `longitude` | number | Longitude |
172
+ | `levelIndex` | number | Level index |
173
+ | `siteInternalIdentifier` | string | Site internal ID |
174
+ | `siteExternalIdentifier` | string | Site external ID |
175
+ | `buildingInternalIdentifier` | string | Building internal ID |
176
+ | `buildingExternalIdentifier` | string | Building external ID |
177
+ | `accuracy` | number | Accuracy in meters (optional) |
178
+ | `heading` | number | Heading in degrees (optional, only when accuracy is high) |
103
179
 
104
- **Example:**
105
180
  ```typescript
106
- NativeModules.PTRNativePointrLibrary.shouldRequestPermissionsAtStartup(true);
181
+ const loc = await pointrSdk.getCurrentLocation();
182
+ if (loc) {
183
+ console.log(loc.latitude, loc.longitude, loc.levelIndex);
184
+ }
107
185
  ```
108
186
 
109
187
  ---
110
188
 
111
- ##### `getCurrentLocation(callback)`
189
+ #### `setMapWidgetConfiguration(config)`
112
190
 
113
- Gets the current calculated position.
191
+ Applies map widget UI settings. Must be called before the `PTRMapWidget` renders to take effect.
114
192
 
115
193
  **Parameters:**
116
- - `callback` (function): Callback function that receives location data or null
194
+ - `config` (`PTRMapWidgetConfiguration`): UI feature toggles
117
195
 
118
- **Example:**
119
196
  ```typescript
120
- NativeModules.PTRNativePointrLibrary.getCurrentLocation((location) => {
121
- if (location) {
122
- console.log('Site:', location.siteExternalIdentifier);
123
- console.log('Building:', location.buildingExternalIdentifier);
124
- console.log('Level:', location.levelIndex);
125
- console.log('Coordinates:', location.latitude, location.longitude);
126
- }
197
+ pointrSdk.setMapWidgetConfiguration({
198
+ isLevelSelectorEnabled: true,
199
+ isJoystickEnabled: true,
200
+ isExitButtonEnabled: false,
127
201
  });
128
202
  ```
129
203
 
130
- **Location Object:**
131
- - `siteInternalIdentifier` (string)
132
- - `siteExternalIdentifier` (string)
133
- - `buildingInternalIdentifier` (string)
134
- - `buildingExternalIdentifier` (string)
135
- - `levelIndex` (number)
136
- - `latitude` (number)
137
- - `longitude` (number)
138
- - `accuracy` (number)
139
- - `heading` (number) - Only present if heading accuracy is high
204
+ ---
205
+
206
+ #### `onPositionUpdate(callback)`
207
+
208
+ Subscribes to `OnPositionManagerCalculatedLocation` events. Returns a subscription object with a `.remove()` method.
209
+
210
+ ```typescript
211
+ const sub = pointrSdk.onPositionUpdate((pos) => {
212
+ console.log(pos);
213
+ });
214
+ // later:
215
+ sub.remove();
216
+ ```
140
217
 
141
218
  ---
142
219
 
143
- ##### `isMyCarMarked(callback)`
220
+ #### `onBuildingClick(callback)`
144
221
 
145
- Checks if a "My Car" location has been saved.
222
+ Subscribes to `OnBuildingClicked` events.
146
223
 
147
- **Parameters:**
148
- - `callback` (function): Callback with no arguments if marked, error message if not
224
+ ```typescript
225
+ const sub = pointrSdk.onBuildingClick((event) => {
226
+ console.log(event.internalIdentifier, event.title);
227
+ });
228
+ ```
229
+
230
+ ---
231
+
232
+ #### `onSiteClick(callback)`
233
+
234
+ Subscribes to `OnSiteClicked` events.
149
235
 
150
- **Example:**
151
236
  ```typescript
152
- NativeModules.PTRNativePointrLibrary.isMyCarMarked((error) => {
153
- if (!error) {
154
- console.log('My car location is marked');
155
- } else {
156
- console.log('No saved location:', error);
157
- }
237
+ const sub = pointrSdk.onSiteClick((event) => {
238
+ console.log(event.internalIdentifier, event.title);
239
+ });
240
+ ```
241
+
242
+ ---
243
+
244
+ #### `onGeofenceEvent(callback)`
245
+
246
+ Subscribes to `OnGeofenceEvent` events.
247
+
248
+ ```typescript
249
+ const sub = pointrSdk.onGeofenceEvent((event) => {
250
+ console.log(event.eventType, event.geofence?.name);
158
251
  });
159
252
  ```
160
253
 
161
254
  ---
162
255
 
163
- ##### `setPointrMapWidgetConfiguration(configJson)`
256
+ #### `getEventEmitter()`
257
+
258
+ Returns the underlying `NativeEventEmitter` for advanced subscription use cases.
259
+
260
+ ```typescript
261
+ const emitter = pointrSdk.getEventEmitter();
262
+ ```
263
+
264
+ ---
265
+
266
+ #### `getNativeModule()`
267
+
268
+ Returns the raw `NativeModules.PTRNativePointrLibrary` reference for advanced use cases.
269
+
270
+ ```typescript
271
+ const native = pointrSdk.getNativeModule();
272
+ native.requestPermissions();
273
+ ```
274
+
275
+ ---
276
+
277
+ ## executeMapCommand
164
278
 
165
- Sets the map widget configuration.
279
+ A convenience function that resolves `findNodeHandle` internally and dispatches a command to the native map widget.
280
+
281
+ **Import:**
282
+ ```typescript
283
+ import { executeMapCommand } from 'react-native-pointr';
284
+ ```
285
+
286
+ **Signature:**
287
+ ```typescript
288
+ function executeMapCommand(
289
+ mapRef: React.RefObject<any> | any,
290
+ command: PTRCommand
291
+ ): void
292
+ ```
166
293
 
167
294
  **Parameters:**
168
- - `configJson` (string): JSON string with configuration options
295
+ - `mapRef`: A React ref (or any object with a `.current` property) pointing to the rendered `PTRMapWidget`.
296
+ - `command`: Any `PTRCommand` subclass instance.
297
+
298
+ **Throws:** `Error` if `mapRef` is null, the native view tag cannot be resolved, or the command is invalid.
169
299
 
170
300
  **Example:**
171
301
  ```typescript
172
- NativeModules.PTRNativePointrLibrary.setPointrMapWidgetConfiguration(
173
- JSON.stringify({
174
- isExitButtonEnabled: false,
175
- isCompassEnabled: true,
176
- isSearchBarEnabled: true,
177
- isLevelSelectorEnabled: true,
178
- isMyCarButtonEnabled: true
179
- })
180
- );
181
- ```
302
+ import { useRef } from 'react';
303
+ import { PTRMapWidget, PTRPoiCommand, executeMapCommand } from 'react-native-pointr';
304
+
305
+ function MyMap() {
306
+ const mapRef = useRef(null);
182
307
 
183
- **Configuration Options:**
184
- - `isExitButtonEnabled` (boolean): Show/hide exit button
185
- - `isCompassEnabled` (boolean): Show/hide compass
186
- - `isSearchBarEnabled` (boolean): Show/hide search bar
187
- - `isLevelSelectorEnabled` (boolean): Show/hide level selector
188
- - `isMyCarButtonEnabled` (boolean): Show/hide my car button
189
- - `isRouteFooterViewEnabled` (boolean): Show/hide route footer
308
+ const navigateToLobby = () => {
309
+ executeMapCommand(mapRef, new PTRPoiCommand('<SITE_ID>', 'Lobby'));
310
+ };
311
+
312
+ return <PTRMapWidget ref={mapRef} style={{ flex: 1 }} />;
313
+ }
314
+ ```
190
315
 
191
316
  ---
192
317
 
193
- ##### `getPois(siteId)`
318
+ ## React Hooks
194
319
 
195
- Retrieves all Points of Interest (POIs) for a specific site.
320
+ All hooks are exported from `react-native-pointr`.
321
+
322
+ ### `usePointrSdk(config?)`
323
+
324
+ Manages SDK initialization and startup. When `config` is provided the hook initializes and starts the SDK automatically on mount.
196
325
 
197
326
  **Parameters:**
198
- - `siteId` (string): Site external identifier
327
+ - `config` (`PTRConfiguration`, optional): When supplied, triggers `initialize` + `start` automatically.
199
328
 
200
- **Returns:** Promise<PTRPoi[]> - Array of POI objects
329
+ **Returns:**
330
+ ```typescript
331
+ {
332
+ sdk: PointrSdk; // The singleton SDK instance
333
+ state: PTRState; // Current SDK state
334
+ isInitialized: boolean;
335
+ isStarted: boolean;
336
+ error: Error | null;
337
+ }
338
+ ```
201
339
 
202
340
  **Example:**
203
341
  ```typescript
204
- import { getPois } from 'react-native-pointr';
342
+ import { usePointrSdk } from 'react-native-pointr';
205
343
 
206
- try {
207
- const pois = await getPois('site-external-id');
208
- console.log(`Found ${pois.length} POIs`);
209
-
210
- pois.forEach(poi => {
211
- console.log(`${poi.name} (${poi.typeCode})`);
212
- console.log(` Location: ${poi.position.lat}, ${poi.position.lon}`);
213
- console.log(` Level: ${poi.position.lvl}`);
344
+ function App() {
345
+ const { sdk, isStarted, state, error } = usePointrSdk({
346
+ clientId: '<CLIENT_ID>',
347
+ licenseKey: '<LICENSE_KEY>',
348
+ baseUrl: 'https://<your-instance>.pointr.cloud',
214
349
  });
215
- } catch (error) {
216
- console.error('Failed to get POIs:', error);
350
+
351
+ // sdk is available immediately; isStarted turns true once the SDK is running
217
352
  }
218
353
  ```
219
354
 
220
- **POI Object Structure:**
355
+ ---
356
+
357
+ ### `usePointrPosition(enabled?)`
358
+
359
+ Listens to live position updates from the SDK.
360
+
361
+ **Parameters:**
362
+ - `enabled` (boolean, default: `true`): Set to `false` to pause the subscription without unmounting.
363
+
364
+ **Returns:** The raw native position event or `null`. The payload fields match those described under [`getCurrentLocation()`](#getcurrentlocation).
365
+
221
366
  ```typescript
222
- interface PTRPoi {
223
- identifier: string; // Internal ID
224
- externalIdentifier: string; // External ID
225
- name: string; // Display name
226
- typeCode: string; // POI type code
227
- position: PTRPosition; // Geographic position
228
- geometry: PTRGeometry; // Shape data
229
- attributes: PTRPoiAttributes; // Metadata
367
+ import { usePointrPosition } from 'react-native-pointr';
368
+
369
+ function PositionView() {
370
+ const position = usePointrPosition();
371
+ if (position) {
372
+ console.log(position.latitude, position.longitude, position.levelIndex);
373
+ }
230
374
  }
375
+ ```
231
376
 
232
- interface PTRPosition {
233
- lat: number; // Latitude
234
- lon: number; // Longitude
235
- isValid: boolean; // Position validity
236
- sid: string; // Site ID
237
- bid: string; // Building ID
238
- lvl: number; // Level index
377
+ ---
378
+
379
+ ### `usePointrPois(siteId?)`
380
+
381
+ Fetches and manages the POI list for a given site.
382
+
383
+ **Parameters:**
384
+ - `siteId` (string, optional): External site identifier. POIs are fetched when this value changes.
385
+
386
+ **Returns:**
387
+ ```typescript
388
+ {
389
+ pois: PTRPoi[];
390
+ loading: boolean;
391
+ error: Error | null;
392
+ refetch: () => void;
239
393
  }
394
+ ```
240
395
 
241
- interface PTRPoiAttributes {
242
- logo?: string; // Logo URL
243
- images?: string[]; // Image URLs
244
- description?: string; // Description text
245
- extra?: Record<string, any>; // Custom data
246
- [key: string]: any; // Other attributes
396
+ ```typescript
397
+ import { usePointrPois } from 'react-native-pointr';
398
+
399
+ function PoiList() {
400
+ const { pois, loading, error } = usePointrPois('<SITE_ID>');
247
401
  }
248
402
  ```
249
403
 
250
404
  ---
251
405
 
252
- ## Map Widget Component
406
+ ### `usePointrGeofence(callback)`
253
407
 
254
- ### `PTRMapWidget`
408
+ Subscribes to geofence events for the lifetime of the component.
255
409
 
256
- A native UI component that displays the Pointr map.
410
+ **Parameters:**
411
+ - `callback` (`(event: PTRGeofenceEvent) => void`): Called on every geofence enter/exit event.
257
412
 
258
- **Props:**
259
- - `style` (ViewStyle): Component styling
260
- - `onMapWidgetDidEndLoading` (function): Callback when map finishes loading with a command
261
- - `ref` (ref): Reference to the component for sending commands
413
+ ```typescript
414
+ import { usePointrGeofence } from 'react-native-pointr';
262
415
 
263
- **Example:**
416
+ usePointrGeofence((event) => {
417
+ console.log(event.eventType, event.geofence?.name);
418
+ });
419
+ ```
420
+
421
+ ---
422
+
423
+ ### `usePointrBuildingClick(callback)`
424
+
425
+ Subscribes to building-tap events on the map.
426
+
427
+ **Parameters:**
428
+ - `callback` (`(event: PTRBuildingClickEvent) => void`)
429
+
430
+ ```typescript
431
+ import { usePointrBuildingClick } from 'react-native-pointr';
432
+
433
+ usePointrBuildingClick((event) => {
434
+ console.log('Building tapped:', event.internalIdentifier, event.title);
435
+ });
436
+ ```
437
+
438
+ ---
439
+
440
+ ### `usePointrSiteClick(callback)`
441
+
442
+ Subscribes to site-tap events on the map.
443
+
444
+ **Parameters:**
445
+ - `callback` (`(event: PTRSiteClickEvent) => void`)
446
+
447
+ ```typescript
448
+ import { usePointrSiteClick } from 'react-native-pointr';
449
+
450
+ usePointrSiteClick((event) => {
451
+ console.log('Site tapped:', event.internalIdentifier, event.title);
452
+ });
453
+ ```
454
+
455
+ ---
456
+
457
+ ## PTRMapWidget Component
458
+
459
+ `PTRMapWidget` is a typed React component (with `forwardRef`) that wraps the native map view.
460
+
461
+ **Import:**
264
462
  ```typescript
265
- import { requireNativeComponent } from 'react-native';
463
+ import { PTRMapWidget } from 'react-native-pointr';
464
+ import type { PTRMapWidgetProps } from 'react-native-pointr';
465
+ ```
466
+
467
+ ### Props
468
+
469
+ | Prop | Type | Description |
470
+ |------|------|-------------|
471
+ | `style` | `ViewStyle` | Component styling |
472
+ | `command` | `PTRCommand` | Declarative command executed automatically on mount |
473
+ | `sdkConfig` | `PTRConfiguration` | When provided, initializes the SDK without a prior `pointrSdk.initialize()` call |
474
+ | `mapWidgetConfig` | `PTRMapWidgetConfiguration` | UI configuration applied before the widget renders |
475
+ | `onMapWidgetDidEndLoading` | `(event: NativeSyntheticEvent<PTRMapCommandResponse>) => void` | Fired when the map finishes loading a command |
476
+ | `onWayfindingEvent` | `(event: NativeSyntheticEvent<PTRWayfindingEvent>) => void` | Fired during wayfinding (start / end / cancel) |
477
+
478
+ ### Declarative usage (recommended)
479
+
480
+ Pass `command`, `sdkConfig`, and `mapWidgetConfig` as props so the widget handles initialization automatically:
481
+
482
+ ```tsx
483
+ import { PTRMapWidget, PTRSiteCommand } from 'react-native-pointr';
484
+ import type { PTRConfiguration, PTRMapWidgetConfiguration } from 'react-native-pointr';
485
+
486
+ const SDK_CONFIG: PTRConfiguration = {
487
+ clientId: '<CLIENT_ID>',
488
+ licenseKey: '<LICENSE_KEY>',
489
+ baseUrl: 'https://<your-instance>.pointr.cloud',
490
+ };
491
+
492
+ const MAP_CONFIG: PTRMapWidgetConfiguration = {
493
+ isLevelSelectorEnabled: true,
494
+ isJoystickEnabled: true,
495
+ isExitButtonEnabled: false,
496
+ };
266
497
 
267
- interface PTRMapWidgetProps {
268
- style?: any;
269
- onMapWidgetDidEndLoading?: (event: NativeSyntheticEvent<any>) => void;
498
+ function MapScreen() {
499
+ return (
500
+ <PTRMapWidget
501
+ style={{ flex: 1 }}
502
+ sdkConfig={SDK_CONFIG}
503
+ mapWidgetConfig={MAP_CONFIG}
504
+ command={new PTRSiteCommand('<SITE_ID>')}
505
+ onMapWidgetDidEndLoading={(e) => console.log('Loaded', e.nativeEvent)}
506
+ onWayfindingEvent={(e) => console.log('Navigation', e.nativeEvent)}
507
+ />
508
+ );
270
509
  }
510
+ ```
271
511
 
272
- const PTRMapWidget = requireNativeComponent<PTRMapWidgetProps>('PTRMapWidget');
512
+ ### Imperative usage
273
513
 
274
- function MyComponent() {
275
- const ref = useRef(null);
514
+ Use `ref` + `executeMapCommand` to dispatch commands after mount:
276
515
 
277
- const handleLoadingComplete = (event) => {
278
- const { command, siteExternalIdentifier, error } = event.nativeEvent;
279
- console.log('Map loaded:', command, siteExternalIdentifier, error);
516
+ ```tsx
517
+ import { useRef } from 'react';
518
+ import { PTRMapWidget, PTRPoiCommand, executeMapCommand } from 'react-native-pointr';
519
+
520
+ function MapScreen() {
521
+ const mapRef = useRef(null);
522
+
523
+ const goToLobby = () => {
524
+ executeMapCommand(mapRef, new PTRPoiCommand('<SITE_ID>', 'Lobby'));
280
525
  };
281
526
 
282
527
  return (
283
- <PTRMapWidget
284
- ref={ref}
528
+ <PTRMapWidget
529
+ ref={mapRef}
285
530
  style={{ flex: 1 }}
286
- onMapWidgetDidEndLoading={handleLoadingComplete}
531
+ onMapWidgetDidEndLoading={(e) => {
532
+ const { command, error } = e.nativeEvent;
533
+ if (error) console.error(command, error);
534
+ }}
287
535
  />
288
536
  );
289
537
  }
@@ -291,464 +539,588 @@ function MyComponent() {
291
539
 
292
540
  ---
293
541
 
294
- ## Command Types
542
+ ## Constants & Enums
295
543
 
296
- ### `PTRCommandType`
544
+ All constants and enums are exported from `react-native-pointr`.
297
545
 
298
- Enum of supported command types.
546
+ ### `PTRLogLevel`
299
547
 
300
548
  ```typescript
301
- enum PTRCommandType {
302
- SITE = "site",
303
- BUILDING = "building",
304
- LEVEL = "level",
305
- POI = "poi",
306
- PATH = "path",
307
- STATIC_PATH = "staticPath",
308
- MARK_MY_CAR_LEVEL = "markMyCarForLevel",
309
- MARK_MY_CAR_SITE = "markMyCarForSite",
310
- SHOW_MY_CAR_SITE = "showMyCarForSite",
311
- START_AND_FOCUS = "startAndFocus"
549
+ enum PTRLogLevel {
550
+ VERBOSE = 0,
551
+ DEBUG = 1,
552
+ INFO = 2,
553
+ WARNING = 3,
554
+ ERROR = 4, // default
555
+ NONE = 5,
312
556
  }
313
557
  ```
314
558
 
315
559
  ---
316
560
 
317
- ### Command Classes
561
+ ### `PTRState`
318
562
 
319
- #### `PTRSiteCommand`
563
+ Tracks the lifecycle state of the SDK as managed by `PointrSdk`.
320
564
 
321
- Shows a specific site on the map.
565
+ ```typescript
566
+ enum PTRState {
567
+ NOT_INITIALIZED = 'NOT_INITIALIZED',
568
+ INITIALIZING = 'INITIALIZING',
569
+ INITIALIZED = 'INITIALIZED',
570
+ STARTING = 'STARTING',
571
+ RUNNING = 'RUNNING',
572
+ STOPPED = 'STOPPED',
573
+ ERROR = 'ERROR',
574
+ }
575
+ ```
576
+
577
+ ---
578
+
579
+ ### `PTRCommandType`
322
580
 
323
- **Constructor:**
324
581
  ```typescript
325
- new PTRSiteCommand(site: string)
582
+ enum PTRCommandType {
583
+ SITE = 'site',
584
+ BUILDING = 'building',
585
+ LEVEL = 'level',
586
+ POI = 'poi',
587
+ PATH = 'path',
588
+ STATIC_PATH = 'staticPath',
589
+ STATIC_WAYFINDING = 'staticWayfinding',
590
+ MARK_MY_CAR_LEVEL = 'markMyCarForLevel',
591
+ MARK_MY_CAR_SITE = 'markMyCarForSite',
592
+ SHOW_MY_CAR_SITE = 'showMyCarForSite',
593
+ START_AND_FOCUS = 'startAndFocus',
594
+ }
326
595
  ```
327
596
 
328
- **Parameters:**
329
- - `site` (string): Site external identifier
597
+ ---
598
+
599
+ ### `PTRAnimationType`
600
+
601
+ Animation type constants for map transitions.
330
602
 
331
- **Example:**
332
603
  ```typescript
333
- import { PTRSiteCommand } from 'react-native-pointr/src/PTRCommand';
334
- import { showMapWidget } from 'react-native-pointr/src/PTRMapWidgetUtils';
604
+ enum PTRAnimationType {
605
+ NONE = 'none',
606
+ FADE = 'fade',
607
+ SLIDE = 'slide',
608
+ ZOOM = 'zoom',
609
+ }
610
+ ```
611
+
612
+ > **Note:** The "My Car" command constructors (`PTRMarkMyCarLevelCommand`, `PTRMarkMyCarSiteCommand`, `PTRShowMyCarSiteCommand`) accept `animationType` as a plain `number` parameter, not as `PTRAnimationType`. The numeric values `0`, `1`, `2` correspond to none, standard, and fly-over animation respectively.
613
+
614
+ ---
335
615
 
336
- const command = new PTRSiteCommand("site-external-id");
337
- showMapWidget(reactTag, command);
616
+ ### `PTRGeofenceEventType`
617
+
618
+ ```typescript
619
+ enum PTRGeofenceEventType {
620
+ ENTER = 'enter',
621
+ EXIT = 'exit',
622
+ }
338
623
  ```
339
624
 
625
+ > **Note:** `DWELL` is not emitted by the native SDK on either platform.
626
+
340
627
  ---
341
628
 
342
- #### `PTRBuildingCommand`
629
+ ### `PTREvents`
343
630
 
344
- Shows a specific building within a site.
631
+ Event name constants used internally and available for direct `NativeEventEmitter` subscriptions.
345
632
 
346
- **Constructor:**
347
633
  ```typescript
348
- new PTRBuildingCommand(site: string, building: string)
634
+ const PTREvents = {
635
+ ON_POSITION_CALCULATED: 'OnPositionManagerCalculatedLocation',
636
+ ON_BUILDING_CLICKED: 'OnBuildingClicked',
637
+ ON_SITE_CLICKED: 'OnSiteClicked',
638
+ ON_GEOFENCE_EVENT: 'OnGeofenceEvent',
639
+ ON_WAYFINDING_EVENT: 'onWayfindingEvent',
640
+ ON_MAP_WIDGET_DID_END_LOADING:'onMapWidgetDidEndLoading',
641
+ }
349
642
  ```
350
643
 
351
- **Parameters:**
352
- - `site` (string): Site external identifier
353
- - `building` (string): Building external identifier
644
+ ---
645
+
646
+ ### `PTRErrorMessages`
647
+
648
+ Pre-defined error message strings thrown by `PointrSdk` and `executeMapCommand`.
354
649
 
355
- **Example:**
356
650
  ```typescript
357
- const command = new PTRBuildingCommand("site-id", "building-id");
358
- showMapWidget(reactTag, command);
651
+ const PTRErrorMessages = {
652
+ NOT_INITIALIZED: 'Pointr SDK is not initialized. Call initialize() first.',
653
+ NOT_STARTED: 'Pointr SDK is not started. Call start() first.',
654
+ ALREADY_INITIALIZED:'Pointr SDK is already initialized.',
655
+ INVALID_CONFIG: 'Invalid configuration provided.',
656
+ INVALID_COMMAND: 'Invalid map command provided.',
657
+ NO_MAP_REF: 'Map widget reference is required.',
658
+ NATIVE_ERROR: 'Native module error occurred.',
659
+ }
359
660
  ```
360
661
 
361
662
  ---
362
663
 
363
- #### `PTRLevelCommand`
664
+ ## Command Classes
364
665
 
365
- Shows a specific level within a building.
666
+ All command classes are exported from `react-native-pointr`.
667
+
668
+ ### `PTRSiteCommand`
669
+
670
+ Shows a specific site on the map.
366
671
 
367
- **Constructor:**
368
672
  ```typescript
369
- new PTRLevelCommand(site: string, building: string, level: number)
673
+ new PTRSiteCommand(site: string)
370
674
  ```
371
675
 
372
- **Parameters:**
373
- - `site` (string): Site external identifier
374
- - `building` (string): Building external identifier
375
- - `level` (number): Level index
376
-
377
- **Example:**
378
676
  ```typescript
379
- const command = new PTRLevelCommand("site-id", "building-id", 1);
380
- showMapWidget(reactTag, command);
677
+ new PTRSiteCommand('<SITE_EXTERNAL_ID>')
381
678
  ```
382
679
 
383
680
  ---
384
681
 
385
- #### `PTRPoiCommand`
682
+ ### `PTRBuildingCommand`
386
683
 
387
- Shows and focuses on a specific POI (Point of Interest).
684
+ Shows a specific building within a site.
388
685
 
389
- **Constructor:**
390
686
  ```typescript
391
- new PTRPoiCommand(site: string, poi: string)
687
+ new PTRBuildingCommand(site: string, building: string)
392
688
  ```
393
689
 
394
- **Parameters:**
395
- - `site` (string): Site external identifier
396
- - `poi` (string): POI external identifier or name
690
+ ---
691
+
692
+ ### `PTRLevelCommand`
693
+
694
+ Shows a specific level within a building.
397
695
 
398
- **Example:**
399
696
  ```typescript
400
- const command = new PTRPoiCommand("site-id", "Lobby");
401
- showMapWidget(reactTag, command);
697
+ new PTRLevelCommand(site: string, building: string, level: number)
402
698
  ```
403
699
 
404
700
  ---
405
701
 
406
- #### `PTRPathCommand`
702
+ ### `PTRPoiCommand`
407
703
 
408
- Shows navigation path from current location to a POI.
704
+ Focuses the map on a specific POI.
409
705
 
410
- **Constructor:**
411
706
  ```typescript
412
- new PTRPathCommand(site: string, poi: string)
707
+ new PTRPoiCommand(site: string, poi: string)
413
708
  ```
414
709
 
415
- **Parameters:**
416
- - `site` (string): Site external identifier
417
- - `poi` (string): Destination POI external identifier
710
+ - `poi`: POI external identifier or name.
711
+
712
+ ---
713
+
714
+ ### `PTRPathCommand`
715
+
716
+ Shows a navigation path from the user's current location to a POI.
418
717
 
419
- **Example:**
420
718
  ```typescript
421
- const command = new PTRPathCommand("site-id", "Lobby");
422
- showMapWidget(reactTag, command);
719
+ new PTRPathCommand(site: string, poi: string)
423
720
  ```
424
721
 
425
722
  ---
426
723
 
427
- #### `PTRStaticPathCommand`
724
+ ### `PTRStaticPathCommand`
428
725
 
429
726
  Shows a static path between two POIs.
430
727
 
431
- **Constructor:**
432
728
  ```typescript
433
729
  new PTRStaticPathCommand(site: string, fromPoi: string, toPoi: string)
434
730
  ```
435
731
 
436
- **Parameters:**
437
- - `site` (string): Site external identifier
438
- - `fromPoi` (string): Starting POI external identifier
439
- - `toPoi` (string): Destination POI external identifier
732
+ ---
733
+
734
+ ### `PTRStaticWayfindingCommand`
735
+
736
+ Initiates static wayfinding between two POIs.
440
737
 
441
- **Example:**
442
738
  ```typescript
443
- const command = new PTRStaticPathCommand("site-id", "Lobby", "Cafeteria");
444
- showMapWidget(reactTag, command);
739
+ new PTRStaticWayfindingCommand(site: string, sourcePoi: string, destinationPoi: string)
445
740
  ```
446
741
 
447
742
  ---
448
743
 
449
- #### `PTRMarkMyCarLevelCommand`
744
+ ### `PTRMarkMyCarLevelCommand`
450
745
 
451
- Marks "My Car" location on a specific level.
746
+ Marks the "My Car" location at a specific level.
452
747
 
453
- **Constructor:**
454
748
  ```typescript
455
749
  new PTRMarkMyCarLevelCommand(
456
750
  site: string,
457
751
  building: string,
458
752
  level: number,
459
- shouldShowPopup?: boolean,
460
- animationType?: number
753
+ shouldShowPopup?: boolean, // default: true
754
+ animationType?: number // default: 1
461
755
  )
462
756
  ```
463
757
 
464
- **Parameters:**
465
- - `site` (string): Site external identifier
466
- - `building` (string): Building external identifier
467
- - `level` (number): Level index
468
- - `shouldShowPopup` (boolean): Show popup after marking (default: true)
469
- - `animationType` (number): Animation type (default: 1)
470
- - `0`: None
471
- - `1`: Standard
472
- - `2`: Fly over
473
-
474
- **Example:**
475
- ```typescript
476
- const command = new PTRMarkMyCarLevelCommand("site-id", "building-id", 1, true, 1);
477
- showMapWidget(reactTag, command);
478
- ```
479
-
480
758
  ---
481
759
 
482
- #### `PTRMarkMyCarSiteCommand`
760
+ ### `PTRMarkMyCarSiteCommand`
483
761
 
484
- Marks "My Car" location at the current position within a site.
762
+ Marks the "My Car" location at the current position within a site.
485
763
 
486
- **Constructor:**
487
764
  ```typescript
488
765
  new PTRMarkMyCarSiteCommand(
489
766
  site: string,
490
- shouldShowPopup?: boolean,
491
- animationType?: number
767
+ shouldShowPopup?: boolean, // default: true
768
+ animationType?: number // default: 1
492
769
  )
493
770
  ```
494
771
 
495
- **Parameters:**
496
- - `site` (string): Site external identifier
497
- - `shouldShowPopup` (boolean): Show popup after marking (default: true)
498
- - `animationType` (number): Animation type (default: 1)
499
-
500
- **Example:**
501
- ```typescript
502
- const command = new PTRMarkMyCarSiteCommand("site-id", true, 1);
503
- showMapWidget(reactTag, command);
504
- ```
505
-
506
772
  ---
507
773
 
508
- #### `PTRShowMyCarSiteCommand`
774
+ ### `PTRShowMyCarSiteCommand`
509
775
 
510
- Shows the saved "My Car" location.
776
+ Shows the previously saved "My Car" location.
511
777
 
512
- **Constructor:**
513
778
  ```typescript
514
779
  new PTRShowMyCarSiteCommand(
515
780
  site: string,
516
- shouldShowPopup?: boolean,
517
- animationType?: number
781
+ shouldShowPopup?: boolean, // default: true
782
+ animationType?: number // default: 1
518
783
  )
519
784
  ```
520
785
 
521
- **Parameters:**
522
- - `site` (string): Site external identifier
523
- - `shouldShowPopup` (boolean): Show popup (default: true)
524
- - `animationType` (number): Animation type (default: 1)
525
-
526
- **Example:**
527
- ```typescript
528
- const command = new PTRShowMyCarSiteCommand("site-id", true, 1);
529
- showMapWidget(reactTag, command);
530
- ```
531
-
532
786
  ---
533
787
 
534
- #### `PTRStartAndFocusCommand`
788
+ ### `PTRStartAndFocusCommand`
535
789
 
536
- Initializes the SDK and focuses on a location in a single command.
790
+ Combines SDK initialization with a focus command in a single native dispatch. Use for deep-link or notification-driven entry points where the SDK may not yet be running.
791
+
792
+ > **Note:** This command is dispatched via `showMapWidget` / the legacy `UIManager` path and is not a `PTRCommand` subclass. Prefer the declarative `sdkConfig` + `command` props on `PTRMapWidget` for new code.
537
793
 
538
- **Constructor:**
539
794
  ```typescript
540
795
  new PTRStartAndFocusCommand(
541
796
  clientId: string,
542
797
  licenseKey: string,
543
798
  baseUrl: string,
544
- logLevel: number,
799
+ logLevel: number, // default: 4 (ERROR)
545
800
  command: PTRSiteCommand | PTRBuildingCommand | PTRLevelCommand | PTRPoiCommand
546
801
  )
547
802
  ```
548
803
 
549
- **Parameters:**
550
- - `clientId` (string): Client identifier
551
- - `licenseKey` (string): License key
552
- - `baseUrl` (string): Base URL
553
- - `logLevel` (number): Log level (0-4)
554
- - `command` (PTRCommand): Focus command to execute after initialization
555
-
556
- **Example:**
557
804
  ```typescript
558
- const focusCommand = new PTRSiteCommand("site-id");
559
- const command = new PTRStartAndFocusCommand(
560
- "client-id",
561
- "license-key",
562
- "https://your-instance.pointr.cloud",
563
- 3,
564
- focusCommand
805
+ import { PTRStartAndFocusCommand, PTRSiteCommand, showMapWidget } from 'react-native-pointr';
806
+ import { findNodeHandle } from 'react-native';
807
+
808
+ const cmd = new PTRStartAndFocusCommand(
809
+ '<CLIENT_ID>',
810
+ '<LICENSE_KEY>',
811
+ 'https://<your-instance>.pointr.cloud',
812
+ 4,
813
+ new PTRSiteCommand('<SITE_ID>')
565
814
  );
566
- showMapWidget(reactTag, command);
815
+ showMapWidget(findNodeHandle(mapRef.current), cmd);
567
816
  ```
568
817
 
569
818
  ---
570
819
 
571
- ## Utility Functions
820
+ ### Type guards
572
821
 
573
- ### `showMapWidget(reactTag, command)`
822
+ ```typescript
823
+ import {
824
+ isPTRSiteCommand,
825
+ isPTRBuildingCommand,
826
+ isPTRLevelCommand,
827
+ isPTRPoiCommand,
828
+ isPTRPathCommand,
829
+ isPTRStaticPathCommand,
830
+ } from 'react-native-pointr';
831
+ ```
574
832
 
575
- Sends a command to the map widget.
833
+ ---
576
834
 
577
- **Parameters:**
578
- - `reactTag` (number): The native view tag obtained from `findNodeHandle()`
579
- - `command` (PTRCommand | PTRStartAndFocusCommand): The command to execute
835
+ ## Types
836
+
837
+ All types are exported from `react-native-pointr`.
838
+
839
+ ### `PTRConfiguration`
580
840
 
581
- **Example:**
582
841
  ```typescript
583
- import { findNodeHandle } from 'react-native';
584
- import { showMapWidget } from 'react-native-pointr/src/PTRMapWidgetUtils';
585
- import { PTRSiteCommand } from 'react-native-pointr/src/PTRCommand';
586
-
587
- const showSite = () => {
588
- const reactTag = findNodeHandle(mapWidgetRef.current);
589
- if (reactTag) {
590
- const command = new PTRSiteCommand("site-id");
591
- showMapWidget(reactTag, command);
592
- }
593
- };
842
+ interface PTRConfiguration {
843
+ readonly clientId: string;
844
+ readonly licenseKey: string;
845
+ readonly baseUrl: string;
846
+ readonly logLevel?: PTRLogLevel; // default: PTRLogLevel.ERROR
847
+ }
594
848
  ```
595
849
 
596
850
  ---
597
851
 
598
- ## Event Listeners
852
+ ### `PTRMapWidgetConfiguration`
853
+
854
+ Parameter names must match the boolean properties of `PTRMapWidgetConfiguration` declared in the native SDK. See [iOS SDK reference](https://pointr.blob.core.windows.net/api-ref/ios/9.6/Classes/PTRMapWidgetConfiguration.html) and [Android SDK reference](https://pointr.blob.core.windows.net/api-ref/android/9.6/-pointr%20-android%20-s-d-k%209.6.0/com.pointrlabs.ui.map.viewmodels/-p-t-r-map-widget-configuration/index.html) for the full list.
599
855
 
600
- ### Position Manager Events
856
+ ```typescript
857
+ interface PTRMapWidgetConfiguration {
858
+ readonly isLevelSelectorEnabled?: boolean;
859
+ readonly isLoadingViewEnabled?: boolean;
860
+ readonly isMapTrackingModeButtonEnabled?: boolean;
861
+ readonly isExitButtonEnabled?: boolean;
862
+ readonly isSplashScreenEnabled?: boolean;
863
+ readonly isOnboardingEnabled?: boolean;
864
+ readonly isJoystickEnabled?: boolean;
865
+ readonly isToastMessagesEnabled?: boolean;
866
+ readonly isInfoButtonEnabled?: boolean;
867
+ readonly isLocationIndicatorEnabled?: boolean;
868
+ readonly shouldFocusOnFirstUserPosition?: boolean;
869
+ readonly isQuickAccessEnabled?: boolean;
870
+ readonly isAppBannerEnabled?: boolean;
871
+ }
872
+ ```
601
873
 
602
- Subscribe to position updates using the native event emitter.
874
+ ---
603
875
 
604
- **Events:**
605
- - `OnPositionManagerCalculatedLocation`: Fired when a new position is calculated
606
- - `OnBuildingClicked`: Fired when a building is tapped on the map
607
- - `OnSiteClicked`: Fired when a site is tapped on the map
608
- - `OnGeofenceEvent`: Fired when a geofence enter/exit event occurs
876
+ ### `PTRPosition`
609
877
 
610
- **Example:**
611
878
  ```typescript
612
- import { NativeEventEmitter, NativeModules } from 'react-native';
879
+ interface PTRPosition {
880
+ lat: number; // Latitude
881
+ lon: number; // Longitude
882
+ isValid: boolean;
883
+ sid: string; // Site identifier
884
+ bid: string; // Building identifier
885
+ lvl: number; // Level index
886
+ }
887
+ ```
613
888
 
614
- const PTREventEmitter = NativeModules.PTRNativePointrLibrary;
615
- const eventEmitter = new NativeEventEmitter(PTREventEmitter);
889
+ ---
616
890
 
617
- // Listen to position updates
618
- const subscription = eventEmitter.addListener(
619
- 'OnPositionManagerCalculatedLocation',
620
- (location) => {
621
- console.log('New position:', location);
622
- }
623
- );
891
+ ### `PTRPositionEvent`
624
892
 
625
- // Listen to building clicks
626
- const buildingSubscription = eventEmitter.addListener(
627
- 'OnBuildingClicked',
628
- (building) => {
629
- console.log('Building clicked:', building);
630
- }
631
- );
893
+ The TypeScript return type of `usePointrPosition` and the argument type of `pointrSdk.onPositionUpdate` callbacks. The actual runtime payload is the raw native event object with the fields listed under [`getCurrentLocation()`](#getcurrentlocation) rather than a wrapped `PTRPosition`.
632
894
 
633
- // Listen to geofence events
634
- const geofenceSubscription = eventEmitter.addListener(
635
- 'OnGeofenceEvent',
636
- (geofenceEvent) => {
637
- console.log('Geofence event:', geofenceEvent);
638
- }
639
- );
895
+ ---
896
+
897
+ ### `PTRPoi`
898
+
899
+ ```typescript
900
+ interface PTRPoi {
901
+ identifier: string;
902
+ externalIdentifier: string;
903
+ name: string;
904
+ typeCode: string;
905
+ position: PTRPosition;
906
+ geometry: PTRGeometry;
907
+ attributes: PTRPoiAttributes;
908
+ }
640
909
 
641
- // Clean up
642
- subscription.remove();
643
- buildingSubscription.remove();
644
- geofenceSubscription.remove();
910
+ interface PTRPoiAttributes {
911
+ logo?: string;
912
+ images?: string[];
913
+ description?: string;
914
+ extra?: Record<string, any>;
915
+ [key: string]: any;
916
+ }
645
917
  ```
646
918
 
647
919
  ---
648
920
 
649
- ### Geofence Events
921
+ ### `PTRGeometry`
650
922
 
651
- The `OnGeofenceEvent` is fired when the user enters or exits a geofence area. This feature requires the Pointr SDK to be running and geofence listeners are automatically registered when calling `start()`.
923
+ Union of all supported geometry shapes.
652
924
 
653
- **Event Structure:**
654
925
  ```typescript
655
- {
656
- eventType: 'enter' | 'exit', // Event type
657
- timestamp?: number, // Unix timestamp in milliseconds (iOS only)
658
- geofence: {
659
- id: string, // Internal geofence ID
660
- externalId: string, // External geofence ID
661
- name: string, // Geofence name
662
- geofenceType: 'beacon' | 'gps', // Type of geofence
663
- position: {
664
- latitude: number, // Geofence latitude
665
- longitude: number // Geofence longitude
666
- }
667
- },
668
- geofenceNotification?: { // Optional notification data
669
- id: string,
670
- message: string,
671
- timestamp: number // Unix timestamp in milliseconds (Android)
672
- // or milliseconds (iOS)
673
- }
926
+ type PTRGeometry =
927
+ | PTRGeoPoint
928
+ | PTRGeoPolygon
929
+ | PTRGeoMultiPoint
930
+ | PTRGeoLineString
931
+ | PTRGeoMultiLineString
932
+ | PTRGeoMultiPolygon
933
+ | PTRGeoCircle;
934
+ ```
935
+
936
+ | Type | Key fields |
937
+ |------|-----------|
938
+ | `PTRGeoPoint` | `type: 'Point'`, `lat`, `lon` |
939
+ | `PTRGeoPolygon` | `type: 'Polygon'`, `outer: PTRGeoPoint[]`, `inners: PTRGeoPoint[][]` |
940
+ | `PTRGeoMultiPoint` | `type: 'MultiPoint'`, `points: PTRGeoPoint[]` |
941
+ | `PTRGeoLineString` | `type: 'LineString'`, `points: PTRGeoPoint[]` |
942
+ | `PTRGeoMultiLineString` | `type: 'MultiLineString'`, `lines: PTRGeoPoint[][]` |
943
+ | `PTRGeoMultiPolygon` | `type: 'MultiPolygon'`, `polygons: PTRGeoPolygon[]` |
944
+ | `PTRGeoCircle` | `type: 'Circle'`, `center: PTRGeoPoint`, `radius: number` |
945
+
946
+ ---
947
+
948
+ ### `PTRWayfindingEvent`
949
+
950
+ Fired via `onWayfindingEvent` prop on `PTRMapWidget`.
951
+
952
+ ```typescript
953
+ interface PTRWayfindingEvent {
954
+ type: -1 | 0 | 1; // -1 Cancelled, 0 Started, 1 Ended
955
+ poi: PTRPoi; // Destination POI
674
956
  }
675
957
  ```
676
958
 
677
- **Platform Differences:**
678
- - iOS: Includes `timestamp` field at the root level with milliseconds since epoch
679
- - Android: Timestamp only available in `geofenceNotification` object
680
- - Both platforms support beacon and GPS geofence types
959
+ | `type` value | Meaning |
960
+ |-----|---------|
961
+ | `-1` | Wayfinding cancelled by user |
962
+ | `0` | Wayfinding started |
963
+ | `1` | Wayfinding ended (completed or dismissed) |
964
+
965
+ ---
966
+
967
+ ### `PTRMapCommandResponse`
968
+
969
+ Payload of the `onMapWidgetDidEndLoading` event.
681
970
 
682
- **Example:**
683
971
  ```typescript
684
- import React, { useEffect } from 'react';
685
- import { NativeEventEmitter, NativeModules } from 'react-native';
972
+ interface PTRMapCommandResponse {
973
+ readonly command: string;
974
+ readonly siteExternalIdentifier?: string;
975
+ readonly error?: string;
976
+ }
977
+ ```
978
+
979
+ ---
980
+
981
+ ### `PTRGeofenceEvent`
686
982
 
687
- function GeofenceMonitor() {
688
- useEffect(() => {
689
- const eventEmitter = new NativeEventEmitter(
690
- NativeModules.PTRNativePointrLibrary
691
- );
692
-
693
- const geofenceSubscription = eventEmitter.addListener(
694
- 'OnGeofenceEvent',
695
- (geofenceEvent) => {
696
- console.log(`Geofence ${geofenceEvent.eventType}: ${geofenceEvent.geofence.name}`);
697
- console.log(`Type: ${geofenceEvent.geofence.geofenceType}`);
698
- console.log(`Location: ${geofenceEvent.geofence.position.latitude}, ${geofenceEvent.geofence.position.longitude}`);
699
-
700
- if (geofenceEvent.geofenceNotification) {
701
- console.log(`Notification: ${geofenceEvent.geofenceNotification.message}`);
702
- }
703
-
704
- if (geofenceEvent.timestamp) {
705
- console.log(`Timestamp: ${new Date(geofenceEvent.timestamp).toISOString()}`);
706
- }
707
- }
708
- );
709
-
710
- return () => {
711
- geofenceSubscription.remove();
712
- };
713
- }, []);
714
-
715
- return null;
716
- }
717
- ```
718
-
719
- ---
720
-
721
- ### Map Widget Load Events
722
-
723
- The `onMapWidgetDidEndLoading` prop receives events when commands complete.
724
-
725
- **Event Object:**
726
983
  ```typescript
727
- {
728
- command: string, // Command type that was executed
729
- siteExternalIdentifier?: string,
730
- buildingExternalIdentifier?: string,
731
- levelIndex?: number,
732
- poiExternalIdentifier?: string,
733
- error?: string // Error message if command failed
984
+ interface PTRGeofenceEvent {
985
+ readonly eventType: 'enter' | 'exit';
986
+ readonly geofence: PTRGeofence;
987
+ readonly geofenceNotification?: PTRGeofenceNotification;
988
+ readonly timestamp?: number; // iOS only — milliseconds since epoch at root level
989
+ readonly geofenceType?: string; // Android only — 'beacon' | 'gps' at root level
990
+ }
991
+
992
+ interface PTRGeofence {
993
+ readonly id: string;
994
+ readonly externalId: string;
995
+ readonly name: string;
996
+ readonly geofenceType?: string; // iOS only — 'beacon' | 'gps' inside geofence object
997
+ readonly position: {
998
+ readonly latitude: number;
999
+ readonly longitude: number;
1000
+ };
1001
+ }
1002
+
1003
+ interface PTRGeofenceNotification {
1004
+ readonly id: string;
1005
+ readonly message: string;
1006
+ readonly timestamp: number; // milliseconds
734
1007
  }
735
1008
  ```
736
1009
 
737
- **Example:**
1010
+ **Platform differences:**
1011
+ - iOS: `timestamp` (milliseconds since epoch) is present at the event root level; `geofenceType` is inside the `geofence` object.
1012
+ - Android: No `timestamp` at the root level; `geofenceType` is at the event root level (not inside `geofence`).
1013
+
1014
+ ---
1015
+
1016
+ ### `PTRBuildingClickEvent` / `PTRSiteClickEvent`
1017
+
1018
+ Both events share the same payload shape emitted by the native SDK:
1019
+
738
1020
  ```typescript
739
- const handleMapWidgetDidEndLoading = (event) => {
740
- const { command, siteExternalIdentifier, error } = event.nativeEvent;
741
-
742
- if (error) {
743
- console.error(`Command ${command} failed:`, error);
744
- } else {
745
- console.log(`Command ${command} completed for site:`, siteExternalIdentifier);
746
- }
747
- };
1021
+ interface PTRBuildingClickEvent {
1022
+ readonly internalIdentifier: string;
1023
+ readonly externalIdentifier: string;
1024
+ readonly title: string;
1025
+ }
748
1026
 
1027
+ interface PTRSiteClickEvent {
1028
+ readonly internalIdentifier: string;
1029
+ readonly externalIdentifier: string;
1030
+ readonly title: string;
1031
+ }
1032
+ ```
1033
+
1034
+ ---
1035
+
1036
+ ## Event Listeners
1037
+
1038
+ There are three ways to subscribe to SDK events, in order of preference:
1039
+
1040
+ ### 1. React Hooks (recommended)
1041
+
1042
+ ```typescript
1043
+ import {
1044
+ usePointrPosition,
1045
+ usePointrGeofence,
1046
+ usePointrBuildingClick,
1047
+ usePointrSiteClick,
1048
+ } from 'react-native-pointr';
1049
+
1050
+ // Position
1051
+ const position = usePointrPosition();
1052
+
1053
+ // Geofence
1054
+ usePointrGeofence((event) => {
1055
+ console.log(event.eventType, event.geofence?.name);
1056
+ });
1057
+
1058
+ // Building click
1059
+ usePointrBuildingClick((event) => {
1060
+ console.log('Building:', event.internalIdentifier, event.title);
1061
+ });
1062
+
1063
+ // Site click
1064
+ usePointrSiteClick((event) => {
1065
+ console.log('Site:', event.internalIdentifier, event.title);
1066
+ });
1067
+ ```
1068
+
1069
+ ---
1070
+
1071
+ ### 2. `PointrSdk` subscription methods
1072
+
1073
+ ```typescript
1074
+ import { pointrSdk } from 'react-native-pointr';
1075
+
1076
+ useEffect(() => {
1077
+ const sub = pointrSdk.onPositionUpdate((pos) => {
1078
+ console.log(pos);
1079
+ });
1080
+ return () => sub.remove();
1081
+ }, []);
1082
+ ```
1083
+
1084
+ Available methods: `onPositionUpdate`, `onBuildingClick`, `onSiteClick`, `onGeofenceEvent`.
1085
+
1086
+ ---
1087
+
1088
+ ### 3. Direct `NativeEventEmitter` (advanced)
1089
+
1090
+ ```typescript
1091
+ import { NativeEventEmitter, NativeModules } from 'react-native';
1092
+ import { PTREvents } from 'react-native-pointr';
1093
+
1094
+ const emitter = new NativeEventEmitter(NativeModules.PTRNativePointrLibrary);
1095
+
1096
+ const sub = emitter.addListener(PTREvents.ON_POSITION_CALCULATED, (pos) => {
1097
+ console.log(pos);
1098
+ });
1099
+ // cleanup:
1100
+ sub.remove();
1101
+ ```
1102
+
1103
+ ---
1104
+
1105
+ ### Map widget events
1106
+
1107
+ The `PTRMapWidget` component exposes two event callbacks as props:
1108
+
1109
+ - **`onMapWidgetDidEndLoading`** — fires when the map finishes loading a command.
1110
+ - **`onWayfindingEvent`** — fires during wayfinding (started / ended / cancelled).
1111
+
1112
+ ```tsx
749
1113
  <PTRMapWidget
750
- ref={ref}
751
- onMapWidgetDidEndLoading={handleMapWidgetDidEndLoading}
1114
+ style={{ flex: 1 }}
1115
+ onMapWidgetDidEndLoading={(e) => {
1116
+ const { command, siteExternalIdentifier, error } = e.nativeEvent;
1117
+ if (error) console.error(`Command ${command} failed:`, error);
1118
+ }}
1119
+ onWayfindingEvent={(e) => {
1120
+ const { type, poi } = e.nativeEvent;
1121
+ // type: -1 cancelled, 0 started, 1 ended
1122
+ console.log('Wayfinding', type, poi.name);
1123
+ }}
752
1124
  />
753
1125
  ```
754
1126
 
@@ -756,129 +1128,235 @@ const handleMapWidgetDidEndLoading = (event) => {
756
1128
 
757
1129
  ## Complete Example
758
1130
 
759
- ```typescript
760
- import React, { useRef, useEffect } from 'react';
1131
+ ### Declarative (recommended for simple integrations)
1132
+
1133
+ Pass `sdkConfig`, `mapWidgetConfig`, and `command` directly as props. No hook or imperative call is needed.
1134
+
1135
+ ```tsx
1136
+ import React from 'react';
1137
+ import { StyleSheet } from 'react-native';
1138
+ import { PTRMapWidget, PTRSiteCommand } from 'react-native-pointr';
1139
+ import type { PTRConfiguration, PTRMapWidgetConfiguration } from 'react-native-pointr';
1140
+
1141
+ const SDK_CONFIG: PTRConfiguration = {
1142
+ clientId: '<CLIENT_ID>',
1143
+ licenseKey: '<LICENSE_KEY>',
1144
+ baseUrl: 'https://<your-instance>.pointr.cloud',
1145
+ };
1146
+
1147
+ const MAP_CONFIG: PTRMapWidgetConfiguration = {
1148
+ isLevelSelectorEnabled: true,
1149
+ isJoystickEnabled: true,
1150
+ isExitButtonEnabled: false,
1151
+ isQuickAccessEnabled: true,
1152
+ };
1153
+
1154
+ export default function MapScreen() {
1155
+ return (
1156
+ <PTRMapWidget
1157
+ style={styles.map}
1158
+ sdkConfig={SDK_CONFIG}
1159
+ mapWidgetConfig={MAP_CONFIG}
1160
+ command={new PTRSiteCommand('<SITE_EXTERNAL_ID>')}
1161
+ onMapWidgetDidEndLoading={(e) => {
1162
+ const { command, error } = e.nativeEvent;
1163
+ if (error) console.error(`${command} failed:`, error);
1164
+ else console.log(`${command} loaded`);
1165
+ }}
1166
+ onWayfindingEvent={(e) => {
1167
+ const { type, poi } = e.nativeEvent;
1168
+ console.log('Wayfinding event', type, 'to', poi.name);
1169
+ }}
1170
+ />
1171
+ );
1172
+ }
1173
+
1174
+ const styles = StyleSheet.create({
1175
+ map: { flex: 1 },
1176
+ });
1177
+ ```
1178
+
1179
+ ### App-level SDK management (hooks)
1180
+
1181
+ Use `usePointrSdk` when you need SDK state, position updates, or other events outside of the map widget.
1182
+
1183
+ ```tsx
1184
+ import React, { useState } from 'react';
1185
+ import { Text, View, TouchableOpacity, StyleSheet } from 'react-native';
761
1186
  import {
762
- requireNativeComponent,
763
- findNodeHandle,
764
- NativeModules,
765
- NativeEventEmitter,
766
- View
767
- } from 'react-native';
768
- import {
1187
+ PTRMapWidget,
769
1188
  PTRSiteCommand,
770
- showMapWidget,
771
- getPois,
772
- type PTRPoi
1189
+ usePointrSdk,
773
1190
  } from 'react-native-pointr';
1191
+ import type { PTRConfiguration } from 'react-native-pointr';
774
1192
 
775
- const PTRMapWidget = requireNativeComponent('PTRMapWidget');
776
-
777
- function PointrMapExample() {
778
- const mapRef = useRef(null);
1193
+ const SDK_CONFIG: PTRConfiguration = {
1194
+ clientId: '<CLIENT_ID>',
1195
+ licenseKey: '<LICENSE_KEY>',
1196
+ baseUrl: 'https://<your-instance>.pointr.cloud',
1197
+ };
779
1198
 
780
- useEffect(() => {
781
- // Initialize SDK
782
- NativeModules.PTRNativePointrLibrary.initialize(
783
- "client-id",
784
- "license-key",
785
- "https://your-instance.pointr.cloud",
786
- 3
787
- );
788
-
789
- // Start SDK
790
- NativeModules.PTRNativePointrLibrary.start((state) => {
791
- console.log('Pointr state:', state);
792
-
793
- if (state === 'running') {
794
- // Show site when SDK is running
795
- const reactTag = findNodeHandle(mapRef.current);
796
- if (reactTag) {
797
- const command = new PTRSiteCommand("site-id");
798
- showMapWidget(reactTag, command);
799
- }
800
-
801
- // Fetch POIs for the site
802
- fetchPOIs();
803
- }
804
- });
805
-
806
- // Listen to position updates
807
- const eventEmitter = new NativeEventEmitter(
808
- NativeModules.PTRNativePointrLibrary
809
- );
810
- const subscription = eventEmitter.addListener(
811
- 'OnPositionManagerCalculatedLocation',
812
- (location) => {
813
- console.log('Position update:', location);
814
- }
815
- );
816
-
817
- // Listen to geofence events
818
- const geofenceSubscription = eventEmitter.addListener(
819
- 'OnGeofenceEvent',
820
- (event) => {
821
- console.log(`Geofence ${event.eventType}: ${event.geofence.name}`);
822
- }
823
- );
824
-
825
- return () => {
826
- subscription.remove();
827
- geofenceSubscription.remove();
828
- NativeModules.PTRNativePointrLibrary.stop();
829
- };
830
- }, []);
831
-
832
- const fetchPOIs = async () => {
833
- try {
834
- const pois: PTRPoi[] = await getPois('site-id');
835
- console.log(`Found ${pois.length} POIs`);
836
- pois.forEach(poi => {
837
- console.log(`${poi.name} at level ${poi.position.lvl}`);
838
- });
839
- } catch (error) {
840
- console.error('Failed to fetch POIs:', error);
841
- }
842
- };
1199
+ export default function App() {
1200
+ const [showMap, setShowMap] = useState(false);
1201
+ const { sdk, isStarted, state } = usePointrSdk(SDK_CONFIG);
843
1202
 
844
- const handleMapLoaded = (event) => {
845
- const { command, error } = event.nativeEvent;
846
- console.log('Map command completed:', command, error);
1203
+ const checkPosition = async () => {
1204
+ const loc = await sdk.getCurrentLocation();
1205
+ if (loc) console.log(loc.latitude, loc.longitude, loc.levelIndex);
847
1206
  };
848
1207
 
849
1208
  return (
850
- <View style={{ flex: 1 }}>
851
- <PTRMapWidget
852
- ref={mapRef}
853
- style={{ flex: 1 }}
854
- onMapWidgetDidEndLoading={handleMapLoaded}
855
- />
1209
+ <View style={styles.container}>
1210
+ <Text>SDK State: {state}</Text>
1211
+ <TouchableOpacity onPress={checkPosition} disabled={!isStarted}>
1212
+ <Text>Get Position</Text>
1213
+ </TouchableOpacity>
1214
+ <TouchableOpacity onPress={() => setShowMap(true)}>
1215
+ <Text>Open Map</Text>
1216
+ </TouchableOpacity>
1217
+
1218
+ {showMap && (
1219
+ <PTRMapWidget
1220
+ style={StyleSheet.absoluteFill}
1221
+ command={new PTRSiteCommand('<SITE_EXTERNAL_ID>')}
1222
+ />
1223
+ )}
856
1224
  </View>
857
1225
  );
858
1226
  }
859
1227
 
860
- export default PointrMapExample;
1228
+ const styles = StyleSheet.create({
1229
+ container: { flex: 1 },
1230
+ });
1231
+ ```
1232
+
1233
+ ---
1234
+
1235
+ ## Legacy API
1236
+
1237
+ The original imperative API is still fully functional. Existing integrations do not need to migrate.
1238
+
1239
+ ### `NativeModules.PTRNativePointrLibrary`
1240
+
1241
+ All native module methods remain available via `NativeModules`:
1242
+
1243
+ ```typescript
1244
+ import { NativeModules } from 'react-native';
1245
+
1246
+ const { PTRNativePointrLibrary } = NativeModules;
1247
+
1248
+ // Initialize
1249
+ PTRNativePointrLibrary.initialize('<CLIENT_ID>', '<LICENSE_KEY>', '<BASE_URL>', 3);
1250
+
1251
+ // Start
1252
+ PTRNativePointrLibrary.start((state: string) => {
1253
+ console.log('State:', state);
1254
+ });
1255
+
1256
+ // Stop
1257
+ PTRNativePointrLibrary.stop();
1258
+
1259
+ // Permissions
1260
+ PTRNativePointrLibrary.requestPermissions();
1261
+ PTRNativePointrLibrary.shouldRequestPermissionsAtStartup(true);
1262
+
1263
+ // Position
1264
+ PTRNativePointrLibrary.getCurrentLocation((location: any) => {
1265
+ if (location) console.log(location);
1266
+ });
1267
+
1268
+ // Map widget configuration
1269
+ PTRNativePointrLibrary.setPointrMapWidgetConfiguration(
1270
+ JSON.stringify({ isExitButtonEnabled: false })
1271
+ );
1272
+
1273
+ // POIs
1274
+ const pois = await PTRNativePointrLibrary.getPois('<SITE_ID>');
1275
+
1276
+ // My car
1277
+ PTRNativePointrLibrary.isMyCarMarked((error: any) => {
1278
+ if (!error) console.log('Car is marked');
1279
+ });
1280
+ ```
1281
+
1282
+ **Native start state strings:**
1283
+
1284
+ | Value | Meaning |
1285
+ |-------|---------|
1286
+ | `"running"` | SDK running successfully |
1287
+ | `"failed registration"` | Registration failed |
1288
+ | `"failed validation"` | Validation failed |
1289
+ | `"failed invalid deep link url"` | Invalid deep link URL |
1290
+ | `"failed no internet"` | No internet connection |
1291
+ | `"off"` | SDK is off |
1292
+
1293
+ ---
1294
+
1295
+ ### `showMapWidget(reactTag, command)`
1296
+
1297
+ Dispatches a command directly by numeric native view tag.
1298
+
1299
+ ```typescript
1300
+ import { showMapWidget } from 'react-native-pointr';
1301
+ import { findNodeHandle } from 'react-native';
1302
+
1303
+ const reactTag = findNodeHandle(mapRef.current);
1304
+ if (reactTag) {
1305
+ showMapWidget(reactTag, new PTRSiteCommand('<SITE_ID>'));
1306
+ }
1307
+ ```
1308
+
1309
+ ---
1310
+
1311
+ ### `getPois(siteId)`
1312
+
1313
+ ```typescript
1314
+ import { getPois } from 'react-native-pointr';
1315
+
1316
+ const pois = await getPois('<SITE_EXTERNAL_ID>');
1317
+ ```
1318
+
1319
+ ---
1320
+
1321
+ ### Legacy command imports
1322
+
1323
+ ```typescript
1324
+ import {
1325
+ LegacyPTRCommand,
1326
+ LegacyPTRSiteCommand,
1327
+ LegacyPTRBuildingCommand,
1328
+ LegacyPTRLevelCommand,
1329
+ LegacyPTRPoiCommand,
1330
+ LegacyPTRPathCommand,
1331
+ LegacyPTRStaticPathCommand,
1332
+ LegacyPTRStaticWayfindingCommand,
1333
+ LegacyPTRMarkMyCarLevelCommand,
1334
+ LegacyPTRMarkMyCarSiteCommand,
1335
+ LegacyPTRShowMyCarSiteCommand,
1336
+ LegacyPTRStartAndFocusCommand,
1337
+ } from 'react-native-pointr';
861
1338
  ```
862
1339
 
863
1340
  ---
864
1341
 
865
1342
  ## Error Handling
866
1343
 
867
- ### Common Errors
1344
+ ### Common errors
868
1345
 
869
- - **"Pointr is not initialized"**: Call `initialize()` before other methods
870
- - **"Pointr is not running"**: Call `start()` and wait for "running" state
871
- - **"Failed to find node handle for ref"**: Ensure ref is properly attached to component
872
- - **"No saved location"**: User hasn't marked a "My Car" location yet
1346
+ | Error | Cause | Fix |
1347
+ |-------|-------|-----|
1348
+ | `"Pointr SDK is not initialized. Call initialize() first."` | `start()` or other methods called before `initialize()` | Call `pointrSdk.initialize(config)` first |
1349
+ | `"Pointr SDK is not started. Call start() first."` | SDK method called before `start()` completes | Wait for `isStarted` / `PTRState.RUNNING` |
1350
+ | `"Pointr SDK is already initialized."` | `initialize()` called twice on the same singleton | Guard with `pointrSdk.isInitialized()` |
1351
+ | `"Map widget reference is required."` | `executeMapCommand` called with a null or unmounted ref | Ensure the ref is attached and the component is mounted |
1352
+ | `"Invalid map command provided."` | `executeMapCommand` called with a command missing a `type` | Use a proper `PTRCommand` subclass |
873
1353
 
874
- ### Best Practices
1354
+ ### Best practices
875
1355
 
876
- 1. Always check the Pointr state before executing commands
877
- 2. Handle errors in callbacks and event handlers
878
- 3. Clean up event listeners when component unmounts
879
- 4. Use `findNodeHandle()` to get the correct native view tag
880
- 5. Initialize SDK before rendering the map widget
881
- 6. Request permissions before starting SDK if needed
1356
+ 1. Use `usePointrSdk(config)` or the declarative `sdkConfig` prop they handle lifecycle errors internally.
1357
+ 2. Always remove event subscriptions in cleanup functions (`useEffect` return / `componentWillUnmount`).
1358
+ 3. Check `isStarted` before calling `getCurrentLocation()` or dispatching map commands imperatively.
1359
+ 4. Initialize SDK once at the application root — `PointrSdk` is a singleton and re-initialization throws.
882
1360
 
883
1361
  ---
884
1362