@trustchex/react-native-sdk 1.374.0 → 1.409.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/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +1 -21
- package/android/src/main/java/com/trustchex/reactnativesdk/mlkit/MLKitModule.kt +1 -1
- package/android/src/main/java/com/trustchex/reactnativesdk/opencv/OpenCVModule.kt +636 -301
- package/ios/Camera/TrustchexCameraView.swift +9 -20
- package/ios/MLKit/MLKitModule.swift +1 -1
- package/ios/OpenCV/OpenCVHelper.h +0 -7
- package/ios/OpenCV/OpenCVHelper.mm +0 -60
- package/ios/OpenCV/OpenCVModule.h +0 -4
- package/ios/OpenCV/OpenCVModule.mm +440 -358
- package/lib/module/Screens/Debug/BarcodeTestScreen.js +308 -0
- package/lib/module/Screens/Debug/MRZTestScreen.js +105 -13
- package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +49 -29
- package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +5 -0
- package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +5 -0
- package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +26 -6
- package/lib/module/Screens/Dynamic/VideoCallScreen.js +676 -0
- package/lib/module/Screens/Static/OTPVerificationScreen.js +6 -0
- package/lib/module/Screens/Static/QrCodeScanningScreen.js +7 -1
- package/lib/module/Screens/Static/ResultScreen.js +27 -13
- package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +51 -51
- package/lib/module/Shared/Animations/video-call.json +1 -0
- package/lib/module/Shared/Components/DebugNavigationPanel.js +180 -14
- package/lib/module/Shared/Components/DebugOverlay.js +541 -0
- package/lib/module/Shared/Components/EIDScanner.js +1 -4
- package/lib/module/Shared/Components/IdentityDocumentCamera.constants.js +44 -0
- package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +270 -0
- package/lib/module/Shared/Components/IdentityDocumentCamera.js +702 -1703
- package/lib/module/Shared/Components/IdentityDocumentCamera.types.js +3 -0
- package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +273 -0
- package/lib/module/Shared/Components/NavigationManager.js +15 -3
- package/lib/module/Shared/Contexts/AppContext.js +1 -0
- package/lib/module/Shared/Libs/SignalingClient.js +128 -0
- package/lib/module/Shared/Libs/analytics.utils.js +4 -0
- package/lib/module/Shared/Libs/deeplink.utils.js +9 -1
- package/lib/module/Shared/Libs/http-client.js +9 -0
- package/lib/module/Shared/Libs/promise.utils.js +16 -2
- package/lib/module/Shared/Libs/status-bar.utils.js +21 -0
- package/lib/module/Shared/Services/DataUploadService.js +294 -0
- package/lib/module/Shared/Services/VideoSessionService.js +156 -0
- package/lib/module/Shared/Services/WebRTCService.js +510 -0
- package/lib/module/Shared/Types/analytics.types.js +2 -0
- package/lib/module/Translation/Resources/en.js +20 -0
- package/lib/module/Translation/Resources/tr.js +20 -0
- package/lib/module/Trustchex.js +10 -0
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts +3 -0
- package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts.map +1 -0
- package/lib/typescript/src/Screens/Debug/MRZTestScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/IdentityDocumentScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts +3 -0
- package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts.map +1 -0
- package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/QrCodeScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/DebugOverlay.d.ts +30 -0
- package/lib/typescript/src/Shared/Components/DebugOverlay.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.constants.d.ts +35 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.constants.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts +3 -56
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +88 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts +116 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +93 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Contexts/AppContext.d.ts +1 -0
- package/lib/typescript/src/Shared/Contexts/AppContext.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts +24 -0
- package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/deeplink.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/promise.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts +9 -0
- package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Services/DataUploadService.d.ts +25 -0
- package/lib/typescript/src/Shared/Services/DataUploadService.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts +33 -0
- package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Services/WebRTCService.d.ts +58 -0
- package/lib/typescript/src/Shared/Services/WebRTCService.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Types/analytics.types.d.ts +2 -0
- package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +4 -1
- package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/en.d.ts +20 -0
- package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/tr.d.ts +20 -0
- package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
- package/lib/typescript/src/Trustchex.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +29 -2
- package/src/Screens/Debug/BarcodeTestScreen.tsx +317 -0
- package/src/Screens/Debug/MRZTestScreen.tsx +107 -13
- package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +59 -33
- package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +6 -0
- package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +6 -0
- package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +34 -6
- package/src/Screens/Dynamic/VideoCallScreen.tsx +764 -0
- package/src/Screens/Static/OTPVerificationScreen.tsx +6 -0
- package/src/Screens/Static/QrCodeScanningScreen.tsx +7 -1
- package/src/Screens/Static/ResultScreen.tsx +58 -23
- package/src/Screens/Static/VerificationSessionCheckScreen.tsx +58 -72
- package/src/Shared/Animations/video-call.json +1 -0
- package/src/Shared/Components/DebugNavigationPanel.tsx +185 -9
- package/src/Shared/Components/DebugOverlay.tsx +656 -0
- package/src/Shared/Components/EIDScanner.tsx +1 -5
- package/src/Shared/Components/IdentityDocumentCamera.constants.ts +44 -0
- package/src/Shared/Components/IdentityDocumentCamera.flows.ts +342 -0
- package/src/Shared/Components/IdentityDocumentCamera.tsx +1089 -2465
- package/src/Shared/Components/IdentityDocumentCamera.types.ts +136 -0
- package/src/Shared/Components/IdentityDocumentCamera.utils.ts +364 -0
- package/src/Shared/Components/NavigationManager.tsx +14 -1
- package/src/Shared/Contexts/AppContext.ts +2 -0
- package/src/Shared/Libs/SignalingClient.ts +189 -0
- package/src/Shared/Libs/analytics.utils.ts +4 -0
- package/src/Shared/Libs/deeplink.utils.ts +12 -1
- package/src/Shared/Libs/http-client.ts +10 -0
- package/src/Shared/Libs/promise.utils.ts +16 -2
- package/src/Shared/Libs/status-bar.utils.ts +19 -0
- package/src/Shared/Services/DataUploadService.ts +395 -0
- package/src/Shared/Services/VideoSessionService.ts +190 -0
- package/src/Shared/Services/WebRTCService.ts +636 -0
- package/src/Shared/Types/analytics.types.ts +2 -0
- package/src/Shared/Types/identificationInfo.ts +5 -1
- package/src/Translation/Resources/en.ts +25 -0
- package/src/Translation/Resources/tr.ts +27 -0
- package/src/Trustchex.tsx +12 -2
- package/src/version.ts +1 -1
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import EventSource, { type EventSourceListener } from 'react-native-sse';
|
|
2
|
+
|
|
3
|
+
export type SignalingMessageType =
|
|
4
|
+
| 'offer'
|
|
5
|
+
| 'answer'
|
|
6
|
+
| 'ice-candidate'
|
|
7
|
+
| 'command';
|
|
8
|
+
|
|
9
|
+
export interface SignalingMessage {
|
|
10
|
+
id?: string;
|
|
11
|
+
type: SignalingMessageType;
|
|
12
|
+
payload: any;
|
|
13
|
+
createdAt?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type SignalingEventHandler = (message: SignalingMessage) => void;
|
|
17
|
+
export type SessionEndedHandler = (state: string) => void;
|
|
18
|
+
|
|
19
|
+
export class SignalingClient {
|
|
20
|
+
private eventSource: EventSource | null = null;
|
|
21
|
+
private sessionId: string;
|
|
22
|
+
private baseUrl: string;
|
|
23
|
+
private onMessage: SignalingEventHandler;
|
|
24
|
+
private onConnected?: () => void;
|
|
25
|
+
private onDisconnected?: () => void;
|
|
26
|
+
private onSessionEnded?: SessionEndedHandler;
|
|
27
|
+
private identificationId?: string;
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
baseUrl: string,
|
|
31
|
+
sessionId: string,
|
|
32
|
+
onMessage: SignalingEventHandler,
|
|
33
|
+
identificationId?: string,
|
|
34
|
+
onSessionEnded?: SessionEndedHandler
|
|
35
|
+
) {
|
|
36
|
+
this.baseUrl = baseUrl;
|
|
37
|
+
this.sessionId = sessionId;
|
|
38
|
+
this.onMessage = onMessage;
|
|
39
|
+
this.identificationId = identificationId;
|
|
40
|
+
this.onSessionEnded = onSessionEnded;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public connect(): void {
|
|
44
|
+
if (this.eventSource) {
|
|
45
|
+
this.eventSource.close();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const urlParams = new URLSearchParams();
|
|
49
|
+
if (this.identificationId) {
|
|
50
|
+
urlParams.append('identificationId', this.identificationId);
|
|
51
|
+
}
|
|
52
|
+
const url = `${this.baseUrl}/api/app/mobile/video-sessions/${this.sessionId}/signaling/stream?${urlParams.toString()}`;
|
|
53
|
+
|
|
54
|
+
console.log('[SignalingClient] Connecting to SSE:', url);
|
|
55
|
+
|
|
56
|
+
this.eventSource = new EventSource(url, {
|
|
57
|
+
pollingInterval: 0, // 0 means default/no polling if SSE is real
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const listener: EventSourceListener = (event) => {
|
|
61
|
+
if (event.type === 'open') {
|
|
62
|
+
console.log('[SignalingClient] Connected');
|
|
63
|
+
this.onConnected?.();
|
|
64
|
+
} else if (event.type === 'error') {
|
|
65
|
+
console.error(
|
|
66
|
+
'[SignalingClient] Connection error:',
|
|
67
|
+
JSON.stringify(event, null, 2)
|
|
68
|
+
);
|
|
69
|
+
this.onDisconnected?.();
|
|
70
|
+
} else if (event.type === 'message') {
|
|
71
|
+
try {
|
|
72
|
+
const data = JSON.parse(event.data || '{}');
|
|
73
|
+
|
|
74
|
+
if (
|
|
75
|
+
['offer', 'answer', 'ice-candidate', 'command'].includes(data.type)
|
|
76
|
+
) {
|
|
77
|
+
this.onMessage({
|
|
78
|
+
id: data.id,
|
|
79
|
+
type: data.type as SignalingMessageType,
|
|
80
|
+
payload: data.payload,
|
|
81
|
+
createdAt: data.createdAt,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
} catch (e) {
|
|
85
|
+
console.error('[SignalingClient] Error parsing message:', e);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const customEventListener = (event: any) => {
|
|
91
|
+
try {
|
|
92
|
+
const data = JSON.parse(event.data || '{}');
|
|
93
|
+
const eventType = event.type;
|
|
94
|
+
|
|
95
|
+
if (
|
|
96
|
+
['offer', 'answer', 'ice-candidate', 'command'].includes(eventType)
|
|
97
|
+
) {
|
|
98
|
+
this.onMessage({
|
|
99
|
+
id: data.id,
|
|
100
|
+
type: eventType as SignalingMessageType,
|
|
101
|
+
payload: data.payload,
|
|
102
|
+
createdAt: data.createdAt,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
} catch (e) {
|
|
106
|
+
console.error('[SignalingClient] Error parsing custom event:', e);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Ping handler - server sends pings to keep connection alive
|
|
111
|
+
const pingListener = (event: any) => {
|
|
112
|
+
console.log('[SignalingClient] Received ping');
|
|
113
|
+
// Just acknowledge by doing nothing, server-side heartbeat keeps connection alive
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Session-ended event listener
|
|
117
|
+
const sessionEndedListener = (event: any) => {
|
|
118
|
+
try {
|
|
119
|
+
const data = JSON.parse(event.data || '{}');
|
|
120
|
+
console.log('[SignalingClient] Session ended:', data.state);
|
|
121
|
+
if (this.onSessionEnded) {
|
|
122
|
+
this.onSessionEnded(data.state);
|
|
123
|
+
}
|
|
124
|
+
} catch (e) {
|
|
125
|
+
console.error(
|
|
126
|
+
'[SignalingClient] Error parsing session-ended event:',
|
|
127
|
+
e
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
this.eventSource.addEventListener('open', listener);
|
|
133
|
+
this.eventSource.addEventListener('message', listener);
|
|
134
|
+
this.eventSource.addEventListener('error', listener);
|
|
135
|
+
this.eventSource.addEventListener('ping' as any, pingListener);
|
|
136
|
+
this.eventSource.addEventListener('offer' as any, customEventListener);
|
|
137
|
+
this.eventSource.addEventListener('answer' as any, customEventListener);
|
|
138
|
+
this.eventSource.addEventListener(
|
|
139
|
+
'ice-candidate' as any,
|
|
140
|
+
customEventListener
|
|
141
|
+
);
|
|
142
|
+
this.eventSource.addEventListener('command' as any, customEventListener);
|
|
143
|
+
this.eventSource.addEventListener(
|
|
144
|
+
'session-ended' as any,
|
|
145
|
+
sessionEndedListener
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public async send(type: SignalingMessageType, payload: any): Promise<void> {
|
|
150
|
+
const urlParams = new URLSearchParams();
|
|
151
|
+
if (this.identificationId) {
|
|
152
|
+
urlParams.append('identificationId', this.identificationId);
|
|
153
|
+
}
|
|
154
|
+
const url = `${this.baseUrl}/api/app/mobile/video-sessions/${this.sessionId}/signaling?${urlParams.toString()}`;
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const body = {
|
|
158
|
+
type,
|
|
159
|
+
data: payload,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const response = await fetch(url, {
|
|
163
|
+
method: 'POST',
|
|
164
|
+
headers: {
|
|
165
|
+
'Content-Type': 'application/json',
|
|
166
|
+
},
|
|
167
|
+
body: JSON.stringify(body),
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (!response.ok) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
`Failed to send signaling message: ${response.statusText}`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.error('[SignalingClient] Error sending message:', error);
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
public disconnect(): void {
|
|
182
|
+
if (this.eventSource) {
|
|
183
|
+
this.eventSource.removeAllEventListeners();
|
|
184
|
+
this.eventSource.close();
|
|
185
|
+
this.eventSource = null;
|
|
186
|
+
this.onDisconnected?.();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -289,6 +289,10 @@ const STEP_EVENT_MAP: Record<
|
|
|
289
289
|
started: AnalyticsEventName.LIVENESS_CHECK_STARTED,
|
|
290
290
|
completed: AnalyticsEventName.LIVENESS_CHECK_COMPLETED,
|
|
291
291
|
},
|
|
292
|
+
video_call: {
|
|
293
|
+
started: AnalyticsEventName.VIDEO_CALL_STARTED,
|
|
294
|
+
completed: AnalyticsEventName.VIDEO_CALL_COMPLETED,
|
|
295
|
+
},
|
|
292
296
|
};
|
|
293
297
|
|
|
294
298
|
function getStepEventName(
|
|
@@ -10,7 +10,10 @@ const handleDeepLink = ({ url }: { url: string }) => {
|
|
|
10
10
|
let sessionId = '';
|
|
11
11
|
|
|
12
12
|
for (let i = 0; i < segments.length; i++) {
|
|
13
|
-
if (
|
|
13
|
+
if (
|
|
14
|
+
segments[i] === 'verification-session' ||
|
|
15
|
+
segments[i] === 'verification-sessions'
|
|
16
|
+
) {
|
|
14
17
|
sessionId = segments[i + 1] ?? '';
|
|
15
18
|
debugLog('handleDeepLink', 'Found sessionId:', sessionId);
|
|
16
19
|
} else if (segments[i] === 'app-url') {
|
|
@@ -19,6 +22,14 @@ const handleDeepLink = ({ url }: { url: string }) => {
|
|
|
19
22
|
}
|
|
20
23
|
}
|
|
21
24
|
|
|
25
|
+
// If no app-url segment found, derive baseUrl from the URL itself
|
|
26
|
+
if (!baseUrl && sessionId) {
|
|
27
|
+
const match = url.match(/^(https?:\/\/[^/]+)/);
|
|
28
|
+
if (match) {
|
|
29
|
+
baseUrl = match[1];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
22
33
|
debugLog('handleDeepLink', 'Returning:', { baseUrl, sessionId });
|
|
23
34
|
return [baseUrl, sessionId];
|
|
24
35
|
};
|
|
@@ -64,6 +64,11 @@ const request = async <TResponse, TBody>(
|
|
|
64
64
|
let statusCode = 0;
|
|
65
65
|
let success = false;
|
|
66
66
|
|
|
67
|
+
console.log(`[HTTP] ${httpMethod} ${url}`);
|
|
68
|
+
if (body) {
|
|
69
|
+
console.log('[HTTP] Request body:', JSON.stringify(body).substring(0, 200) + '...');
|
|
70
|
+
}
|
|
71
|
+
|
|
67
72
|
try {
|
|
68
73
|
const response = await fetch(url, {
|
|
69
74
|
method: httpMethod,
|
|
@@ -75,13 +80,18 @@ const request = async <TResponse, TBody>(
|
|
|
75
80
|
|
|
76
81
|
statusCode = response.status;
|
|
77
82
|
success = response.ok;
|
|
83
|
+
console.log(`[HTTP] Response status: ${statusCode} ${response.ok ? '✓' : '✗'}`);
|
|
78
84
|
|
|
79
85
|
let responseJson = null;
|
|
80
86
|
|
|
81
87
|
try {
|
|
82
88
|
responseJson = await response.json();
|
|
89
|
+
if (responseJson) {
|
|
90
|
+
console.log('[HTTP] Response body:', JSON.stringify(responseJson).substring(0, 200) + '...');
|
|
91
|
+
}
|
|
83
92
|
} catch (error) {
|
|
84
93
|
// Invalid JSON response
|
|
94
|
+
console.log('[HTTP] Response body: (non-JSON)');
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
// Track API call performance (non-blocking)
|
|
@@ -4,17 +4,31 @@ const runWithRetry = async <T>(
|
|
|
4
4
|
delay: number = 1000
|
|
5
5
|
): Promise<T> => {
|
|
6
6
|
let retries = 0;
|
|
7
|
+
let lastError: Error | unknown;
|
|
7
8
|
let result: T;
|
|
8
9
|
while (retries < maxRetries) {
|
|
9
10
|
try {
|
|
11
|
+
if (retries > 0) {
|
|
12
|
+
console.log(`[Retry] Attempt ${retries + 1}/${maxRetries}...`);
|
|
13
|
+
}
|
|
10
14
|
result = await fn();
|
|
15
|
+
if (retries > 0) {
|
|
16
|
+
console.log(`[Retry] ✓ Success on attempt ${retries + 1}`);
|
|
17
|
+
}
|
|
11
18
|
return result;
|
|
12
19
|
} catch (error) {
|
|
20
|
+
lastError = error;
|
|
13
21
|
retries++;
|
|
14
|
-
|
|
22
|
+
console.error(`[Retry] ✗ Attempt ${retries}/${maxRetries} failed:`, error instanceof Error ? error.message : error);
|
|
23
|
+
if (retries < maxRetries) {
|
|
24
|
+
const waitTime = delay * retries;
|
|
25
|
+
console.log(`[Retry] Waiting ${waitTime}ms before retry...`);
|
|
26
|
+
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
27
|
+
}
|
|
15
28
|
}
|
|
16
29
|
}
|
|
17
|
-
|
|
30
|
+
console.error('[Retry] ✗ All retries exhausted. Last error:', lastError);
|
|
31
|
+
throw new Error(`Max retries (${maxRetries}) exceeded. Last error: ${lastError instanceof Error ? lastError.message : String(lastError)}`);
|
|
18
32
|
};
|
|
19
33
|
|
|
20
34
|
export { runWithRetry };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { StatusBar } from 'react-native';
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Configure status bar for white background with dark content for better contrast
|
|
6
|
+
*/
|
|
7
|
+
export const configureStatusBarForWhiteBackground = (): void => {
|
|
8
|
+
StatusBar.setBarStyle('dark-content', true);
|
|
9
|
+
StatusBar.setBackgroundColor('#ffffff', true);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Hook to configure status bar for white background on mount
|
|
14
|
+
*/
|
|
15
|
+
export const useStatusBarWhiteBackground = (): void => {
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
configureStatusBarForWhiteBackground();
|
|
18
|
+
}, []);
|
|
19
|
+
};
|