@trustchex/react-native-sdk 1.362.6 → 1.381.0

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 (58) hide show
  1. package/TrustchexSDK.podspec +3 -3
  2. package/android/build.gradle +3 -3
  3. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +64 -19
  4. package/android/src/main/java/com/trustchex/reactnativesdk/opencv/OpenCVModule.kt +636 -301
  5. package/ios/Camera/TrustchexCameraView.swift +166 -119
  6. package/ios/OpenCV/OpenCVHelper.h +0 -7
  7. package/ios/OpenCV/OpenCVHelper.mm +0 -60
  8. package/ios/OpenCV/OpenCVModule.h +0 -4
  9. package/ios/OpenCV/OpenCVModule.mm +440 -358
  10. package/lib/module/Shared/Components/DebugOverlay.js +541 -0
  11. package/lib/module/Shared/Components/FaceCamera.js +1 -0
  12. package/lib/module/Shared/Components/IdentityDocumentCamera.constants.js +44 -0
  13. package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +270 -0
  14. package/lib/module/Shared/Components/IdentityDocumentCamera.js +708 -1593
  15. package/lib/module/Shared/Components/IdentityDocumentCamera.types.js +3 -0
  16. package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +273 -0
  17. package/lib/module/Shared/Components/QrCodeScannerCamera.js +1 -8
  18. package/lib/module/Shared/Libs/mrz.utils.js +202 -9
  19. package/lib/module/Translation/Resources/en.js +0 -4
  20. package/lib/module/Translation/Resources/tr.js +0 -4
  21. package/lib/module/version.js +1 -1
  22. package/lib/typescript/src/Shared/Components/DebugOverlay.d.ts +30 -0
  23. package/lib/typescript/src/Shared/Components/DebugOverlay.d.ts.map +1 -0
  24. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
  25. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.constants.d.ts +35 -0
  26. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.constants.d.ts.map +1 -0
  27. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts +3 -56
  28. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  29. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +88 -0
  30. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -0
  31. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts +116 -0
  32. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts.map +1 -0
  33. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +93 -0
  34. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -0
  35. package/lib/typescript/src/Shared/Components/QrCodeScannerCamera.d.ts.map +1 -1
  36. package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts +1 -0
  37. package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts.map +1 -1
  38. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts +8 -0
  39. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  40. package/lib/typescript/src/Translation/Resources/en.d.ts +0 -4
  41. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  42. package/lib/typescript/src/Translation/Resources/tr.d.ts +0 -4
  43. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  44. package/lib/typescript/src/version.d.ts +1 -1
  45. package/package.json +1 -1
  46. package/src/Shared/Components/DebugOverlay.tsx +656 -0
  47. package/src/Shared/Components/FaceCamera.tsx +1 -0
  48. package/src/Shared/Components/IdentityDocumentCamera.constants.ts +44 -0
  49. package/src/Shared/Components/IdentityDocumentCamera.flows.ts +342 -0
  50. package/src/Shared/Components/IdentityDocumentCamera.tsx +1105 -2324
  51. package/src/Shared/Components/IdentityDocumentCamera.types.ts +136 -0
  52. package/src/Shared/Components/IdentityDocumentCamera.utils.ts +364 -0
  53. package/src/Shared/Components/QrCodeScannerCamera.tsx +1 -9
  54. package/src/Shared/Components/TrustchexCamera.tsx +1 -0
  55. package/src/Shared/Libs/mrz.utils.ts +238 -26
  56. package/src/Translation/Resources/en.ts +0 -4
  57. package/src/Translation/Resources/tr.ts +0 -4
  58. package/src/version.ts +1 -1
@@ -0,0 +1,136 @@
1
+ export type SecurityFeature =
2
+ | 'icao_photo' // ICAO uyumlu fotoğraf - ICAO compliant photo
3
+ | 'barcode' // Barkod
4
+ | 'hologram' // DOVID/OVD - hologram
5
+ | 'wet_signature' // Islak imza - wet signature
6
+ | 'guilloche' // Giyoş - complex line patterns
7
+ | 'rainbow_print' // Gökkuşağı baskı - color gradient
8
+ | 'ovi' // Optik değişken mürekkep - color-shifting ink
9
+ | 'latent_image' // Gizli görüntü - visible at angles
10
+ | 'microtext'; // Mikro yazı - tiny text
11
+
12
+ export type DocumentScannedData = {
13
+ documentType: 'ID_FRONT' | 'ID_BACK' | 'PASSPORT' | 'UNKNOWN';
14
+ image: string;
15
+ faceImage?: string;
16
+ secondaryFaceImage?: string;
17
+ hologramImage?: string;
18
+ barcodeValue?: string;
19
+ mrzText?: string;
20
+ mrzFields?: MRZFields;
21
+ // BDDK Madde 7 - Visual security validation (NFC fallback)
22
+ securityFeaturesDetected?: SecurityFeature[];
23
+ documentTiltValidated?: boolean;
24
+ interactiveTouchValidated?: boolean;
25
+ serialNumberVerified?: boolean;
26
+ transitionPointsValidated?: boolean;
27
+ };
28
+
29
+ export type BlockText = {
30
+ blocks: BlocksData[];
31
+ resultText: string;
32
+ mrzOnlyText?: string;
33
+ };
34
+
35
+ export type BlocksData = {
36
+ blockFrame: FrameType;
37
+ blockCornerPoints: CornerPointsType;
38
+ lines: LinesData;
39
+ blockLanguages: string[] | [];
40
+ blockText: string;
41
+ };
42
+
43
+ export type CornerPointsType = [{ x: number; y: number }];
44
+
45
+ export type FrameType = {
46
+ boundingCenterX: number;
47
+ boundingCenterY: number;
48
+ height: number;
49
+ width: number;
50
+ x: number;
51
+ y: number;
52
+ };
53
+
54
+ export type LinesData = [
55
+ lineCornerPoints: CornerPointsType,
56
+ elements: ElementsData,
57
+ lineFrame: FrameType,
58
+ lineLanguages: string[] | [],
59
+ lineText: string,
60
+ ];
61
+
62
+ export type ElementsData = [
63
+ elementCornerPoints: CornerPointsType,
64
+ elementFrame: FrameType,
65
+ elementText: string,
66
+ ];
67
+
68
+ export type PhotoOptions = {
69
+ uri: string;
70
+ orientation?:
71
+ | 'landscapeRight'
72
+ | 'portrait'
73
+ | 'portraitUpsideDown'
74
+ | 'landscapeLeft';
75
+ };
76
+
77
+ export interface IdentityDocumentCameraProps {
78
+ onlyMRZScan: boolean;
79
+ onIdentityDocumentScanned: (scannedData: DocumentScannedData) => void;
80
+ testMode?: boolean;
81
+ }
82
+
83
+ export interface Face {
84
+ bounds: { x: number; y: number; width: number; height: number };
85
+ rollAngle?: number;
86
+ pitchAngle?: number;
87
+ yawAngle?: number;
88
+ leftEyeOpenProbability?: number;
89
+ rightEyeOpenProbability?: number;
90
+ smilingProbability?: number;
91
+ }
92
+
93
+ export interface Barcode {
94
+ rawValue: string;
95
+ displayValue: string;
96
+ format: number;
97
+ boundingBox: { left: number; top: number; right: number; bottom: number };
98
+ cornerPoints: Array<{ x: number; y: number }>;
99
+ value?: string;
100
+ }
101
+
102
+ import type { MRZFields } from '../Types/mrzFields';
103
+
104
+ export type ScanStep =
105
+ | 'SCAN_ID_FRONT_OR_PASSPORT'
106
+ | 'SCAN_ID_BACK'
107
+ | 'SCAN_HOLOGRAM'
108
+ | 'COMPLETED';
109
+
110
+ export type ScanStatus = 'SEARCHING' | 'SCANNING' | 'SCANNED' | 'INCORRECT';
111
+
112
+ export type DocumentType = 'ID_FRONT' | 'ID_BACK' | 'PASSPORT' | 'UNKNOWN';
113
+
114
+ export type CompletedStep =
115
+ | 'SCAN_ID_FRONT_OR_PASSPORT'
116
+ | 'SCAN_ID_BACK'
117
+ | 'SCAN_HOLOGRAM'
118
+ | null;
119
+
120
+ export type BoundsWithRotation = {
121
+ x: number;
122
+ y: number;
123
+ width: number;
124
+ height: number;
125
+ rollAngle?: number;
126
+ cropPadding?: number;
127
+ };
128
+
129
+ export type BoundsWithAngle = {
130
+ x: number;
131
+ y: number;
132
+ width: number;
133
+ height: number;
134
+ angle?: number;
135
+ corners?: Array<{ x: number; y: number }>;
136
+ };
@@ -0,0 +1,364 @@
1
+ import { Dimensions } from 'react-native';
2
+ import type {
3
+ ScanStep,
4
+ ScanStatus,
5
+ DocumentType,
6
+ Face,
7
+ } from './IdentityDocumentCamera.types';
8
+ import type { MRZFields } from '../Types/mrzFields';
9
+ import {
10
+ SIGNATURE_REGEX,
11
+ PASSPORT_MRZ_PATTERN,
12
+ } from './IdentityDocumentCamera.constants';
13
+ import { debugLog, isDebugEnabled } from '../Libs/debug.utils';
14
+
15
+ /**
16
+ * Frame-to-screen coordinate transform using FILL_CENTER scaling
17
+ */
18
+ export function getFrameToScreenTransform(
19
+ frameWidth: number,
20
+ frameHeight: number
21
+ ) {
22
+ const screen = Dimensions.get('window');
23
+ const frameAspect = frameWidth / frameHeight;
24
+ const screenAspect = screen.width / screen.height;
25
+
26
+ let scale: number;
27
+ let offsetX = 0;
28
+ let offsetY = 0;
29
+
30
+ if (frameAspect > screenAspect) {
31
+ scale = screen.height / frameHeight;
32
+ offsetX = (frameWidth * scale - screen.width) / 2;
33
+ } else {
34
+ scale = screen.width / frameWidth;
35
+ offsetY = (frameHeight * scale - screen.height) / 2;
36
+ }
37
+
38
+ return { scale, offsetX, offsetY, screen };
39
+ }
40
+
41
+ /**
42
+ * Compute scan area bounds in frame coordinates
43
+ */
44
+ export function getScanAreaBounds(frameWidth: number, frameHeight: number) {
45
+ const { scale, offsetX, offsetY, screen } = getFrameToScreenTransform(
46
+ frameWidth,
47
+ frameHeight
48
+ );
49
+
50
+ const scanLeft = (screen.width * 0.05 + offsetX) / scale;
51
+ const scanTop = (screen.height * 0.36 + offsetY) / scale;
52
+ const scanRight = (screen.width * 0.95 + offsetX) / scale;
53
+ const scanBottom = (screen.height * 0.64 + offsetY) / scale;
54
+
55
+ const isInsideScan = (x: number, y: number, w: number, h: number) =>
56
+ x >= scanLeft && y >= scanTop && x + w <= scanRight && y + h <= scanBottom;
57
+
58
+ return { scanLeft, scanTop, scanRight, scanBottom, isInsideScan };
59
+ }
60
+
61
+ /**
62
+ * Transform frame-space bounds to screen-space bounds
63
+ */
64
+ export function transformBoundsToScreen(
65
+ bounds: { x: number; y: number; width: number; height: number },
66
+ scale: number,
67
+ offsetX: number,
68
+ offsetY: number
69
+ ) {
70
+ return {
71
+ x: bounds.x * scale - offsetX,
72
+ y: bounds.y * scale - offsetY,
73
+ width: bounds.width * scale,
74
+ height: bounds.height * scale,
75
+ };
76
+ }
77
+
78
+ /**
79
+ * Unified status message logic used by both voice guidance and render text.
80
+ * Returns the appropriate i18n key arguments for the current scan state.
81
+ */
82
+ export function getStatusMessage(
83
+ nextStep: ScanStep,
84
+ status: ScanStatus,
85
+ detectedDocumentType: DocumentType,
86
+ isBrightnessLow: boolean,
87
+ isFrameBlurry: boolean,
88
+ allElementsDetected: boolean,
89
+ elementsOutsideScanArea: string[],
90
+ t: (key: string, params?: Record<string, unknown>) => string
91
+ ): string {
92
+ if (nextStep === 'COMPLETED') {
93
+ return t('identityDocumentCamera.scanCompleted');
94
+ }
95
+
96
+ if (status === 'INCORRECT') {
97
+ if (nextStep === 'SCAN_ID_FRONT_OR_PASSPORT') {
98
+ return t('identityDocumentCamera.alignIDFront');
99
+ }
100
+ if (nextStep === 'SCAN_ID_BACK') {
101
+ return t('identityDocumentCamera.alignIDBack');
102
+ }
103
+ if (nextStep === 'SCAN_HOLOGRAM') {
104
+ return t('identityDocumentCamera.alignIDFront');
105
+ }
106
+ return t('identityDocumentCamera.alignPhotoSide');
107
+ }
108
+
109
+ if (isBrightnessLow) {
110
+ return t('identityDocumentCamera.lowBrightness');
111
+ }
112
+
113
+ if (isFrameBlurry) {
114
+ return t('identityDocumentCamera.avoidBlur');
115
+ }
116
+
117
+ if (
118
+ status === 'SCANNING' &&
119
+ allElementsDetected &&
120
+ elementsOutsideScanArea.length === 0
121
+ ) {
122
+ if (nextStep === 'SCAN_ID_BACK') {
123
+ return t('identityDocumentCamera.idCardBackDetected');
124
+ }
125
+ if (detectedDocumentType === 'PASSPORT') {
126
+ return t('identityDocumentCamera.passportDetected');
127
+ }
128
+ if (detectedDocumentType === 'ID_FRONT') {
129
+ return t('identityDocumentCamera.idCardFrontDetected');
130
+ }
131
+ if (nextStep === 'SCAN_HOLOGRAM') {
132
+ return t('identityDocumentCamera.alignHologram');
133
+ }
134
+ return t('identityDocumentCamera.readingDocument');
135
+ }
136
+
137
+ if (elementsOutsideScanArea.length > 0) {
138
+ return t('identityDocumentCamera.centerDocument');
139
+ }
140
+
141
+ if (
142
+ (status === 'SCANNING' || status === 'SEARCHING') &&
143
+ !allElementsDetected
144
+ ) {
145
+ if (nextStep === 'SCAN_ID_BACK') {
146
+ return t('identityDocumentCamera.alignIDBack');
147
+ }
148
+ if (nextStep === 'SCAN_ID_FRONT_OR_PASSPORT') {
149
+ if (detectedDocumentType === 'PASSPORT') {
150
+ return t('identityDocumentCamera.alignPassport');
151
+ }
152
+ if (detectedDocumentType === 'ID_FRONT') {
153
+ return t('identityDocumentCamera.alignIDFront');
154
+ }
155
+ return t('identityDocumentCamera.alignPhotoSide');
156
+ }
157
+ if (nextStep === 'SCAN_HOLOGRAM') {
158
+ return t('identityDocumentCamera.alignHologram');
159
+ }
160
+ return t('identityDocumentCamera.readingDocument');
161
+ }
162
+
163
+ // Default fallback
164
+ if (nextStep === 'SCAN_ID_FRONT_OR_PASSPORT') {
165
+ return status === 'SCANNING'
166
+ ? t('identityDocumentCamera.readingDocument')
167
+ : t('identityDocumentCamera.alignPhotoSide');
168
+ }
169
+ if (nextStep === 'SCAN_HOLOGRAM') {
170
+ return t('identityDocumentCamera.alignHologram');
171
+ }
172
+ if (nextStep === 'SCAN_ID_BACK') {
173
+ return status === 'SCANNING'
174
+ ? t('identityDocumentCamera.readingDocument')
175
+ : t('identityDocumentCamera.alignIDBackSide');
176
+ }
177
+
178
+ return '';
179
+ }
180
+
181
+ /**
182
+ * Calculate angle from two points in degrees
183
+ */
184
+ export function angleBetweenPoints(
185
+ p1: { x: number; y: number },
186
+ p2: { x: number; y: number }
187
+ ): number {
188
+ const dx = p2.x - p1.x;
189
+ const dy = p2.y - p1.y;
190
+ return Math.atan2(dy, dx) * (180 / Math.PI);
191
+ }
192
+
193
+ /**
194
+ * Calculate distance between two points
195
+ */
196
+ export function distanceBetweenPoints(
197
+ p1: { x: number; y: number },
198
+ p2: { x: number; y: number }
199
+ ): number {
200
+ const dx = p2.x - p1.x;
201
+ const dy = p2.y - p1.y;
202
+ return Math.sqrt(dx * dx + dy * dy);
203
+ }
204
+
205
+ /**
206
+ * Detect document type based on faces, OCR text, and MRZ fields
207
+ */
208
+ export function detectDocumentType(
209
+ faces: Face[],
210
+ ocrText: string,
211
+ mrzFields?: MRZFields,
212
+ frameWidth?: number,
213
+ mrzText?: string | null
214
+ ): DocumentType {
215
+ // Relaxed signature detection: matches signature/imza variants and OCR errors
216
+ const hasSignatureMatch = SIGNATURE_REGEX.test(ocrText);
217
+
218
+ if (isDebugEnabled()) {
219
+ debugLog('IdentityDocumentCamera.utils', '[DocType] Detection', {
220
+ faces: faces.length,
221
+ hasMRZ: !!mrzFields,
222
+ hasMRZText: !!mrzText,
223
+ textLength: ocrText?.length,
224
+ hasSignature: hasSignatureMatch,
225
+ });
226
+ }
227
+
228
+ // ID Back: no face + ID MRZ
229
+ if (faces.length === 0 && mrzFields?.documentCode === 'I') {
230
+ return 'ID_BACK';
231
+ }
232
+
233
+ // Passport: face + passport MRZ
234
+ if (faces.length > 0 && mrzFields?.documentCode === 'P') {
235
+ return 'PASSPORT';
236
+ }
237
+
238
+ // Check for PASSPORT MRZ pattern BEFORE classifying as ID_FRONT
239
+ // Both passports and ID cards have face + signature, so we MUST check MRZ first
240
+ // Check BOTH parsed mrzText AND raw ocrText — MRZ parsing can fail while
241
+ // the raw OCR still contains P<TUR/P<USA pattern
242
+ const hasPassportMRZPattern =
243
+ (mrzText && mrzText.length > 20 && PASSPORT_MRZ_PATTERN.test(mrzText)) ||
244
+ PASSPORT_MRZ_PATTERN.test(ocrText);
245
+
246
+ if (hasPassportMRZPattern) {
247
+ // Passport MRZ pattern detected (P<TUR, P<USA, etc.)
248
+ // Even if not fully parsed, this is definitely a passport, not ID card
249
+ return 'PASSPORT';
250
+ }
251
+
252
+ // ID Front: face detected
253
+ // CRITICAL: Only classify as ID_FRONT when we're confident it's NOT a passport
254
+ // This means we must have either:
255
+ // 1. MRZ code 'I' (definitive ID card), OR
256
+ // 2. Face + signature but NO passport MRZ pattern visible
257
+ if (faces.length > 0 && ocrText?.length >= 5) {
258
+ // Filter to card-sized faces only (min 5% of frame width to exclude tiny background faces)
259
+ const cardSizedFaces = frameWidth
260
+ ? faces.filter(
261
+ (face) =>
262
+ face.bounds.width >= frameWidth * 0.05 &&
263
+ face.bounds.height >= frameWidth * 0.05
264
+ )
265
+ : faces;
266
+
267
+ if (cardSizedFaces.length > 0) {
268
+ // If we have MRZ code 'I', it's definitely an ID card
269
+ if (mrzFields?.documentCode === 'I') {
270
+ return 'ID_FRONT';
271
+ }
272
+
273
+ // If signature present and NO passport MRZ pattern, likely ID_FRONT
274
+ // But we can't be 100% sure yet - passport MRZ might appear in next frames
275
+ const hasSignature = hasSignatureMatch;
276
+ if (hasSignature && !hasPassportMRZPattern) {
277
+ return 'ID_FRONT';
278
+ }
279
+ }
280
+ }
281
+
282
+ return 'UNKNOWN';
283
+ }
284
+
285
+ /**
286
+ * Determine the document type to set based on current frame analysis
287
+ * Handles correction of misdetections and passport pattern checking
288
+ */
289
+ export function determineDocumentTypeToSet(
290
+ documentType: DocumentType,
291
+ cardSizedFaces: Face[],
292
+ parsedMRZFields?: MRZFields,
293
+ mrzText?: string | null
294
+ ): DocumentType {
295
+ // CRITICAL: Passport MRZ always takes precedence over other detections
296
+ const hasPassportMRZ =
297
+ (mrzText && mrzText.length > 20 && PASSPORT_MRZ_PATTERN.test(mrzText)) ||
298
+ parsedMRZFields?.documentCode === 'P';
299
+
300
+ if (hasPassportMRZ) {
301
+ return 'PASSPORT';
302
+ }
303
+
304
+ // After passport MRZ check, accept the detected type directly
305
+ if (documentType === 'PASSPORT') return 'PASSPORT';
306
+ if (documentType === 'ID_FRONT') return 'ID_FRONT';
307
+ return 'UNKNOWN';
308
+ }
309
+
310
+ /**
311
+ * Compare MRZ field values (ignore raw text variations)
312
+ */
313
+ export function areMRZFieldsEqual(fields1: any, fields2: any): boolean {
314
+ if (!fields1 || !fields2) return false;
315
+ // Compare critical fields that define document identity
316
+ return (
317
+ fields1.documentNumber === fields2.documentNumber &&
318
+ fields1.birthDate === fields2.birthDate &&
319
+ fields1.expirationDate === fields2.expirationDate &&
320
+ fields1.firstName === fields2.firstName &&
321
+ fields1.lastName === fields2.lastName &&
322
+ fields1.issuingState === fields2.issuingState
323
+ );
324
+ }
325
+
326
+ /**
327
+ * Check if all required MRZ fields are present
328
+ */
329
+ export function hasRequiredMRZFields(fields: any): boolean {
330
+ return (
331
+ !!fields?.firstName &&
332
+ !!fields?.lastName &&
333
+ !!fields?.documentNumber &&
334
+ !!fields?.birthDate
335
+ );
336
+ }
337
+
338
+ /**
339
+ * Validate if face position has changed within acceptable tolerance
340
+ * Returns true if position is valid (within tolerance)
341
+ */
342
+ export function validateFacePosition(
343
+ currentBounds: { x: number; y: number; width: number; height: number },
344
+ referenceBounds: { x: number; y: number; width: number; height: number },
345
+ isHologramStep: boolean
346
+ ): boolean {
347
+ const xDiff = Math.abs(currentBounds.x - referenceBounds.x);
348
+ const yDiff = Math.abs(currentBounds.y - referenceBounds.y);
349
+ const widthDiff = Math.abs(currentBounds.width - referenceBounds.width);
350
+ const heightDiff = Math.abs(currentBounds.height - referenceBounds.height);
351
+
352
+ // Use looser tolerance during hologram step since flash toggling causes position jitter
353
+ const tolerance = isHologramStep ? 0.5 : 0.2;
354
+ const xTolerance = referenceBounds.width * tolerance;
355
+ const yTolerance = referenceBounds.height * tolerance;
356
+ const sizeTolerance = referenceBounds.width * tolerance;
357
+
358
+ return (
359
+ xDiff <= xTolerance &&
360
+ yDiff <= yTolerance &&
361
+ widthDiff <= sizeTolerance &&
362
+ heightDiff <= sizeTolerance
363
+ );
364
+ }
@@ -20,7 +20,6 @@ import { useKeepAwake } from '../Libs/native-keep-awake.utils';
20
20
  import { useIsFocused } from '@react-navigation/native';
21
21
  import { useTranslation } from 'react-i18next';
22
22
  import { debugLog, logError } from '../Libs/debug.utils';
23
- import LottieView from 'lottie-react-native';
24
23
  import StyledButton from './StyledButton';
25
24
  import { useTheme } from '../Contexts/ThemeContext';
26
25
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
@@ -193,14 +192,7 @@ const QrCodeScannerCamera = ({
193
192
  <View style={styles.leftZone} />
194
193
  <View style={styles.rightZone} />
195
194
  <View style={styles.bottomZone} />
196
- <View style={styles.scanArea}>
197
- <LottieView
198
- source={require('../../Shared/Animations/scanning.json')}
199
- style={styles.animation}
200
- loop={true}
201
- autoPlay
202
- />
203
- </View>
195
+ <View style={styles.scanArea} />
204
196
  {onClose && (
205
197
  <TouchableOpacity
206
198
  onPress={onClose}
@@ -102,6 +102,7 @@ interface TrustchexCameraProps {
102
102
  torchEnabled?: boolean;
103
103
  enableFrameProcessing?: boolean;
104
104
  targetFps?: number;
105
+ resolution?: string;
105
106
  // ML Kit detection modes — processing runs natively, results arrive in Frame
106
107
  enableFaceDetection?: boolean;
107
108
  enableTextRecognition?: boolean;