@sanctum-key/react-native-sdk 1.0.14 → 1.0.15
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/build/package.json +1 -1
- package/build/src/components/EnhancedCameraView.d.ts.map +1 -1
- package/build/src/components/EnhancedCameraView.js +148 -8
- package/build/src/components/EnhancedCameraView.js.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.js +42 -15
- package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/src/utils/cropByObb.d.ts +0 -12
- package/build/src/utils/cropByObb.d.ts.map +1 -1
- package/build/src/utils/cropByObb.js +73 -98
- package/build/src/utils/cropByObb.js.map +1 -1
- package/package.json +1 -1
- package/src/components/EnhancedCameraView.tsx +190 -33
- package/src/components/KYCElements/IDCardCapture.tsx +49 -15
- package/src/utils/cropByObb.ts +90 -127
package/build/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EnhancedCameraView.d.ts","sourceRoot":"","sources":["../../../src/components/EnhancedCameraView.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"EnhancedCameraView.d.ts","sourceRoot":"","sources":["../../../src/components/EnhancedCameraView.tsx"],"names":[],"mappings":"AAuKA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAKxE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAGzD,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CA2IhE,CAAC"}
|
|
@@ -1,3 +1,146 @@
|
|
|
1
|
+
// import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
// import { View, StyleSheet, Text, AppState } from 'react-native';
|
|
3
|
+
// import { Camera, useCameraDevice, useCameraFormat } from 'react-native-vision-camera';
|
|
4
|
+
// import VisionCameraModule from '../modules/camera/VisionCameraModule';
|
|
5
|
+
// import { useI18n } from '../hooks/useI18n';
|
|
6
|
+
// import { EnhancedCameraViewProps } from './OverLay/type';
|
|
7
|
+
// import { Button } from './ui/Button';
|
|
8
|
+
// export const EnhancedCameraView: React.FC<EnhancedCameraViewProps> = ({
|
|
9
|
+
// showCamera,
|
|
10
|
+
// cameraType: initialCameraType = 'front',
|
|
11
|
+
// style,
|
|
12
|
+
// onError,
|
|
13
|
+
// onSilentCapture,
|
|
14
|
+
// silentCaptureResult,
|
|
15
|
+
// isProcessing = false,
|
|
16
|
+
// overlayComponent,
|
|
17
|
+
// }) => {
|
|
18
|
+
// const { t } = useI18n();
|
|
19
|
+
// const camera = useRef<Camera>(null);
|
|
20
|
+
// const isCapturingRef = useRef(false);
|
|
21
|
+
// const isProcessingRef = useRef(isProcessing);
|
|
22
|
+
// const [cameraType] = useState<'front' | 'back'>(initialCameraType);
|
|
23
|
+
// const [hasPermission, setHasPermission] = useState<boolean | null>(null);
|
|
24
|
+
// const [isInitialized, setIsInitialized] = useState(false);
|
|
25
|
+
// const [refreshCamera, setRefreshCamera] = useState(false);
|
|
26
|
+
// const [layout, setLayout] = useState({ width: 0, height: 0 });
|
|
27
|
+
// const device = useCameraDevice(cameraType, {
|
|
28
|
+
// physicalDevices: [
|
|
29
|
+
// 'wide-angle-camera' // Explicitly request standard 1x lens
|
|
30
|
+
// ]
|
|
31
|
+
// });
|
|
32
|
+
// const targetRatio = layout.width > 0 ? layout.height / layout.width : 16 / 9;
|
|
33
|
+
// const format = useCameraFormat(device, [
|
|
34
|
+
// { photoAspectRatio: targetRatio },
|
|
35
|
+
// { photoResolution: 'max' }
|
|
36
|
+
// ]);
|
|
37
|
+
// useEffect(() => {
|
|
38
|
+
// isProcessingRef.current = isProcessing;
|
|
39
|
+
// }, [isProcessing]);
|
|
40
|
+
// const checkPermissions = async () => {
|
|
41
|
+
// try {
|
|
42
|
+
// const hasAllPermissions = await VisionCameraModule.hasAllPermissions();
|
|
43
|
+
// if (!hasAllPermissions) {
|
|
44
|
+
// const granted = await VisionCameraModule.requestAllPermissions();
|
|
45
|
+
// if (!granted) {
|
|
46
|
+
// setHasPermission(false);
|
|
47
|
+
// onError?.({ message: t('camera.permissionRequired') });
|
|
48
|
+
// return;
|
|
49
|
+
// }
|
|
50
|
+
// }
|
|
51
|
+
// setHasPermission(true);
|
|
52
|
+
// } catch (error) {
|
|
53
|
+
// setHasPermission(false);
|
|
54
|
+
// onError?.({ message: t('camera.errorOccurred') });
|
|
55
|
+
// }
|
|
56
|
+
// };
|
|
57
|
+
// useEffect(() => {
|
|
58
|
+
// if (showCamera) checkPermissions();
|
|
59
|
+
// }, [showCamera, refreshCamera]);
|
|
60
|
+
// useEffect(() => {
|
|
61
|
+
// const subscription = AppState.addEventListener('change', nextAppState => {
|
|
62
|
+
// if (nextAppState === 'active' && showCamera && hasPermission === false) {
|
|
63
|
+
// checkPermissions();
|
|
64
|
+
// }
|
|
65
|
+
// });
|
|
66
|
+
// return () => subscription.remove();
|
|
67
|
+
// }, [showCamera, hasPermission]);
|
|
68
|
+
// const onInitialized = useCallback(() => setIsInitialized(true), []);
|
|
69
|
+
// const onCameraError = useCallback((error: any) => {
|
|
70
|
+
// onError?.({ message: error.message || t('camera.errorOccurred') });
|
|
71
|
+
// }, [onError, t]);
|
|
72
|
+
// const captureSilentPhoto = useCallback(async () => {
|
|
73
|
+
// if (!camera.current || !isInitialized || isProcessingRef.current || isCapturingRef.current) return;
|
|
74
|
+
// if (silentCaptureResult?.isAnalyzing) return;
|
|
75
|
+
// try {
|
|
76
|
+
// isCapturingRef.current = true;
|
|
77
|
+
// const photo = await camera.current.takePhoto({
|
|
78
|
+
// enableShutterSound: false,
|
|
79
|
+
// flash: 'off',
|
|
80
|
+
// });
|
|
81
|
+
// const result = await VisionCameraModule.processPhotoResult(photo);
|
|
82
|
+
// onSilentCapture?.({
|
|
83
|
+
// ...result,
|
|
84
|
+
// path: result.path || photo.path,
|
|
85
|
+
// });
|
|
86
|
+
// } catch (error) {
|
|
87
|
+
// // Silent background fail
|
|
88
|
+
// } finally {
|
|
89
|
+
// isCapturingRef.current = false;
|
|
90
|
+
// }
|
|
91
|
+
// }, [isInitialized, onSilentCapture, silentCaptureResult]);
|
|
92
|
+
// useEffect(() => {
|
|
93
|
+
// if (!showCamera || !isInitialized || isProcessing) return;
|
|
94
|
+
// let isActive = true;
|
|
95
|
+
// let intervalId: ReturnType<typeof setInterval>;
|
|
96
|
+
// const warmupTimer = setTimeout(() => {
|
|
97
|
+
// if (!isActive) return;
|
|
98
|
+
// intervalId = setInterval(() => {
|
|
99
|
+
// captureSilentPhoto();
|
|
100
|
+
// }, 1500);
|
|
101
|
+
// }, 1000);
|
|
102
|
+
// return () => {
|
|
103
|
+
// isActive = false;
|
|
104
|
+
// clearTimeout(warmupTimer);
|
|
105
|
+
// if (intervalId) clearInterval(intervalId);
|
|
106
|
+
// };
|
|
107
|
+
// }, [showCamera, isInitialized, isProcessing, captureSilentPhoto]);
|
|
108
|
+
// if (hasPermission === null) return <View style={[styles.container, style]} />;
|
|
109
|
+
// if (hasPermission === false) {
|
|
110
|
+
// return (
|
|
111
|
+
// <View style={[styles.container, style, { justifyContent: 'center', alignItems: 'center' }]}>
|
|
112
|
+
// <Text style={styles.permissionMessage}>{t('camera.permissionRequired')}</Text>
|
|
113
|
+
// <Button title="Refresh Camera" onPress={() => setRefreshCamera(prev => !prev)} variant="primary" />
|
|
114
|
+
// </View>
|
|
115
|
+
// );
|
|
116
|
+
// }
|
|
117
|
+
// if (!device || !showCamera) return <View style={[styles.container, style]} />;
|
|
118
|
+
// return (
|
|
119
|
+
// <View
|
|
120
|
+
// style={[styles.container, style]}
|
|
121
|
+
// onLayout={(e) => setLayout({ width: e.nativeEvent.layout.width, height: e.nativeEvent.layout.height })}
|
|
122
|
+
// >
|
|
123
|
+
// <Camera
|
|
124
|
+
// ref={camera}
|
|
125
|
+
// style={StyleSheet.absoluteFill}
|
|
126
|
+
// device={device}
|
|
127
|
+
// format={format}
|
|
128
|
+
// resizeMode="cover"
|
|
129
|
+
// isActive={showCamera && !isProcessing}
|
|
130
|
+
// photo={true}
|
|
131
|
+
// video={false}
|
|
132
|
+
// audio={false}
|
|
133
|
+
// onInitialized={onInitialized}
|
|
134
|
+
// onError={onCameraError}
|
|
135
|
+
// />
|
|
136
|
+
// {overlayComponent}
|
|
137
|
+
// </View>
|
|
138
|
+
// );
|
|
139
|
+
// };
|
|
140
|
+
// const styles = StyleSheet.create({
|
|
141
|
+
// container: { flex: 1, backgroundColor: 'black' },
|
|
142
|
+
// permissionMessage: { color: 'white', textAlign: 'center', margin: 20, fontSize: 16 },
|
|
143
|
+
// });
|
|
1
144
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
145
|
import { View, StyleSheet, Text, AppState } from 'react-native';
|
|
3
146
|
import { Camera, useCameraDevice } from 'react-native-vision-camera';
|
|
@@ -10,10 +153,11 @@ export const EnhancedCameraView = ({ showCamera, cameraType: initialCameraType =
|
|
|
10
153
|
const isCapturingRef = useRef(false);
|
|
11
154
|
const isProcessingRef = useRef(isProcessing);
|
|
12
155
|
const [cameraType] = useState(initialCameraType);
|
|
13
|
-
// 🚨 BUG FIX: Initialize to null to prevent the "Flicker" on retake
|
|
14
156
|
const [hasPermission, setHasPermission] = useState(null);
|
|
15
157
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
16
158
|
const [refreshCamera, setRefreshCamera] = useState(false);
|
|
159
|
+
// 🚨 THE FIX: Removed the strict physical device filtering and custom format.
|
|
160
|
+
// This allows Android to select its own supported, stable hardware stream, preventing crashes.
|
|
17
161
|
const device = useCameraDevice(cameraType);
|
|
18
162
|
useEffect(() => {
|
|
19
163
|
isProcessingRef.current = isProcessing;
|
|
@@ -76,7 +220,6 @@ export const EnhancedCameraView = ({ showCamera, cameraType: initialCameraType =
|
|
|
76
220
|
isCapturingRef.current = false;
|
|
77
221
|
}
|
|
78
222
|
}, [isInitialized, onSilentCapture, silentCaptureResult]);
|
|
79
|
-
// 🚨 BUG FIX: The Warm-up Timer (Fixes Blurry Images)
|
|
80
223
|
useEffect(() => {
|
|
81
224
|
if (!showCamera || !isInitialized || isProcessing)
|
|
82
225
|
return;
|
|
@@ -87,7 +230,7 @@ export const EnhancedCameraView = ({ showCamera, cameraType: initialCameraType =
|
|
|
87
230
|
return;
|
|
88
231
|
intervalId = setInterval(() => {
|
|
89
232
|
captureSilentPhoto();
|
|
90
|
-
}, 1500);
|
|
233
|
+
}, 1500);
|
|
91
234
|
}, 1000);
|
|
92
235
|
return () => {
|
|
93
236
|
isActive = false;
|
|
@@ -96,11 +239,8 @@ export const EnhancedCameraView = ({ showCamera, cameraType: initialCameraType =
|
|
|
96
239
|
clearInterval(intervalId);
|
|
97
240
|
};
|
|
98
241
|
}, [showCamera, isInitialized, isProcessing, captureSilentPhoto]);
|
|
99
|
-
|
|
100
|
-
// 🚨 BUG FIX: Show nothing while checking permissions (Stops flicker)
|
|
101
|
-
if (hasPermission === null) {
|
|
242
|
+
if (hasPermission === null)
|
|
102
243
|
return <View style={[styles.container, style]}/>;
|
|
103
|
-
}
|
|
104
244
|
if (hasPermission === false) {
|
|
105
245
|
return (<View style={[styles.container, style, { justifyContent: 'center', alignItems: 'center' }]}>
|
|
106
246
|
<Text style={styles.permissionMessage}>{t('camera.permissionRequired')}</Text>
|
|
@@ -110,7 +250,7 @@ export const EnhancedCameraView = ({ showCamera, cameraType: initialCameraType =
|
|
|
110
250
|
if (!device || !showCamera)
|
|
111
251
|
return <View style={[styles.container, style]}/>;
|
|
112
252
|
return (<View style={[styles.container, style]}>
|
|
113
|
-
<Camera ref={camera} style={StyleSheet.absoluteFill} device={device} isActive={showCamera && !isProcessing} photo={true} video={false} audio={false} onInitialized={onInitialized} onError={onCameraError}/>
|
|
253
|
+
<Camera ref={camera} style={StyleSheet.absoluteFill} device={device} resizeMode="cover" isActive={showCamera && !isProcessing} photo={true} video={false} audio={false} onInitialized={onInitialized} onError={onCameraError}/>
|
|
114
254
|
{overlayComponent}
|
|
115
255
|
</View>);
|
|
116
256
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EnhancedCameraView.js","sourceRoot":"","sources":["../../../src/components/EnhancedCameraView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,kBAAkB,MAAM,sCAAsC,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,CAAC,MAAM,kBAAkB,GAAsC,CAAC,EACpE,UAAU,EACV,UAAU,EAAE,iBAAiB,GAAG,OAAO,EACvC,KAAK,EACL,OAAO,EACP,eAAe,EACf,mBAAmB,EACnB,YAAY,GAAG,KAAK,EACpB,gBAAgB,GACjB,EAAE,EAAE;IACH,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;IACxB,MAAM,MAAM,GAAG,MAAM,CAAS,IAAI,CAAC,CAAC;IAEpC,MAAM,cAAc,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,eAAe,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IAE7C,MAAM,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAmB,iBAAiB,CAAC,CAAC;IAEnE,oEAAoE;IACpE,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAiB,IAAI,CAAC,CAAC;IACzE,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC1D,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE1D,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAE3C,SAAS,CAAC,GAAG,EAAE;QACb,eAAe,CAAC,OAAO,GAAG,YAAY,CAAC;IACzC,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,MAAM,gBAAgB,GAAG,KAAK,IAAI,EAAE;QAClC,IAAI,CAAC;YACH,MAAM,iBAAiB,GAAG,MAAM,kBAAkB,CAAC,iBAAiB,EAAE,CAAC;YACvE,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBACvB,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,qBAAqB,EAAE,CAAC;gBACjE,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,gBAAgB,CAAC,KAAK,CAAC,CAAC;oBACxB,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,2BAA2B,CAAC,EAAE,CAAC,CAAC;oBACvD,OAAO;gBACT,CAAC;YACH,CAAC;YACD,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,gBAAgB,CAAC,KAAK,CAAC,CAAC;YACxB,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC;QACpD,CAAC;IACH,CAAC,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,UAAU;YAAE,gBAAgB,EAAE,CAAC;IACrC,CAAC,EAAE,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC;IAEhC,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,YAAY,GAAG,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE;YACtE,IAAI,YAAY,KAAK,QAAQ,IAAI,UAAU,IAAI,aAAa,KAAK,KAAK,EAAE,CAAC;gBACvE,gBAAgB,EAAE,CAAC;YACrB,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;IACrC,CAAC,EAAE,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC;IAEhC,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IACpE,MAAM,aAAa,GAAG,WAAW,CAAC,CAAC,KAAU,EAAE,EAAE;QAC/C,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC;IACrE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IAEjB,MAAM,kBAAkB,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QAChD,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,aAAa,IAAI,eAAe,CAAC,OAAO,IAAI,cAAc,CAAC,OAAO;YAAE,OAAO;QACnG,IAAI,mBAAmB,EAAE,WAAW;YAAE,OAAO;QAE7C,IAAI,CAAC;YACH,cAAc,CAAC,OAAO,GAAG,IAAI,CAAC;YAC9B,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;gBAC3C,kBAAkB,EAAE,KAAK;gBACzB,KAAK,EAAE,KAAK;aACb,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;YAElE,eAAe,EAAE,CAAC;gBACd,GAAG,MAAM;gBACT,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI;aAClC,CAAC,CAAC;QAEL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,yBAAyB;QAC3B,CAAC;gBAAS,CAAC;YACT,cAAc,CAAC,OAAO,GAAG,KAAK,CAAC;QACjC,CAAC;IACH,CAAC,EAAE,CAAC,aAAa,EAAE,eAAe,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAE1D,sDAAsD;IACtD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,UAAU,IAAI,CAAC,aAAa,IAAI,YAAY;YAAE,OAAO;QAE1D,IAAI,QAAQ,GAAG,IAAI,CAAC;QACpB,IAAI,UAA0C,CAAC;QAE/C,MAAM,WAAW,GAAG,UAAU,CAAC,GAAG,EAAE;YAClC,IAAI,CAAC,QAAQ;gBAAE,OAAO;YACtB,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;gBAC5B,kBAAkB,EAAE,CAAC;YACvB,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,+DAA+D;QAC3E,CAAC,EAAE,IAAI,CAAC,CAAC;QAET,OAAO,GAAG,EAAE;YACV,QAAQ,GAAG,KAAK,CAAC;YACjB,YAAY,CAAC,WAAW,CAAC,CAAC;YAC1B,IAAI,UAAU;gBAAE,aAAa,CAAC,UAAU,CAAC,CAAC;QAC5C,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAElE,oBAAoB;IAEpB,sEAAsE;IACtE,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;QAC3B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,EAAG,CAAC;IACpD,CAAC;IAED,IAAI,aAAa,KAAK,KAAK,EAAE,CAAC;QAC5B,OAAO,CACL,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC,CACzF;QAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,EAAE,IAAI,CAC7E;QAAA,CAAC,MAAM,CACJ,KAAK,CAAC,gBAAgB,CACtB,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAC/C,OAAO,CAAC,SAAS,EAEtB;MAAA,EAAE,IAAI,CAAC,CACR,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU;QAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,EAAG,CAAC;IAE9E,OAAO,CACL,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CACrC;MAAA,CAAC,MAAM,CACL,GAAG,CAAC,CAAC,MAAM,CAAC,CACZ,KAAK,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC,CAC/B,MAAM,CAAC,CAAC,MAAM,CAAC,CACf,QAAQ,CAAC,CAAC,UAAU,IAAI,CAAC,YAAY,CAAC,CACtC,KAAK,CAAC,CAAC,IAAI,CAAC,CACZ,KAAK,CAAC,CAAC,KAAK,CAAC,CACb,KAAK,CAAC,CAAC,KAAK,CAAC,CACb,aAAa,CAAC,CAAC,aAAa,CAAC,CAC7B,OAAO,CAAC,CAAC,aAAa,CAAC,EAEzB;MAAA,CAAC,gBAAgB,CACnB;IAAA,EAAE,IAAI,CAAC,CACR,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IAC/B,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,eAAe,EAAE,OAAO,EAAE;IAChD,iBAAiB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;CACrF,CAAC,CAAC","sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from 'react';\nimport { View, StyleSheet, Text, AppState } from 'react-native';\nimport { Camera, useCameraDevice } from 'react-native-vision-camera';\nimport VisionCameraModule from '../modules/camera/VisionCameraModule';\nimport { useI18n } from '../hooks/useI18n';\nimport { EnhancedCameraViewProps } from './OverLay/type';\nimport { Button } from './ui/Button';\n\nexport const EnhancedCameraView: React.FC<EnhancedCameraViewProps> = ({ \n showCamera,\n cameraType: initialCameraType = 'front',\n style,\n onError,\n onSilentCapture, \n silentCaptureResult,\n isProcessing = false, \n overlayComponent,\n}) => {\n const { t } = useI18n();\n const camera = useRef<Camera>(null);\n \n const isCapturingRef = useRef(false);\n const isProcessingRef = useRef(isProcessing);\n\n const [cameraType] = useState<'front' | 'back'>(initialCameraType);\n \n // 🚨 BUG FIX: Initialize to null to prevent the \"Flicker\" on retake\n const [hasPermission, setHasPermission] = useState<boolean | null>(null);\n const [isInitialized, setIsInitialized] = useState(false);\n const [refreshCamera, setRefreshCamera] = useState(false);\n\n const device = useCameraDevice(cameraType);\n\n useEffect(() => {\n isProcessingRef.current = isProcessing;\n }, [isProcessing]);\n\n const checkPermissions = async () => {\n try {\n const hasAllPermissions = await VisionCameraModule.hasAllPermissions();\n if (!hasAllPermissions) {\n const granted = await VisionCameraModule.requestAllPermissions();\n if (!granted) {\n setHasPermission(false);\n onError?.({ message: t('camera.permissionRequired') });\n return;\n }\n }\n setHasPermission(true);\n } catch (error) {\n setHasPermission(false);\n onError?.({ message: t('camera.errorOccurred') });\n }\n };\n\n useEffect(() => {\n if (showCamera) checkPermissions();\n }, [showCamera, refreshCamera]);\n\n useEffect(() => {\n const subscription = AppState.addEventListener('change', nextAppState => {\n if (nextAppState === 'active' && showCamera && hasPermission === false) {\n checkPermissions();\n }\n });\n return () => subscription.remove();\n }, [showCamera, hasPermission]);\n\n const onInitialized = useCallback(() => setIsInitialized(true), []);\n const onCameraError = useCallback((error: any) => {\n onError?.({ message: error.message || t('camera.errorOccurred') });\n }, [onError, t]);\n\n const captureSilentPhoto = useCallback(async () => {\n if (!camera.current || !isInitialized || isProcessingRef.current || isCapturingRef.current) return;\n if (silentCaptureResult?.isAnalyzing) return;\n\n try {\n isCapturingRef.current = true; \n const photo = await camera.current.takePhoto({\n enableShutterSound: false, \n flash: 'off', \n });\n\n const result = await VisionCameraModule.processPhotoResult(photo);\n \n onSilentCapture?.({\n ...result,\n path: result.path || photo.path, \n });\n\n } catch (error) {\n // Silent background fail\n } finally {\n isCapturingRef.current = false; \n }\n }, [isInitialized, onSilentCapture, silentCaptureResult]);\n\n // 🚨 BUG FIX: The Warm-up Timer (Fixes Blurry Images)\n useEffect(() => {\n if (!showCamera || !isInitialized || isProcessing) return;\n \n let isActive = true;\n let intervalId: ReturnType<typeof setInterval>;\n\n const warmupTimer = setTimeout(() => {\n if (!isActive) return;\n intervalId = setInterval(() => {\n captureSilentPhoto();\n }, 1500); // 1.5s gives the hardware more time to stabilize between shots\n }, 1000); \n \n return () => {\n isActive = false;\n clearTimeout(warmupTimer);\n if (intervalId) clearInterval(intervalId);\n };\n }, [showCamera, isInitialized, isProcessing, captureSilentPhoto]);\n\n // --- RENDERERS ---\n \n // 🚨 BUG FIX: Show nothing while checking permissions (Stops flicker)\n if (hasPermission === null) {\n return <View style={[styles.container, style]} />;\n }\n\n if (hasPermission === false) {\n return (\n <View style={[styles.container, style, { justifyContent: 'center', alignItems: 'center' }]}>\n <Text style={styles.permissionMessage}>{t('camera.permissionRequired')}</Text>\n <Button \n title=\"Refresh Camera\" \n onPress={() => setRefreshCamera(prev => !prev)} \n variant=\"primary\"\n />\n </View>\n );\n }\n\n if (!device || !showCamera) return <View style={[styles.container, style]} />;\n\n return (\n <View style={[styles.container, style]}>\n <Camera\n ref={camera}\n style={StyleSheet.absoluteFill}\n device={device}\n isActive={showCamera && !isProcessing}\n photo={true}\n video={false} \n audio={false} \n onInitialized={onInitialized}\n onError={onCameraError}\n />\n {overlayComponent}\n </View>\n );\n};\n\nconst styles = StyleSheet.create({\n container: { flex: 1, backgroundColor: 'black' },\n permissionMessage: { color: 'white', textAlign: 'center', margin: 20, fontSize: 16 },\n});"]}
|
|
1
|
+
{"version":3,"file":"EnhancedCameraView.js","sourceRoot":"","sources":["../../../src/components/EnhancedCameraView.tsx"],"names":[],"mappings":"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","sourcesContent":["// import React, { useCallback, useEffect, useRef, useState } from 'react';\n// import { View, StyleSheet, Text, AppState } from 'react-native';\n// import { Camera, useCameraDevice, useCameraFormat } from 'react-native-vision-camera'; \n// import VisionCameraModule from '../modules/camera/VisionCameraModule';\n// import { useI18n } from '../hooks/useI18n';\n// import { EnhancedCameraViewProps } from './OverLay/type';\n// import { Button } from './ui/Button';\n\n// export const EnhancedCameraView: React.FC<EnhancedCameraViewProps> = ({\n// showCamera,\n// cameraType: initialCameraType = 'front',\n// style,\n// onError,\n// onSilentCapture,\n// silentCaptureResult,\n// isProcessing = false,\n// overlayComponent,\n// }) => {\n// const { t } = useI18n();\n// const camera = useRef<Camera>(null);\n\n// const isCapturingRef = useRef(false);\n// const isProcessingRef = useRef(isProcessing);\n\n// const [cameraType] = useState<'front' | 'back'>(initialCameraType);\n// const [hasPermission, setHasPermission] = useState<boolean | null>(null);\n// const [isInitialized, setIsInitialized] = useState(false);\n// const [refreshCamera, setRefreshCamera] = useState(false);\n// const [layout, setLayout] = useState({ width: 0, height: 0 });\n\n\n// const device = useCameraDevice(cameraType, {\n// physicalDevices: [\n// 'wide-angle-camera' // Explicitly request standard 1x lens\n// ]\n// });\n\n// const targetRatio = layout.width > 0 ? layout.height / layout.width : 16 / 9;\n\n// const format = useCameraFormat(device, [\n// { photoAspectRatio: targetRatio },\n// { photoResolution: 'max' } \n// ]);\n\n// useEffect(() => {\n// isProcessingRef.current = isProcessing;\n// }, [isProcessing]);\n\n// const checkPermissions = async () => {\n// try {\n// const hasAllPermissions = await VisionCameraModule.hasAllPermissions();\n// if (!hasAllPermissions) {\n// const granted = await VisionCameraModule.requestAllPermissions();\n// if (!granted) {\n// setHasPermission(false);\n// onError?.({ message: t('camera.permissionRequired') });\n// return;\n// }\n// }\n// setHasPermission(true);\n// } catch (error) {\n// setHasPermission(false);\n// onError?.({ message: t('camera.errorOccurred') });\n// }\n// };\n\n// useEffect(() => {\n// if (showCamera) checkPermissions();\n// }, [showCamera, refreshCamera]);\n\n// useEffect(() => {\n// const subscription = AppState.addEventListener('change', nextAppState => {\n// if (nextAppState === 'active' && showCamera && hasPermission === false) {\n// checkPermissions();\n// }\n// });\n// return () => subscription.remove();\n// }, [showCamera, hasPermission]);\n\n// const onInitialized = useCallback(() => setIsInitialized(true), []);\n// const onCameraError = useCallback((error: any) => {\n// onError?.({ message: error.message || t('camera.errorOccurred') });\n// }, [onError, t]);\n\n// const captureSilentPhoto = useCallback(async () => {\n// if (!camera.current || !isInitialized || isProcessingRef.current || isCapturingRef.current) return;\n// if (silentCaptureResult?.isAnalyzing) return;\n\n// try {\n// isCapturingRef.current = true;\n// const photo = await camera.current.takePhoto({\n// enableShutterSound: false,\n// flash: 'off',\n// });\n// const result = await VisionCameraModule.processPhotoResult(photo);\n\n// onSilentCapture?.({\n// ...result,\n// path: result.path || photo.path,\n// });\n// } catch (error) {\n// // Silent background fail\n// } finally {\n// isCapturingRef.current = false;\n// }\n// }, [isInitialized, onSilentCapture, silentCaptureResult]);\n\n// useEffect(() => {\n// if (!showCamera || !isInitialized || isProcessing) return;\n// let isActive = true;\n// let intervalId: ReturnType<typeof setInterval>;\n\n// const warmupTimer = setTimeout(() => {\n// if (!isActive) return;\n// intervalId = setInterval(() => {\n// captureSilentPhoto();\n// }, 1500);\n// }, 1000);\n\n// return () => {\n// isActive = false;\n// clearTimeout(warmupTimer);\n// if (intervalId) clearInterval(intervalId);\n// };\n// }, [showCamera, isInitialized, isProcessing, captureSilentPhoto]);\n\n// if (hasPermission === null) return <View style={[styles.container, style]} />;\n\n// if (hasPermission === false) {\n// return (\n// <View style={[styles.container, style, { justifyContent: 'center', alignItems: 'center' }]}>\n// <Text style={styles.permissionMessage}>{t('camera.permissionRequired')}</Text>\n// <Button title=\"Refresh Camera\" onPress={() => setRefreshCamera(prev => !prev)} variant=\"primary\" />\n// </View>\n// );\n// }\n\n// if (!device || !showCamera) return <View style={[styles.container, style]} />;\n\n// return (\n// <View \n// style={[styles.container, style]}\n// onLayout={(e) => setLayout({ width: e.nativeEvent.layout.width, height: e.nativeEvent.layout.height })}\n// >\n// <Camera\n// ref={camera}\n// style={StyleSheet.absoluteFill}\n// device={device}\n// format={format} \n// resizeMode=\"cover\" \n// isActive={showCamera && !isProcessing}\n// photo={true}\n// video={false}\n// audio={false}\n// onInitialized={onInitialized}\n// onError={onCameraError}\n// />\n// {overlayComponent}\n// </View>\n// );\n// };\n\n// const styles = StyleSheet.create({\n// container: { flex: 1, backgroundColor: 'black' },\n// permissionMessage: { color: 'white', textAlign: 'center', margin: 20, fontSize: 16 },\n// });\n\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\nimport { View, StyleSheet, Text, AppState } from 'react-native';\nimport { Camera, useCameraDevice } from 'react-native-vision-camera'; \nimport VisionCameraModule from '../modules/camera/VisionCameraModule';\nimport { useI18n } from '../hooks/useI18n';\nimport { EnhancedCameraViewProps } from './OverLay/type';\nimport { Button } from './ui/Button';\n\nexport const EnhancedCameraView: React.FC<EnhancedCameraViewProps> = ({\n showCamera,\n cameraType: initialCameraType = 'front',\n style,\n onError,\n onSilentCapture,\n silentCaptureResult,\n isProcessing = false,\n overlayComponent,\n}) => {\n const { t } = useI18n();\n const camera = useRef<Camera>(null);\n\n const isCapturingRef = useRef(false);\n const isProcessingRef = useRef(isProcessing);\n\n const [cameraType] = useState<'front' | 'back'>(initialCameraType);\n const [hasPermission, setHasPermission] = useState<boolean | null>(null);\n const [isInitialized, setIsInitialized] = useState(false);\n const [refreshCamera, setRefreshCamera] = useState(false);\n\n // 🚨 THE FIX: Removed the strict physical device filtering and custom format.\n // This allows Android to select its own supported, stable hardware stream, preventing crashes.\n const device = useCameraDevice(cameraType);\n\n useEffect(() => {\n isProcessingRef.current = isProcessing;\n }, [isProcessing]);\n\n const checkPermissions = async () => {\n try {\n const hasAllPermissions = await VisionCameraModule.hasAllPermissions();\n if (!hasAllPermissions) {\n const granted = await VisionCameraModule.requestAllPermissions();\n if (!granted) {\n setHasPermission(false);\n onError?.({ message: t('camera.permissionRequired') });\n return;\n }\n }\n setHasPermission(true);\n } catch (error) {\n setHasPermission(false);\n onError?.({ message: t('camera.errorOccurred') });\n }\n };\n\n useEffect(() => {\n if (showCamera) checkPermissions();\n }, [showCamera, refreshCamera]);\n\n useEffect(() => {\n const subscription = AppState.addEventListener('change', nextAppState => {\n if (nextAppState === 'active' && showCamera && hasPermission === false) {\n checkPermissions();\n }\n });\n return () => subscription.remove();\n }, [showCamera, hasPermission]);\n\n const onInitialized = useCallback(() => setIsInitialized(true), []);\n const onCameraError = useCallback((error: any) => {\n onError?.({ message: error.message || t('camera.errorOccurred') });\n }, [onError, t]);\n\n const captureSilentPhoto = useCallback(async () => {\n if (!camera.current || !isInitialized || isProcessingRef.current || isCapturingRef.current) return;\n if (silentCaptureResult?.isAnalyzing) return;\n\n try {\n isCapturingRef.current = true;\n const photo = await camera.current.takePhoto({\n enableShutterSound: false,\n flash: 'off',\n });\n \n const result = await VisionCameraModule.processPhotoResult(photo);\n\n onSilentCapture?.({\n ...result,\n path: result.path || photo.path,\n });\n } catch (error) {\n // Silent background fail\n } finally {\n isCapturingRef.current = false;\n }\n }, [isInitialized, onSilentCapture, silentCaptureResult]);\n\n useEffect(() => {\n if (!showCamera || !isInitialized || isProcessing) return;\n \n let isActive = true;\n let intervalId: ReturnType<typeof setInterval>;\n\n const warmupTimer = setTimeout(() => {\n if (!isActive) return;\n intervalId = setInterval(() => {\n captureSilentPhoto();\n }, 1500);\n }, 1000);\n\n return () => {\n isActive = false;\n clearTimeout(warmupTimer);\n if (intervalId) clearInterval(intervalId);\n };\n }, [showCamera, isInitialized, isProcessing, captureSilentPhoto]);\n\n if (hasPermission === null) return <View style={[styles.container, style]} />;\n\n if (hasPermission === false) {\n return (\n <View style={[styles.container, style, { justifyContent: 'center', alignItems: 'center' }]}>\n <Text style={styles.permissionMessage}>{t('camera.permissionRequired')}</Text>\n <Button title=\"Refresh Camera\" onPress={() => setRefreshCamera(prev => !prev)} variant=\"primary\" />\n </View>\n );\n }\n\n if (!device || !showCamera) return <View style={[styles.container, style]} />;\n\n return (\n <View style={[styles.container, style]}>\n <Camera\n ref={camera}\n style={StyleSheet.absoluteFill}\n device={device}\n resizeMode=\"cover\" \n isActive={showCamera && !isProcessing}\n photo={true}\n video={false}\n audio={false}\n onInitialized={onInitialized}\n onError={onCameraError}\n />\n {overlayComponent}\n </View>\n );\n};\n\nconst styles = StyleSheet.create({\n container: { flex: 1, backgroundColor: 'black' },\n permissionMessage: { color: 'white', textAlign: 'center', margin: 20, fontSize: 16 },\n});"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IDCardCapture.d.ts","sourceRoot":"","sources":["../../../../src/components/KYCElements/IDCardCapture.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAI5D,OAAO,EAAE,iBAAiB,EAAoI,MAAM,uBAAuB,CAAC;AAc5L,UAAU,cAAc;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAAE;AAC3F,UAAU,kBAAkB;IAAG,SAAS,EAAE,iBAAiB,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAAE;AAExO,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,
|
|
1
|
+
{"version":3,"file":"IDCardCapture.d.ts","sourceRoot":"","sources":["../../../../src/components/KYCElements/IDCardCapture.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAI5D,OAAO,EAAE,iBAAiB,EAAoI,MAAM,uBAAuB,CAAC;AAc5L,UAAU,cAAc;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAAE;AAC3F,UAAU,kBAAkB;IAAG,SAAS,EAAE,iBAAiB,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAAE;AAExO,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAyftD,CAAC"}
|
|
@@ -74,9 +74,12 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
74
74
|
const cameraConfig = useMemo(() => {
|
|
75
75
|
const instructions = selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).instructions.en : getDocumentTypeInfo(selectedDocumentType.type).instructions.fr) : getLocalizedText(component.instructions);
|
|
76
76
|
return {
|
|
77
|
-
cameraType: Platform.OS === 'web' ? cameraType : 'back',
|
|
77
|
+
cameraType: Platform.OS === 'web' ? cameraType : 'back',
|
|
78
78
|
flashMode: 'auto',
|
|
79
|
-
overlay: {
|
|
79
|
+
overlay: {
|
|
80
|
+
guideText: instructions,
|
|
81
|
+
bbox: { xMin: 5, yMin: 15, xMax: 95, yMax: 85, borderColor: '#2DBD60', borderWidth: 3, cornerRadius: 8 }
|
|
82
|
+
}
|
|
80
83
|
};
|
|
81
84
|
}, [selectedDocumentType, locale, component.instructions, cameraType]);
|
|
82
85
|
const retakePicture = (sideToRetake) => {
|
|
@@ -154,13 +157,22 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
154
157
|
setProcessingImagePath(capturePath);
|
|
155
158
|
try {
|
|
156
159
|
let imagePathForUpload = capturePath;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
160
|
+
// 🚨 THE FIX: Ignore the backend's broken absolute pixels.
|
|
161
|
+
// Calculate the crop using the Green UI Frame percentages (0.0 to 1.0)
|
|
162
|
+
const overlayBbox = cameraConfig.overlay.bbox;
|
|
163
|
+
const uiCropBbox = {
|
|
164
|
+
minX: overlayBbox.xMin / 100,
|
|
165
|
+
minY: overlayBbox.yMin / 100,
|
|
166
|
+
width: (overlayBbox.xMax - overlayBbox.xMin) / 100,
|
|
167
|
+
height: (overlayBbox.yMax - overlayBbox.yMin) / 100,
|
|
168
|
+
};
|
|
169
|
+
try {
|
|
170
|
+
// Apply the crop using the UI percentages and a tight 2% tolerance
|
|
171
|
+
imagePathForUpload = await cropImageWithBBoxWithTolerance(capturePath, uiCropBbox, 0.02);
|
|
172
|
+
}
|
|
173
|
+
catch (e) {
|
|
174
|
+
console.warn("Crop failed, falling back to original image", e);
|
|
175
|
+
imagePathForUpload = capturePath; // Fallback to raw image if crop fails
|
|
164
176
|
}
|
|
165
177
|
const base64 = await pathToBase64(imagePathForUpload);
|
|
166
178
|
const newImages = {
|
|
@@ -248,12 +260,27 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
248
260
|
await autoCapture(result.path, verifiedResult);
|
|
249
261
|
}
|
|
250
262
|
catch (error) {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
263
|
+
// 1. Keep the technical log for your debugging console
|
|
264
|
+
console.error("Backend Verification Error:", error);
|
|
265
|
+
// 2. Define a map of technical keywords to user-friendly messages
|
|
266
|
+
const rawMessage = (error?.message || '').toLowerCase();
|
|
267
|
+
let userFriendlyMessage = 'Verification failed. Please try again.';
|
|
268
|
+
if (rawMessage.includes('impossible') || rawMessage.includes('unreadable')) {
|
|
269
|
+
userFriendlyMessage = 'Document unreadable. Please hold the camera steady in good light.';
|
|
270
|
+
}
|
|
271
|
+
else if (rawMessage.includes('country mismatch') || rawMessage.includes('pays sélectionné')) {
|
|
272
|
+
userFriendlyMessage = 'The document does not match your selected country.';
|
|
273
|
+
}
|
|
274
|
+
else if (rawMessage.includes('too far') || rawMessage.includes('card_not_fully_in_frame')) {
|
|
275
|
+
userFriendlyMessage = 'Move closer to the document and ensure all corners are visible.';
|
|
276
|
+
}
|
|
277
|
+
// 3. Set ONLY the friendly message to the UI state
|
|
278
|
+
setSilentCaptureResult(prev => ({
|
|
279
|
+
...prev,
|
|
280
|
+
isAnalyzing: false,
|
|
281
|
+
success: false,
|
|
282
|
+
error: userFriendlyMessage
|
|
283
|
+
}));
|
|
257
284
|
}
|
|
258
285
|
}
|
|
259
286
|
};
|