@savers_app/react-native-sandbox-sdk 1.2.6

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.
Files changed (212) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +556 -0
  3. package/lib/module/@types/@react-native-async-storage__async-storage.d.js +2 -0
  4. package/lib/module/@types/@react-native-async-storage__async-storage.d.js.map +1 -0
  5. package/lib/module/@types/@react-native-community__geolocation.d.js +2 -0
  6. package/lib/module/@types/@react-native-community__geolocation.d.js.map +1 -0
  7. package/lib/module/@types/react-native-aes-gcm-crypto.d.js +2 -0
  8. package/lib/module/@types/react-native-aes-gcm-crypto.d.js.map +1 -0
  9. package/lib/module/@types/react-native-device-info.d.js +2 -0
  10. package/lib/module/@types/react-native-device-info.d.js.map +1 -0
  11. package/lib/module/core/runtime.js +70 -0
  12. package/lib/module/core/runtime.js.map +1 -0
  13. package/lib/module/data/models/apiResponse.js +19 -0
  14. package/lib/module/data/models/apiResponse.js.map +1 -0
  15. package/lib/module/data/models/onboardingModel.js +67 -0
  16. package/lib/module/data/models/onboardingModel.js.map +1 -0
  17. package/lib/module/data/models/tokenModel.js +25 -0
  18. package/lib/module/data/models/tokenModel.js.map +1 -0
  19. package/lib/module/data/network/apiClient.js +109 -0
  20. package/lib/module/data/network/apiClient.js.map +1 -0
  21. package/lib/module/data/network/apiEndpoints.js +7 -0
  22. package/lib/module/data/network/apiEndpoints.js.map +1 -0
  23. package/lib/module/data/network/apiExceptionHandler.js +16 -0
  24. package/lib/module/data/network/apiExceptionHandler.js.map +1 -0
  25. package/lib/module/data/network/apiRepository.js +84 -0
  26. package/lib/module/data/network/apiRepository.js.map +1 -0
  27. package/lib/module/data/network/apiService.js +102 -0
  28. package/lib/module/data/network/apiService.js.map +1 -0
  29. package/lib/module/data/network/dataTypeUtils.js +77 -0
  30. package/lib/module/data/network/dataTypeUtils.js.map +1 -0
  31. package/lib/module/data/network/interceptors/authInterceptor.js +72 -0
  32. package/lib/module/data/network/interceptors/authInterceptor.js.map +1 -0
  33. package/lib/module/data/network/interceptors/errorInterceptor.js +15 -0
  34. package/lib/module/data/network/interceptors/errorInterceptor.js.map +1 -0
  35. package/lib/module/data/network/interceptors/loggingInterceptor.js +17 -0
  36. package/lib/module/data/network/interceptors/loggingInterceptor.js.map +1 -0
  37. package/lib/module/data/network/interceptors/networkInterceptor.js +22 -0
  38. package/lib/module/data/network/interceptors/networkInterceptor.js.map +1 -0
  39. package/lib/module/data/network/interceptors/retryInterceptor.js +22 -0
  40. package/lib/module/data/network/interceptors/retryInterceptor.js.map +1 -0
  41. package/lib/module/data/storage/asyncStorageManager.js +50 -0
  42. package/lib/module/data/storage/asyncStorageManager.js.map +1 -0
  43. package/lib/module/data/storage/deviceIdManager.js +45 -0
  44. package/lib/module/data/storage/deviceIdManager.js.map +1 -0
  45. package/lib/module/data/storage/keysManager.js +70 -0
  46. package/lib/module/data/storage/keysManager.js.map +1 -0
  47. package/lib/module/data/storage/locationManager.js +30 -0
  48. package/lib/module/data/storage/locationManager.js.map +1 -0
  49. package/lib/module/data/storage/reactRefManager.js +22 -0
  50. package/lib/module/data/storage/reactRefManager.js.map +1 -0
  51. package/lib/module/data/storage/sessionManager.js +12 -0
  52. package/lib/module/data/storage/sessionManager.js.map +1 -0
  53. package/lib/module/data/storage/storageKeys.js +7 -0
  54. package/lib/module/data/storage/storageKeys.js.map +1 -0
  55. package/lib/module/data/storage/storageManager.js +76 -0
  56. package/lib/module/data/storage/storageManager.js.map +1 -0
  57. package/lib/module/index.js +20 -0
  58. package/lib/module/index.js.map +1 -0
  59. package/lib/module/package.json +1 -0
  60. package/lib/module/services/device/location.js +47 -0
  61. package/lib/module/services/device/location.js.map +1 -0
  62. package/lib/module/services/navigation/dialPad.js +29 -0
  63. package/lib/module/services/navigation/dialPad.js.map +1 -0
  64. package/lib/module/services/navigation/goBackNavigation.js +19 -0
  65. package/lib/module/services/navigation/goBackNavigation.js.map +1 -0
  66. package/lib/module/services/navigation/openBrowser.js +8 -0
  67. package/lib/module/services/navigation/openBrowser.js.map +1 -0
  68. package/lib/module/services/navigation/openMap.js +42 -0
  69. package/lib/module/services/navigation/openMap.js.map +1 -0
  70. package/lib/module/services/permissions/permissionManager.js +28 -0
  71. package/lib/module/services/permissions/permissionManager.js.map +1 -0
  72. package/lib/module/services/url/urlGenerator.js +94 -0
  73. package/lib/module/services/url/urlGenerator.js.map +1 -0
  74. package/lib/module/services/webview/messageHandler.js +72 -0
  75. package/lib/module/services/webview/messageHandler.js.map +1 -0
  76. package/lib/module/utils/config.js +34 -0
  77. package/lib/module/utils/config.js.map +1 -0
  78. package/lib/module/utils/dependencyManager.js +51 -0
  79. package/lib/module/utils/dependencyManager.js.map +1 -0
  80. package/lib/module/utils/encryption.js +63 -0
  81. package/lib/module/utils/encryption.js.map +1 -0
  82. package/lib/module/utils/errors.js +9 -0
  83. package/lib/module/utils/errors.js.map +1 -0
  84. package/lib/module/utils/logger.js +14 -0
  85. package/lib/module/utils/logger.js.map +1 -0
  86. package/lib/module/utils/platformManager.js +14 -0
  87. package/lib/module/utils/platformManager.js.map +1 -0
  88. package/lib/module/utils/validator.js +26 -0
  89. package/lib/module/utils/validator.js.map +1 -0
  90. package/lib/typescript/package.json +1 -0
  91. package/lib/typescript/src/core/runtime.d.ts +11 -0
  92. package/lib/typescript/src/core/runtime.d.ts.map +1 -0
  93. package/lib/typescript/src/data/models/apiResponse.d.ts +9 -0
  94. package/lib/typescript/src/data/models/apiResponse.d.ts.map +1 -0
  95. package/lib/typescript/src/data/models/onboardingModel.d.ts +24 -0
  96. package/lib/typescript/src/data/models/onboardingModel.d.ts.map +1 -0
  97. package/lib/typescript/src/data/models/tokenModel.d.ts +15 -0
  98. package/lib/typescript/src/data/models/tokenModel.d.ts.map +1 -0
  99. package/lib/typescript/src/data/network/apiClient.d.ts +17 -0
  100. package/lib/typescript/src/data/network/apiClient.d.ts.map +1 -0
  101. package/lib/typescript/src/data/network/apiEndpoints.d.ts +5 -0
  102. package/lib/typescript/src/data/network/apiEndpoints.d.ts.map +1 -0
  103. package/lib/typescript/src/data/network/apiExceptionHandler.d.ts +3 -0
  104. package/lib/typescript/src/data/network/apiExceptionHandler.d.ts.map +1 -0
  105. package/lib/typescript/src/data/network/apiRepository.d.ts +21 -0
  106. package/lib/typescript/src/data/network/apiRepository.d.ts.map +1 -0
  107. package/lib/typescript/src/data/network/apiService.d.ts +25 -0
  108. package/lib/typescript/src/data/network/apiService.d.ts.map +1 -0
  109. package/lib/typescript/src/data/network/dataTypeUtils.d.ts +10 -0
  110. package/lib/typescript/src/data/network/dataTypeUtils.d.ts.map +1 -0
  111. package/lib/typescript/src/data/network/interceptors/authInterceptor.d.ts +15 -0
  112. package/lib/typescript/src/data/network/interceptors/authInterceptor.d.ts.map +1 -0
  113. package/lib/typescript/src/data/network/interceptors/errorInterceptor.d.ts +4 -0
  114. package/lib/typescript/src/data/network/interceptors/errorInterceptor.d.ts.map +1 -0
  115. package/lib/typescript/src/data/network/interceptors/loggingInterceptor.d.ts +5 -0
  116. package/lib/typescript/src/data/network/interceptors/loggingInterceptor.d.ts.map +1 -0
  117. package/lib/typescript/src/data/network/interceptors/networkInterceptor.d.ts +4 -0
  118. package/lib/typescript/src/data/network/interceptors/networkInterceptor.d.ts.map +1 -0
  119. package/lib/typescript/src/data/network/interceptors/retryInterceptor.d.ts +7 -0
  120. package/lib/typescript/src/data/network/interceptors/retryInterceptor.d.ts.map +1 -0
  121. package/lib/typescript/src/data/storage/asyncStorageManager.d.ts +13 -0
  122. package/lib/typescript/src/data/storage/asyncStorageManager.d.ts.map +1 -0
  123. package/lib/typescript/src/data/storage/deviceIdManager.d.ts +2 -0
  124. package/lib/typescript/src/data/storage/deviceIdManager.d.ts.map +1 -0
  125. package/lib/typescript/src/data/storage/keysManager.d.ts +29 -0
  126. package/lib/typescript/src/data/storage/keysManager.d.ts.map +1 -0
  127. package/lib/typescript/src/data/storage/locationManager.d.ts +21 -0
  128. package/lib/typescript/src/data/storage/locationManager.d.ts.map +1 -0
  129. package/lib/typescript/src/data/storage/reactRefManager.d.ts +11 -0
  130. package/lib/typescript/src/data/storage/reactRefManager.d.ts.map +1 -0
  131. package/lib/typescript/src/data/storage/sessionManager.d.ts +3 -0
  132. package/lib/typescript/src/data/storage/sessionManager.d.ts.map +1 -0
  133. package/lib/typescript/src/data/storage/storageKeys.d.ts +5 -0
  134. package/lib/typescript/src/data/storage/storageKeys.d.ts.map +1 -0
  135. package/lib/typescript/src/data/storage/storageManager.d.ts +16 -0
  136. package/lib/typescript/src/data/storage/storageManager.d.ts.map +1 -0
  137. package/lib/typescript/src/index.d.ts +18 -0
  138. package/lib/typescript/src/index.d.ts.map +1 -0
  139. package/lib/typescript/src/services/device/location.d.ts +8 -0
  140. package/lib/typescript/src/services/device/location.d.ts.map +1 -0
  141. package/lib/typescript/src/services/navigation/dialPad.d.ts +3 -0
  142. package/lib/typescript/src/services/navigation/dialPad.d.ts.map +1 -0
  143. package/lib/typescript/src/services/navigation/goBackNavigation.d.ts +3 -0
  144. package/lib/typescript/src/services/navigation/goBackNavigation.d.ts.map +1 -0
  145. package/lib/typescript/src/services/navigation/openBrowser.d.ts +2 -0
  146. package/lib/typescript/src/services/navigation/openBrowser.d.ts.map +1 -0
  147. package/lib/typescript/src/services/navigation/openMap.d.ts +2 -0
  148. package/lib/typescript/src/services/navigation/openMap.d.ts.map +1 -0
  149. package/lib/typescript/src/services/permissions/permissionManager.d.ts +4 -0
  150. package/lib/typescript/src/services/permissions/permissionManager.d.ts.map +1 -0
  151. package/lib/typescript/src/services/url/urlGenerator.d.ts +44 -0
  152. package/lib/typescript/src/services/url/urlGenerator.d.ts.map +1 -0
  153. package/lib/typescript/src/services/webview/messageHandler.d.ts +2 -0
  154. package/lib/typescript/src/services/webview/messageHandler.d.ts.map +1 -0
  155. package/lib/typescript/src/utils/config.d.ts +5 -0
  156. package/lib/typescript/src/utils/config.d.ts.map +1 -0
  157. package/lib/typescript/src/utils/dependencyManager.d.ts +31 -0
  158. package/lib/typescript/src/utils/dependencyManager.d.ts.map +1 -0
  159. package/lib/typescript/src/utils/encryption.d.ts +5 -0
  160. package/lib/typescript/src/utils/encryption.d.ts.map +1 -0
  161. package/lib/typescript/src/utils/errors.d.ts +4 -0
  162. package/lib/typescript/src/utils/errors.d.ts.map +1 -0
  163. package/lib/typescript/src/utils/logger.d.ts +6 -0
  164. package/lib/typescript/src/utils/logger.d.ts.map +1 -0
  165. package/lib/typescript/src/utils/platformManager.d.ts +4 -0
  166. package/lib/typescript/src/utils/platformManager.d.ts.map +1 -0
  167. package/lib/typescript/src/utils/validator.d.ts +4 -0
  168. package/lib/typescript/src/utils/validator.d.ts.map +1 -0
  169. package/package.json +184 -0
  170. package/src/@types/@react-native-async-storage__async-storage.d.ts +7 -0
  171. package/src/@types/@react-native-community__geolocation.d.ts +18 -0
  172. package/src/@types/react-native-aes-gcm-crypto.d.ts +22 -0
  173. package/src/@types/react-native-device-info.d.ts +3 -0
  174. package/src/core/runtime.ts +84 -0
  175. package/src/data/models/apiResponse.ts +28 -0
  176. package/src/data/models/onboardingModel.ts +97 -0
  177. package/src/data/models/tokenModel.ts +43 -0
  178. package/src/data/network/apiClient.ts +148 -0
  179. package/src/data/network/apiEndpoints.ts +4 -0
  180. package/src/data/network/apiExceptionHandler.ts +16 -0
  181. package/src/data/network/apiRepository.ts +130 -0
  182. package/src/data/network/apiService.ts +113 -0
  183. package/src/data/network/dataTypeUtils.ts +78 -0
  184. package/src/data/network/interceptors/authInterceptor.ts +86 -0
  185. package/src/data/network/interceptors/errorInterceptor.ts +13 -0
  186. package/src/data/network/interceptors/loggingInterceptor.ts +16 -0
  187. package/src/data/network/interceptors/networkInterceptor.ts +20 -0
  188. package/src/data/network/interceptors/retryInterceptor.ts +24 -0
  189. package/src/data/storage/asyncStorageManager.ts +56 -0
  190. package/src/data/storage/deviceIdManager.ts +46 -0
  191. package/src/data/storage/keysManager.ts +89 -0
  192. package/src/data/storage/locationManager.ts +39 -0
  193. package/src/data/storage/reactRefManager.ts +25 -0
  194. package/src/data/storage/sessionManager.ts +11 -0
  195. package/src/data/storage/storageKeys.ts +4 -0
  196. package/src/data/storage/storageManager.ts +83 -0
  197. package/src/index.tsx +17 -0
  198. package/src/services/device/location.ts +51 -0
  199. package/src/services/navigation/dialPad.ts +39 -0
  200. package/src/services/navigation/goBackNavigation.ts +19 -0
  201. package/src/services/navigation/openBrowser.ts +6 -0
  202. package/src/services/navigation/openMap.ts +53 -0
  203. package/src/services/permissions/permissionManager.ts +33 -0
  204. package/src/services/url/urlGenerator.ts +140 -0
  205. package/src/services/webview/messageHandler.ts +45 -0
  206. package/src/utils/config.ts +35 -0
  207. package/src/utils/dependencyManager.ts +82 -0
  208. package/src/utils/encryption.ts +96 -0
  209. package/src/utils/errors.ts +6 -0
  210. package/src/utils/logger.ts +11 -0
  211. package/src/utils/platformManager.ts +14 -0
  212. package/src/utils/validator.ts +29 -0
@@ -0,0 +1,39 @@
1
+ class LocationManager {
2
+ private lat: number | null = null;
3
+ private lng: number | null = null;
4
+
5
+ setCoordinates(lat: number, lng: number): void {
6
+ this.lat = lat;
7
+ this.lng = lng;
8
+ }
9
+
10
+ getCoordinates(): { lat: number; lng: number } | null {
11
+ if (this.lat === null || this.lng === null) {
12
+ return null;
13
+ }
14
+ return { lat: this.lat, lng: this.lng };
15
+ }
16
+ }
17
+
18
+ export const locationManager = new LocationManager();
19
+
20
+ export const setLocationCoordinates = async (
21
+ lat: number,
22
+ lng: number
23
+ ): Promise<void> => {
24
+ locationManager.setCoordinates(lat, lng);
25
+ };
26
+
27
+ export async function getLocationCoordinates(): Promise<{
28
+ lat: number;
29
+ lng: number;
30
+ } | null> {
31
+ return locationManager.getCoordinates();
32
+ }
33
+
34
+ export function getLocationCoordinatesSync(): {
35
+ lat: number;
36
+ lng: number;
37
+ } | null {
38
+ return locationManager.getCoordinates();
39
+ }
@@ -0,0 +1,25 @@
1
+ class ReactRefManager {
2
+ private ref: any | null = null;
3
+
4
+ setRef(ref: any): void {
5
+ this.ref = ref;
6
+ }
7
+
8
+ getRef(): any | null {
9
+ return this.ref;
10
+ }
11
+ }
12
+
13
+ export const reactRefManager = new ReactRefManager();
14
+
15
+ export const setReactRef = async (ref: any): Promise<void> => {
16
+ reactRefManager.setRef(ref);
17
+ };
18
+
19
+ export async function getReactRef(): Promise<any | null> {
20
+ return reactRefManager.getRef();
21
+ }
22
+
23
+ export function getReactRefSync(): any | null {
24
+ return reactRefManager.getRef();
25
+ }
@@ -0,0 +1,11 @@
1
+ // Storage Utilities
2
+ import { storageSet, storageGet } from './asyncStorageManager';
3
+ import { SDK_SESSION_ID } from './storageKeys';
4
+
5
+ export const setSessionId = async (sessionId: string) => {
6
+ await storageSet(SDK_SESSION_ID, sessionId);
7
+ };
8
+
9
+ export const getSessionId = async (): Promise<string | null> => {
10
+ return storageGet(SDK_SESSION_ID);
11
+ };
@@ -0,0 +1,4 @@
1
+ export const SAVERS_API_KEY = 'SAVERS_API_KEY';
2
+ export const SAVERS_ENCRYPTION_KEY = 'SAVERS_ENCRYPTION_KEY';
3
+ export const SDK_SESSION_ID = 'SDK_SESSION_ID';
4
+ export const SDK_DEVICE_ID = 'SDK_DEVICE_ID';
@@ -0,0 +1,83 @@
1
+ // Storage Utilities
2
+ import { storageGet, storageSet, storageRemove } from './asyncStorageManager';
3
+
4
+ const cachePrefix = 'cache_';
5
+
6
+ const userIdKey = 'userId';
7
+ const isOnboardingSkipedKey = 'isOnboardingSkipedKey';
8
+ const isAuthSkipedKey = 'isAuthSkipedKey';
9
+ const authSkippedTimestampKey = 'authSkippedTimestamp';
10
+ const userSelectedLocationKey = 'userSelectedLocation';
11
+ const browsingModeKey = 'browsingMode';
12
+
13
+ export const StorageKeys = {
14
+ userId: userIdKey,
15
+ isOnboardingSkiped: isOnboardingSkipedKey,
16
+ isAuthSkiped: isAuthSkipedKey,
17
+ authSkippedTimestamp: authSkippedTimestampKey,
18
+ userSelectedLocation: userSelectedLocationKey,
19
+ browsingMode: browsingModeKey,
20
+ };
21
+
22
+ export async function save(key: string, value: any): Promise<void> {
23
+ const fullKey = cachePrefix + key;
24
+ let dataToSave: string;
25
+ if (typeof value === 'string') {
26
+ dataToSave = value;
27
+ } else {
28
+ try {
29
+ dataToSave = JSON.stringify(value);
30
+ } catch {
31
+ dataToSave = String(value);
32
+ }
33
+ }
34
+ await storageSet(fullKey, dataToSave);
35
+ }
36
+
37
+ export async function get(key: string): Promise<any> {
38
+ const fullKey = cachePrefix + key;
39
+ const raw = await storageGet(fullKey);
40
+ if (raw === null) return null;
41
+ if (raw.startsWith('{') || raw.startsWith('[')) {
42
+ try {
43
+ return JSON.parse(raw);
44
+ } catch {
45
+ return raw;
46
+ }
47
+ }
48
+ return raw;
49
+ }
50
+
51
+ export async function clear(key: string): Promise<void> {
52
+ const fullKey = cachePrefix + key;
53
+ await storageRemove(fullKey);
54
+ }
55
+
56
+ export async function clearAll(): Promise<void> {
57
+ // Not supported by asyncStorageManager wrapper yet, would need extension.
58
+ // For now, we'll leave it as a TODO or implement using low level if exposed,
59
+ // but better to remove direct dependency on @react-native-async-storage/async-storage
60
+ // to support the unified approach.
61
+ // However, asyncStorageManager hides the implementation.
62
+ // Let's rely on the fact that clearAll was mainly for cache.
63
+ console.warn('clearAll not fully supported in unified storage yet');
64
+ }
65
+
66
+ export async function saveAuthSkippedTime(): Promise<void> {
67
+ const timestamp = new Date().toISOString();
68
+ await save(authSkippedTimestampKey, timestamp);
69
+ }
70
+
71
+ export async function getAuthSkippedTime(): Promise<Date | null> {
72
+ const data = await get(authSkippedTimestampKey);
73
+ if (typeof data === 'string') {
74
+ try {
75
+ return new Date(data);
76
+ } catch {}
77
+ }
78
+ return null;
79
+ }
80
+
81
+ export async function clearAuthSkippedTime(): Promise<void> {
82
+ await clear(authSkippedTimestampKey);
83
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,17 @@
1
+ export * from './services/navigation/openMap';
2
+ export * from './services/navigation/goBackNavigation';
3
+ export * from './services/permissions/permissionManager';
4
+ export * from './utils/platformManager';
5
+ export * from './utils/errors';
6
+ export * from './utils/logger';
7
+ export * from './utils/validator';
8
+ export * from './services/webview/messageHandler';
9
+ export * from './data/storage/deviceIdManager';
10
+ export * from './services/navigation/dialPad';
11
+ export * from './services/navigation/openBrowser';
12
+ export * from './data/storage/sessionManager';
13
+ export * from './services/url/urlGenerator';
14
+ export * from './data/network/apiService';
15
+ export * from './core/runtime';
16
+ export * from './data/storage/keysManager';
17
+ export * from './data/storage/locationManager';
@@ -0,0 +1,51 @@
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
+ }
@@ -0,0 +1,39 @@
1
+ // React Native Core
2
+ import { Linking, Platform } from 'react-native';
3
+
4
+ // Utilities / Validators
5
+ import { isValidPhoneNumber } from '../../utils/validator';
6
+
7
+ export const callPhone = async (phone: string): Promise<void> => {
8
+ if (!isValidPhoneNumber(phone)) {
9
+ throw new Error('INVALID_PHONE_NUMBER');
10
+ }
11
+
12
+ const url =
13
+ Platform.OS === 'ios'
14
+ ? `telprompt:${phone}` // iOS preferred
15
+ : `tel:${phone}`;
16
+
17
+ try {
18
+ await Linking.openURL(url);
19
+ } catch {
20
+ throw new Error('CALL_NOT_SUPPORTED');
21
+ }
22
+ };
23
+
24
+ export async function openDialPad(phone?: string): Promise<void> {
25
+ const url =
26
+ Platform.OS === 'ios'
27
+ ? phone
28
+ ? `telprompt:${phone}`
29
+ : 'tel:' // open empty dial pad
30
+ : phone
31
+ ? `tel:${phone}`
32
+ : 'tel:';
33
+
34
+ try {
35
+ await Linking.openURL(url);
36
+ } catch {
37
+ throw new Error('DIAL_PAD_NOT_SUPPORTED');
38
+ }
39
+ }
@@ -0,0 +1,19 @@
1
+ // React Native Core
2
+ import { BackHandler, Platform } from 'react-native';
3
+ import { getReactRefSync } from '../../data/storage/reactRefManager';
4
+
5
+ export function closeCurrentScreen(): boolean {
6
+ if (getReactRefSync().isReady() && getReactRefSync().canGoBack()) {
7
+ getReactRefSync().goBack();
8
+ return true;
9
+ }
10
+
11
+ return false;
12
+ }
13
+
14
+ export function closeCurrentScreenSafe(): void {
15
+ const closed = closeCurrentScreen();
16
+ if (!closed && Platform.OS === 'android') {
17
+ BackHandler.exitApp();
18
+ }
19
+ }
@@ -0,0 +1,6 @@
1
+ // React Native Core
2
+ import { Linking } from 'react-native';
3
+
4
+ export async function openBrowser(url: string): Promise<void> {
5
+ await Linking.openURL(url);
6
+ }
@@ -0,0 +1,53 @@
1
+ // React Native Core
2
+ import { Linking } from 'react-native';
3
+
4
+ // Utilities / Helpers
5
+ import { isValidCoordinates } from '../../utils/validator';
6
+
7
+ // Platform / Environment helpers
8
+ import { isIOS } from '../../utils/platformManager';
9
+
10
+ export async function openMap(
11
+ lat: number,
12
+ lng: number,
13
+ label?: string
14
+ ): Promise<void> {
15
+ if (!isValidCoordinates(lat, lng)) {
16
+ throw new Error('INVALID_COORDINATES');
17
+ }
18
+
19
+ const iosUrl = `maps:0,0?q=${encodeURIComponent(label ?? '')}@${lat},${lng}`;
20
+
21
+ if (isIOS()) {
22
+ try {
23
+ await Linking.openURL(iosUrl);
24
+ return;
25
+ } catch {}
26
+ const browserUrl = `https://maps.apple.com/?q=${encodeURIComponent(
27
+ label ?? ''
28
+ )}&ll=${lat},${lng}`;
29
+ await Linking.openURL(browserUrl);
30
+ return;
31
+ }
32
+
33
+ const encLabel = label ? encodeURIComponent(label) : undefined;
34
+ const query = `${lat},${lng}${encLabel ? `(${encLabel})` : ''}`;
35
+ const candidates = [
36
+ // Prefer geo to show a pin at the destination (not navigation)
37
+ `geo:${lat},${lng}?q=${query}`,
38
+ `geo:0,0?q=${query}`,
39
+ // Fallback to Google Maps custom scheme as a generic search/pin
40
+ `comgooglemaps://?q=${query}`,
41
+ ];
42
+ for (const url of candidates) {
43
+ try {
44
+ await Linking.openURL(url);
45
+ return;
46
+ } catch {}
47
+ }
48
+ const browserQuery = label ? `${label} ${lat},${lng}` : `${lat},${lng}`;
49
+ const browserUrl = `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(
50
+ browserQuery
51
+ )}`;
52
+ await Linking.openURL(browserUrl);
53
+ }
@@ -0,0 +1,33 @@
1
+ // React Native Core
2
+ import { Platform, PermissionsAndroid } from 'react-native';
3
+
4
+ export const requestPermission = async (
5
+ permission: Parameters<typeof PermissionsAndroid.request>[0]
6
+ ) => {
7
+ if (Platform.OS === 'ios') return true;
8
+
9
+ const already = await PermissionsAndroid.check(permission);
10
+ if (already) return true;
11
+ const result = await PermissionsAndroid.request(permission);
12
+ if (result === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) return false;
13
+ return result === PermissionsAndroid.RESULTS.GRANTED;
14
+ };
15
+
16
+ export async function ensureLocationPermission(): Promise<boolean> {
17
+ if (Platform.OS === 'ios') return true;
18
+ const fine = PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION;
19
+ const coarse = PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION;
20
+ const fineGranted = await PermissionsAndroid.check(fine);
21
+ const coarseGranted = await PermissionsAndroid.check(coarse);
22
+ if (fineGranted || coarseGranted) return true;
23
+ const res = await PermissionsAndroid.requestMultiple([fine, coarse]);
24
+ const fineOk = res[fine] === PermissionsAndroid.RESULTS.GRANTED;
25
+ const coarseOk = res[coarse] === PermissionsAndroid.RESULTS.GRANTED;
26
+ if (
27
+ res[fine] === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN ||
28
+ res[coarse] === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN
29
+ ) {
30
+ return false;
31
+ }
32
+ return fineOk || coarseOk;
33
+ }
@@ -0,0 +1,140 @@
1
+ // Utilities / Helpers
2
+ import { SDKError } from '../../utils/errors';
3
+ import { aesEncrypt } from '../../utils/encryption';
4
+
5
+ // Storage / Session
6
+ import { getDeviceId } from '../../data/storage/deviceIdManager';
7
+ import { getSessionId } from '../../data/storage/sessionManager';
8
+ import { getLocationCoordinates } from '../../data/storage/locationManager';
9
+ import { getEncryptionKey, getPRefCode } from '../../data/storage/keysManager';
10
+
11
+ import { getBaseUrl } from '../../utils/config';
12
+
13
+ export type Attribute = { key: string; value: string };
14
+ export type Screen = { name?: string; attributes?: Attribute[] };
15
+ export type Profile = {
16
+ userId: string;
17
+ phone?: string;
18
+ firstname: string;
19
+ lastname: string;
20
+ email: string;
21
+ dob?: string;
22
+ pv?: '0' | '1';
23
+ ev?: '0' | '1';
24
+ city?: string;
25
+ zipcode?: string;
26
+ referrer_user_id?: string;
27
+ username?: string;
28
+ };
29
+ export type AuthType = 'PHONE' | 'EMAIL' | 'USERNAME';
30
+ export type DeviceInfo = {
31
+ dId: string;
32
+ location?: { lat: number; lng: number };
33
+ };
34
+
35
+ export type UrlInput = {
36
+ screen?: Screen;
37
+ profile: Profile;
38
+ authType?: AuthType;
39
+ deviceInfo?: DeviceInfo;
40
+ sessionId?: string;
41
+ nonce: string;
42
+ };
43
+ export function validateInput(input: UrlInput): {
44
+ ok: boolean;
45
+ errors: string[];
46
+ } {
47
+ const errors: string[] = [];
48
+ const auth = input.authType ?? 'PHONE';
49
+ if (!input.profile) errors.push('profile is mandatory');
50
+ if (!input.profile?.userId) errors.push('userId is mandatory');
51
+ if (!input.profile?.email) errors.push('email is mandatory');
52
+ if (!input.nonce) errors.push('nonce is mandatory');
53
+
54
+ const pv = input.profile?.pv;
55
+ const ev = input.profile?.ev;
56
+ if (typeof pv !== 'undefined' && pv !== '0' && pv !== '1') {
57
+ errors.push('pv must be 0 or 1');
58
+ }
59
+ if (typeof ev !== 'undefined' && ev !== '0' && ev !== '1') {
60
+ errors.push('ev must be 0 or 1');
61
+ }
62
+ if (!auth) errors.push('authType is mandatory');
63
+
64
+ if (auth === 'USERNAME' && !input.profile?.username) {
65
+ errors.push('username is mandatory for USERNAME authType');
66
+ }
67
+ if (auth === 'PHONE' && !input.profile?.phone) {
68
+ errors.push('phone is mandatory for PHONE authType');
69
+ }
70
+
71
+ const screenName = input.screen?.name;
72
+ if (screenName === 'OfrDetails') {
73
+ const attrs = input.screen?.attributes ?? [];
74
+ if (!attrs.length) {
75
+ errors.push(
76
+ 'screen.attributes is mandatory when screen.name is OfrDetails'
77
+ );
78
+ }
79
+ }
80
+
81
+ return { ok: errors.length === 0, errors };
82
+ }
83
+
84
+ export async function generateUrl(input: UrlInput): Promise<string> {
85
+ const deviceId = await getDeviceId();
86
+ const storedLocation = await getLocationCoordinates();
87
+ const sessionId = await getSessionId();
88
+
89
+ const deviceInfo: DeviceInfo =
90
+ storedLocation != null
91
+ ? { dId: deviceId, location: storedLocation }
92
+ : { dId: deviceId };
93
+
94
+ const normalized: Required<Pick<UrlInput, 'profile'>> &
95
+ Pick<UrlInput, 'screen' | 'authType' | 'sessionId'> & {
96
+ deviceInfo: DeviceInfo;
97
+ nonce: string;
98
+ } = {
99
+ profile: input.profile,
100
+ screen: input.screen,
101
+ authType: input.authType ?? 'PHONE',
102
+ sessionId: sessionId ?? '',
103
+ deviceInfo,
104
+ nonce: input.nonce,
105
+ };
106
+
107
+ const validation = validateInput({
108
+ ...normalized,
109
+ deviceInfo: normalized.deviceInfo,
110
+ });
111
+ if (!validation.ok) {
112
+ throw new SDKError(`Validation failed: ${validation.errors.join(', ')}`);
113
+ }
114
+
115
+ const payload = {
116
+ screen: normalized.screen ?? { name: 'Explore' },
117
+ profile: normalized.profile,
118
+ authType: normalized.authType,
119
+ deviceInfo: normalized.deviceInfo,
120
+ sessionId: normalized.sessionId,
121
+ nonce: normalized.nonce,
122
+ };
123
+
124
+ const json = JSON.stringify(payload);
125
+ const key = await getEncryptionKey();
126
+ if (!key || key === '-') {
127
+ throw new SDKError(
128
+ 'Encryption key is mandatory. Initialize SDK with encryptionKey.'
129
+ );
130
+ }
131
+ const encoded = await aesEncrypt(json, key);
132
+
133
+ const programCode = await getPRefCode();
134
+ if (!programCode || programCode === '-') {
135
+ throw new SDKError('pRefCode is mandatory. Initialize SDK with pRefCode.');
136
+ }
137
+ return `${getBaseUrl()}?pRefCode=${encodeURIComponent(
138
+ programCode
139
+ )}&qP=${encodeURIComponent(encoded)}`;
140
+ }
@@ -0,0 +1,45 @@
1
+ import { openMap } from '../navigation/openMap';
2
+ import { openDialPad } from '../navigation/dialPad';
3
+ import { openBrowser } from '../navigation/openBrowser';
4
+ import { closeCurrentScreenSafe } from '../navigation/goBackNavigation';
5
+ import { setSessionId } from '../../data/storage/sessionManager';
6
+
7
+ export function handleWebMessage(raw: string, postBack?: (data: any) => void) {
8
+ let msg: { action?: string; payload?: any };
9
+ try {
10
+ msg = JSON.parse(raw);
11
+ } catch {
12
+ return;
13
+ }
14
+ const action = msg?.action;
15
+ const payload = msg?.payload;
16
+
17
+ const reply = (data: any) => postBack?.(data);
18
+
19
+ (async () => {
20
+ try {
21
+ if (action === 'OPEN_MAP') {
22
+ const { lat, lng, label } = payload || {};
23
+ await openMap(lat, lng, label);
24
+ reply({ ok: true, action });
25
+ } else if (action === 'SHOW_DIAL_PAD') {
26
+ await openDialPad(payload?.number);
27
+ reply({ ok: true, action });
28
+ } else if (action === 'MERCHANT_PORTAL_REDIRECT') {
29
+ await openBrowser(payload?.url);
30
+ reply({ ok: true, action });
31
+ } else if (action === 'SESSION_ID') {
32
+ const { sessionId } = payload || {};
33
+ const id = await setSessionId(sessionId);
34
+ reply({ ok: true, action, sessionId: id });
35
+ } else if (action === 'END_SESSION') {
36
+ closeCurrentScreenSafe();
37
+ // reply({ ok: true, action, closed });
38
+ } else {
39
+ reply({ ok: false, error: 'UNKNOWN_ACTION', action });
40
+ }
41
+ } catch (e: any) {
42
+ reply({ ok: false, error: e?.message ?? String(e), action });
43
+ }
44
+ })();
45
+ }
@@ -0,0 +1,35 @@
1
+ // config.ts
2
+
3
+ export type Environment = 'sandbox' | 'production';
4
+
5
+ // Default environment (build-time injected)
6
+ const injectedEnv = 'sandbox' as string;
7
+ const DEFAULT_ENVIRONMENT: Environment =
8
+ injectedEnv === 'production' ? 'production' : 'sandbox';
9
+
10
+ const ENV_URLS: Record<Environment, string> = {
11
+ sandbox: 'https://testm.saversapp.com/',
12
+ production: 'https://m.saversapp.com/',
13
+ };
14
+
15
+ // Current environment
16
+ let currentEnvironment: Environment = DEFAULT_ENVIRONMENT;
17
+
18
+ // Current base URL
19
+ let BASE_URL: string = ENV_URLS[currentEnvironment];
20
+
21
+ // Change environment
22
+ export const setEnvironment = (env: Environment) => {
23
+ currentEnvironment = env;
24
+ BASE_URL = ENV_URLS[env];
25
+ };
26
+
27
+ // Get current environment
28
+ export const getEnvironment = (): Environment => {
29
+ return currentEnvironment;
30
+ };
31
+
32
+ // Get base URL
33
+ export const getBaseUrl = (): string => {
34
+ return BASE_URL;
35
+ };