@sanctum-key/react-native-sdk 1.0.27 → 1.0.29
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 +186 -50
- 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/build/src/components/TemplateKYCExample.d.ts +2 -1
- package/build/src/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/src/components/TemplateKYCExample.js +2 -2
- package/build/src/components/TemplateKYCExample.js.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.d.ts +1 -0
- package/build/src/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.js +2 -2
- package/build/src/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.d.ts +1 -0
- package/build/src/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.js +31 -17
- package/build/src/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/src/modules/api/KYCService.d.ts +8 -0
- package/build/src/modules/api/KYCService.d.ts.map +1 -1
- package/build/src/modules/api/KYCService.js +30 -0
- package/build/src/modules/api/KYCService.js.map +1 -1
- package/package.json +1 -1
- package/src/components/EnhancedCameraView.tsx +60 -27
- package/src/components/KYCElements/IDCardCapture.tsx +236 -80
- package/src/components/OverLay/type.ts +5 -1
- package/src/components/TemplateKYCExample.tsx +7 -3
- package/src/components/TemplateKYCFlowRefactored.tsx +3 -0
- package/src/hooks/useTemplateKYCFlow.tsx +65 -34
- package/src/modules/api/KYCService.ts +41 -0
package/build/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EnhancedCameraView.d.ts","sourceRoot":"","sources":["../../../src/components/EnhancedCameraView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAExE,OAAO,EAAU,QAAQ,EAAmC,MAAM,4BAA4B,CAAC;AAG/F,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAGzD,MAAM,WAAW,mBAAoB,SAAQ,uBAAuB;IAClE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,
|
|
1
|
+
{"version":3,"file":"EnhancedCameraView.d.ts","sourceRoot":"","sources":["../../../src/components/EnhancedCameraView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAExE,OAAO,EAAU,QAAQ,EAAmC,MAAM,4BAA4B,CAAC;AAG/F,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAGzD,MAAM,WAAW,mBAAoB,SAAQ,uBAAuB;IAClE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAwN5D,CAAC"}
|
|
@@ -14,7 +14,6 @@ export const EnhancedCameraView = ({ showCamera, cameraType: initialCameraType =
|
|
|
14
14
|
const [hasPermission, setHasPermission] = useState(null);
|
|
15
15
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
16
16
|
const [refreshCamera] = useState(false);
|
|
17
|
-
// 🚨 NEW STATE: Tells React when it is safe to mount the Web Scanner
|
|
18
17
|
const [isVideoFound, setIsVideoFound] = useState(false);
|
|
19
18
|
const scannedBarcodeRef = useRef(null);
|
|
20
19
|
const containerRef = useRef(null);
|
|
@@ -29,15 +28,23 @@ export const EnhancedCameraView = ({ showCamera, cameraType: initialCameraType =
|
|
|
29
28
|
const granted = await VisionCameraModule.requestAllPermissions();
|
|
30
29
|
if (!granted) {
|
|
31
30
|
setHasPermission(false);
|
|
32
|
-
onError?.({
|
|
31
|
+
onError?.({
|
|
32
|
+
message: t('camera.permissionRequired'),
|
|
33
|
+
name: 'NotAllowedError', // Emulate web standard for easy catching
|
|
34
|
+
code: 'permission-denied'
|
|
35
|
+
});
|
|
33
36
|
return;
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
setHasPermission(true);
|
|
37
40
|
}
|
|
38
|
-
catch {
|
|
41
|
+
catch (e) {
|
|
39
42
|
setHasPermission(false);
|
|
40
|
-
onError?.({
|
|
43
|
+
onError?.({
|
|
44
|
+
message: e.message || t('camera.errorOccurred'),
|
|
45
|
+
name: e.name || 'UnknownError',
|
|
46
|
+
code: e.code || 'unknown'
|
|
47
|
+
});
|
|
41
48
|
}
|
|
42
49
|
};
|
|
43
50
|
useEffect(() => {
|
|
@@ -45,8 +52,14 @@ export const EnhancedCameraView = ({ showCamera, cameraType: initialCameraType =
|
|
|
45
52
|
checkPermissions();
|
|
46
53
|
}, [showCamera, refreshCamera]);
|
|
47
54
|
const onInitialized = useCallback(() => setIsInitialized(true), []);
|
|
55
|
+
// 🚨 UPDATED: Catch Vision Camera deep errors (e.g. session-in-use) and pass them up
|
|
48
56
|
const onCameraError = useCallback((error) => {
|
|
49
|
-
|
|
57
|
+
console.warn("🎥 [Vision Camera Error Caught]:", error);
|
|
58
|
+
onError?.({
|
|
59
|
+
message: error.message || t('camera.errorOccurred'),
|
|
60
|
+
code: error.code, // Contains specific RNVC codes (e.g., 'session/camera-in-use')
|
|
61
|
+
name: error.name // Contains Web standard names (e.g., 'NotReadableError')
|
|
62
|
+
});
|
|
50
63
|
}, [onError, t]);
|
|
51
64
|
const codeScanner = useCodeScanner({
|
|
52
65
|
codeTypes: barcodeTypes || ['pdf-417', 'qr', 'code-128', 'code-39'],
|
|
@@ -69,11 +82,19 @@ export const EnhancedCameraView = ({ showCamera, cameraType: initialCameraType =
|
|
|
69
82
|
return;
|
|
70
83
|
try {
|
|
71
84
|
isCapturingRef.current = true;
|
|
72
|
-
const photo = await camera.current.takePhoto({
|
|
85
|
+
const photo = await camera.current.takePhoto({
|
|
86
|
+
enableShutterSound: false,
|
|
87
|
+
flash: 'off'
|
|
88
|
+
});
|
|
73
89
|
const result = await VisionCameraModule.processPhotoResult(photo);
|
|
74
|
-
onSilentCapture?.({
|
|
90
|
+
onSilentCapture?.({
|
|
91
|
+
...result,
|
|
92
|
+
path: result.path || photo.path,
|
|
93
|
+
barcode: scannedBarcodeRef.current
|
|
94
|
+
});
|
|
75
95
|
}
|
|
76
96
|
catch {
|
|
97
|
+
// Intentionally swallow silent capture errors so we don't spam the UI
|
|
77
98
|
}
|
|
78
99
|
finally {
|
|
79
100
|
isCapturingRef.current = false;
|
|
@@ -97,14 +118,11 @@ export const EnhancedCameraView = ({ showCamera, cameraType: initialCameraType =
|
|
|
97
118
|
clearInterval(id);
|
|
98
119
|
};
|
|
99
120
|
}, [showCamera, isInitialized, isProcessing, captureSilentPhoto, isScanningBarcode]);
|
|
100
|
-
// Web: Aggressive Video Element Hunter
|
|
101
121
|
useEffect(() => {
|
|
102
|
-
// 🚨 Kick off the search immediately if we are on the web and the camera should be showing
|
|
103
122
|
if (Platform.OS !== 'web' || !showCamera)
|
|
104
123
|
return;
|
|
105
124
|
let timeoutId;
|
|
106
125
|
const findVideo = () => {
|
|
107
|
-
console.log('🔍 [WEB ENGINE] Searching for <video> element in DOM...');
|
|
108
126
|
let video = null;
|
|
109
127
|
if (containerRef.current?.querySelector) {
|
|
110
128
|
video = containerRef.current.querySelector('video');
|
|
@@ -113,23 +131,17 @@ export const EnhancedCameraView = ({ showCamera, cameraType: initialCameraType =
|
|
|
113
131
|
video = document.querySelector('video');
|
|
114
132
|
}
|
|
115
133
|
if (video) {
|
|
116
|
-
console.log('✅ [WEB ENGINE] <video> element locked! Booting ZXing Scanner.');
|
|
117
134
|
webVideoRef.current = video;
|
|
118
|
-
setIsVideoFound(true);
|
|
135
|
+
setIsVideoFound(true);
|
|
119
136
|
}
|
|
120
137
|
else {
|
|
121
|
-
console.log('⏳ [WEB ENGINE] <video> not found yet, retrying in 500ms...');
|
|
122
138
|
timeoutId = setTimeout(findVideo, 500);
|
|
123
139
|
}
|
|
124
140
|
};
|
|
125
|
-
// Start hunting immediately, do NOT wait for isInitialized
|
|
126
141
|
findVideo();
|
|
127
142
|
return () => clearTimeout(timeoutId);
|
|
128
143
|
}, [showCamera]);
|
|
129
|
-
// 🚨 FIX: Memoized Web Callback to prevent infinite re-rendering loop
|
|
130
144
|
const handleWebBarcodeScanned = useCallback((value) => {
|
|
131
|
-
console.log(`🎉 [WEB SCANNER] SUCCESS! Decoded value:`, value);
|
|
132
|
-
alert(`Decoded: ${value.substring(0, 50)}...`);
|
|
133
145
|
if (!isProcessingRef.current && value !== scannedBarcodeRef.current) {
|
|
134
146
|
scannedBarcodeRef.current = value;
|
|
135
147
|
onBarcodeScanned?.(value);
|
|
@@ -138,14 +150,27 @@ export const EnhancedCameraView = ({ showCamera, cameraType: initialCameraType =
|
|
|
138
150
|
if (hasPermission === null)
|
|
139
151
|
return <View style={[styles.container, style]}/>;
|
|
140
152
|
if (hasPermission === false)
|
|
141
|
-
return <View style={[styles.container, style]}
|
|
153
|
+
return (<View style={[styles.container, style]}>
|
|
154
|
+
<Text style={styles.permissionMessage}>{t('camera.permissionRequired')}</Text>
|
|
155
|
+
</View>);
|
|
156
|
+
if (!device && showCamera) {
|
|
157
|
+
// We delay the missing device error slightly to account for the camera mounting delay
|
|
158
|
+
setTimeout(() => {
|
|
159
|
+
if (!device) {
|
|
160
|
+
onError?.({
|
|
161
|
+
message: "No camera detected",
|
|
162
|
+
name: "NotFoundError",
|
|
163
|
+
code: "device/not-found"
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}, 1000);
|
|
167
|
+
return <View style={[styles.container, style]}/>;
|
|
168
|
+
}
|
|
142
169
|
if (!device || !showCamera)
|
|
143
170
|
return <View style={[styles.container, style]}/>;
|
|
144
171
|
return (<View ref={containerRef} style={[styles.container, style]}>
|
|
145
172
|
<Camera ref={camera} style={StyleSheet.absoluteFill} device={device} resizeMode="cover" isActive={showCamera && !isProcessing} photo={true} video={false} audio={false} onInitialized={onInitialized} onError={onCameraError} codeScanner={Platform.OS !== 'web' && isScanningBarcode ? codeScanner : undefined}/>
|
|
146
|
-
|
|
147
173
|
{Platform.OS === 'web' && isScanningBarcode && isVideoFound && (<WebBarcodeScanner videoRef={webVideoRef} onBarcodeScanned={handleWebBarcodeScanned} active={!isProcessingRef.current}/>)}
|
|
148
|
-
|
|
149
174
|
{overlayComponent}
|
|
150
175
|
</View>);
|
|
151
176
|
};
|
|
@@ -1 +1 @@
|
|
|
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';\nimport { View, StyleSheet, Text, Platform } from 'react-native';\nimport { Camera, CodeType, useCameraDevice, useCodeScanner } from 'react-native-vision-camera';\nimport VisionCameraModule from '../modules/camera/VisionCameraModule';\nimport { useI18n } from '../hooks/useI18n';\nimport { EnhancedCameraViewProps } from './OverLay/type';\nimport { WebBarcodeScanner } from './WebBarcodeScanner';\n\nexport interface ExtendedCameraProps extends EnhancedCameraViewProps {\n isScanningBarcode?: boolean;\n onBarcodeScanned?: (data: string) => void;\n barcodeTypes?: CodeType[];\n}\n\nexport const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({\n showCamera,\n cameraType: initialCameraType = 'front',\n style,\n onError,\n onSilentCapture,\n silentCaptureResult,\n isProcessing = false,\n overlayComponent,\n isScanningBarcode = false,\n onBarcodeScanned,\n barcodeTypes\n}) => {\n const { t } = useI18n();\n const camera = useRef<Camera>(null);\n \n const webVideoRef = useRef<HTMLVideoElement | null>(null);\n const isCapturingRef = useRef(false);\n const isProcessingRef = useRef(isProcessing);\n const [cameraType] = useState<'front' | 'back'>(initialCameraType);\n const [hasPermission, setHasPermission] = useState<boolean | null>(null);\n const [isInitialized, setIsInitialized] = useState(false);\n const [refreshCamera] = useState(false);\n \n // 🚨 NEW STATE: Tells React when it is safe to mount the Web Scanner\n const [isVideoFound, setIsVideoFound] = useState(false);\n\n const scannedBarcodeRef = useRef<string | null>(null);\n const containerRef = useRef<any>(null);\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 {\n setHasPermission(false);\n onError?.({ message: t('camera.errorOccurred') });\n }\n };\n\n useEffect(() => {\n if (showCamera) checkPermissions();\n }, [showCamera, refreshCamera]);\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 codeScanner = useCodeScanner({\n codeTypes: barcodeTypes || ['pdf-417', 'qr', 'code-128', 'code-39'],\n onCodeScanned: (codes) => {\n if (!isProcessingRef.current && isScanningBarcode) {\n for (const code of codes) {\n if (code.value && scannedBarcodeRef.current !== code.value) {\n scannedBarcodeRef.current = code.value;\n onBarcodeScanned?.(code.value);\n break;\n }\n }\n }\n }\n });\n\n const captureSilentPhoto = useCallback(async () => {\n if (!camera.current || !isInitialized || isProcessingRef.current || isCapturingRef.current) return;\n if (silentCaptureResult?.isAnalyzing) return;\n try {\n isCapturingRef.current = true;\n const photo = await camera.current.takePhoto({ enableShutterSound: false, flash: 'off' });\n const result = await VisionCameraModule.processPhotoResult(photo);\n onSilentCapture?.({ ...result, path: result.path || photo.path, barcode: scannedBarcodeRef.current });\n } catch {\n } finally {\n isCapturingRef.current = false;\n }\n }, [isInitialized, onSilentCapture, silentCaptureResult]);\n\n useEffect(() => {\n if (!showCamera || !isInitialized || isProcessing) return;\n let active = true;\n let id: ReturnType<typeof setInterval>;\n const timer = setTimeout(() => {\n if (!active) return;\n const intervalDelay = isScanningBarcode ? 3000 : 1500;\n id = setInterval(captureSilentPhoto, intervalDelay);\n }, 1500);\n return () => {\n active = false;\n clearTimeout(timer);\n if (id) clearInterval(id);\n };\n }, [showCamera, isInitialized, isProcessing, captureSilentPhoto, isScanningBarcode]);\n\n // Web: Aggressive Video Element Hunter\n useEffect(() => {\n // 🚨 Kick off the search immediately if we are on the web and the camera should be showing\n if (Platform.OS !== 'web' || !showCamera) return;\n \n let timeoutId: ReturnType<typeof setTimeout>;\n\n const findVideo = () => {\n console.log('🔍 [WEB ENGINE] Searching for <video> element in DOM...');\n \n let video: HTMLVideoElement | null = null;\n if (containerRef.current?.querySelector) {\n video = containerRef.current.querySelector('video');\n }\n if (!video) {\n video = document.querySelector('video');\n }\n\n if (video) {\n console.log('✅ [WEB ENGINE] <video> element locked! Booting ZXing Scanner.');\n webVideoRef.current = video;\n setIsVideoFound(true); // This tells React to mount the scanner!\n } else {\n console.log('⏳ [WEB ENGINE] <video> not found yet, retrying in 500ms...');\n timeoutId = setTimeout(findVideo, 500);\n }\n };\n \n // Start hunting immediately, do NOT wait for isInitialized\n findVideo();\n\n return () => clearTimeout(timeoutId);\n }, [showCamera]);\n\n // 🚨 FIX: Memoized Web Callback to prevent infinite re-rendering loop\n const handleWebBarcodeScanned = useCallback((value: string) => {\n console.log(`🎉 [WEB SCANNER] SUCCESS! Decoded value:`, value);\n alert(`Decoded: ${value.substring(0, 50)}...`);\n \n if (!isProcessingRef.current && value !== scannedBarcodeRef.current) {\n scannedBarcodeRef.current = value;\n onBarcodeScanned?.(value);\n }\n }, [onBarcodeScanned]);\n\n if (hasPermission === null) return <View style={[styles.container, style]} />;\n if (hasPermission === false) return <View style={[styles.container, style]}><Text style={styles.permissionMessage}>{t('camera.permissionRequired')}</Text></View>;\n if (!device || !showCamera) return <View style={[styles.container, style]} />;\n\n return (\n <View ref={containerRef} 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 codeScanner={Platform.OS !== 'web' && isScanningBarcode ? codeScanner : undefined}\n />\n \n {Platform.OS === 'web' && isScanningBarcode && isVideoFound && (\n <WebBarcodeScanner\n videoRef={webVideoRef as React.RefObject<HTMLVideoElement>}\n onBarcodeScanned={handleWebBarcodeScanned}\n active={!isProcessingRef.current}\n />\n )}\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';\nimport { View, StyleSheet, Text, Platform } from 'react-native';\nimport { Camera, CodeType, useCameraDevice, useCodeScanner } from 'react-native-vision-camera';\nimport VisionCameraModule from '../modules/camera/VisionCameraModule';\nimport { useI18n } from '../hooks/useI18n';\nimport { EnhancedCameraViewProps } from './OverLay/type';\nimport { WebBarcodeScanner } from './WebBarcodeScanner';\n\nexport interface ExtendedCameraProps extends EnhancedCameraViewProps {\n isScanningBarcode?: boolean;\n onBarcodeScanned?: (data: string) => void;\n barcodeTypes?: CodeType[];\n}\n\nexport const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({\n showCamera,\n cameraType: initialCameraType = 'front',\n style,\n onError,\n onSilentCapture,\n silentCaptureResult,\n isProcessing = false,\n overlayComponent,\n isScanningBarcode = false,\n onBarcodeScanned,\n barcodeTypes\n}) => {\n const { t } = useI18n();\n const camera = useRef<Camera>(null);\n const webVideoRef = useRef<HTMLVideoElement | null>(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] = useState(false);\n \n const [isVideoFound, setIsVideoFound] = useState(false);\n const scannedBarcodeRef = useRef<string | null>(null);\n const containerRef = useRef<any>(null);\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?.({ \n message: t('camera.permissionRequired'),\n name: 'NotAllowedError', // Emulate web standard for easy catching\n code: 'permission-denied'\n });\n return;\n }\n }\n setHasPermission(true);\n } catch (e: any) {\n setHasPermission(false);\n onError?.({ \n message: e.message || t('camera.errorOccurred'),\n name: e.name || 'UnknownError',\n code: e.code || 'unknown'\n });\n }\n };\n\n useEffect(() => {\n if (showCamera) checkPermissions();\n }, [showCamera, refreshCamera]);\n\n const onInitialized = useCallback(() => setIsInitialized(true), []);\n\n // 🚨 UPDATED: Catch Vision Camera deep errors (e.g. session-in-use) and pass them up\n const onCameraError = useCallback((error: any) => {\n console.warn(\"🎥 [Vision Camera Error Caught]:\", error);\n onError?.({ \n message: error.message || t('camera.errorOccurred'),\n code: error.code, // Contains specific RNVC codes (e.g., 'session/camera-in-use')\n name: error.name // Contains Web standard names (e.g., 'NotReadableError')\n });\n }, [onError, t]);\n\n const codeScanner = useCodeScanner({\n codeTypes: barcodeTypes || ['pdf-417', 'qr', 'code-128', 'code-39'],\n onCodeScanned: (codes) => {\n if (!isProcessingRef.current && isScanningBarcode) {\n for (const code of codes) {\n if (code.value && scannedBarcodeRef.current !== code.value) {\n scannedBarcodeRef.current = code.value;\n onBarcodeScanned?.(code.value);\n break;\n }\n }\n }\n }\n });\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 onSilentCapture?.({ \n ...result, \n path: result.path || photo.path,\n barcode: scannedBarcodeRef.current \n });\n } catch {\n // Intentionally swallow silent capture errors so we don't spam the UI\n } finally {\n isCapturingRef.current = false;\n }\n }, [isInitialized, onSilentCapture, silentCaptureResult]);\n\n useEffect(() => {\n if (!showCamera || !isInitialized || isProcessing) return;\n let active = true;\n let id: ReturnType<typeof setInterval>;\n \n const timer = setTimeout(() => {\n if (!active) return;\n const intervalDelay = isScanningBarcode ? 3000 : 1500;\n id = setInterval(captureSilentPhoto, intervalDelay);\n }, 1500);\n\n return () => {\n active = false;\n clearTimeout(timer);\n if (id) clearInterval(id);\n };\n }, [showCamera, isInitialized, isProcessing, captureSilentPhoto, isScanningBarcode]);\n\n useEffect(() => {\n if (Platform.OS !== 'web' || !showCamera) return;\n \n let timeoutId: ReturnType<typeof setTimeout>;\n \n const findVideo = () => {\n let video: HTMLVideoElement | null = null;\n if (containerRef.current?.querySelector) {\n video = containerRef.current.querySelector('video');\n }\n if (!video) {\n video = document.querySelector('video');\n }\n \n if (video) {\n webVideoRef.current = video;\n setIsVideoFound(true);\n } else {\n timeoutId = setTimeout(findVideo, 500);\n }\n };\n \n findVideo();\n return () => clearTimeout(timeoutId);\n }, [showCamera]);\n\n const handleWebBarcodeScanned = useCallback((value: string) => {\n if (!isProcessingRef.current && value !== scannedBarcodeRef.current) {\n scannedBarcodeRef.current = value;\n onBarcodeScanned?.(value);\n }\n }, [onBarcodeScanned]);\n\n if (hasPermission === null) return <View style={[styles.container, style]} />;\n \n if (hasPermission === false) return (\n <View style={[styles.container, style]}>\n <Text style={styles.permissionMessage}>{t('camera.permissionRequired')}</Text>\n </View>\n );\n\n if (!device && showCamera) {\n // We delay the missing device error slightly to account for the camera mounting delay\n setTimeout(() => {\n if (!device) {\n onError?.({ \n message: \"No camera detected\", \n name: \"NotFoundError\", \n code: \"device/not-found\" \n });\n }\n }, 1000);\n return <View style={[styles.container, style]} />;\n }\n\n if (!device || !showCamera) return <View style={[styles.container, style]} />;\n\n return (\n <View ref={containerRef} 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 codeScanner={Platform.OS !== 'web' && isScanningBarcode ? codeScanner : undefined}\n />\n {Platform.OS === 'web' && isScanningBarcode && isVideoFound && (\n <WebBarcodeScanner\n videoRef={webVideoRef as React.RefObject<HTMLVideoElement>}\n onBarcodeScanned={handleWebBarcodeScanned}\n active={!isProcessingRef.current}\n />\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,KAA+C,MAAM,OAAO,CAAC;AAIpE,OAAO,EAAE,iBAAiB,EAAoI,MAAM,uBAAuB,CAAC;AAsB5L,UAAU,cAAc;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,kBAAkB;IAC1B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACvC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAUD,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,KAA+C,MAAM,OAAO,CAAC;AAIpE,OAAO,EAAE,iBAAiB,EAAoI,MAAM,uBAAuB,CAAC;AAsB5L,UAAU,cAAc;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,kBAAkB;IAC1B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACvC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAUD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA6lCtD,CAAC"}
|
|
@@ -47,6 +47,58 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
47
47
|
const [scanPhase, setScanPhase] = useState('image');
|
|
48
48
|
const [pendingCAImage, setPendingCAImage] = useState(null);
|
|
49
49
|
const scannedBarcodeRef = useRef(null);
|
|
50
|
+
// 🚨 NEW: State for the expiring link code
|
|
51
|
+
const [generatedCode, setGeneratedCode] = useState(null);
|
|
52
|
+
const [isGeneratingCode, setIsGeneratingCode] = useState(false);
|
|
53
|
+
const [codeTimeLeft, setCodeTimeLeft] = useState(0);
|
|
54
|
+
// 🚨 NEW: Countdown Timer Effect
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (codeTimeLeft <= 0) {
|
|
57
|
+
if (generatedCode)
|
|
58
|
+
setGeneratedCode(null); // Clear the code when timer hits 0
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const timer = setInterval(() => {
|
|
62
|
+
setCodeTimeLeft((prev) => prev - 1);
|
|
63
|
+
}, 1000);
|
|
64
|
+
return () => clearInterval(timer);
|
|
65
|
+
}, [codeTimeLeft, generatedCode]);
|
|
66
|
+
// 🚨 NEW: Formatting helper
|
|
67
|
+
const formatTime = (seconds) => {
|
|
68
|
+
const m = Math.floor(seconds / 60);
|
|
69
|
+
const s = seconds % 60;
|
|
70
|
+
return `${m}:${s.toString().padStart(2, '0')}`;
|
|
71
|
+
};
|
|
72
|
+
const handleGenerateCode = async () => {
|
|
73
|
+
if (isGeneratingCode)
|
|
74
|
+
return;
|
|
75
|
+
setIsGeneratingCode(true);
|
|
76
|
+
try {
|
|
77
|
+
const apiKey = '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';
|
|
78
|
+
if (!apiKey) {
|
|
79
|
+
showAlert('Error', 'API Key is missing.');
|
|
80
|
+
setIsGeneratingCode(false);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// 🚨 Pass the apiKey as the strictly required second parameter
|
|
84
|
+
const res = await kycService.generateReferralCode({ session_id: state.session.session_id }, apiKey);
|
|
85
|
+
// Extract the code from the response
|
|
86
|
+
const newCode = res.code || res.referral_code || res?.data?.code || res?.data?.referral_code;
|
|
87
|
+
if (newCode) {
|
|
88
|
+
setGeneratedCode(newCode);
|
|
89
|
+
setCodeTimeLeft(300); // 300 seconds = 5 minutes
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
showAlert('Error', 'Failed to parse code from server response.');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
showAlert('Error', error.message || 'Failed to generate code');
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
setIsGeneratingCode(false);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
50
102
|
useEffect(() => {
|
|
51
103
|
if (typeof window !== 'undefined' && window.location)
|
|
52
104
|
setCurrentUrl(window.location.href);
|
|
@@ -130,7 +182,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
130
182
|
setTimeout(() => {
|
|
131
183
|
setCameraKey(prev => prev + 1);
|
|
132
184
|
setIsRebootingCamera(false);
|
|
133
|
-
},
|
|
185
|
+
}, 1000);
|
|
134
186
|
};
|
|
135
187
|
const toggleCameraLens = () => {
|
|
136
188
|
setCameraType(prev => prev === 'back' ? 'front' : 'back');
|
|
@@ -557,7 +609,35 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
557
609
|
}
|
|
558
610
|
}, [scanPhase, pendingCAImage, silentCaptureResult.barcode]);
|
|
559
611
|
const handleError = (event) => {
|
|
560
|
-
|
|
612
|
+
logger.error("Camera Error:", event);
|
|
613
|
+
// Normalize the error to lowercase for easy checking
|
|
614
|
+
const errString = `${event.message} ${event.name} ${event.code}`.toLowerCase();
|
|
615
|
+
let friendlyMessage = state.currentLanguage === 'en'
|
|
616
|
+
? 'An unknown camera error occurred.'
|
|
617
|
+
: 'Une erreur de caméra inconnue est survenue.';
|
|
618
|
+
// 1. Detect Hardware Lock (Camera in use by Zoom, Teams, or another app)
|
|
619
|
+
if (errString.includes('notreadableerror') ||
|
|
620
|
+
errString.includes('trackstarterror') ||
|
|
621
|
+
errString.includes('in use') ||
|
|
622
|
+
errString.includes('concurrent') ||
|
|
623
|
+
errString.includes('camera unavailable')) {
|
|
624
|
+
friendlyMessage = state.currentLanguage === 'en'
|
|
625
|
+
? 'Your camera is currently being used by another application (like Zoom or Teams). Please close the other app and try again.'
|
|
626
|
+
: 'Votre caméra est actuellement utilisée par une autre application. Veuillez fermer l\'autre application et réessayer.';
|
|
627
|
+
}
|
|
628
|
+
// 2. Detect Permission Denied
|
|
629
|
+
else if (errString.includes('notallowederror') || errString.includes('permission')) {
|
|
630
|
+
friendlyMessage = state.currentLanguage === 'en'
|
|
631
|
+
? 'Camera permission denied. Please allow camera access in your settings to proceed.'
|
|
632
|
+
: 'Autorisation de la caméra refusée. Veuillez autoriser l\'accès dans vos paramètres.';
|
|
633
|
+
}
|
|
634
|
+
// 3. Detect No Camera Found
|
|
635
|
+
else if (errString.includes('notfounderror') || errString.includes('no camera')) {
|
|
636
|
+
friendlyMessage = state.currentLanguage === 'en'
|
|
637
|
+
? 'No camera detected on this device.'
|
|
638
|
+
: 'Aucune caméra détectée sur cet appareil.';
|
|
639
|
+
}
|
|
640
|
+
showAlert(state.currentLanguage === 'en' ? 'Camera Error' : 'Erreur de Caméra', friendlyMessage);
|
|
561
641
|
setShowCamera(false);
|
|
562
642
|
setIsProcessingCapture(false);
|
|
563
643
|
};
|
|
@@ -814,62 +894,71 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
814
894
|
</View>
|
|
815
895
|
|
|
816
896
|
{showQRModal && Platform.OS === 'web' && (<View style={styles.qrModalOverlay}>
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
897
|
+
<View style={styles.qrModalContainer}>
|
|
898
|
+
<View style={styles.qrModalHeader}>
|
|
899
|
+
<Text style={styles.qrModalTitle}>
|
|
900
|
+
{state.currentLanguage === 'en' ? 'Continue on your phone' : 'Continuer sur votre téléphone'}
|
|
901
|
+
</Text>
|
|
902
|
+
<Text style={styles.qrModalText}>
|
|
903
|
+
{state.currentLanguage === 'en'
|
|
824
904
|
? "Scan the QR code to download our mobile app and complete the scanning process securely."
|
|
825
905
|
: "Scannez le code QR pour télécharger notre application et terminer la numérisation en toute sécurité."}
|
|
826
|
-
|
|
827
|
-
|
|
906
|
+
</Text>
|
|
907
|
+
</View>
|
|
828
908
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
909
|
+
<View style={styles.qrToggleContainer}>
|
|
910
|
+
<TouchableOpacity style={[styles.qrToggleButton, qrPlatform === 'android' && styles.qrToggleButtonActive]} onPress={() => setQrPlatform('android')}>
|
|
911
|
+
<Text style={[styles.qrToggleText, qrPlatform === 'android' && styles.qrToggleTextActive]}>
|
|
912
|
+
Android
|
|
913
|
+
</Text>
|
|
914
|
+
</TouchableOpacity>
|
|
915
|
+
<TouchableOpacity style={[styles.qrToggleButton, qrPlatform === 'ios' && styles.qrToggleButtonActive]} onPress={() => setQrPlatform('ios')}>
|
|
916
|
+
<Text style={[styles.qrToggleText, qrPlatform === 'ios' && styles.qrToggleTextActive]}>
|
|
917
|
+
iOS (Apple)
|
|
918
|
+
</Text>
|
|
919
|
+
</TouchableOpacity>
|
|
920
|
+
</View>
|
|
841
921
|
|
|
842
|
-
|
|
843
|
-
|
|
922
|
+
<View style={styles.qrCodeWrapper}>
|
|
923
|
+
<QRCode value={qrPlatform === 'android'
|
|
844
924
|
? 'https://play.google.com/store/apps/details?id=transfergratis.com&hl=en_CA&gl=US'
|
|
845
|
-
: 'https://apps.apple.com/ca/app/transfergratis-money-transfer/id1605411731'} size={
|
|
846
|
-
|
|
847
|
-
</View>
|
|
925
|
+
: 'https://apps.apple.com/ca/app/transfergratis-money-transfer/id1605411731'} size={180} backgroundColor="#FFFFFF" color="#0F172A"/>
|
|
926
|
+
</View>
|
|
848
927
|
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
928
|
+
<View style={styles.linkCodeSection}>
|
|
929
|
+
<Text style={styles.linkCodeHeader}>
|
|
930
|
+
{state.currentLanguage === 'en' ? 'Use code below to proceed on the Sanctum Key App.' : 'Vous avez déjà l\'application ? Liez-la :'}
|
|
931
|
+
</Text>
|
|
932
|
+
|
|
933
|
+
{generatedCode && codeTimeLeft > 0 ? (<View style={styles.activeCodeContainer}>
|
|
934
|
+
<Text style={styles.activeCodeText}>{generatedCode}</Text>
|
|
935
|
+
<Text style={styles.timerText}>
|
|
936
|
+
{state.currentLanguage === 'en' ? `Expires in ${formatTime(codeTimeLeft)}` : `Expire dans ${formatTime(codeTimeLeft)}`}
|
|
937
|
+
</Text>
|
|
938
|
+
</View>) : (<TouchableOpacity style={styles.generateCodeButton} onPress={handleGenerateCode} disabled={isGeneratingCode}>
|
|
939
|
+
{isGeneratingCode ? (<ActivityIndicator size="small" color="#0F172A"/>) : (<Text style={styles.generateCodeButtonText}>
|
|
940
|
+
{state.currentLanguage === 'en' ? 'Generate 5-Digit Link Code' : 'Générer un code à 5 chiffres'}
|
|
941
|
+
</Text>)}
|
|
942
|
+
</TouchableOpacity>)}
|
|
943
|
+
</View>
|
|
862
944
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
945
|
+
{isWebScanningRestricted && (<View style={styles.storeLinksContainer}>
|
|
946
|
+
<Text style={styles.storeLinksTitle}>
|
|
947
|
+
{state.currentLanguage === 'en' ? 'Or click to open directly:' : 'Ou cliquez pour ouvrir directement :'}
|
|
948
|
+
</Text>
|
|
949
|
+
<View style={styles.storeButtonsRow}>
|
|
950
|
+
<TouchableOpacity style={styles.storeButton} onPress={() => window.open('https://play.google.com/store/apps/details?id=transfergratis.com&hl=en_CA&gl=US', '_blank')}>
|
|
951
|
+
<Text style={styles.storeButtonText}>Google Play</Text>
|
|
952
|
+
</TouchableOpacity>
|
|
953
|
+
<TouchableOpacity style={styles.storeButton} onPress={() => window.open('https://apps.apple.com/ca/app/transfergratis-money-transfer/id1605411731', '_blank')}>
|
|
954
|
+
<Text style={styles.storeButtonText}>App Store</Text>
|
|
955
|
+
</TouchableOpacity>
|
|
956
|
+
</View>
|
|
957
|
+
</View>)}
|
|
869
958
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
959
|
+
<Button title={state.currentLanguage === 'en' ? "Cancel & Stay on Laptop" : "Annuler et rester sur l'ordinateur"} onPress={() => setShowQRModal(false)} variant="outline" fullWidth/>
|
|
960
|
+
</View>
|
|
961
|
+
</View>)}
|
|
873
962
|
</View>);
|
|
874
963
|
};
|
|
875
964
|
const styles = StyleSheet.create({
|
|
@@ -986,5 +1075,52 @@ const styles = StyleSheet.create({
|
|
|
986
1075
|
qrToggleTextActive: {
|
|
987
1076
|
color: '#0F172A', // Active text color
|
|
988
1077
|
},
|
|
1078
|
+
linkCodeSection: {
|
|
1079
|
+
width: '100%',
|
|
1080
|
+
alignItems: 'center',
|
|
1081
|
+
backgroundColor: '#F8FAFC', // Very light slate
|
|
1082
|
+
borderRadius: 16,
|
|
1083
|
+
padding: 16,
|
|
1084
|
+
marginBottom: 20,
|
|
1085
|
+
borderWidth: 1,
|
|
1086
|
+
borderColor: '#E2E8F0',
|
|
1087
|
+
},
|
|
1088
|
+
linkCodeHeader: {
|
|
1089
|
+
fontSize: 13,
|
|
1090
|
+
color: '#475569',
|
|
1091
|
+
fontWeight: '600',
|
|
1092
|
+
marginBottom: 12,
|
|
1093
|
+
},
|
|
1094
|
+
generateCodeButton: {
|
|
1095
|
+
backgroundColor: '#E2E8F0',
|
|
1096
|
+
paddingVertical: 12,
|
|
1097
|
+
paddingHorizontal: 20,
|
|
1098
|
+
borderRadius: 10,
|
|
1099
|
+
width: '100%',
|
|
1100
|
+
alignItems: 'center',
|
|
1101
|
+
justifyContent: 'center',
|
|
1102
|
+
height: 44, // Fixed height to prevent jumping during loading state
|
|
1103
|
+
},
|
|
1104
|
+
generateCodeButtonText: {
|
|
1105
|
+
color: '#0F172A',
|
|
1106
|
+
fontWeight: '700',
|
|
1107
|
+
fontSize: 14,
|
|
1108
|
+
},
|
|
1109
|
+
activeCodeContainer: {
|
|
1110
|
+
alignItems: 'center',
|
|
1111
|
+
width: '100%',
|
|
1112
|
+
},
|
|
1113
|
+
activeCodeText: {
|
|
1114
|
+
fontSize: 32,
|
|
1115
|
+
fontWeight: '900',
|
|
1116
|
+
color: '#2DBD60',
|
|
1117
|
+
letterSpacing: 4,
|
|
1118
|
+
marginBottom: 4,
|
|
1119
|
+
},
|
|
1120
|
+
timerText: {
|
|
1121
|
+
fontSize: 12,
|
|
1122
|
+
color: '#EF4444', // Red to indicate urgency
|
|
1123
|
+
fontWeight: '600',
|
|
1124
|
+
},
|
|
989
1125
|
});
|
|
990
1126
|
//# sourceMappingURL=IDCardCapture.js.map
|