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 +1035 -557
- package/CHANGELOG.md +12 -0
- package/README.md +75 -216
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/pointr/PTRMapWidgetManager.kt +165 -2
- package/ios/PTRMapWidgetContainerView.swift +185 -2
- package/ios/PTRMapWidgetManager-Bridging.m +6 -0
- package/ios/PTRNativeLibrary.swift +14 -0
- package/package.json +16 -5
- package/react-native-pointr.podspec +1 -1
- package/src/PTRMapWidgetUtils.ts +2 -8
- package/src/api/MapWidgetApi.ts +45 -0
- package/src/api/PointrSdk.ts +241 -0
- package/src/api/index.ts +9 -0
- package/src/commands/index.ts +275 -0
- package/src/components/index.tsx +130 -0
- package/src/constants/index.ts +104 -0
- package/src/hooks/index.ts +8 -0
- package/src/hooks/usePointrEvents.ts +40 -0
- package/src/hooks/usePointrPois.ts +41 -0
- package/src/hooks/usePointrPosition.ts +31 -0
- package/src/hooks/usePointrSdk.ts +41 -0
- package/src/index.tsx +87 -7
- package/src/{PTRPoiManager.ts → managers/PTRPoiManager.ts} +1 -1
- package/src/types/config.ts +57 -0
- package/src/types/events.ts +106 -0
- package/src/types/index.ts +35 -0
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
|
+
## [9.8.0] - 2026-04-16
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Mobile SDK 9.8.0 integration.
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## [9.7.0] - 2026-04-02
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- Mobile SDK 9.7.0 integration.
|
|
17
|
+
|
|
18
|
+
|
|
7
19
|
## [9.6.0] - 2026-03-26
|
|
8
20
|
|
|
9
21
|
### Changed
|
package/README.md
CHANGED
|
@@ -153,243 +153,87 @@ Then run `npm install` or `yarn install` to install the dependencies.
|
|
|
153
153
|
|
|
154
154
|
## 4. Usage
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
Import the package exports and render the map widget:
|
|
157
157
|
|
|
158
158
|
```typescript
|
|
159
|
-
import
|
|
160
|
-
import {
|
|
161
|
-
requireNativeComponent,
|
|
162
|
-
findNodeHandle,
|
|
163
|
-
Button,
|
|
164
|
-
SafeAreaView,
|
|
165
|
-
View,
|
|
166
|
-
ScrollView,
|
|
167
|
-
NativeSyntheticEvent,
|
|
168
|
-
NativeEventEmitter,
|
|
169
|
-
NativeModules
|
|
170
|
-
} from 'react-native';
|
|
171
|
-
import {
|
|
172
|
-
PTRSiteCommand,
|
|
173
|
-
PTRBuildingCommand,
|
|
174
|
-
PTRLevelCommand,
|
|
175
|
-
PTRPoiCommand,
|
|
176
|
-
PTRPathCommand,
|
|
177
|
-
PTRStaticPathCommand,
|
|
178
|
-
PTRMarkMyCarLevelCommand,
|
|
179
|
-
PTRMarkMyCarSiteCommand,
|
|
180
|
-
PTRShowMyCarSiteCommand,
|
|
181
|
-
PTRStartAndFocusCommand
|
|
182
|
-
} from 'react-native-pointr/src/PTRCommand';
|
|
183
|
-
import { showMapWidget } from 'react-native-pointr/src/PTRMapWidgetUtils';
|
|
159
|
+
import { PTRMapWidget, PTRSiteCommand } from 'react-native-pointr';
|
|
160
|
+
import type { PTRConfiguration, PTRMapWidgetConfiguration } from 'react-native-pointr';
|
|
184
161
|
```
|
|
185
162
|
|
|
186
|
-
|
|
163
|
+
The simplest integration passes `sdkConfig`, `mapWidgetConfig`, and `command` as props directly — the widget handles initialization automatically:
|
|
187
164
|
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const PTRMapWidget = requireNativeComponent<PTRMapWidgetProps>('PTRMapWidget');
|
|
195
|
-
```
|
|
165
|
+
```tsx
|
|
166
|
+
const SDK_CONFIG: PTRConfiguration = {
|
|
167
|
+
clientId: '<CLIENT_ID>',
|
|
168
|
+
licenseKey: '<LICENSE_KEY>',
|
|
169
|
+
baseUrl: 'https://<your-instance>.pointr.cloud',
|
|
170
|
+
};
|
|
196
171
|
|
|
197
|
-
|
|
172
|
+
const MAP_CONFIG: PTRMapWidgetConfiguration = {
|
|
173
|
+
isLevelSelectorEnabled: true,
|
|
174
|
+
isExitButtonEnabled: false,
|
|
175
|
+
};
|
|
198
176
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
// Create event emitter for Pointr events (optional)
|
|
214
|
-
const PTREventEmitter = NativeModules.PTRNativePointrLibrary;
|
|
215
|
-
const nativeEventEmitter = new NativeEventEmitter(PTREventEmitter);
|
|
177
|
+
function MapScreen() {
|
|
178
|
+
return (
|
|
179
|
+
<PTRMapWidget
|
|
180
|
+
style={{ flex: 1 }}
|
|
181
|
+
sdkConfig={SDK_CONFIG}
|
|
182
|
+
mapWidgetConfig={MAP_CONFIG}
|
|
183
|
+
command={new PTRSiteCommand('<SITE_EXTERNAL_ID>')}
|
|
184
|
+
onMapWidgetDidEndLoading={(e) => console.log('Map loaded', e.nativeEvent)}
|
|
185
|
+
onWayfindingEvent={(e) => console.log('Navigation event', e.nativeEvent)}
|
|
186
|
+
/>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
216
189
|
```
|
|
217
190
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
```typescript
|
|
221
|
-
function App(): React.JSX.Element {
|
|
222
|
-
const ref = useRef<any>(null);
|
|
223
|
-
|
|
224
|
-
const handleMapWidgetDidEndLoading = (event: NativeSyntheticEvent<any>) => {
|
|
225
|
-
const ptrCommandResponse = event.nativeEvent;
|
|
226
|
-
if (ptrCommandResponse.command === "site") {
|
|
227
|
-
console.log("Map did end loading:", ptrCommandResponse.command, ptrCommandResponse.siteExternalIdentifier, ptrCommandResponse.error);
|
|
228
|
-
} else {
|
|
229
|
-
console.log("Map did end loading with unknown parameters:", ptrCommandResponse);
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
const showSite = () => {
|
|
234
|
-
const reactTag = findNodeHandle(ref.current);
|
|
235
|
-
if (reactTag) {
|
|
236
|
-
let command = new PTRSiteCommand("your-site-id");
|
|
237
|
-
showMapWidget(reactTag, command);
|
|
238
|
-
} else {
|
|
239
|
-
console.error("Failed to find node handle for ref");
|
|
240
|
-
}
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
const showBuilding = () => {
|
|
244
|
-
const reactTag = findNodeHandle(ref.current);
|
|
245
|
-
if (reactTag) {
|
|
246
|
-
let command = new PTRBuildingCommand("your-site-id", "your-building-id");
|
|
247
|
-
showMapWidget(reactTag, command);
|
|
248
|
-
} else {
|
|
249
|
-
console.error("Failed to find node handle for ref");
|
|
250
|
-
}
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
const showLevel = () => {
|
|
254
|
-
const reactTag = findNodeHandle(ref.current);
|
|
255
|
-
if (reactTag) {
|
|
256
|
-
let command = new PTRLevelCommand("your-site-id", "your-building-id", 2);
|
|
257
|
-
showMapWidget(reactTag, command);
|
|
258
|
-
} else {
|
|
259
|
-
console.error("Failed to find node handle for ref");
|
|
260
|
-
}
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
const showPoi = () => {
|
|
264
|
-
const reactTag = findNodeHandle(ref.current);
|
|
265
|
-
if (reactTag) {
|
|
266
|
-
let command = new PTRPoiCommand("your-site-id", "your-poi-id");
|
|
267
|
-
showMapWidget(reactTag, command);
|
|
268
|
-
} else {
|
|
269
|
-
console.error("Failed to find node handle for ref");
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
const showPath = () => {
|
|
274
|
-
const reactTag = findNodeHandle(ref.current);
|
|
275
|
-
if (reactTag) {
|
|
276
|
-
let command = new PTRPathCommand("your-site-id", "your-poi-id");
|
|
277
|
-
showMapWidget(reactTag, command);
|
|
278
|
-
} else {
|
|
279
|
-
console.error("Failed to find node handle for ref");
|
|
280
|
-
}
|
|
281
|
-
};
|
|
191
|
+
To manage the SDK lifecycle and position updates at the app level, use the `usePointrSdk` hook:
|
|
282
192
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (reactTag) {
|
|
286
|
-
let command = new PTRStaticPathCommand("your-site-id", "from-poi-id", "to-poi-id");
|
|
287
|
-
showMapWidget(reactTag, command);
|
|
288
|
-
} else {
|
|
289
|
-
console.error("Failed to find node handle for ref");
|
|
290
|
-
}
|
|
291
|
-
};
|
|
193
|
+
```tsx
|
|
194
|
+
import { usePointrSdk, usePointrPosition } from 'react-native-pointr';
|
|
292
195
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (reactTag) {
|
|
296
|
-
let command = new PTRMarkMyCarLevelCommand("your-site-id", "your-building-id", 1, true);
|
|
297
|
-
showMapWidget(reactTag, command);
|
|
298
|
-
} else {
|
|
299
|
-
console.error("Failed to find node handle for ref");
|
|
300
|
-
}
|
|
301
|
-
};
|
|
196
|
+
function App() {
|
|
197
|
+
const { sdk, isStarted, state } = usePointrSdk(SDK_CONFIG);
|
|
302
198
|
|
|
303
|
-
const
|
|
304
|
-
const reactTag = findNodeHandle(ref.current);
|
|
305
|
-
if (reactTag) {
|
|
306
|
-
let command = new PTRMarkMyCarSiteCommand("your-site-id", true);
|
|
307
|
-
showMapWidget(reactTag, command);
|
|
308
|
-
} else {
|
|
309
|
-
console.error("Failed to find node handle for ref");
|
|
310
|
-
}
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
const showMyCarSite = () => {
|
|
314
|
-
const reactTag = findNodeHandle(ref.current);
|
|
315
|
-
if (reactTag) {
|
|
316
|
-
let command = new PTRShowMyCarSiteCommand("your-site-id", true);
|
|
317
|
-
showMapWidget(reactTag, command);
|
|
318
|
-
} else {
|
|
319
|
-
console.error("Failed to find node handle for ref");
|
|
320
|
-
}
|
|
321
|
-
};
|
|
199
|
+
const position = usePointrPosition(isStarted);
|
|
322
200
|
|
|
323
|
-
const
|
|
324
|
-
const
|
|
325
|
-
if (
|
|
326
|
-
const siteCommand = new PTRSiteCommand("your-site-id");
|
|
327
|
-
const command = new PTRStartAndFocusCommand(
|
|
328
|
-
"your-client-id",
|
|
329
|
-
"your-license-key",
|
|
330
|
-
"your-base-url",
|
|
331
|
-
0,
|
|
332
|
-
siteCommand
|
|
333
|
-
);
|
|
334
|
-
showMapWidget(reactTag, command);
|
|
335
|
-
} else {
|
|
336
|
-
console.error("Failed to find node handle for ref");
|
|
337
|
-
}
|
|
201
|
+
const checkPosition = async () => {
|
|
202
|
+
const loc = await sdk.getCurrentLocation();
|
|
203
|
+
if (loc) console.log(loc.latitude, loc.longitude, loc.levelIndex);
|
|
338
204
|
};
|
|
339
|
-
|
|
340
|
-
return (
|
|
341
|
-
<SafeAreaView style={{ flex: 1 }}>
|
|
342
|
-
<View style={{ flex: 1 }}>
|
|
343
|
-
<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16 }}>
|
|
344
|
-
<Button title="Show Site" onPress={showSite} />
|
|
345
|
-
<View style={{ height: 12 }} />
|
|
346
|
-
<Button title="Show Building" onPress={showBuilding} />
|
|
347
|
-
<View style={{ height: 12 }} />
|
|
348
|
-
<Button title="Show Level" onPress={showLevel} />
|
|
349
|
-
<View style={{ height: 12 }} />
|
|
350
|
-
<Button title="Show POI" onPress={showPoi} />
|
|
351
|
-
<View style={{ height: 12 }} />
|
|
352
|
-
<Button title="Show Path" onPress={showPath} />
|
|
353
|
-
<View style={{ height: 12 }} />
|
|
354
|
-
<Button title="Show Static Path" onPress={showStaticPath} />
|
|
355
|
-
<View style={{ height: 12 }} />
|
|
356
|
-
<Button title="Mark My Car Level" onPress={markMyCarLevel} />
|
|
357
|
-
<View style={{ height: 12 }} />
|
|
358
|
-
<Button title="Mark My Car Site" onPress={markMyCarSite} />
|
|
359
|
-
<View style={{ height: 12 }} />
|
|
360
|
-
<Button title="Show My Car Site" onPress={showMyCarSite} />
|
|
361
|
-
<View style={{ height: 12 }} />
|
|
362
|
-
<Button title="Start & Focus Site" onPress={startAndFocusSite} />
|
|
363
|
-
</ScrollView>
|
|
364
|
-
</View>
|
|
365
|
-
<PTRMapWidget
|
|
366
|
-
ref={ref}
|
|
367
|
-
style={{ flex: 4 }}
|
|
368
|
-
onMapWidgetDidEndLoading={handleMapWidgetDidEndLoading}
|
|
369
|
-
/>
|
|
370
|
-
</SafeAreaView>
|
|
371
|
-
);
|
|
372
205
|
}
|
|
373
|
-
|
|
374
|
-
export default App;
|
|
375
206
|
```
|
|
376
207
|
|
|
377
208
|
### Available Commands
|
|
378
209
|
|
|
379
|
-
|
|
210
|
+
```typescript
|
|
211
|
+
import { PTRSiteCommand, PTRBuildingCommand, PTRLevelCommand, PTRPoiCommand,
|
|
212
|
+
PTRPathCommand, PTRStaticPathCommand, executeMapCommand } from 'react-native-pointr';
|
|
213
|
+
|
|
214
|
+
executeMapCommand(mapRef, new PTRSiteCommand('site-id'));
|
|
215
|
+
executeMapCommand(mapRef, new PTRBuildingCommand('site-id', 'building-id'));
|
|
216
|
+
executeMapCommand(mapRef, new PTRLevelCommand('site-id', 'building-id', 2));
|
|
217
|
+
executeMapCommand(mapRef, new PTRPoiCommand('site-id', 'poi-id'));
|
|
218
|
+
executeMapCommand(mapRef, new PTRPathCommand('site-id', 'poi-id'));
|
|
219
|
+
executeMapCommand(mapRef, new PTRStaticPathCommand('site-id', 'from-poi', 'to-poi'));
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Available Hooks
|
|
380
223
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
224
|
+
```typescript
|
|
225
|
+
import { usePointrSdk, usePointrPosition, usePointrPois,
|
|
226
|
+
usePointrGeofence, usePointrBuildingClick, usePointrSiteClick } from 'react-native-pointr';
|
|
227
|
+
|
|
228
|
+
const { sdk, state, isInitialized, isStarted } = usePointrSdk(config);
|
|
229
|
+
const position = usePointrPosition(enabled);
|
|
230
|
+
const { pois, loading, error, refetch } = usePointrPois(siteId);
|
|
231
|
+
usePointrGeofence((event) => console.log('Geofence:', event));
|
|
232
|
+
usePointrBuildingClick((event) => console.log('Building:', event));
|
|
233
|
+
usePointrSiteClick((event) => console.log('Site:', event));
|
|
234
|
+
```
|
|
391
235
|
|
|
392
|
-
For
|
|
236
|
+
For the complete API see the [API Reference](./API_REFERENCE.md).
|
|
393
237
|
|
|
394
238
|
## 5. Run the App
|
|
395
239
|
|
|
@@ -408,3 +252,18 @@ After completing the setup, start your app by running:
|
|
|
408
252
|
```
|
|
409
253
|
|
|
410
254
|
That’s it! Your package should now be installed and ready to use in your React Native project. For more details on usage and advanced configuration, refer to the [API documentation](https://docs.pointr.tech/docs/8.x/Developer%20Portal/API%20Reference/api%20ref-mobile/).
|
|
255
|
+
## 6. Run the Example App
|
|
256
|
+
|
|
257
|
+
To explore the included demo app:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
cd example/pointr_rn_demo
|
|
261
|
+
npm install
|
|
262
|
+
|
|
263
|
+
# iOS
|
|
264
|
+
cd ios && pod install && cd ..
|
|
265
|
+
npm run ios
|
|
266
|
+
|
|
267
|
+
# Android
|
|
268
|
+
npm run android
|
|
269
|
+
```
|
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:9.
|
|
96
|
+
implementation("com.pointrlabs:pointr:9.8.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'
|
|
@@ -12,6 +12,7 @@ import com.facebook.react.bridge.WritableMap
|
|
|
12
12
|
import com.facebook.react.bridge.WritableNativeMap
|
|
13
13
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
14
14
|
import com.facebook.react.uimanager.ViewGroupManager
|
|
15
|
+
import com.facebook.react.uimanager.annotations.ReactProp
|
|
15
16
|
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
16
17
|
import com.pointrlabs.core.management.DataManager
|
|
17
18
|
import com.pointrlabs.core.management.Pointr
|
|
@@ -57,10 +58,160 @@ class PTRMapWidgetManager(private val reactContext: ReactApplicationContext) :
|
|
|
57
58
|
|
|
58
59
|
private var ptrMapWidgetFragment: PTRMapWidgetFragment? = null
|
|
59
60
|
|
|
61
|
+
// Declarative prop support -----------------------------------------------
|
|
62
|
+
// When a `command` JSON prop is passed on <PTRMapWidget />, the manager
|
|
63
|
+
// auto-executes it once the view is attached rather than requiring a JS
|
|
64
|
+
// imperative command dispatch.
|
|
65
|
+
//
|
|
66
|
+
// JSON shape: { "type": "site"|"building"|"level"|"poi"|"path"|"staticPath"|"staticWayfinding",
|
|
67
|
+
// "site": String, "building"?: String, "level"?: Int,
|
|
68
|
+
// "poi"?: String, "fromPoi"?: String, "toPoi"?: String,
|
|
69
|
+
// "sourcePoi"?: String, "destinationPoi"?: String }
|
|
70
|
+
private var pendingCommandJson: String? = null
|
|
71
|
+
private var pendingSdkConfigJson: String? = null
|
|
72
|
+
private var commandDidExecute = false
|
|
73
|
+
// Reset per view instance in createViewInstance so re-navigation works.
|
|
74
|
+
private var commandTriggered = false
|
|
75
|
+
|
|
76
|
+
@ReactProp(name = "command")
|
|
77
|
+
fun setCommand(view: FrameLayout, commandJson: String?) {
|
|
78
|
+
pendingCommandJson = commandJson
|
|
79
|
+
commandDidExecute = false
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@ReactProp(name = "sdkConfig")
|
|
83
|
+
fun setSdkConfig(view: FrameLayout, configJson: String?) {
|
|
84
|
+
pendingSdkConfigJson = configJson
|
|
85
|
+
applySdkConfigIfNeeded()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@ReactProp(name = "mapWidgetConfig")
|
|
89
|
+
fun setMapWidgetConfig(view: FrameLayout, configJson: String?) {
|
|
90
|
+
if (configJson == null) return
|
|
91
|
+
try {
|
|
92
|
+
val obj = org.json.JSONObject(configJson)
|
|
93
|
+
val cfg = PointrModule.mapWidgetConfiguration
|
|
94
|
+
cfg::class.members.filterIsInstance<kotlin.reflect.KMutableProperty1<*, *>>().forEach { prop ->
|
|
95
|
+
val jsonKey = if (prop.name == "isBuildingLevelSelectorEnabled") "isLevelSelectorEnabled" else prop.name
|
|
96
|
+
if (!obj.has(jsonKey)) return@forEach
|
|
97
|
+
try {
|
|
98
|
+
val value = obj.getBoolean(jsonKey)
|
|
99
|
+
@Suppress("UNCHECKED_CAST")
|
|
100
|
+
(prop as kotlin.reflect.KMutableProperty1<Any, Boolean>).set(cfg, value)
|
|
101
|
+
} catch (_: Exception) {}
|
|
102
|
+
}
|
|
103
|
+
} catch (e: Exception) {
|
|
104
|
+
Log.e(name, "mapWidgetConfig parse error: ${e.message}")
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private fun applySdkConfigIfNeeded() {
|
|
109
|
+
val json = pendingSdkConfigJson ?: return
|
|
110
|
+
try {
|
|
111
|
+
val obj = org.json.JSONObject(json)
|
|
112
|
+
val clientId = obj.optString("clientId")
|
|
113
|
+
val licenseKey = obj.optString("licenseKey")
|
|
114
|
+
val baseUrl = obj.optString("baseUrl")
|
|
115
|
+
if (clientId.isNotEmpty() && licenseKey.isNotEmpty() && baseUrl.isNotEmpty()) {
|
|
116
|
+
val params = PTRParams(clientId, licenseKey, baseUrl)
|
|
117
|
+
val logLevel = obj.optInt("logLevel", Plog.LogLevel.ERROR.ordinal)
|
|
118
|
+
params.logLevel = try {
|
|
119
|
+
Plog.LogLevel.entries[logLevel]
|
|
120
|
+
} catch (_: Exception) {
|
|
121
|
+
Plog.LogLevel.ERROR
|
|
122
|
+
}
|
|
123
|
+
PointrModule.mapWidgetConfiguration.sdkParams = params
|
|
124
|
+
// Initialize the Pointr SDK if it has not been initialized yet
|
|
125
|
+
// (covers the case where sdkConfig is used without a prior pointrSdk.initialize() call).
|
|
126
|
+
if (Pointr.getPointr() == null) {
|
|
127
|
+
Pointr.with(reactContext, params)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
} catch (e: Exception) {
|
|
131
|
+
Log.e(name, "sdkConfig parse error: ${e.message}")
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private fun executePendingCommandIfNeeded() {
|
|
136
|
+
val json = pendingCommandJson ?: return
|
|
137
|
+
if (commandDidExecute) return
|
|
138
|
+
commandDidExecute = true
|
|
139
|
+
Thread {
|
|
140
|
+
// Block on background thread until Pointr is running (semaphore wait).
|
|
141
|
+
// If Pointr never reaches RUNNING (e.g. SDK not initialized), skip entirely
|
|
142
|
+
// so we never block the main thread inside getMapWidgetFragment.
|
|
143
|
+
if (!waitForPointrToRun()) {
|
|
144
|
+
Log.e(name, "Pointr did not reach RUNNING state — skipping declarative command")
|
|
145
|
+
return@Thread
|
|
146
|
+
}
|
|
147
|
+
// Fragment transactions must happen on the main thread.
|
|
148
|
+
android.os.Handler(android.os.Looper.getMainLooper()).post {
|
|
149
|
+
// Guard: activity may be null if the user navigated away while we were waiting.
|
|
150
|
+
if (reactContext.currentActivity == null) {
|
|
151
|
+
Log.e(name, "No current activity — cannot execute declarative map command")
|
|
152
|
+
return@post
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
val obj = org.json.JSONObject(json)
|
|
156
|
+
val type = obj.optString("type")
|
|
157
|
+
val site = obj.optString("site", "")
|
|
158
|
+
val building = obj.optString("building", "")
|
|
159
|
+
val level = obj.optInt("level", 0)
|
|
160
|
+
val poi = obj.optString("poi", "")
|
|
161
|
+
val fromPoi = obj.optString("fromPoi", "")
|
|
162
|
+
val toPoi = obj.optString("toPoi", "")
|
|
163
|
+
val sourcePoi = obj.optString("sourcePoi", "")
|
|
164
|
+
val destinationPoi = obj.optString("destinationPoi", "")
|
|
165
|
+
|
|
166
|
+
val args = com.facebook.react.bridge.Arguments.createArray()
|
|
167
|
+
when (type) {
|
|
168
|
+
PTRMapWidgetCommandType.SITE -> {
|
|
169
|
+
args.pushString(site)
|
|
170
|
+
executeCommand(PTRMapWidgetCommandType.SITE, args)
|
|
171
|
+
}
|
|
172
|
+
PTRMapWidgetCommandType.BUILDING -> {
|
|
173
|
+
args.pushString(site); args.pushString(building)
|
|
174
|
+
executeCommand(PTRMapWidgetCommandType.BUILDING, args)
|
|
175
|
+
}
|
|
176
|
+
PTRMapWidgetCommandType.LEVEL -> {
|
|
177
|
+
args.pushString(site); args.pushString(building); args.pushInt(level)
|
|
178
|
+
executeCommand(PTRMapWidgetCommandType.LEVEL, args)
|
|
179
|
+
}
|
|
180
|
+
PTRMapWidgetCommandType.POI -> {
|
|
181
|
+
args.pushString(site); args.pushString(poi)
|
|
182
|
+
executeCommand(PTRMapWidgetCommandType.POI, args)
|
|
183
|
+
}
|
|
184
|
+
PTRMapWidgetCommandType.PATH -> {
|
|
185
|
+
args.pushString(site); args.pushString(poi)
|
|
186
|
+
executeCommand(PTRMapWidgetCommandType.PATH, args)
|
|
187
|
+
}
|
|
188
|
+
PTRMapWidgetCommandType.STATIC_PATH -> {
|
|
189
|
+
args.pushString(site); args.pushString(fromPoi); args.pushString(toPoi)
|
|
190
|
+
executeCommand(PTRMapWidgetCommandType.STATIC_PATH, args)
|
|
191
|
+
}
|
|
192
|
+
PTRMapWidgetCommandType.STATIC_WAYFINDING -> {
|
|
193
|
+
args.pushString(site); args.pushString(sourcePoi); args.pushString(destinationPoi)
|
|
194
|
+
executeCommand(PTRMapWidgetCommandType.STATIC_WAYFINDING, args)
|
|
195
|
+
}
|
|
196
|
+
else -> Log.w(name, "Unknown command type in command prop: $type")
|
|
197
|
+
}
|
|
198
|
+
} catch (e: Exception) {
|
|
199
|
+
Log.e(name, "Failed to execute command prop: ${e.message}")
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}.start()
|
|
203
|
+
}
|
|
204
|
+
// ------------------------------------------------------------------------
|
|
205
|
+
|
|
60
206
|
private fun getMapWidgetFragment(action: PTRMapWidgetAction): PTRMapWidgetFragment {
|
|
61
|
-
Log.i(name, "Creating map widget fragment with action: $action")
|
|
62
207
|
waitForPointrToRun()
|
|
63
|
-
ptrMapWidgetFragment
|
|
208
|
+
val existingFragment = ptrMapWidgetFragment
|
|
209
|
+
if (existingFragment != null) {
|
|
210
|
+
Log.i(name, "Reusing existing map widget fragment, performing new action: $action")
|
|
211
|
+
existingFragment.performAction(action)
|
|
212
|
+
return existingFragment
|
|
213
|
+
}
|
|
214
|
+
Log.i(name, "Creating new map widget fragment with action: $action")
|
|
64
215
|
ptrMapWidgetFragment = PTRMapWidgetFragment.newInstance(
|
|
65
216
|
(reactContext.currentActivity as FragmentActivity).supportFragmentManager,
|
|
66
217
|
frameLayout.id,
|
|
@@ -77,7 +228,14 @@ class PTRMapWidgetManager(private val reactContext: ReactApplicationContext) :
|
|
|
77
228
|
* Return a FrameLayout which will later hold the Fragment
|
|
78
229
|
*/
|
|
79
230
|
override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
|
|
231
|
+
ptrMapWidgetFragment?.removeListener(this)
|
|
232
|
+
ptrMapWidgetFragment = null
|
|
80
233
|
frameLayout = FrameLayout(reactContext)
|
|
234
|
+
commandDidExecute = false
|
|
235
|
+
commandTriggered = false // reset so re-navigation triggers the command again
|
|
236
|
+
// Start the frame loop immediately so the declarative command prop can
|
|
237
|
+
// fire once the view has been measured and laid out by React Native.
|
|
238
|
+
Choreographer.getInstance().postFrameCallback(frameCallback)
|
|
81
239
|
return frameLayout
|
|
82
240
|
}
|
|
83
241
|
|
|
@@ -679,6 +837,11 @@ class PTRMapWidgetManager(private val reactContext: ReactApplicationContext) :
|
|
|
679
837
|
frameLayout.bottom
|
|
680
838
|
)
|
|
681
839
|
frameLayout.viewTreeObserver.dispatchOnGlobalLayout()
|
|
840
|
+
// Execute a declarative command prop once the view has non-zero dimensions.
|
|
841
|
+
if (!commandTriggered && frameLayout.width > 0 && frameLayout.height > 0) {
|
|
842
|
+
commandTriggered = true
|
|
843
|
+
executePendingCommandIfNeeded()
|
|
844
|
+
}
|
|
682
845
|
Choreographer.getInstance().postFrameCallback(this)
|
|
683
846
|
}
|
|
684
847
|
}
|