@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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanctum-key/react-native-sdk",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "Sanctum Key React Native SDK",
5
5
  "main": "build/src/index.js",
6
6
  "types": "build/src/index.d.ts",
@@ -1 +1 @@
1
- {"version":3,"file":"EnhancedCameraView.d.ts","sourceRoot":"","sources":["../../../src/components/EnhancedCameraView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAKxE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAGzD,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAqJhE,CAAC"}
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); // 1.5s gives the hardware more time to stabilize between shots
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
- // --- RENDERERS ---
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,CAudtD,CAAC"}
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', // Keep strictly 'back' on mobile
77
+ cameraType: Platform.OS === 'web' ? cameraType : 'back',
78
78
  flashMode: 'auto',
79
- overlay: { guideText: instructions, bbox: { xMin: 15, yMin: 20, xMax: 85, yMax: 70, borderColor: '#2DBD60', borderWidth: 3, cornerRadius: 8 } }
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
- if (verified.bbox) {
158
- try {
159
- imagePathForUpload = await cropImageWithBBoxWithTolerance(capturePath, verified.bbox, 0.15);
160
- }
161
- catch {
162
- imagePathForUpload = capturePath;
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
- console.error("Backend Error:", error);
252
- const isCardNotFullyInFrame = error?.message === 'CARD_NOT_FULLY_IN_FRAME' || error?.message?.includes('entirement');
253
- const errorMessage = isCardNotFullyInFrame
254
- ? t('kyc.idCardCapture.cardNotFullyInFrame')
255
- : (t('errors.genericVerificationFailed') || 'Verification failed. Please ensure the document is clear and try again.');
256
- setSilentCaptureResult(prev => ({ ...prev, isAnalyzing: false, success: false, error: errorMessage }));
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
  };