@sanctum-key/react-native-sdk 1.0.28 → 1.0.30
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 +45 -20
- 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 +30 -2
- package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/src/components/OverLay/type.d.ts +3 -1
- package/build/src/components/OverLay/type.d.ts.map +1 -1
- package/build/src/components/OverLay/type.js.map +1 -1
- package/package.json +1 -1
- package/src/components/EnhancedCameraView.tsx +60 -27
- package/src/components/KYCElements/IDCardCapture.tsx +38 -3
- package/src/components/OverLay/type.ts +5 -1
- /package/ios/{TransfergratisSdk.podspec → SanctumKeySdk.podspec} +0 -0
- /package/ios/{TransfergratisSdkModule.swift → SanctumKeySdkModule.swift} +0 -0
|
@@ -59,8 +59,10 @@ export interface EnhancedCameraViewProps {
|
|
|
59
59
|
barcode: unknown;
|
|
60
60
|
}) => void;
|
|
61
61
|
silentCaptureResult?: ISilentCaptureResult;
|
|
62
|
-
onError?: (
|
|
62
|
+
onError?: (error: {
|
|
63
63
|
message: string;
|
|
64
|
+
name?: string;
|
|
65
|
+
code?: string;
|
|
64
66
|
}) => void;
|
|
65
67
|
onClose?: () => void;
|
|
66
68
|
enableFlash?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../../src/components/OverLay/type.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,WAAW,kBAAkB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,WAAW,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC1C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAA;CACnB;AAGD,UAAU,YAAY;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACnC,WAAW,EAAE;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,eAAe,EAAE,MAAM,CAAC;QACxB,0BAA0B,EAAE,MAAM,CAAC;QACnC,gBAAgB,EAAE,MAAM,CAAC;QACzB,UAAU,EAAE,MAAM,CAAC;QACnB,GAAG,EAAE,MAAM,CAAC;QACZ,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,OAAO,EAAE,OAAO,CAAC;CACpB;AAID,MAAM,WAAW,uBAAuB;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC9B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAClF,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAC,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IACzG,mBAAmB,CAAC,EAAE,oBAAoB,CAAC;IAC3C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE;
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../../src/components/OverLay/type.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type.js","sourceRoot":"","sources":["../../../../src/components/OverLay/type.ts"],"names":[],"mappings":"","sourcesContent":["import { ViewStyle } from \"react-native\";\nimport { ISilentCaptureResult } from \"../../types/KYC.types\";\n\nexport interface IdCardOverlayProps {\n xMin: number;\n yMin: number;\n xMax: number;\n yMax: number;\n cornerOpacity: number;\n instructions: string;\n cornerColor?: string;\n stepperProps?: StepperProps;\n instructionsTile?: string;\n instructionsSubtitle?: string;\n orientation?: 'center' | 'left' | 'right';\n isAnalyzing?: boolean;\n isSuccess?: boolean;\n language: string\n}\n\n\ninterface StepperProps {\n back: () => void;\n selectedDocumentType: string;\n step: number;\n totalSteps: number;\n side: string;\n}\n\nexport interface ExtractMrzTextResponse {\n parsed_data: {\n mrz_type: string;\n document_code: string;\n issuer_code: string;\n surname: string;\n given_name: string;\n document_number: string;\n document_number_checkdigit: string;\n nationality_code: string;\n birth_date: string;\n sex: string;\n expiry_date: string;\n optional_data: string;\n mrz_text: string;\n status: string;\n },\n success: boolean;\n}\n\n\n\nexport interface EnhancedCameraViewProps {\n instructions?: string;\n showCamera: boolean;\n cameraType?: 'front' | 'back';\n style?: ViewStyle;\n onCapture?: (result: { success: boolean; path?: string; error?: string }) => void;\n onSilentCapture?: (result: { success: boolean; path?: string; error?: string; barcode:unknown }) => void;\n silentCaptureResult?: ISilentCaptureResult;\n onError?: (
|
|
1
|
+
{"version":3,"file":"type.js","sourceRoot":"","sources":["../../../../src/components/OverLay/type.ts"],"names":[],"mappings":"","sourcesContent":["import { ViewStyle } from \"react-native\";\nimport { ISilentCaptureResult } from \"../../types/KYC.types\";\n\nexport interface IdCardOverlayProps {\n xMin: number;\n yMin: number;\n xMax: number;\n yMax: number;\n cornerOpacity: number;\n instructions: string;\n cornerColor?: string;\n stepperProps?: StepperProps;\n instructionsTile?: string;\n instructionsSubtitle?: string;\n orientation?: 'center' | 'left' | 'right';\n isAnalyzing?: boolean;\n isSuccess?: boolean;\n language: string\n}\n\n\ninterface StepperProps {\n back: () => void;\n selectedDocumentType: string;\n step: number;\n totalSteps: number;\n side: string;\n}\n\nexport interface ExtractMrzTextResponse {\n parsed_data: {\n mrz_type: string;\n document_code: string;\n issuer_code: string;\n surname: string;\n given_name: string;\n document_number: string;\n document_number_checkdigit: string;\n nationality_code: string;\n birth_date: string;\n sex: string;\n expiry_date: string;\n optional_data: string;\n mrz_text: string;\n status: string;\n },\n success: boolean;\n}\n\n\n\nexport interface EnhancedCameraViewProps {\n instructions?: string;\n showCamera: boolean;\n cameraType?: 'front' | 'back';\n style?: ViewStyle;\n onCapture?: (result: { success: boolean; path?: string; error?: string }) => void;\n onSilentCapture?: (result: { success: boolean; path?: string; error?: string; barcode:unknown }) => void;\n silentCaptureResult?: ISilentCaptureResult;\n onError?: (error: { \n message: string; \n name?: string;\n code?: string;\n }) => void;\n onClose?: () => void;\n enableFlash?: boolean;\n enableHdr?: boolean;\n quality?: 'low' | 'medium' | 'high';\n showCaptureButton?: boolean;\n showSwitchCamera?: boolean;\n overlayComponent?: React.ReactNode;\n isProcessing?: boolean; \n bbox?: {\n xMin: number;\n yMin: number;\n xMax: number;\n yMax: number;\n borderColor?: string;\n borderWidth?: number;\n cornerRadius?: number;\n };\n canFlip?: boolean;\n // Video recording props\n enableVideo?: boolean;\n isRecording?: boolean;\n onVideoRecordingStart?: () => void;\n onVideoRecordingStop?: (result: { success: boolean; path?: string; error?: string }) => void;\n videoDuration?: number;\n /** Delay in ms before first auto-capture (lets camera focus and user position document). Recommended 2500–3000 for ID/document capture. */\n captureStabilizationDelayMs?: number;\n}\n\nexport interface CheckTemplateTypeResponse {\n template_path: string;\n lpips_score: number;\n}"]}
|
package/package.json
CHANGED
|
@@ -27,20 +27,20 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
|
|
|
27
27
|
}) => {
|
|
28
28
|
const { t } = useI18n();
|
|
29
29
|
const camera = useRef<Camera>(null);
|
|
30
|
-
|
|
31
30
|
const webVideoRef = useRef<HTMLVideoElement | null>(null);
|
|
31
|
+
|
|
32
32
|
const isCapturingRef = useRef(false);
|
|
33
33
|
const isProcessingRef = useRef(isProcessing);
|
|
34
|
+
|
|
34
35
|
const [cameraType] = useState<'front' | 'back'>(initialCameraType);
|
|
35
36
|
const [hasPermission, setHasPermission] = useState<boolean | null>(null);
|
|
36
37
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
37
38
|
const [refreshCamera] = useState(false);
|
|
38
39
|
|
|
39
|
-
// 🚨 NEW STATE: Tells React when it is safe to mount the Web Scanner
|
|
40
40
|
const [isVideoFound, setIsVideoFound] = useState(false);
|
|
41
|
-
|
|
42
41
|
const scannedBarcodeRef = useRef<string | null>(null);
|
|
43
42
|
const containerRef = useRef<any>(null);
|
|
43
|
+
|
|
44
44
|
const device = useCameraDevice(cameraType);
|
|
45
45
|
|
|
46
46
|
useEffect(() => {
|
|
@@ -54,14 +54,22 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
|
|
|
54
54
|
const granted = await VisionCameraModule.requestAllPermissions();
|
|
55
55
|
if (!granted) {
|
|
56
56
|
setHasPermission(false);
|
|
57
|
-
onError?.({
|
|
57
|
+
onError?.({
|
|
58
|
+
message: t('camera.permissionRequired'),
|
|
59
|
+
name: 'NotAllowedError', // Emulate web standard for easy catching
|
|
60
|
+
code: 'permission-denied'
|
|
61
|
+
});
|
|
58
62
|
return;
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
65
|
setHasPermission(true);
|
|
62
|
-
} catch {
|
|
66
|
+
} catch (e: any) {
|
|
63
67
|
setHasPermission(false);
|
|
64
|
-
onError?.({
|
|
68
|
+
onError?.({
|
|
69
|
+
message: e.message || t('camera.errorOccurred'),
|
|
70
|
+
name: e.name || 'UnknownError',
|
|
71
|
+
code: e.code || 'unknown'
|
|
72
|
+
});
|
|
65
73
|
}
|
|
66
74
|
};
|
|
67
75
|
|
|
@@ -70,8 +78,15 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
|
|
|
70
78
|
}, [showCamera, refreshCamera]);
|
|
71
79
|
|
|
72
80
|
const onInitialized = useCallback(() => setIsInitialized(true), []);
|
|
81
|
+
|
|
82
|
+
// 🚨 UPDATED: Catch Vision Camera deep errors (e.g. session-in-use) and pass them up
|
|
73
83
|
const onCameraError = useCallback((error: any) => {
|
|
74
|
-
|
|
84
|
+
console.warn("🎥 [Vision Camera Error Caught]:", error);
|
|
85
|
+
onError?.({
|
|
86
|
+
message: error.message || t('camera.errorOccurred'),
|
|
87
|
+
code: error.code, // Contains specific RNVC codes (e.g., 'session/camera-in-use')
|
|
88
|
+
name: error.name // Contains Web standard names (e.g., 'NotReadableError')
|
|
89
|
+
});
|
|
75
90
|
}, [onError, t]);
|
|
76
91
|
|
|
77
92
|
const codeScanner = useCodeScanner({
|
|
@@ -92,12 +107,22 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
|
|
|
92
107
|
const captureSilentPhoto = useCallback(async () => {
|
|
93
108
|
if (!camera.current || !isInitialized || isProcessingRef.current || isCapturingRef.current) return;
|
|
94
109
|
if (silentCaptureResult?.isAnalyzing) return;
|
|
110
|
+
|
|
95
111
|
try {
|
|
96
112
|
isCapturingRef.current = true;
|
|
97
|
-
const photo = await camera.current.takePhoto({
|
|
113
|
+
const photo = await camera.current.takePhoto({
|
|
114
|
+
enableShutterSound: false,
|
|
115
|
+
flash: 'off'
|
|
116
|
+
});
|
|
117
|
+
|
|
98
118
|
const result = await VisionCameraModule.processPhotoResult(photo);
|
|
99
|
-
onSilentCapture?.({
|
|
119
|
+
onSilentCapture?.({
|
|
120
|
+
...result,
|
|
121
|
+
path: result.path || photo.path,
|
|
122
|
+
barcode: scannedBarcodeRef.current
|
|
123
|
+
});
|
|
100
124
|
} catch {
|
|
125
|
+
// Intentionally swallow silent capture errors so we don't spam the UI
|
|
101
126
|
} finally {
|
|
102
127
|
isCapturingRef.current = false;
|
|
103
128
|
}
|
|
@@ -107,11 +132,13 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
|
|
|
107
132
|
if (!showCamera || !isInitialized || isProcessing) return;
|
|
108
133
|
let active = true;
|
|
109
134
|
let id: ReturnType<typeof setInterval>;
|
|
135
|
+
|
|
110
136
|
const timer = setTimeout(() => {
|
|
111
137
|
if (!active) return;
|
|
112
138
|
const intervalDelay = isScanningBarcode ? 3000 : 1500;
|
|
113
139
|
id = setInterval(captureSilentPhoto, intervalDelay);
|
|
114
140
|
}, 1500);
|
|
141
|
+
|
|
115
142
|
return () => {
|
|
116
143
|
active = false;
|
|
117
144
|
clearTimeout(timer);
|
|
@@ -119,16 +146,12 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
|
|
|
119
146
|
};
|
|
120
147
|
}, [showCamera, isInitialized, isProcessing, captureSilentPhoto, isScanningBarcode]);
|
|
121
148
|
|
|
122
|
-
// Web: Aggressive Video Element Hunter
|
|
123
149
|
useEffect(() => {
|
|
124
|
-
// 🚨 Kick off the search immediately if we are on the web and the camera should be showing
|
|
125
150
|
if (Platform.OS !== 'web' || !showCamera) return;
|
|
126
151
|
|
|
127
152
|
let timeoutId: ReturnType<typeof setTimeout>;
|
|
128
|
-
|
|
153
|
+
|
|
129
154
|
const findVideo = () => {
|
|
130
|
-
console.log('🔍 [WEB ENGINE] Searching for <video> element in DOM...');
|
|
131
|
-
|
|
132
155
|
let video: HTMLVideoElement | null = null;
|
|
133
156
|
if (containerRef.current?.querySelector) {
|
|
134
157
|
video = containerRef.current.querySelector('video');
|
|
@@ -136,28 +159,20 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
|
|
|
136
159
|
if (!video) {
|
|
137
160
|
video = document.querySelector('video');
|
|
138
161
|
}
|
|
139
|
-
|
|
162
|
+
|
|
140
163
|
if (video) {
|
|
141
|
-
console.log('✅ [WEB ENGINE] <video> element locked! Booting ZXing Scanner.');
|
|
142
164
|
webVideoRef.current = video;
|
|
143
|
-
setIsVideoFound(true);
|
|
165
|
+
setIsVideoFound(true);
|
|
144
166
|
} else {
|
|
145
|
-
console.log('⏳ [WEB ENGINE] <video> not found yet, retrying in 500ms...');
|
|
146
167
|
timeoutId = setTimeout(findVideo, 500);
|
|
147
168
|
}
|
|
148
169
|
};
|
|
149
170
|
|
|
150
|
-
// Start hunting immediately, do NOT wait for isInitialized
|
|
151
171
|
findVideo();
|
|
152
|
-
|
|
153
172
|
return () => clearTimeout(timeoutId);
|
|
154
173
|
}, [showCamera]);
|
|
155
174
|
|
|
156
|
-
// 🚨 FIX: Memoized Web Callback to prevent infinite re-rendering loop
|
|
157
175
|
const handleWebBarcodeScanned = useCallback((value: string) => {
|
|
158
|
-
console.log(`🎉 [WEB SCANNER] SUCCESS! Decoded value:`, value);
|
|
159
|
-
alert(`Decoded: ${value.substring(0, 50)}...`);
|
|
160
|
-
|
|
161
176
|
if (!isProcessingRef.current && value !== scannedBarcodeRef.current) {
|
|
162
177
|
scannedBarcodeRef.current = value;
|
|
163
178
|
onBarcodeScanned?.(value);
|
|
@@ -165,7 +180,27 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
|
|
|
165
180
|
}, [onBarcodeScanned]);
|
|
166
181
|
|
|
167
182
|
if (hasPermission === null) return <View style={[styles.container, style]} />;
|
|
168
|
-
|
|
183
|
+
|
|
184
|
+
if (hasPermission === false) return (
|
|
185
|
+
<View style={[styles.container, style]}>
|
|
186
|
+
<Text style={styles.permissionMessage}>{t('camera.permissionRequired')}</Text>
|
|
187
|
+
</View>
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
if (!device && showCamera) {
|
|
191
|
+
// We delay the missing device error slightly to account for the camera mounting delay
|
|
192
|
+
setTimeout(() => {
|
|
193
|
+
if (!device) {
|
|
194
|
+
onError?.({
|
|
195
|
+
message: "No camera detected",
|
|
196
|
+
name: "NotFoundError",
|
|
197
|
+
code: "device/not-found"
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}, 1000);
|
|
201
|
+
return <View style={[styles.container, style]} />;
|
|
202
|
+
}
|
|
203
|
+
|
|
169
204
|
if (!device || !showCamera) return <View style={[styles.container, style]} />;
|
|
170
205
|
|
|
171
206
|
return (
|
|
@@ -183,7 +218,6 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
|
|
|
183
218
|
onError={onCameraError}
|
|
184
219
|
codeScanner={Platform.OS !== 'web' && isScanningBarcode ? codeScanner : undefined}
|
|
185
220
|
/>
|
|
186
|
-
|
|
187
221
|
{Platform.OS === 'web' && isScanningBarcode && isVideoFound && (
|
|
188
222
|
<WebBarcodeScanner
|
|
189
223
|
videoRef={webVideoRef as React.RefObject<HTMLVideoElement>}
|
|
@@ -191,7 +225,6 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
|
|
|
191
225
|
active={!isProcessingRef.current}
|
|
192
226
|
/>
|
|
193
227
|
)}
|
|
194
|
-
|
|
195
228
|
{overlayComponent}
|
|
196
229
|
</View>
|
|
197
230
|
);
|
|
@@ -221,7 +221,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
221
221
|
setTimeout(() => {
|
|
222
222
|
setCameraKey(prev => prev + 1);
|
|
223
223
|
setIsRebootingCamera(false);
|
|
224
|
-
},
|
|
224
|
+
}, 1000);
|
|
225
225
|
};
|
|
226
226
|
|
|
227
227
|
const toggleCameraLens = () => {
|
|
@@ -686,8 +686,43 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
686
686
|
}
|
|
687
687
|
}, [scanPhase, pendingCAImage, silentCaptureResult.barcode]);
|
|
688
688
|
|
|
689
|
-
const handleError = (event: { message: string }) => {
|
|
690
|
-
|
|
689
|
+
const handleError = (event: { message: string, name?: string, code?: string }) => {
|
|
690
|
+
logger.error("Camera Error:", event);
|
|
691
|
+
|
|
692
|
+
// Normalize the error to lowercase for easy checking
|
|
693
|
+
const errString = `${event.message} ${event.name} ${event.code}`.toLowerCase();
|
|
694
|
+
|
|
695
|
+
let friendlyMessage = state.currentLanguage === 'en'
|
|
696
|
+
? 'An unknown camera error occurred.'
|
|
697
|
+
: 'Une erreur de caméra inconnue est survenue.';
|
|
698
|
+
|
|
699
|
+
// 1. Detect Hardware Lock (Camera in use by Zoom, Teams, or another app)
|
|
700
|
+
if (
|
|
701
|
+
errString.includes('notreadableerror') ||
|
|
702
|
+
errString.includes('trackstarterror') ||
|
|
703
|
+
errString.includes('in use') ||
|
|
704
|
+
errString.includes('concurrent') ||
|
|
705
|
+
errString.includes('camera unavailable')
|
|
706
|
+
) {
|
|
707
|
+
friendlyMessage = state.currentLanguage === 'en'
|
|
708
|
+
? 'Your camera is currently being used by another application (like Zoom or Teams). Please close the other app and try again.'
|
|
709
|
+
: 'Votre caméra est actuellement utilisée par une autre application. Veuillez fermer l\'autre application et réessayer.';
|
|
710
|
+
}
|
|
711
|
+
// 2. Detect Permission Denied
|
|
712
|
+
else if (errString.includes('notallowederror') || errString.includes('permission')) {
|
|
713
|
+
friendlyMessage = state.currentLanguage === 'en'
|
|
714
|
+
? 'Camera permission denied. Please allow camera access in your settings to proceed.'
|
|
715
|
+
: 'Autorisation de la caméra refusée. Veuillez autoriser l\'accès dans vos paramètres.';
|
|
716
|
+
}
|
|
717
|
+
// 3. Detect No Camera Found
|
|
718
|
+
else if (errString.includes('notfounderror') || errString.includes('no camera')) {
|
|
719
|
+
friendlyMessage = state.currentLanguage === 'en'
|
|
720
|
+
? 'No camera detected on this device.'
|
|
721
|
+
: 'Aucune caméra détectée sur cet appareil.';
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
showAlert(state.currentLanguage === 'en' ? 'Camera Error' : 'Erreur de Caméra', friendlyMessage);
|
|
725
|
+
|
|
691
726
|
setShowCamera(false);
|
|
692
727
|
setIsProcessingCapture(false);
|
|
693
728
|
};
|
|
@@ -57,7 +57,11 @@ export interface EnhancedCameraViewProps {
|
|
|
57
57
|
onCapture?: (result: { success: boolean; path?: string; error?: string }) => void;
|
|
58
58
|
onSilentCapture?: (result: { success: boolean; path?: string; error?: string; barcode:unknown }) => void;
|
|
59
59
|
silentCaptureResult?: ISilentCaptureResult;
|
|
60
|
-
onError?: (
|
|
60
|
+
onError?: (error: {
|
|
61
|
+
message: string;
|
|
62
|
+
name?: string;
|
|
63
|
+
code?: string;
|
|
64
|
+
}) => void;
|
|
61
65
|
onClose?: () => void;
|
|
62
66
|
enableFlash?: boolean;
|
|
63
67
|
enableHdr?: boolean;
|
|
File without changes
|
|
File without changes
|