omnipay-reactnative-sdk 1.2.2-beta.0 → 1.2.2-beta.2

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 (69) hide show
  1. package/README.md +45 -136
  2. package/android/build.gradle +13 -0
  3. package/android/src/main/AndroidManifest.xml +5 -0
  4. package/android/src/main/java/com/omniretail/omnipay/LivenessCameraViewManager.java +116 -0
  5. package/android/src/main/java/com/omniretail/omnipay/LivenessDetectionModule.java +588 -0
  6. package/android/src/main/java/com/omniretail/omnipay/OmnipayActivityPackage.java +4 -1
  7. package/ios/LivenessCameraView.h +22 -0
  8. package/ios/LivenessCameraView.m +135 -0
  9. package/ios/LivenessCameraViewManager.h +12 -0
  10. package/ios/LivenessCameraViewManager.m +24 -0
  11. package/ios/LivenessDetectionModule.h +46 -0
  12. package/ios/LivenessDetectionModule.m +603 -0
  13. package/lib/commonjs/components/OmnipayProvider.js +6 -56
  14. package/lib/commonjs/components/OmnipayProvider.js.map +1 -1
  15. package/lib/commonjs/components/biometrics/FaceVerification.js +439 -0
  16. package/lib/commonjs/components/biometrics/FaceVerification.js.map +1 -0
  17. package/lib/commonjs/components/biometrics/LivenessCameraView.js +43 -0
  18. package/lib/commonjs/components/biometrics/LivenessCameraView.js.map +1 -0
  19. package/lib/commonjs/components/biometrics/LivenessDetection.js +252 -0
  20. package/lib/commonjs/components/biometrics/LivenessDetection.js.map +1 -0
  21. package/lib/commonjs/index.js +28 -0
  22. package/lib/commonjs/index.js.map +1 -1
  23. package/lib/module/components/OmnipayProvider.js +6 -56
  24. package/lib/module/components/OmnipayProvider.js.map +1 -1
  25. package/lib/module/components/biometrics/FaceVerification.js +429 -0
  26. package/lib/module/components/biometrics/FaceVerification.js.map +1 -0
  27. package/lib/module/components/biometrics/LivenessCameraView.js +38 -0
  28. package/lib/module/components/biometrics/LivenessCameraView.js.map +1 -0
  29. package/lib/module/components/biometrics/LivenessDetection.js +244 -0
  30. package/lib/module/components/biometrics/LivenessDetection.js.map +1 -0
  31. package/lib/module/index.js +5 -0
  32. package/lib/module/index.js.map +1 -1
  33. package/lib/typescript/components/OmnipayProvider.d.ts.map +1 -1
  34. package/lib/typescript/components/biometrics/FaceVerification.d.ts +12 -0
  35. package/lib/typescript/components/biometrics/FaceVerification.d.ts.map +1 -0
  36. package/lib/typescript/components/biometrics/LivenessCameraView.d.ts +22 -0
  37. package/lib/typescript/components/biometrics/LivenessCameraView.d.ts.map +1 -0
  38. package/lib/typescript/components/biometrics/LivenessDetection.d.ts +73 -0
  39. package/lib/typescript/components/biometrics/LivenessDetection.d.ts.map +1 -0
  40. package/lib/typescript/index.d.ts +3 -0
  41. package/lib/typescript/index.d.ts.map +1 -1
  42. package/omnipay-reactnative-sdk.podspec +47 -0
  43. package/package.json +6 -11
  44. package/src/components/OmnipayProvider.tsx +8 -65
  45. package/src/components/biometrics/FaceVerification.tsx +484 -0
  46. package/src/components/biometrics/LivenessCameraView.tsx +61 -0
  47. package/src/components/biometrics/LivenessDetection.ts +305 -0
  48. package/src/index.tsx +18 -0
  49. package/lib/commonjs/components/FaceVerification.js +0 -755
  50. package/lib/commonjs/components/FaceVerification.js.map +0 -1
  51. package/lib/commonjs/types/faceVerification.js +0 -2
  52. package/lib/commonjs/types/faceVerification.js.map +0 -1
  53. package/lib/commonjs/types/index.js +0 -17
  54. package/lib/commonjs/types/index.js.map +0 -1
  55. package/lib/module/components/FaceVerification.js +0 -746
  56. package/lib/module/components/FaceVerification.js.map +0 -1
  57. package/lib/module/types/faceVerification.js +0 -2
  58. package/lib/module/types/faceVerification.js.map +0 -1
  59. package/lib/module/types/index.js +0 -2
  60. package/lib/module/types/index.js.map +0 -1
  61. package/lib/typescript/components/FaceVerification.d.ts +0 -10
  62. package/lib/typescript/components/FaceVerification.d.ts.map +0 -1
  63. package/lib/typescript/types/faceVerification.d.ts +0 -18
  64. package/lib/typescript/types/faceVerification.d.ts.map +0 -1
  65. package/lib/typescript/types/index.d.ts +0 -2
  66. package/lib/typescript/types/index.d.ts.map +0 -1
  67. package/src/components/FaceVerification.tsx +0 -884
  68. package/src/types/faceVerification.ts +0 -27
  69. package/src/types/index.ts +0 -1
@@ -1,746 +0,0 @@
1
- import React, { useEffect, useRef, useState } from 'react';
2
- import { StyleSheet, Text, View, TouchableOpacity, Animated, Image, Linking, Platform } from 'react-native';
3
- import { useCameraDevice, useCameraPermission, useCameraFormat } from 'react-native-vision-camera';
4
- import { Camera } from 'react-native-vision-camera-face-detector';
5
- import Svg, { Circle } from 'react-native-svg';
6
- const AnimatedCircle = Animated.createAnimatedComponent(Circle);
7
- const FaceVerification = _ref => {
8
- let {
9
- onSuccess,
10
- onFailure,
11
- onCancel,
12
- onImageCaptured
13
- } = _ref;
14
- const device = useCameraDevice('front');
15
- const faceDetectionOptions = useRef({
16
- landmarkMode: 'all',
17
- classificationMode: 'all'
18
- }).current;
19
- const photoFormat = useCameraFormat(device, [{
20
- photoResolution: {
21
- width: 1280,
22
- height: 720
23
- }
24
- }]);
25
- const {
26
- hasPermission,
27
- requestPermission
28
- } = useCameraPermission();
29
- const spinValue = useRef(new Animated.Value(0)).current;
30
- const progressValue = useRef(new Animated.Value(955)).current; // 955 = circle circumference for r=152
31
-
32
- const [isCameraActive, setIsCameraActive] = useState(false);
33
- const [isLoading, setIsLoading] = useState(false);
34
- const [permissionDenied, setPermissionDenied] = useState(false);
35
- const [isInitializingCamera, setIsInitializingCamera] = useState(true);
36
- const [currentStep, setCurrentStep] = useState('position_face');
37
- const [isCapturing, setIsCapturing] = useState(false);
38
- const [capturedImage, setCapturedImage] = useState(null);
39
- const frameCountRef = useRef(0);
40
- const finalPositionFrameCountRef = useRef(0);
41
- const faceHistoryRef = useRef([]);
42
- const cameraRef = useRef(null);
43
-
44
- // RAF optimization: buffer latest face data and process on animation frames
45
- const latestFaceDataRef = useRef(null);
46
- const rafIdRef = useRef(null);
47
- const isProcessingRef = useRef(false);
48
-
49
- // Handle camera device initialization and auto-start
50
- useEffect(() => {
51
- if (device !== undefined) {
52
- setIsInitializingCamera(false);
53
-
54
- // If permissions are already granted, start camera immediately
55
- if (hasPermission && !isCameraActive && !permissionDenied && !capturedImage) {
56
- setIsCameraActive(true);
57
- progressValue.setValue(955);
58
- }
59
- }
60
- }, [device, hasPermission, isCameraActive, permissionDenied, capturedImage, progressValue]);
61
-
62
- // Start loading animation during camera initialization
63
- useEffect(() => {
64
- if (isInitializingCamera) {
65
- setIsLoading(true);
66
- } else {
67
- setIsLoading(false);
68
- }
69
- }, [isInitializingCamera]);
70
- useEffect(() => {
71
- const spin = Animated.loop(Animated.timing(spinValue, {
72
- toValue: 1,
73
- duration: 1000,
74
- useNativeDriver: true
75
- }));
76
- if (isLoading) {
77
- spin.start();
78
- } else {
79
- spin.stop();
80
- spinValue.setValue(0);
81
- }
82
- return () => spin.stop();
83
- }, [isLoading, spinValue]);
84
- useEffect(() => {
85
- const progressPercentage = calculateProgress();
86
- const strokeDasharray = 955;
87
- const targetOffset = strokeDasharray - progressPercentage / 100 * strokeDasharray;
88
- if (isCameraActive && progressPercentage >= 0) {
89
- Animated.timing(progressValue, {
90
- toValue: targetOffset,
91
- duration: 800,
92
- useNativeDriver: false
93
- }).start();
94
- }
95
- }, [currentStep, progressValue, isCameraActive]);
96
-
97
- // RAF-based face processing to throttle heavy computations
98
- const processFaceData = () => {
99
- if (!latestFaceDataRef.current || isProcessingRef.current) {
100
- rafIdRef.current = null;
101
- return;
102
- }
103
- isProcessingRef.current = true;
104
- const {
105
- faces
106
- } = latestFaceDataRef.current;
107
-
108
- // Clear the buffer since we're processing this data
109
- latestFaceDataRef.current = null;
110
- try {
111
- handleFaceDetectionLogic(faces);
112
- } finally {
113
- isProcessingRef.current = false;
114
- rafIdRef.current = null;
115
- }
116
- };
117
-
118
- // Cleanup RAF on unmount
119
- useEffect(() => {
120
- return () => {
121
- if (rafIdRef.current) {
122
- cancelAnimationFrame(rafIdRef.current);
123
- }
124
- };
125
- }, []);
126
- const getInstructionText = () => {
127
- switch (currentStep) {
128
- case 'position_face':
129
- return 'Position your face in the frame';
130
- case 'blink':
131
- return 'Please blink your eyes';
132
- case 'smile':
133
- return 'Now smile';
134
- case 'final_position':
135
- return 'Look straight at the camera and hold still';
136
- case 'complete':
137
- return '';
138
- default:
139
- return 'Position your face in the frame';
140
- }
141
- };
142
- const calculateProgress = () => {
143
- switch (currentStep) {
144
- case 'position_face':
145
- return 10;
146
- case 'blink':
147
- return 30;
148
- case 'smile':
149
- return 60;
150
- case 'final_position':
151
- return 80;
152
- case 'complete':
153
- return 100;
154
- default:
155
- return 0;
156
- }
157
- };
158
- const detectBlink = () => {
159
- if (frameCountRef.current < 20 || faceHistoryRef.current.length < 4) return false;
160
- const threshold = {
161
- eyeOpen: 0.5,
162
- eyeClosed: 0.3
163
- };
164
- const recentFrames = faceHistoryRef.current.slice(-4);
165
- for (let i = 0; i < recentFrames.length - 1; i++) {
166
- const frame1 = recentFrames[i];
167
- const frame2 = recentFrames[i + 1];
168
- if (!frame1 || !frame2) continue;
169
- const eyesWereOpen = frame1.leftEyeOpenProbability > threshold.eyeOpen && frame1.rightEyeOpenProbability > threshold.eyeOpen;
170
- const eyesAreClosed = frame2.leftEyeOpenProbability < threshold.eyeClosed && frame2.rightEyeOpenProbability < threshold.eyeClosed;
171
- if (eyesWereOpen && eyesAreClosed) {
172
- return true;
173
- }
174
- }
175
- return false;
176
- };
177
- const detectSmile = face => {
178
- if (frameCountRef.current < 20 || faceHistoryRef.current.length < 3) return false;
179
- const threshold = 0.7;
180
- const currentSmiling = face.smilingProbability > threshold;
181
- const recentFrames = faceHistoryRef.current.slice(-3);
182
- const hadNotSmilingFrames = recentFrames.some(frame => frame.smilingProbability < 0.4);
183
- return currentSmiling && hadNotSmilingFrames;
184
- };
185
- const isInFinalPosition = face => {
186
- const isLookingStraight = Math.abs(face.yawAngle) < 15;
187
- const eyesOpen = face.leftEyeOpenProbability > 0.5 && face.rightEyeOpenProbability > 0.5;
188
- const isNotSmiling = face.smilingProbability < 0.1;
189
- return isLookingStraight && eyesOpen && isNotSmiling;
190
- };
191
-
192
- // Fast callback that just buffers data - called by camera at high frequency
193
- function handleFacesDetection(faces, frame) {
194
- // Store the latest face data
195
- latestFaceDataRef.current = {
196
- faces,
197
- frame
198
- };
199
-
200
- // Schedule processing on next animation frame if not already scheduled
201
- if (!rafIdRef.current) {
202
- rafIdRef.current = requestAnimationFrame(processFaceData);
203
- }
204
- }
205
-
206
- // Heavy processing logic - called at animation frame rate (60fps max)
207
- function handleFaceDetectionLogic(faces) {
208
- if (faces.length === 0) {
209
- if (currentStep !== 'position_face' && currentStep !== 'complete') {
210
- setCurrentStep('position_face');
211
- frameCountRef.current = 0;
212
- faceHistoryRef.current = [];
213
- finalPositionFrameCountRef.current = 0;
214
- }
215
- return;
216
- }
217
- const face = faces[0];
218
- if (!face) return;
219
- frameCountRef.current++;
220
- faceHistoryRef.current = [...faceHistoryRef.current.slice(-7), face];
221
- switch (currentStep) {
222
- case 'position_face':
223
- if (frameCountRef.current > 10) {
224
- setCurrentStep('blink');
225
- }
226
- break;
227
- case 'blink':
228
- if (detectBlink()) {
229
- setCurrentStep('smile');
230
- }
231
- break;
232
- case 'smile':
233
- if (detectSmile(face)) {
234
- setCurrentStep('final_position');
235
- }
236
- break;
237
- case 'final_position':
238
- if (isInFinalPosition(face)) {
239
- finalPositionFrameCountRef.current++;
240
- // Require 5 consecutive frames for stability before capturing
241
- if (finalPositionFrameCountRef.current >= 5) {
242
- setCurrentStep('complete');
243
- captureCurrentFrame();
244
- }
245
- } else {
246
- finalPositionFrameCountRef.current = 0;
247
- }
248
- break;
249
- default:
250
- break;
251
- }
252
- }
253
- const captureCurrentFrame = async () => {
254
- if (isCapturing || !cameraRef.current) return;
255
- try {
256
- setIsCapturing(true);
257
- const photo = await cameraRef.current.takePhoto({
258
- enableShutterSound: false
259
- });
260
- const response = await fetch(`file://${photo.path}`);
261
- const blob = await response.blob();
262
- const reader = new FileReader();
263
- reader.onloadend = () => {
264
- const base64String = reader.result;
265
- setCapturedImage(base64String);
266
- setIsCameraActive(false);
267
- };
268
- reader.readAsDataURL(blob);
269
- } catch (error) {
270
- onFailure === null || onFailure === void 0 ? void 0 : onFailure();
271
- } finally {
272
- setIsCapturing(false);
273
- }
274
- };
275
- const handleContinue = () => {
276
- if (capturedImage) {
277
- const base64String = capturedImage.replace(/^data:image\/[a-z]+;base64,/, '');
278
-
279
- // Call onImageCaptured immediately for UI feedback
280
- onImageCaptured === null || onImageCaptured === void 0 ? void 0 : onImageCaptured(base64String);
281
-
282
- // Still call onSuccess for backward compatibility
283
- onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess(base64String);
284
- }
285
- };
286
- const handleRetake = () => {
287
- setCurrentStep('position_face');
288
- faceHistoryRef.current = [];
289
- frameCountRef.current = 0;
290
- setCapturedImage(null);
291
- finalPositionFrameCountRef.current = 0;
292
- progressValue.setValue(955);
293
- setIsCameraActive(true);
294
- setIsLoading(true);
295
- setTimeout(() => {
296
- setIsLoading(false);
297
- }, 1000);
298
- };
299
- const startCamera = async () => {
300
- setIsLoading(true);
301
- setPermissionDenied(false);
302
- try {
303
- const permission = await requestPermission();
304
- if (permission === true) {
305
- setIsCameraActive(true);
306
- progressValue.setValue(955);
307
- setTimeout(() => {
308
- setIsLoading(false);
309
- }, 1000);
310
- } else {
311
- setPermissionDenied(true);
312
- setIsLoading(false);
313
- }
314
- } catch (error) {
315
- setPermissionDenied(true);
316
- setIsLoading(false);
317
- }
318
- };
319
- const openSettings = () => {
320
- if (Platform.OS === 'ios') {
321
- Linking.openURL('app-settings:');
322
- } else {
323
- Linking.openSettings();
324
- }
325
- };
326
- if (permissionDenied) {
327
- return /*#__PURE__*/React.createElement(View, {
328
- style: [styles.container]
329
- }, /*#__PURE__*/React.createElement(View, {
330
- style: styles.centeredContainer
331
- }, /*#__PURE__*/React.createElement(View, {
332
- style: styles.permissionDeniedContainer
333
- }, /*#__PURE__*/React.createElement(Text, {
334
- style: styles.permissionDeniedTitle
335
- }, "Camera Access Required"), /*#__PURE__*/React.createElement(Text, {
336
- style: styles.permissionDeniedText
337
- }, "This app needs camera access to verify your identity. Please allow camera permission in your device settings."), /*#__PURE__*/React.createElement(View, {
338
- style: styles.permissionActions
339
- }, /*#__PURE__*/React.createElement(TouchableOpacity, {
340
- style: styles.settingsButton,
341
- onPress: openSettings
342
- }, /*#__PURE__*/React.createElement(Text, {
343
- style: styles.settingsButtonText
344
- }, "Open Settings")), /*#__PURE__*/React.createElement(TouchableOpacity, {
345
- style: styles.retryButton,
346
- onPress: () => setPermissionDenied(false)
347
- }, /*#__PURE__*/React.createElement(Text, {
348
- style: styles.retryButtonText
349
- }, "Try Again"))))));
350
- }
351
- if (isInitializingCamera || device === undefined) {
352
- const spin = spinValue.interpolate({
353
- inputRange: [0, 1],
354
- outputRange: ['0deg', '360deg']
355
- });
356
- return /*#__PURE__*/React.createElement(View, {
357
- style: [styles.container]
358
- }, /*#__PURE__*/React.createElement(View, {
359
- style: styles.centeredContainer
360
- }, /*#__PURE__*/React.createElement(View, {
361
- style: styles.loadingContainer
362
- }, /*#__PURE__*/React.createElement(Animated.View, {
363
- style: [styles.loadingSpinner, {
364
- transform: [{
365
- rotate: spin
366
- }]
367
- }]
368
- }), /*#__PURE__*/React.createElement(Text, {
369
- style: styles.loadingText
370
- }, isInitializingCamera ? 'Initializing camera...' : 'No camera device available'))));
371
- }
372
- const strokeDasharray = 955;
373
- const spin = spinValue.interpolate({
374
- inputRange: [0, 1],
375
- outputRange: ['0deg', '360deg']
376
- });
377
- return /*#__PURE__*/React.createElement(View, {
378
- style: [styles.container]
379
- }, /*#__PURE__*/React.createElement(View, {
380
- style: styles.centeredContainer
381
- }, isLoading ? /*#__PURE__*/React.createElement(View, {
382
- style: styles.loadingContainer
383
- }, /*#__PURE__*/React.createElement(Animated.View, {
384
- style: [styles.loadingSpinner, {
385
- transform: [{
386
- rotate: spin
387
- }]
388
- }]
389
- })) : /*#__PURE__*/React.createElement(View, {
390
- style: styles.contentContainer
391
- }, capturedImage ? /*#__PURE__*/React.createElement(View, {
392
- style: styles.capturedImageContainer
393
- }, /*#__PURE__*/React.createElement(View, {
394
- style: styles.capturedImageWrapper
395
- }, /*#__PURE__*/React.createElement(Image, {
396
- source: {
397
- uri: capturedImage
398
- },
399
- style: styles.capturedImage,
400
- resizeMode: "cover"
401
- })), /*#__PURE__*/React.createElement(View, {
402
- style: styles.capturedImageActions
403
- }, /*#__PURE__*/React.createElement(TouchableOpacity, {
404
- style: styles.continueButton,
405
- onPress: handleContinue
406
- }, /*#__PURE__*/React.createElement(Text, {
407
- style: styles.continueButtonText
408
- }, "Continue")), /*#__PURE__*/React.createElement(TouchableOpacity, {
409
- style: styles.retakeButton,
410
- onPress: handleRetake
411
- }, /*#__PURE__*/React.createElement(Text, {
412
- style: styles.retakeButtonText
413
- }, "Retake")))) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(View, {
414
- style: styles.captureSection
415
- }, /*#__PURE__*/React.createElement(View, {
416
- style: styles.captureBackground
417
- }, isCameraActive ? /*#__PURE__*/React.createElement(Camera, {
418
- ref: cameraRef,
419
- device: device,
420
- isActive: true,
421
- style: styles.camera,
422
- faceDetectionCallback: handleFacesDetection,
423
- faceDetectionOptions: faceDetectionOptions,
424
- photo: true,
425
- format: photoFormat
426
- }) : /*#__PURE__*/React.createElement(View, {
427
- style: styles.avatarPlaceholder
428
- }, /*#__PURE__*/React.createElement(Text, {
429
- style: styles.avatarText
430
- }, "\uD83D\uDC64"))), isCameraActive && /*#__PURE__*/React.createElement(View, {
431
- style: styles.progressRingContainer
432
- }, /*#__PURE__*/React.createElement(Svg, {
433
- width: 320,
434
- height: 320,
435
- viewBox: "0 0 320 320",
436
- style: styles.progressRing
437
- }, /*#__PURE__*/React.createElement(Circle, {
438
- cx: 160,
439
- cy: 160,
440
- r: 150,
441
- stroke: "#E5E7EB",
442
- strokeWidth: "0",
443
- fill: "none"
444
- }), /*#__PURE__*/React.createElement(AnimatedCircle, {
445
- cx: 160,
446
- cy: 160,
447
- r: 152,
448
- stroke: "#214287",
449
- strokeWidth: 6,
450
- fill: "none",
451
- strokeDasharray: strokeDasharray,
452
- strokeDashoffset: progressValue,
453
- strokeLinecap: "round"
454
- }))), !isCameraActive && /*#__PURE__*/React.createElement(View, {
455
- style: styles.cameraIconSection
456
- }, /*#__PURE__*/React.createElement(View, {
457
- style: styles.cameraIconContainer
458
- }, /*#__PURE__*/React.createElement(Text, {
459
- style: styles.cameraIcon
460
- }, "\uD83D\uDCF7")))), isCameraActive && /*#__PURE__*/React.createElement(View, {
461
- style: styles.instructionContainer
462
- }, /*#__PURE__*/React.createElement(Text, {
463
- style: styles.instructionText
464
- }, getInstructionText())), !isCameraActive && !hasPermission && /*#__PURE__*/React.createElement(View, {
465
- style: styles.startContainer
466
- }, /*#__PURE__*/React.createElement(TouchableOpacity, {
467
- style: styles.startButton,
468
- onPress: startCamera
469
- }, /*#__PURE__*/React.createElement(Text, {
470
- style: styles.startButtonText
471
- }, "Proceed")))))), /*#__PURE__*/React.createElement(TouchableOpacity, {
472
- style: styles.closeButton,
473
- onPress: onCancel
474
- }, /*#__PURE__*/React.createElement(Text, {
475
- style: styles.closeButtonText
476
- }, "\u2715")));
477
- };
478
- const styles = StyleSheet.create({
479
- container: {
480
- flex: 1,
481
- marginHorizontal: 'auto',
482
- width: '100%'
483
- },
484
- centeredContainer: {
485
- flex: 1,
486
- alignSelf: 'center',
487
- justifyContent: 'center',
488
- width: '100%',
489
- paddingHorizontal: 0,
490
- paddingBottom: 40,
491
- paddingTop: 20
492
- },
493
- contentContainer: {
494
- flex: 1,
495
- alignItems: 'center',
496
- justifyContent: 'flex-start',
497
- gap: 16
498
- },
499
- captureSection: {
500
- justifyContent: 'flex-start',
501
- alignItems: 'center',
502
- position: 'relative',
503
- width: 320
504
- },
505
- captureBackground: {
506
- width: 300,
507
- height: 300,
508
- backgroundColor: '#F1F5F9',
509
- overflow: 'hidden',
510
- position: 'relative',
511
- justifyContent: 'center',
512
- alignItems: 'center',
513
- borderRadius: 150
514
- },
515
- camera: {
516
- width: '100%',
517
- height: '100%',
518
- transform: [{
519
- scaleX: -1
520
- }]
521
- },
522
- avatarFrame: {
523
- width: 140,
524
- height: 140
525
- },
526
- avatarPlaceholder: {
527
- width: 140,
528
- height: 140,
529
- justifyContent: 'center',
530
- alignItems: 'center'
531
- },
532
- avatarText: {
533
- fontSize: 80
534
- },
535
- progressRingContainer: {
536
- position: 'absolute',
537
- top: '50%',
538
- left: '50%',
539
- transform: [{
540
- translateX: -160
541
- }, {
542
- translateY: -160
543
- }],
544
- width: 320,
545
- height: 320
546
- },
547
- progressRing: {
548
- width: '100%',
549
- height: '100%'
550
- },
551
- cameraIconSection: {
552
- position: 'absolute',
553
- top: 240,
554
- right: 40,
555
- width: 42,
556
- height: 42,
557
- borderRadius: 21,
558
- backgroundColor: '#ffffff',
559
- justifyContent: 'center',
560
- alignItems: 'center'
561
- },
562
- cameraIconContainer: {
563
- width: 38,
564
- height: 38,
565
- borderRadius: 19,
566
- backgroundColor: '#214287',
567
- justifyContent: 'center',
568
- alignItems: 'center'
569
- },
570
- cameraIcon: {
571
- fontSize: 16,
572
- color: '#ffffff'
573
- },
574
- instructionContainer: {
575
- marginBottom: 16,
576
- paddingHorizontal: 20,
577
- alignItems: 'center'
578
- },
579
- instructionText: {
580
- fontSize: 16,
581
- color: '#667085',
582
- textAlign: 'center',
583
- lineHeight: 24
584
- },
585
- startContainer: {
586
- alignItems: 'center',
587
- marginTop: 24
588
- },
589
- startButton: {
590
- backgroundColor: '#214287',
591
- borderRadius: 10,
592
- padding: 15,
593
- alignItems: 'center',
594
- minWidth: 250
595
- },
596
- startButtonText: {
597
- color: '#ffffff',
598
- fontSize: 16,
599
- fontWeight: '600',
600
- textAlign: 'center'
601
- },
602
- loadingContainer: {
603
- flex: 1,
604
- justifyContent: 'center',
605
- alignItems: 'center',
606
- paddingVertical: 50
607
- },
608
- loadingSpinner: {
609
- width: 48,
610
- height: 48,
611
- borderRadius: 24,
612
- borderWidth: 4,
613
- borderColor: '#e5e7eb',
614
- borderTopColor: '#214287',
615
- marginBottom: 16
616
- },
617
- loadingText: {
618
- fontSize: 16,
619
- color: '#667085',
620
- textAlign: 'center'
621
- },
622
- capturedImageContainer: {
623
- alignItems: 'center',
624
- justifyContent: 'center',
625
- gap: 24,
626
- width: '100%'
627
- },
628
- capturedImageWrapper: {
629
- width: '100%',
630
- height: 300,
631
- borderRadius: 12,
632
- overflow: 'hidden',
633
- backgroundColor: '#F1F5F9',
634
- shadowColor: '#000',
635
- shadowOffset: {
636
- width: 0,
637
- height: 4
638
- },
639
- shadowOpacity: 0.1,
640
- shadowRadius: 8,
641
- elevation: 5
642
- },
643
- capturedImage: {
644
- width: '100%',
645
- height: '100%'
646
- },
647
- capturedImageActions: {
648
- width: '100%',
649
- gap: 12
650
- },
651
- continueButton: {
652
- backgroundColor: '#214287',
653
- borderRadius: 10,
654
- padding: 15,
655
- alignItems: 'center',
656
- width: '100%'
657
- },
658
- continueButtonText: {
659
- color: '#ffffff',
660
- fontSize: 16,
661
- fontWeight: '600',
662
- textAlign: 'center'
663
- },
664
- retakeButton: {
665
- borderWidth: 1,
666
- borderColor: '#214287',
667
- borderRadius: 10,
668
- padding: 15,
669
- alignItems: 'center',
670
- backgroundColor: 'transparent',
671
- width: '100%'
672
- },
673
- retakeButtonText: {
674
- color: '#214287',
675
- fontSize: 16,
676
- fontWeight: '600',
677
- textAlign: 'center'
678
- },
679
- permissionDeniedContainer: {
680
- alignItems: 'center'
681
- },
682
- permissionDeniedTitle: {
683
- fontSize: 20,
684
- fontWeight: '600',
685
- color: '#333333',
686
- textAlign: 'center',
687
- marginBottom: 15
688
- },
689
- permissionDeniedText: {
690
- fontSize: 16,
691
- color: '#666666',
692
- textAlign: 'center',
693
- lineHeight: 24,
694
- marginBottom: 30
695
- },
696
- permissionActions: {
697
- width: '100%',
698
- gap: 12
699
- },
700
- settingsButton: {
701
- backgroundColor: '#214287',
702
- borderRadius: 10,
703
- padding: 15,
704
- alignItems: 'center',
705
- width: '100%'
706
- },
707
- settingsButtonText: {
708
- color: '#ffffff',
709
- fontSize: 16,
710
- fontWeight: '600',
711
- textAlign: 'center'
712
- },
713
- retryButton: {
714
- borderWidth: 1,
715
- borderColor: '#214287',
716
- borderRadius: 10,
717
- padding: 15,
718
- alignItems: 'center',
719
- backgroundColor: 'transparent',
720
- width: '100%'
721
- },
722
- retryButtonText: {
723
- color: '#214287',
724
- fontSize: 16,
725
- fontWeight: '600',
726
- textAlign: 'center'
727
- },
728
- closeButton: {
729
- position: 'absolute',
730
- top: 50,
731
- right: 20,
732
- width: 44,
733
- height: 44,
734
- borderRadius: 22,
735
- backgroundColor: 'rgba(0,0,0,0.5)',
736
- justifyContent: 'center',
737
- alignItems: 'center'
738
- },
739
- closeButtonText: {
740
- color: 'white',
741
- fontSize: 18,
742
- fontWeight: 'bold'
743
- }
744
- });
745
- export default FaceVerification;
746
- //# sourceMappingURL=FaceVerification.js.map