@savers_app/react-native-sdk 1.2.5 → 1.2.7
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/README.md +6 -137
- package/lib/module/services/url/urlGenerator.js +20 -6
- package/lib/module/services/url/urlGenerator.js.map +1 -1
- package/lib/typescript/src/services/url/urlGenerator.d.ts +0 -1
- package/lib/typescript/src/services/url/urlGenerator.d.ts.map +1 -1
- package/package.json +13 -7
- package/src/services/url/urlGenerator.ts +18 -7
- package/lib/module/services/device/location.js +0 -47
- package/lib/module/services/device/location.js.map +0 -1
- package/lib/typescript/src/services/device/location.d.ts +0 -8
- package/lib/typescript/src/services/device/location.d.ts.map +0 -1
- package/src/services/device/location.ts +0 -51
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Savers React Native SDK
|
|
2
2
|
|
|
3
|
-
React Native SDK that bridges host app features like maps, dialer, browser, device info, session storage, URL generation, navigation, and WebView event handling. It also includes a robust networking layer
|
|
3
|
+
React Native SDK that bridges host app features like maps, dialer, browser, device info, session storage, URL generation, navigation, and WebView event handling. It also includes a robust networking layer.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
- Open native maps by coordinates with graceful browser fallback
|
|
@@ -8,7 +8,7 @@ React Native SDK that bridges host app features like maps, dialer, browser, devi
|
|
|
8
8
|
- External browser navigation from the host app or from web content
|
|
9
9
|
- Close current screen via global `navigationRef` (React Navigation) using **END_SESSION**
|
|
10
10
|
- Device ID retrieval (via `react-native-device-info`)
|
|
11
|
-
- Optional
|
|
11
|
+
- Optional coordinates enrichment for URL payload
|
|
12
12
|
- Session and keys management (API key, encryption key, program ref code, auth mode)
|
|
13
13
|
- URL generator to compose signed and encrypted Savers mobile URLs
|
|
14
14
|
- AES-GCM encryption for URL payload (AES-256-GCM)
|
|
@@ -28,14 +28,12 @@ yarn add @savers_app/react-native-sdk
|
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
30
|
npm install @react-native-async-storage/async-storage \
|
|
31
|
-
@react-native-community/geolocation \
|
|
32
31
|
@react-native-community/netinfo \
|
|
33
32
|
@react-navigation/native \
|
|
34
33
|
react-native-device-info \
|
|
35
34
|
react-native-aes-gcm-crypto
|
|
36
35
|
# or
|
|
37
36
|
yarn add @react-native-async-storage/async-storage \
|
|
38
|
-
@react-native-community/geolocation \
|
|
39
37
|
@react-native-community/netinfo \
|
|
40
38
|
@react-navigation/native \
|
|
41
39
|
react-native-device-info \
|
|
@@ -61,16 +59,6 @@ yarn add react-native-webview
|
|
|
61
59
|
</array>
|
|
62
60
|
```
|
|
63
61
|
|
|
64
|
-
- Permissions (add keys and descriptions in Info.plist):
|
|
65
|
-
- NSLocationWhenInUseUsageDescription
|
|
66
|
-
|
|
67
|
-
Example:
|
|
68
|
-
|
|
69
|
-
```xml
|
|
70
|
-
<key>NSLocationWhenInUseUsageDescription</key>
|
|
71
|
-
<string>Location access is required to provide location-based features.</string>
|
|
72
|
-
```
|
|
73
|
-
|
|
74
62
|
Ensure native pods are installed and linked for the peer dependencies.
|
|
75
63
|
|
|
76
64
|
## Android Setup
|
|
@@ -79,8 +67,6 @@ Ensure native pods are installed and linked for the peer dependencies.
|
|
|
79
67
|
```xml
|
|
80
68
|
<uses-permission android:name="android.permission.INTERNET" />
|
|
81
69
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
82
|
-
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|
83
|
-
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
|
84
70
|
```
|
|
85
71
|
|
|
86
72
|
Link and configure the peer dependencies according to their documentation.
|
|
@@ -155,48 +141,17 @@ const url = await generateUrl({
|
|
|
155
141
|
},
|
|
156
142
|
// sessionId is optional; when omitted, the SDK uses its stored session id
|
|
157
143
|
// deviceInfo is optional; the SDK builds it internally using device id (and optional coordinates)
|
|
158
|
-
nonce: '<NONCE_TOKEN>',
|
|
159
144
|
});
|
|
160
145
|
// returns https://m.saversapp.com/?pRefCode=...&qP=...
|
|
161
146
|
```
|
|
162
147
|
|
|
163
|
-
Fetch nonce and call generateUrl:
|
|
164
|
-
|
|
165
|
-
```ts
|
|
166
|
-
import { ApiService, generateUrl } from '@savers_app/react-native-sdk';
|
|
167
|
-
|
|
168
|
-
const generator = new ApiService().getNonce({
|
|
169
|
-
extUserId: '12345',
|
|
170
|
-
partnerCode: 'abcde',
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
const { value } = await generator.next();
|
|
174
|
-
if (value?.error) throw new Error(value.error);
|
|
175
|
-
if (!value?.token) throw new Error('Nonce not received');
|
|
176
|
-
|
|
177
|
-
const url = await generateUrl({
|
|
178
|
-
authType: 'PHONE',
|
|
179
|
-
profile: {
|
|
180
|
-
userId: 'USER_ID',
|
|
181
|
-
email: 'jane@example.com',
|
|
182
|
-
firstname: 'Jane',
|
|
183
|
-
lastname: 'Doe',
|
|
184
|
-
phone: '+15555550100',
|
|
185
|
-
pv: '1',
|
|
186
|
-
ev: '1',
|
|
187
|
-
},
|
|
188
|
-
screen: { name: 'Explore', attributes: [{ key: 'offerId', value: '123' }] },
|
|
189
|
-
nonce: value.token,
|
|
190
|
-
});
|
|
191
|
-
```
|
|
192
|
-
|
|
193
148
|
Profile requirements:
|
|
194
149
|
- `userId` and `email` are mandatory
|
|
195
150
|
- `pv` and `ev` are optional (`'0'` or `'1'`)
|
|
196
151
|
- `phone` is mandatory when `authType` is `'PHONE'`
|
|
197
152
|
- `username` is mandatory when `authType` is `'USERNAME'`
|
|
198
153
|
Nonce requirement:
|
|
199
|
-
-
|
|
154
|
+
- The SDK resolves a nonce automatically using `profile.userId` and your `pRefCode`; no manual action is required.
|
|
200
155
|
|
|
201
156
|
Screen requirements:
|
|
202
157
|
- `screen.name` is optional, defaults to `'Explore'`
|
|
@@ -355,29 +310,10 @@ import {
|
|
|
355
310
|
|
|
356
311
|
## Location & Coordinates
|
|
357
312
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
1. **Device location service** (geolocation + permissions):
|
|
361
|
-
|
|
362
|
-
```ts
|
|
363
|
-
import { getDeviceLocation } from '@savers_app/react-native-sdk';
|
|
364
|
-
|
|
365
|
-
const loc = await getDeviceLocation();
|
|
366
|
-
// { lat, lng } or throws if unavailable / permission denied
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
This uses:
|
|
370
|
-
- `@react-native-community/geolocation`
|
|
371
|
-
- `ensureLocationPermission` (Android runtime permissions)
|
|
372
|
-
- `resolveNativeDependencies` to check if the native module is linked
|
|
373
|
-
|
|
374
|
-
2. **Manual coordinates for URL generation**:
|
|
313
|
+
Manual coordinates for URL generation:
|
|
375
314
|
|
|
376
315
|
```ts
|
|
377
|
-
import {
|
|
378
|
-
setLocationCoordinates,
|
|
379
|
-
getLocationCoordinates,
|
|
380
|
-
} from '@savers_app/react-native-sdk';
|
|
316
|
+
import { setLocationCoordinates, getLocationCoordinates } from '@savers_app/react-native-sdk';
|
|
381
317
|
|
|
382
318
|
await setLocationCoordinates(37.7749, -122.4194);
|
|
383
319
|
const coords = await getLocationCoordinates(); // { lat, lng } | null
|
|
@@ -479,76 +415,9 @@ yarn run:ios # uses scripts.run:ios from package.json
|
|
|
479
415
|
yarn run:android # uses scripts.run:android from package.json
|
|
480
416
|
```
|
|
481
417
|
|
|
482
|
-
## Development
|
|
483
|
-
|
|
484
|
-
Common scripts from the root library package (`package.json`):
|
|
485
|
-
|
|
486
|
-
```bash
|
|
487
|
-
# Lint, typecheck, test and build the library
|
|
488
|
-
npm run lint
|
|
489
|
-
npm run typecheck
|
|
490
|
-
npm run test
|
|
491
|
-
npm run prepare # builds the library via react-native-builder-bob
|
|
492
|
-
|
|
493
|
-
# or using yarn
|
|
494
|
-
yarn lint
|
|
495
|
-
yarn typecheck
|
|
496
|
-
yarn test
|
|
497
|
-
yarn prepare
|
|
498
|
-
|
|
499
|
-
# Helper scripts to run the example app from the root
|
|
500
|
-
yarn run:ios
|
|
501
|
-
yarn run:android
|
|
502
|
-
```
|
|
503
|
-
|
|
504
|
-
In the example app workspace (`@saversapp/react-native-sdk-example`), you can also run:
|
|
505
|
-
|
|
506
|
-
```bash
|
|
507
|
-
yarn --workspace @saversapp/react-native-sdk-example run lint
|
|
508
|
-
yarn --workspace @saversapp/react-native-sdk-example run test
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
## Publishing to npm
|
|
512
|
-
|
|
513
|
-
This package is configured to publish to the public npm registry and uses `release-it` for versioning and release automation (see `publishConfig` and `release-it` in `package.json`).
|
|
514
|
-
|
|
515
|
-
Basic flow to publish a new version:
|
|
516
|
-
|
|
517
|
-
```bash
|
|
518
|
-
# 1. Make sure you are logged in to npm
|
|
519
|
-
npm login
|
|
520
|
-
|
|
521
|
-
# 2. Ensure the code passes checks and is built
|
|
522
|
-
yarn lint
|
|
523
|
-
yarn typecheck
|
|
524
|
-
yarn test
|
|
525
|
-
yarn prepare
|
|
526
|
-
|
|
527
|
-
# 3. Run the release script from the repo root
|
|
528
|
-
# This will:
|
|
529
|
-
# - bump the version
|
|
530
|
-
# - create a git tag
|
|
531
|
-
# - publish to npm (using publishConfig.registry)
|
|
532
|
-
# - create a GitHub release
|
|
533
|
-
yarn release
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
If you prefer a fully manual publish flow, you can instead:
|
|
537
|
-
|
|
538
|
-
```bash
|
|
539
|
-
# Bump the version number in package.json (or use `npm version patch|minor|major`)
|
|
540
|
-
npm version patch
|
|
541
|
-
|
|
542
|
-
# Build the library
|
|
543
|
-
yarn prepare
|
|
544
|
-
|
|
545
|
-
# Publish to npm
|
|
546
|
-
npm publish
|
|
547
|
-
```
|
|
548
418
|
|
|
549
419
|
## Troubleshooting
|
|
550
|
-
- Ensure peer dependencies are installed and linked (AsyncStorage,
|
|
551
|
-
- On Android, grant runtime permissions for location when using geolocation.
|
|
420
|
+
- Ensure peer dependencies are installed and linked (AsyncStorage, NetInfo, DeviceInfo, WebView, React Navigation).
|
|
552
421
|
- If `SaversAppSDK.initialized` logs missing modules, install the indicated packages.
|
|
553
422
|
- If AES‑GCM encryption fails, verify that `encryptionKey` is a base64‑encoded 32‑byte (256‑bit) value and that your Android/iOS versions meet the minimum requirements.
|
|
554
423
|
|
|
@@ -10,13 +10,13 @@ import { getSessionId } from "../../data/storage/sessionManager.js";
|
|
|
10
10
|
import { getLocationCoordinates } from "../../data/storage/locationManager.js";
|
|
11
11
|
import { getEncryptionKey, getPRefCode } from "../../data/storage/keysManager.js";
|
|
12
12
|
import { getBaseUrl } from "../../utils/config.js";
|
|
13
|
+
import { ApiService } from "../../data/network/apiService.js";
|
|
13
14
|
export function validateInput(input) {
|
|
14
15
|
const errors = [];
|
|
15
16
|
const auth = input.authType ?? 'PHONE';
|
|
16
17
|
if (!input.profile) errors.push('profile is mandatory');
|
|
17
18
|
if (!input.profile?.userId) errors.push('userId is mandatory');
|
|
18
19
|
if (!input.profile?.email) errors.push('email is mandatory');
|
|
19
|
-
if (!input.nonce) errors.push('nonce is mandatory');
|
|
20
20
|
const pv = input.profile?.pv;
|
|
21
21
|
const ev = input.profile?.ev;
|
|
22
22
|
if (typeof pv !== 'undefined' && pv !== '0' && pv !== '1') {
|
|
@@ -48,6 +48,24 @@ export async function generateUrl(input) {
|
|
|
48
48
|
const deviceId = await getDeviceId();
|
|
49
49
|
const storedLocation = await getLocationCoordinates();
|
|
50
50
|
const sessionId = await getSessionId();
|
|
51
|
+
const programCode = await getPRefCode();
|
|
52
|
+
if (!programCode || programCode === '-') {
|
|
53
|
+
throw new SDKError('pRefCode is mandatory. Initialize SDK with pRefCode.');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// get nonce -- start
|
|
57
|
+
const generator = new ApiService().getNonce({
|
|
58
|
+
extUserId: input.profile?.userId ?? '',
|
|
59
|
+
partnerCode: programCode
|
|
60
|
+
});
|
|
61
|
+
const {
|
|
62
|
+
value
|
|
63
|
+
} = await generator.next();
|
|
64
|
+
if (value?.error) throw new SDKError(String(value.error));
|
|
65
|
+
if (!value?.token) throw new SDKError('Nonce not received');
|
|
66
|
+
const resolvedNonce = value.token;
|
|
67
|
+
// get nonce -- end
|
|
68
|
+
|
|
51
69
|
const deviceInfo = storedLocation != null ? {
|
|
52
70
|
dId: deviceId,
|
|
53
71
|
location: storedLocation
|
|
@@ -60,7 +78,7 @@ export async function generateUrl(input) {
|
|
|
60
78
|
authType: input.authType ?? 'PHONE',
|
|
61
79
|
sessionId: sessionId ?? '',
|
|
62
80
|
deviceInfo,
|
|
63
|
-
nonce:
|
|
81
|
+
nonce: resolvedNonce
|
|
64
82
|
};
|
|
65
83
|
const validation = validateInput({
|
|
66
84
|
...normalized,
|
|
@@ -85,10 +103,6 @@ export async function generateUrl(input) {
|
|
|
85
103
|
throw new SDKError('Encryption key is mandatory. Initialize SDK with encryptionKey.');
|
|
86
104
|
}
|
|
87
105
|
const encoded = await aesEncrypt(json, key);
|
|
88
|
-
const programCode = await getPRefCode();
|
|
89
|
-
if (!programCode || programCode === '-') {
|
|
90
|
-
throw new SDKError('pRefCode is mandatory. Initialize SDK with pRefCode.');
|
|
91
|
-
}
|
|
92
106
|
return `${getBaseUrl()}?pRefCode=${encodeURIComponent(programCode)}&qP=${encodeURIComponent(encoded)}`;
|
|
93
107
|
}
|
|
94
108
|
//# sourceMappingURL=urlGenerator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["SDKError","aesEncrypt","getDeviceId","getSessionId","getLocationCoordinates","getEncryptionKey","getPRefCode","getBaseUrl","validateInput","input","errors","auth","authType","profile","push","userId","email","
|
|
1
|
+
{"version":3,"names":["SDKError","aesEncrypt","getDeviceId","getSessionId","getLocationCoordinates","getEncryptionKey","getPRefCode","getBaseUrl","ApiService","validateInput","input","errors","auth","authType","profile","push","userId","email","pv","ev","username","phone","screenName","screen","name","attrs","attributes","length","ok","generateUrl","deviceId","storedLocation","sessionId","programCode","generator","getNonce","extUserId","partnerCode","value","next","error","String","token","resolvedNonce","deviceInfo","dId","location","normalized","nonce","validation","join","payload","json","JSON","stringify","key","encoded","encodeURIComponent"],"sourceRoot":"../../../../src","sources":["services/url/urlGenerator.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"urlGenerator.d.ts","sourceRoot":"","sources":["../../../../../src/services/url/urlGenerator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"urlGenerator.d.ts","sourceRoot":"","sources":["../../../../../src/services/url/urlGenerator.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,SAAS,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AACvD,MAAM,MAAM,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC;AACjE,MAAM,MAAM,OAAO,GAAG;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;IACf,EAAE,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AACF,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,CAAC;AACtD,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AACF,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,GAAG;IAC9C,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAmCA;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAoElE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savers_app/react-native-sdk",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.7",
|
|
4
4
|
"description": "Cross-platform React Native SDK exposing native features (maps, dial pad, browser), device ID/location and session utilities, a URL generator, and a WebView message bridge to trigger actions from web content.",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"react-native": "./src/index.tsx",
|
|
@@ -33,14 +33,18 @@
|
|
|
33
33
|
"!**/.*"
|
|
34
34
|
],
|
|
35
35
|
"scripts": {
|
|
36
|
-
"
|
|
37
|
-
"
|
|
36
|
+
"example:android:yarn": "yarn workspace @saversapp/react-native-sdk-example android",
|
|
37
|
+
"example:ios:yarn": "yarn workspace @saversapp/react-native-sdk-example ios",
|
|
38
|
+
"example:android:npm": "npm run -w @saversapp/react-native-sdk-example android",
|
|
39
|
+
"example:ios:npm": "npm run -w @saversapp/react-native-sdk-example ios",
|
|
38
40
|
"clean": "del-cli lib",
|
|
39
41
|
"prepare": "bob build",
|
|
40
42
|
"set:sandbox": "cross-env SDK_ENV=sandbox SDK_PACKAGE=@savers_app/react-native-sandbox-sdk node scripts/replace-config.js",
|
|
41
43
|
"set:production": "cross-env SDK_ENV=production SDK_PACKAGE=@savers_app/react-native-sdk node scripts/replace-config.js",
|
|
42
|
-
"prepare:sandbox": "yarn set:sandbox && yarn install && yarn prepare",
|
|
43
|
-
"prepare:production": "yarn set:production && yarn install && yarn prepare",
|
|
44
|
+
"prepare:sandbox:yarn": "del-cli package-lock.json && cross-env PKG_MGR=yarn yarn set:sandbox && yarn install && yarn prepare",
|
|
45
|
+
"prepare:production:yarn": "del-cli package-lock.json && cross-env PKG_MGR=yarn yarn set:production && yarn install && yarn prepare",
|
|
46
|
+
"prepare:sandbox:npm": "cross-env PKG_MGR=npm npm run set:sandbox && npm install && npm run prepare && del-cli package-lock.json && cross-env PKG_MGR=yarn npm run set:sandbox",
|
|
47
|
+
"prepare:production:npm": "cross-env PKG_MGR=npm npm run set:production && npm install && npm run prepare && del-cli package-lock.json && cross-env PKG_MGR=yarn npm run set:production",
|
|
44
48
|
"typecheck": "tsc",
|
|
45
49
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
46
50
|
"test": "jest",
|
|
@@ -62,6 +66,7 @@
|
|
|
62
66
|
},
|
|
63
67
|
"homepage": "https://github.com/pm-code/savers-react-native-sdk#readme",
|
|
64
68
|
"publishConfig": {
|
|
69
|
+
"access": "public",
|
|
65
70
|
"registry": "https://registry.npmjs.org/"
|
|
66
71
|
},
|
|
67
72
|
"devDependencies": {
|
|
@@ -83,7 +88,7 @@
|
|
|
83
88
|
"eslint-config-prettier": "^10.1.8",
|
|
84
89
|
"eslint-plugin-prettier": "^5.5.4",
|
|
85
90
|
"jest": "^29.7.0",
|
|
86
|
-
"lefthook
|
|
91
|
+
"lefthook": "^2.1.4",
|
|
87
92
|
"prettier": "^3.8.1",
|
|
88
93
|
"react": "19.1.0",
|
|
89
94
|
"react-native": "0.81.5",
|
|
@@ -102,7 +107,8 @@
|
|
|
102
107
|
"react-native-device-info": "*"
|
|
103
108
|
},
|
|
104
109
|
"workspaces": [
|
|
105
|
-
"example"
|
|
110
|
+
"example",
|
|
111
|
+
"."
|
|
106
112
|
],
|
|
107
113
|
"packageManager": "yarn@4.11.0",
|
|
108
114
|
"react-native-builder-bob": {
|
|
@@ -9,6 +9,7 @@ import { getLocationCoordinates } from '../../data/storage/locationManager';
|
|
|
9
9
|
import { getEncryptionKey, getPRefCode } from '../../data/storage/keysManager';
|
|
10
10
|
|
|
11
11
|
import { getBaseUrl } from '../../utils/config';
|
|
12
|
+
import { ApiService } from '../../data/network/apiService';
|
|
12
13
|
|
|
13
14
|
export type Attribute = { key: string; value: string };
|
|
14
15
|
export type Screen = { name?: string; attributes?: Attribute[] };
|
|
@@ -38,7 +39,6 @@ export type UrlInput = {
|
|
|
38
39
|
authType?: AuthType;
|
|
39
40
|
deviceInfo?: DeviceInfo;
|
|
40
41
|
sessionId?: string;
|
|
41
|
-
nonce: string;
|
|
42
42
|
};
|
|
43
43
|
export function validateInput(input: UrlInput): {
|
|
44
44
|
ok: boolean;
|
|
@@ -49,7 +49,6 @@ export function validateInput(input: UrlInput): {
|
|
|
49
49
|
if (!input.profile) errors.push('profile is mandatory');
|
|
50
50
|
if (!input.profile?.userId) errors.push('userId is mandatory');
|
|
51
51
|
if (!input.profile?.email) errors.push('email is mandatory');
|
|
52
|
-
if (!input.nonce) errors.push('nonce is mandatory');
|
|
53
52
|
|
|
54
53
|
const pv = input.profile?.pv;
|
|
55
54
|
const ev = input.profile?.ev;
|
|
@@ -86,6 +85,22 @@ export async function generateUrl(input: UrlInput): Promise<string> {
|
|
|
86
85
|
const storedLocation = await getLocationCoordinates();
|
|
87
86
|
const sessionId = await getSessionId();
|
|
88
87
|
|
|
88
|
+
const programCode = await getPRefCode();
|
|
89
|
+
if (!programCode || programCode === '-') {
|
|
90
|
+
throw new SDKError('pRefCode is mandatory. Initialize SDK with pRefCode.');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// get nonce -- start
|
|
94
|
+
const generator = new ApiService().getNonce({
|
|
95
|
+
extUserId: input.profile?.userId ?? '',
|
|
96
|
+
partnerCode: programCode,
|
|
97
|
+
});
|
|
98
|
+
const { value } = await generator.next();
|
|
99
|
+
if (value?.error) throw new SDKError(String(value.error));
|
|
100
|
+
if (!value?.token) throw new SDKError('Nonce not received');
|
|
101
|
+
const resolvedNonce = value.token;
|
|
102
|
+
// get nonce -- end
|
|
103
|
+
|
|
89
104
|
const deviceInfo: DeviceInfo =
|
|
90
105
|
storedLocation != null
|
|
91
106
|
? { dId: deviceId, location: storedLocation }
|
|
@@ -101,7 +116,7 @@ export async function generateUrl(input: UrlInput): Promise<string> {
|
|
|
101
116
|
authType: input.authType ?? 'PHONE',
|
|
102
117
|
sessionId: sessionId ?? '',
|
|
103
118
|
deviceInfo,
|
|
104
|
-
nonce:
|
|
119
|
+
nonce: resolvedNonce,
|
|
105
120
|
};
|
|
106
121
|
|
|
107
122
|
const validation = validateInput({
|
|
@@ -130,10 +145,6 @@ export async function generateUrl(input: UrlInput): Promise<string> {
|
|
|
130
145
|
}
|
|
131
146
|
const encoded = await aesEncrypt(json, key);
|
|
132
147
|
|
|
133
|
-
const programCode = await getPRefCode();
|
|
134
|
-
if (!programCode || programCode === '-') {
|
|
135
|
-
throw new SDKError('pRefCode is mandatory. Initialize SDK with pRefCode.');
|
|
136
|
-
}
|
|
137
148
|
return `${getBaseUrl()}?pRefCode=${encodeURIComponent(
|
|
138
149
|
programCode
|
|
139
150
|
)}&qP=${encodeURIComponent(encoded)}`;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
// Platform Utilities
|
|
4
|
-
import { isAndroid } from "../../utils/platformManager.js";
|
|
5
|
-
import { resolveNativeDependencies } from "../../utils/dependencyManager.js";
|
|
6
|
-
|
|
7
|
-
// Permissions
|
|
8
|
-
import { ensureLocationPermission } from "../permissions/permissionManager.js";
|
|
9
|
-
let locationProvider;
|
|
10
|
-
export function setLocationProvider(provider) {
|
|
11
|
-
locationProvider = provider;
|
|
12
|
-
}
|
|
13
|
-
export const fetchLocation = async () => {
|
|
14
|
-
const {
|
|
15
|
-
Geolocation
|
|
16
|
-
} = resolveNativeDependencies();
|
|
17
|
-
if (!Geolocation) {
|
|
18
|
-
throw new Error('GEOLOCATION_NOT_LINKED: install and link @react-native-community/geolocation');
|
|
19
|
-
}
|
|
20
|
-
return new Promise((resolve, reject) => {
|
|
21
|
-
Geolocation.getCurrentPosition(pos => resolve({
|
|
22
|
-
lat: pos.coords.latitude,
|
|
23
|
-
lng: pos.coords.longitude
|
|
24
|
-
}), err => reject(err), {
|
|
25
|
-
enableHighAccuracy: true,
|
|
26
|
-
timeout: 10000
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
};
|
|
30
|
-
export async function getDeviceLocation() {
|
|
31
|
-
if (locationProvider) {
|
|
32
|
-
return locationProvider();
|
|
33
|
-
}
|
|
34
|
-
if (isAndroid()) {
|
|
35
|
-
const granted = await ensureLocationPermission();
|
|
36
|
-
if (!granted) {
|
|
37
|
-
throw new Error('LOCATION_PERMISSION_DENIED');
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
try {
|
|
41
|
-
const loc = await fetchLocation();
|
|
42
|
-
return loc;
|
|
43
|
-
} catch {
|
|
44
|
-
throw new Error('LOCATION_UNAVAILABLE');
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
//# sourceMappingURL=location.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["isAndroid","resolveNativeDependencies","ensureLocationPermission","locationProvider","setLocationProvider","provider","fetchLocation","Geolocation","Error","Promise","resolve","reject","getCurrentPosition","pos","lat","coords","latitude","lng","longitude","err","enableHighAccuracy","timeout","getDeviceLocation","granted","loc"],"sourceRoot":"../../../../src","sources":["services/device/location.ts"],"mappings":";;AAAA;AACA,SAASA,SAAS,QAAQ,gCAA6B;AACvD,SAASC,yBAAyB,QAAQ,kCAA+B;;AAEzE;AACA,SAASC,wBAAwB,QAAQ,qCAAkC;AAI3E,IAAIC,gBAAyE;AAE7E,OAAO,SAASC,mBAAmBA,CACjCC,QAAmD,EAC7C;EACNF,gBAAgB,GAAGE,QAAQ;AAC7B;AAEA,OAAO,MAAMC,aAAa,GAAG,MAAAA,CAAA,KAAqC;EAChE,MAAM;IAAEC;EAAY,CAAC,GAAGN,yBAAyB,CAAC,CAAC;EACnD,IAAI,CAACM,WAAW,EAAE;IAChB,MAAM,IAAIC,KAAK,CACb,8EACF,CAAC;EACH;EACA,OAAO,IAAIC,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;IACtCJ,WAAW,CAACK,kBAAkB,CAC3BC,GAAwD,IACvDH,OAAO,CAAC;MAAEI,GAAG,EAAED,GAAG,CAACE,MAAM,CAACC,QAAQ;MAAEC,GAAG,EAAEJ,GAAG,CAACE,MAAM,CAACG;IAAU,CAAC,CAAC,EACjEC,GAAY,IAAKR,MAAM,CAACQ,GAAG,CAAC,EAC7B;MAAEC,kBAAkB,EAAE,IAAI;MAAEC,OAAO,EAAE;IAAM,CAC7C,CAAC;EACH,CAAC,CAAC;AACJ,CAAC;AAED,OAAO,eAAeC,iBAAiBA,CAAA,EAAwC;EAC7E,IAAInB,gBAAgB,EAAE;IACpB,OAAOA,gBAAgB,CAAC,CAAC;EAC3B;EACA,IAAIH,SAAS,CAAC,CAAC,EAAE;IACf,MAAMuB,OAAO,GAAG,MAAMrB,wBAAwB,CAAC,CAAC;IAChD,IAAI,CAACqB,OAAO,EAAE;MACZ,MAAM,IAAIf,KAAK,CAAC,4BAA4B,CAAC;IAC/C;EACF;EACA,IAAI;IACF,MAAMgB,GAAG,GAAG,MAAMlB,aAAa,CAAC,CAAC;IACjC,OAAOkB,GAAG;EACZ,CAAC,CAAC,MAAM;IACN,MAAM,IAAIhB,KAAK,CAAC,sBAAsB,CAAC;EACzC;AACF","ignoreList":[]}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export type DeviceLocation = {
|
|
2
|
-
lat: number;
|
|
3
|
-
lng: number;
|
|
4
|
-
};
|
|
5
|
-
export declare function setLocationProvider(provider: () => Promise<DeviceLocation | undefined>): void;
|
|
6
|
-
export declare const fetchLocation: () => Promise<DeviceLocation>;
|
|
7
|
-
export declare function getDeviceLocation(): Promise<DeviceLocation | undefined>;
|
|
8
|
-
//# sourceMappingURL=location.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"location.d.ts","sourceRoot":"","sources":["../../../../../src/services/device/location.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,cAAc,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAI1D,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,GAClD,IAAI,CAEN;AAED,eAAO,MAAM,aAAa,QAAa,OAAO,CAAC,cAAc,CAe5D,CAAC;AAEF,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAgB7E"}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
// Platform Utilities
|
|
2
|
-
import { isAndroid } from '../../utils/platformManager';
|
|
3
|
-
import { resolveNativeDependencies } from '../../utils/dependencyManager';
|
|
4
|
-
|
|
5
|
-
// Permissions
|
|
6
|
-
import { ensureLocationPermission } from '../permissions/permissionManager';
|
|
7
|
-
|
|
8
|
-
export type DeviceLocation = { lat: number; lng: number };
|
|
9
|
-
|
|
10
|
-
let locationProvider: (() => Promise<DeviceLocation | undefined>) | undefined;
|
|
11
|
-
|
|
12
|
-
export function setLocationProvider(
|
|
13
|
-
provider: () => Promise<DeviceLocation | undefined>
|
|
14
|
-
): void {
|
|
15
|
-
locationProvider = provider;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const fetchLocation = async (): Promise<DeviceLocation> => {
|
|
19
|
-
const { Geolocation } = resolveNativeDependencies();
|
|
20
|
-
if (!Geolocation) {
|
|
21
|
-
throw new Error(
|
|
22
|
-
'GEOLOCATION_NOT_LINKED: install and link @react-native-community/geolocation'
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
return new Promise((resolve, reject) => {
|
|
26
|
-
Geolocation.getCurrentPosition(
|
|
27
|
-
(pos: { coords: { latitude: number; longitude: number } }) =>
|
|
28
|
-
resolve({ lat: pos.coords.latitude, lng: pos.coords.longitude }),
|
|
29
|
-
(err: unknown) => reject(err),
|
|
30
|
-
{ enableHighAccuracy: true, timeout: 10000 }
|
|
31
|
-
);
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export async function getDeviceLocation(): Promise<DeviceLocation | undefined> {
|
|
36
|
-
if (locationProvider) {
|
|
37
|
-
return locationProvider();
|
|
38
|
-
}
|
|
39
|
-
if (isAndroid()) {
|
|
40
|
-
const granted = await ensureLocationPermission();
|
|
41
|
-
if (!granted) {
|
|
42
|
-
throw new Error('LOCATION_PERMISSION_DENIED');
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
try {
|
|
46
|
-
const loc = await fetchLocation();
|
|
47
|
-
return loc;
|
|
48
|
-
} catch {
|
|
49
|
-
throw new Error('LOCATION_UNAVAILABLE');
|
|
50
|
-
}
|
|
51
|
-
}
|