@sanctum-key/react-native-sdk 1.0.53 → 1.0.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +7 -2
  2. package/android/build/.transforms/3db9b15041d4285a820fc84462995ea1/results.bin +1 -0
  3. package/android/build/.transforms/3db9b15041d4285a820fc84462995ea1/transformed/classes/classes_dex/classes.dex +0 -0
  4. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  5. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  6. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
  7. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
  8. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
  9. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
  10. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
  11. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.len +0 -0
  12. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
  13. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
  14. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
  15. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
  16. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
  17. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len +0 -0
  18. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
  19. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
  20. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
  21. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
  22. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
  23. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
  24. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
  25. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
  26. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
  27. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
  28. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
  29. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +0 -0
  30. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
  31. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
  32. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
  33. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
  34. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
  35. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
  36. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
  37. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at +0 -0
  38. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab +1 -1
  39. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
  40. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
  41. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
  42. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
  43. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
  44. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
  45. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
  46. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
  47. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
  48. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
  49. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
  50. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
  51. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
  52. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
  53. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  54. package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
  55. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  56. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  57. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/SanctumKeySdkModule$BarcodeRegion.class +0 -0
  58. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/SanctumKeySdkModule$Companion.class +0 -0
  59. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$1$5$1.class → SanctumKeySdkModule$definition$1$6$1.class} +0 -0
  60. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$1$5$2.class → SanctumKeySdkModule$definition$1$6$2.class} +0 -0
  61. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$1.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunction$1.class} +0 -0
  62. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$5.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunction$2.class} +0 -0
  63. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$3.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunction$3.class} +0 -0
  64. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$4.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunction$4.class} +0 -0
  65. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$2.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunction$5.class} +0 -0
  66. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$6.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunction$6.class} +0 -0
  67. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$1.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunctionWithPromise$1.class} +0 -0
  68. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$3.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunctionWithPromise$2.class} +0 -0
  69. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunctionWithPromise$3.class +0 -0
  70. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunctionWithPromise$4.class +0 -0
  71. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$2.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunctionWithPromise$5.class} +0 -0
  72. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunctionWithPromise$6.class +0 -0
  73. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$4.class → SanctumKeySdkModule$definition$lambda$16$$inlined$AsyncFunctionWithPromise$7.class} +0 -0
  74. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$$inlined$View$1.class → SanctumKeySdkModule$definition$lambda$16$$inlined$View$1.class} +0 -0
  75. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$lambda$4$$inlined$Prop$1.class → SanctumKeySdkModule$definition$lambda$16$lambda$15$$inlined$Prop$1.class} +0 -0
  76. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/{SanctumKeySdkModule$definition$lambda$5$lambda$4$$inlined$Prop$2.class → SanctumKeySdkModule$definition$lambda$16$lambda$15$$inlined$Prop$2.class} +0 -0
  77. package/android/build/tmp/kotlin-classes/debug/kyc/sanctumkey/com/SanctumKeySdkModule.class +0 -0
  78. package/android/build.gradle +7 -0
  79. package/android/src/main/java/kyc/sanctumkey/com/SanctumKeySdkModule.kt +428 -1
  80. package/build/package.json +1 -1
  81. package/build/src/SanctumKeySdkModule.d.ts +1 -0
  82. package/build/src/SanctumKeySdkModule.d.ts.map +1 -1
  83. package/build/src/SanctumKeySdkModule.js.map +1 -1
  84. package/build/src/components/EnhancedCameraView.d.ts.map +1 -1
  85. package/build/src/components/EnhancedCameraView.js +50 -41
  86. package/build/src/components/EnhancedCameraView.js.map +1 -1
  87. package/build/src/components/EnhancedCameraView.web.d.ts +2 -0
  88. package/build/src/components/EnhancedCameraView.web.d.ts.map +1 -1
  89. package/build/src/components/EnhancedCameraView.web.js +11 -6
  90. package/build/src/components/EnhancedCameraView.web.js.map +1 -1
  91. package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  92. package/build/src/components/KYCElements/IDCardCapture.js +499 -78
  93. package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
  94. package/build/src/components/TemplateKYCExample.d.ts.map +1 -1
  95. package/build/src/components/TemplateKYCExample.js +3 -9
  96. package/build/src/components/TemplateKYCExample.js.map +1 -1
  97. package/build/src/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  98. package/build/src/components/TemplateKYCFlowRefactored.js +0 -2
  99. package/build/src/components/TemplateKYCFlowRefactored.js.map +1 -1
  100. package/build/src/components/WebBarcodeScanner.d.ts +2 -0
  101. package/build/src/components/WebBarcodeScanner.d.ts.map +1 -1
  102. package/build/src/components/WebBarcodeScanner.js +99 -85
  103. package/build/src/components/WebBarcodeScanner.js.map +1 -1
  104. package/build/src/modules/api/KYCService.d.ts +1 -0
  105. package/build/src/modules/api/KYCService.d.ts.map +1 -1
  106. package/build/src/modules/api/KYCService.js +1 -1
  107. package/build/src/modules/api/KYCService.js.map +1 -1
  108. package/build/src/modules/api/types.d.ts +1 -0
  109. package/build/src/modules/api/types.d.ts.map +1 -1
  110. package/build/src/modules/api/types.js.map +1 -1
  111. package/package.json +1 -1
  112. package/plugin/build/withVisionCamera.d.ts +1 -0
  113. package/plugin/build/withVisionCamera.js +13 -1
  114. package/plugin/src/withVisionCamera.js +18 -2
  115. package/plugin/src/withVisionCamera.ts +19 -2
  116. package/src/SanctumKeySdkModule.ts +1 -0
  117. package/src/components/EnhancedCameraView.tsx +52 -41
  118. package/src/components/EnhancedCameraView.web.tsx +15 -5
  119. package/src/components/KYCElements/IDCardCapture.tsx +552 -75
  120. package/src/components/TemplateKYCExample.tsx +6 -14
  121. package/src/components/TemplateKYCFlowRefactored.tsx +0 -2
  122. package/src/components/WebBarcodeScanner.tsx +147 -198
  123. package/src/modules/api/KYCService.ts +3 -3
  124. package/src/modules/api/types.ts +1 -0
@@ -22,6 +22,7 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
22
22
  silentCaptureResult,
23
23
  isProcessing = false,
24
24
  overlayComponent,
25
+ captureStabilizationDelayMs = 4000,
25
26
  isScanningBarcode = false,
26
27
  onBarcodeScanned,
27
28
  barcodeTypes
@@ -32,6 +33,8 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
32
33
 
33
34
  const isCapturingRef = useRef(false);
34
35
  const isProcessingRef = useRef(isProcessing);
36
+ const isScanningBarcodeRef = useRef(isScanningBarcode);
37
+ const onBarcodeScannedRef = useRef(onBarcodeScanned);
35
38
  // Ref-tracked isAnalyzing so captureSilentPhoto always reads the latest
36
39
  // value — the prop-based closure goes stale on iOS between renders.
37
40
  const isAnalyzingRef = useRef(silentCaptureResult?.isAnalyzing ?? false);
@@ -42,7 +45,7 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
42
45
  const [refreshCamera] = useState(false);
43
46
 
44
47
  const [isVideoFound, setIsVideoFound] = useState(false);
45
- const scannedBarcodeRef = useRef<string | null>(null);
48
+ const scannedBarcodeRef = useRef<{ value: string | null; emittedAt: number }>({ value: null, emittedAt: 0 });
46
49
  const containerRef = useRef<any>(null);
47
50
 
48
51
  const [appState, setAppState] = useState<AppStateStatus>(AppState.currentState);
@@ -50,8 +53,14 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
50
53
  const device = useCameraDevice(cameraType);
51
54
 
52
55
  useEffect(() => { isProcessingRef.current = isProcessing; }, [isProcessing]);
56
+ useEffect(() => { isScanningBarcodeRef.current = isScanningBarcode; }, [isScanningBarcode]);
57
+ useEffect(() => { onBarcodeScannedRef.current = onBarcodeScanned; }, [onBarcodeScanned]);
53
58
  useEffect(() => { isAnalyzingRef.current = silentCaptureResult?.isAnalyzing ?? false; }, [silentCaptureResult?.isAnalyzing]);
54
59
 
60
+ useEffect(() => {
61
+ scannedBarcodeRef.current = { value: null, emittedAt: 0 };
62
+ }, [isScanningBarcode]);
63
+
55
64
  useEffect(() => {
56
65
  const subscription = AppState.addEventListener('change', nextAppState => {
57
66
  setAppState(nextAppState);
@@ -100,7 +109,12 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
100
109
  if (showCamera) checkPermissions();
101
110
  }, [showCamera, refreshCamera]);
102
111
 
103
- const onInitialized = useCallback(() => setIsInitialized(true), []);
112
+ const onInitialized = useCallback(() => {
113
+ setIsInitialized(true);
114
+ if (isScanningBarcodeRef.current) {
115
+ console.log('📷 Barcode camera initialized');
116
+ }
117
+ }, []);
104
118
 
105
119
  const onCameraError = useCallback((error: any) => {
106
120
  console.warn("🎥 [Vision Camera Error Caught]:", error);
@@ -114,11 +128,15 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
114
128
  const codeScanner = useCodeScanner({
115
129
  codeTypes: barcodeTypes || ['pdf-417', 'qr', 'code-128', 'code-39'],
116
130
  onCodeScanned: (codes) => {
117
- if (!isProcessingRef.current && isScanningBarcode) {
131
+ if (!isProcessingRef.current && isScanningBarcodeRef.current) {
118
132
  for (const code of codes) {
119
- if (code.value && scannedBarcodeRef.current !== code.value) {
120
- scannedBarcodeRef.current = code.value;
121
- onBarcodeScanned?.(code.value);
133
+ if (!code.value) continue;
134
+
135
+ const now = Date.now();
136
+ const previous = scannedBarcodeRef.current;
137
+ if (previous.value !== code.value || now - previous.emittedAt > 1500) {
138
+ scannedBarcodeRef.current = { value: code.value, emittedAt: now };
139
+ onBarcodeScannedRef.current?.(code.value);
122
140
  break;
123
141
  }
124
142
  }
@@ -137,8 +155,8 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
137
155
  // Web handles its own stream via WebBarcodeScanner; do not run native camera methods on web.
138
156
  if (Platform.OS === 'web') return;
139
157
 
140
- // We intentionally ignore `isInitialized`. If AVFoundation (iOS) or CameraX (Android)
141
- // aren't ready, the try/catch will handle it gracefully.
158
+ // The scheduling effect waits for camera initialization and a positioning
159
+ // grace period before this function can run.
142
160
  if (!camera.current || isProcessingRef.current || isCapturingRef.current) return;
143
161
  if (isAnalyzingRef.current) return;
144
162
 
@@ -148,8 +166,14 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
148
166
  const photo = await camera.current.takePhoto({
149
167
  enableShutterSound: false,
150
168
  flash: 'off',
151
- // iOS requires aggressive compression to prevent RAM crashes during interval captures
152
- ...(Platform.OS === 'ios' ? { photoCodec: 'jpeg', qualityPrioritization: 'speed' } : {})
169
+ ...(Platform.OS === 'ios'
170
+ ? {
171
+ photoCodec: 'jpeg' as const,
172
+ // Barcode stills are decoded off the live path, so preserve the
173
+ // fine PDF417 modules instead of favoring capture speed.
174
+ qualityPrioritization: isScanningBarcodeRef.current ? 'quality' as const : 'balanced' as const,
175
+ }
176
+ : {})
153
177
  });
154
178
 
155
179
  // 3. CROSS-PLATFORM PATH NORMALIZATION
@@ -172,7 +196,7 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
172
196
  onSilentCaptureRef.current?.({
173
197
  success: captureSuccess,
174
198
  path: finalPath,
175
- barcode: scannedBarcodeRef.current
199
+ barcode: scannedBarcodeRef.current.value
176
200
  });
177
201
 
178
202
  } catch (error: any) {
@@ -185,45 +209,26 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
185
209
 
186
210
  // 5. THE UNBREAKABLE INTERVAL
187
211
  useEffect(() => {
188
- // If we shouldn't scan, or if web (web handles its own stream), abort.
189
- // We intentionally don't require `isInitialized` here, the try/catch inside
190
- // captureSilentPhoto handles unready cameras gracefully without breaking the loop.
191
- if (!showCamera || isProcessing || Platform.OS === 'web') return;
212
+ // Give autofocus/exposure and the user time to settle before the first
213
+ // automatic capture. A remount creates a fresh grace period for each side.
214
+ if (!showCamera || !isInitialized || isProcessing || Platform.OS === 'web') return;
192
215
 
193
216
  let active = true;
194
217
  let id: ReturnType<typeof setInterval>;
195
218
 
196
219
  const timer = setTimeout(() => {
197
220
  if (!active) return;
198
- const intervalDelay = isScanningBarcode ? 3000 : 1500;
199
- id = setInterval(captureSilentPhoto, intervalDelay);
200
- }, 1500);
201
-
202
- return () => {
203
- active = false;
204
- clearTimeout(timer);
205
- if (id) clearInterval(id);
206
- };
207
- }, [showCamera, isProcessing, isScanningBarcode, captureSilentPhoto]);
208
-
209
-
210
- useEffect(() => {
211
- if (!showCamera || !isInitialized || isProcessing) return;
212
- let active = true;
213
- let id: ReturnType<typeof setInterval>;
214
-
215
- const timer = setTimeout(() => {
216
- if (!active) return;
217
- const intervalDelay = isScanningBarcode ? 3000 : 1500;
221
+ captureSilentPhoto();
222
+ const intervalDelay = isScanningBarcode ? 4500 : 3000;
218
223
  id = setInterval(captureSilentPhoto, intervalDelay);
219
- }, 1500);
224
+ }, Math.max(0, captureStabilizationDelayMs));
220
225
 
221
226
  return () => {
222
227
  active = false;
223
228
  clearTimeout(timer);
224
229
  if (id) clearInterval(id);
225
230
  };
226
- }, [showCamera, isInitialized, isProcessing, captureSilentPhoto, isScanningBarcode]);
231
+ }, [showCamera, isInitialized, isProcessing, isScanningBarcode, captureStabilizationDelayMs, captureSilentPhoto]);
227
232
 
228
233
  useEffect(() => {
229
234
  if (Platform.OS !== 'web' || !showCamera) return;
@@ -252,8 +257,8 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
252
257
  }, [showCamera]);
253
258
 
254
259
  const handleWebBarcodeScanned = useCallback((value: string) => {
255
- if (!isProcessingRef.current && value !== scannedBarcodeRef.current) {
256
- scannedBarcodeRef.current = value;
260
+ if (!isProcessingRef.current && value !== scannedBarcodeRef.current.value) {
261
+ scannedBarcodeRef.current = { value, emittedAt: Date.now() };
257
262
  onBarcodeScanned?.(value);
258
263
  }
259
264
  }, [onBarcodeScanned]);
@@ -291,16 +296,22 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
291
296
  resizeMode="cover"
292
297
  isActive={showCamera && !isProcessing && appState === 'active'}
293
298
  photo={true}
299
+ photoQualityBalance={isScanningBarcode ? 'quality' : 'balanced'}
294
300
  video={false}
295
301
  audio={false}
296
302
  onInitialized={onInitialized}
297
303
  onError={onCameraError}
298
- codeScanner={Platform.OS !== 'web' && isScanningBarcode ? codeScanner : undefined}
304
+ // Some Android camera levels cannot configure Preview + ImageCapture +
305
+ // ImageAnalysis together and fail with invalid-output-configuration.
306
+ // Android barcodes use the bundled full-resolution ML Kit/ZXing still
307
+ // decoder; iOS can retain Vision Camera's low-latency live scanner.
308
+ codeScanner={Platform.OS === 'ios' && isScanningBarcode ? codeScanner : undefined}
299
309
  />
300
310
  {Platform.OS === 'web' && isScanningBarcode && isVideoFound && (
301
311
  <WebBarcodeScanner
302
312
  videoRef={webVideoRef as React.RefObject<HTMLVideoElement>}
303
313
  onBarcodeScanned={handleWebBarcodeScanned}
314
+ formats={barcodeTypes}
304
315
  active={!isProcessingRef.current}
305
316
  />
306
317
  )}
@@ -312,4 +323,4 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
312
323
  const styles = StyleSheet.create({
313
324
  container: { flex: 1, backgroundColor: 'black' },
314
325
  permissionMessage: { color: 'white', textAlign: 'center', margin: 20, fontSize: 16 },
315
- });
326
+ });
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import { View, StyleSheet, TouchableOpacity, Text, Platform } from 'react-native';
3
3
  import { EnhancedCameraViewProps } from './OverLay/type';
4
4
  import { useI18n } from '../hooks/useI18n';
5
+ import type { CodeType } from 'react-native-vision-camera';
5
6
  // 🚨 Import the WebBarcodeScanner we built earlier
6
7
  import { WebBarcodeScanner } from './WebBarcodeScanner';
7
8
 
@@ -9,6 +10,7 @@ import { WebBarcodeScanner } from './WebBarcodeScanner';
9
10
  export interface ExtendedCameraProps extends EnhancedCameraViewProps {
10
11
  isScanningBarcode?: boolean;
11
12
  onBarcodeScanned?: (data: string) => void;
13
+ barcodeTypes?: CodeType[];
12
14
  }
13
15
 
14
16
  export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
@@ -34,10 +36,11 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
34
36
  videoDuration = 10,
35
37
  onSilentCapture,
36
38
  silentCaptureResult,
37
- captureStabilizationDelayMs = 2500,
39
+ captureStabilizationDelayMs = 4000,
38
40
  // 🚨 New props for barcode support
39
41
  isScanningBarcode = false,
40
42
  onBarcodeScanned,
43
+ barcodeTypes,
41
44
  }) => {
42
45
  const { t } = useI18n();
43
46
 
@@ -61,12 +64,16 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
61
64
 
62
65
  // 🚨 New Refs for Barcode Logic
63
66
  const isProcessingRef = useRef(silentCaptureResult?.isAnalyzing || false);
64
- const scannedBarcodeRef = useRef<string | null>(null);
67
+ const scannedBarcodeRef = useRef<{ value: string | null; emittedAt: number }>({ value: null, emittedAt: 0 });
65
68
 
66
69
  useEffect(() => {
67
70
  isProcessingRef.current = silentCaptureResult?.isAnalyzing || false;
68
71
  }, [silentCaptureResult?.isAnalyzing]);
69
72
 
73
+ useEffect(() => {
74
+ scannedBarcodeRef.current = { value: null, emittedAt: 0 };
75
+ }, [isScanningBarcode]);
76
+
70
77
  useEffect(() => {
71
78
  if (showCamera) {
72
79
  checkPermissions();
@@ -223,8 +230,10 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
223
230
  // 🚨 Memoized Callback for Barcode Decoding
224
231
  const handleWebBarcodeScanned = useCallback((value: string) => {
225
232
  console.log(`🎉 [WEB SCANNER] SUCCESS! Decoded value:`, value);
226
- if (!isProcessingRef.current && value !== scannedBarcodeRef.current) {
227
- scannedBarcodeRef.current = value;
233
+ const now = Date.now();
234
+ const previous = scannedBarcodeRef.current;
235
+ if (!isProcessingRef.current && (value !== previous.value || now - previous.emittedAt > 1500)) {
236
+ scannedBarcodeRef.current = { value, emittedAt: now };
228
237
  onBarcodeScanned?.(value);
229
238
  }
230
239
  }, [onBarcodeScanned]);
@@ -289,6 +298,7 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
289
298
  <WebBarcodeScanner
290
299
  videoRef={videoRef}
291
300
  onBarcodeScanned={handleWebBarcodeScanned}
301
+ formats={barcodeTypes}
292
302
  // Use silentCaptureResult.isAnalyzing (state) not the ref so
293
303
  // React re-renders with the correct active value
294
304
  active={isScanningBarcode && isInitialized && !(silentCaptureResult?.isAnalyzing ?? false)}
@@ -354,4 +364,4 @@ const styles = StyleSheet.create({
354
364
  bbox: { position: 'absolute', borderStyle: 'solid', backgroundColor: 'transparent' },
355
365
  analyzingContainer: { position: 'absolute', bottom: 100, alignSelf: 'center', backgroundColor: 'rgba(0, 0, 0, 0.7)', padding: 10, borderRadius: 10, zIndex: 1000 },
356
366
  analyzingText: { color: 'white', fontSize: 16, fontWeight: 'bold' },
357
- });
367
+ });