@trustchex/react-native-sdk 1.409.0 → 1.464.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 (156) hide show
  1. package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKModule.kt +2 -8
  2. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +59 -1
  3. package/ios/Camera/TrustchexCameraView.swift +9 -1
  4. package/lib/module/Screens/Debug/NFCScanTestScreen.js +635 -0
  5. package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +1 -4
  6. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +17 -4
  7. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +102 -23
  8. package/lib/module/Screens/Dynamic/VerbalConsentScreen.js +1079 -0
  9. package/lib/module/Screens/Dynamic/VideoCallScreen.js +3 -1
  10. package/lib/module/Screens/Static/ResultScreen.js +128 -22
  11. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +8 -0
  12. package/lib/module/Shared/Animations/recording.json +1 -0
  13. package/lib/module/Shared/Components/DebugNavigationPanel.js +69 -71
  14. package/lib/module/Shared/Components/EIDScanner.js +212 -108
  15. package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +5 -3
  16. package/lib/module/Shared/Components/IdentityDocumentCamera.js +53 -36
  17. package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +13 -4
  18. package/lib/module/Shared/Components/NavigationManager.js +24 -16
  19. package/lib/module/Shared/EIDReader/aesSecureMessagingWrapper.js +51 -0
  20. package/lib/module/Shared/EIDReader/apduLevelPACECapable.js +3 -0
  21. package/lib/module/Shared/EIDReader/bacKey.js +16 -2
  22. package/lib/module/Shared/EIDReader/eidReader.js +354 -13
  23. package/lib/module/Shared/EIDReader/eidService.js +25 -1
  24. package/lib/module/Shared/EIDReader/nfcManagerCardService.js +4 -7
  25. package/lib/module/Shared/EIDReader/paceInfo.js +85 -0
  26. package/lib/module/Shared/EIDReader/paceKeySpec.js +51 -0
  27. package/lib/module/Shared/EIDReader/protocol/paceAPDUSender.js +100 -0
  28. package/lib/module/Shared/EIDReader/protocol/paceProtocol.js +655 -0
  29. package/lib/module/Shared/EIDReader/protocol/paceResult.js +37 -0
  30. package/lib/module/Shared/EIDReader/secureMessagingWrapper.js +27 -4
  31. package/lib/module/Shared/EIDReader/smartcards/commandAPDU.js +2 -1
  32. package/lib/module/Shared/EIDReader/tlv/tlv.helpers.js +1 -1
  33. package/lib/module/Shared/EIDReader/tlv/tlv.utils.js +6 -3
  34. package/lib/module/Shared/EIDReader/utils/aesCrypto.utils.js +189 -0
  35. package/lib/module/Shared/Libs/analytics.utils.js +4 -0
  36. package/lib/module/Shared/Libs/contains.js +1 -40
  37. package/lib/module/Shared/Libs/country-display.utils.js +34 -0
  38. package/lib/module/Shared/Libs/demo.utils.js +8 -0
  39. package/lib/module/Shared/Libs/mrz.utils.js +3 -2
  40. package/lib/module/Shared/Libs/status-bar.utils.js +4 -2
  41. package/lib/module/Shared/Types/analytics.types.js +2 -0
  42. package/lib/module/Translation/Resources/en.js +41 -2
  43. package/lib/module/Translation/Resources/tr.js +41 -2
  44. package/lib/module/Trustchex.js +54 -20
  45. package/lib/module/version.js +1 -1
  46. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts +3 -0
  47. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts.map +1 -0
  48. package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
  49. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  50. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  51. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts +3 -0
  52. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts.map +1 -0
  53. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts.map +1 -1
  54. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  55. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  56. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  57. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  58. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  59. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +1 -1
  60. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -1
  61. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +5 -0
  62. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
  63. package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
  64. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts +18 -0
  65. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts.map +1 -0
  66. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts +23 -0
  67. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts.map +1 -0
  68. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts +6 -0
  69. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts.map +1 -1
  70. package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
  71. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts +9 -0
  72. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts.map +1 -1
  73. package/lib/typescript/src/Shared/EIDReader/nfcManagerCardService.d.ts.map +1 -1
  74. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts +50 -0
  75. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts.map +1 -0
  76. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts +30 -0
  77. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts.map +1 -0
  78. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts +17 -0
  79. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts.map +1 -0
  80. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts +105 -0
  81. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts.map +1 -0
  82. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts +24 -0
  83. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts.map +1 -0
  84. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts +15 -0
  85. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts.map +1 -1
  86. package/lib/typescript/src/Shared/EIDReader/smartcards/commandAPDU.d.ts.map +1 -1
  87. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.utils.d.ts.map +1 -1
  88. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts +39 -0
  89. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts.map +1 -0
  90. package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
  91. package/lib/typescript/src/Shared/Libs/contains.d.ts +0 -7
  92. package/lib/typescript/src/Shared/Libs/contains.d.ts.map +1 -1
  93. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts +2 -0
  94. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts.map +1 -0
  95. package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
  96. package/lib/typescript/src/Shared/Libs/http-client.d.ts +1 -1
  97. package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
  98. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  99. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts.map +1 -1
  100. package/lib/typescript/src/Shared/Types/analytics.types.d.ts +2 -0
  101. package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -1
  102. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +10 -1
  103. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
  104. package/lib/typescript/src/Translation/Resources/en.d.ts +40 -1
  105. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  106. package/lib/typescript/src/Translation/Resources/tr.d.ts +40 -1
  107. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  108. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  109. package/lib/typescript/src/version.d.ts +1 -1
  110. package/package.json +7 -4
  111. package/src/Screens/Debug/NFCScanTestScreen.tsx +692 -0
  112. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +1 -4
  113. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +21 -4
  114. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +124 -23
  115. package/src/Screens/Dynamic/VerbalConsentScreen.tsx +1401 -0
  116. package/src/Screens/Dynamic/VideoCallScreen.tsx +3 -1
  117. package/src/Screens/Static/ResultScreen.tsx +183 -31
  118. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +9 -0
  119. package/src/Shared/Animations/recording.json +1 -0
  120. package/src/Shared/Components/DebugNavigationPanel.tsx +73 -48
  121. package/src/Shared/Components/EIDScanner.tsx +222 -111
  122. package/src/Shared/Components/IdentityDocumentCamera.flows.ts +7 -4
  123. package/src/Shared/Components/IdentityDocumentCamera.tsx +199 -184
  124. package/src/Shared/Components/IdentityDocumentCamera.utils.ts +13 -4
  125. package/src/Shared/Components/NavigationManager.tsx +27 -18
  126. package/src/Shared/EIDReader/aesSecureMessagingWrapper.ts +69 -0
  127. package/src/Shared/EIDReader/apduLevelPACECapable.ts +34 -0
  128. package/src/Shared/EIDReader/bacKey.ts +24 -8
  129. package/src/Shared/EIDReader/eidReader.ts +398 -12
  130. package/src/Shared/EIDReader/eidService.ts +49 -1
  131. package/src/Shared/EIDReader/nfcManagerCardService.ts +4 -6
  132. package/src/Shared/EIDReader/paceInfo.ts +159 -0
  133. package/src/Shared/EIDReader/paceKeySpec.ts +56 -0
  134. package/src/Shared/EIDReader/protocol/paceAPDUSender.ts +163 -0
  135. package/src/Shared/EIDReader/protocol/paceProtocol.ts +946 -0
  136. package/src/Shared/EIDReader/protocol/paceResult.ts +62 -0
  137. package/src/Shared/EIDReader/secureMessagingWrapper.ts +28 -10
  138. package/src/Shared/EIDReader/smartcards/commandAPDU.ts +2 -1
  139. package/src/Shared/EIDReader/tlv/tlv.helpers.ts +1 -1
  140. package/src/Shared/EIDReader/tlv/tlv.utils.ts +8 -5
  141. package/src/Shared/EIDReader/utils/aesCrypto.utils.ts +217 -0
  142. package/src/Shared/Libs/analytics.utils.ts +4 -0
  143. package/src/Shared/Libs/contains.ts +0 -53
  144. package/src/Shared/Libs/country-display.utils.ts +55 -0
  145. package/src/Shared/Libs/crypto.utils.ts +2 -2
  146. package/src/Shared/Libs/demo.utils.ts +10 -0
  147. package/src/Shared/Libs/http-client.ts +12 -4
  148. package/src/Shared/Libs/mrz.utils.ts +3 -2
  149. package/src/Shared/Libs/status-bar.utils.ts +4 -2
  150. package/src/Shared/Services/VideoSessionService.ts +1 -1
  151. package/src/Shared/Types/analytics.types.ts +2 -0
  152. package/src/Shared/Types/identificationInfo.ts +11 -0
  153. package/src/Translation/Resources/en.ts +63 -3
  154. package/src/Translation/Resources/tr.ts +62 -3
  155. package/src/Trustchex.tsx +53 -17
  156. package/src/version.ts +1 -1
@@ -0,0 +1,692 @@
1
+ import React, { useState, useRef, useCallback } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ TextInput,
6
+ TouchableOpacity,
7
+ ScrollView,
8
+ StyleSheet,
9
+ Image,
10
+ Platform,
11
+ StatusBar,
12
+ KeyboardAvoidingView,
13
+ Modal,
14
+ type NativeSyntheticEvent,
15
+ } from 'react-native';
16
+ import { SafeAreaView } from 'react-native-safe-area-context';
17
+ import NFCManager from 'react-native-nfc-manager';
18
+ import { eidReader } from '../../Shared/EIDReader/eidReader';
19
+ import { useKeepAwake } from '../../Shared/Libs/native-keep-awake.utils';
20
+ import type { MRZInfo } from '../../Shared/EIDReader/lds/icao/mrzInfo';
21
+ import mrzUtils from '../../Shared/Libs/mrz.utils';
22
+ import {
23
+ TrustchexCamera,
24
+ type TrustchexCameraHandle,
25
+ type Frame,
26
+ } from '../../Shared/Components/TrustchexCamera';
27
+
28
+ type LogType = 'info' | 'debug' | 'success' | 'error';
29
+
30
+ interface LogEntry {
31
+ time: string;
32
+ message: string;
33
+ type: LogType;
34
+ }
35
+
36
+ const NFCScanTestScreen = () => {
37
+ useKeepAwake();
38
+
39
+ const [documentNumber, setDocumentNumber] = useState('');
40
+ const [dateOfBirth, setDateOfBirth] = useState('');
41
+ const [dateOfExpiry, setDateOfExpiry] = useState('');
42
+ const [isScanning, setIsScanning] = useState(false);
43
+ const [progress, setProgress] = useState(0);
44
+ const [logs, setLogs] = useState<LogEntry[]>([]);
45
+ const [faceImage, setFaceImage] = useState<string | null>(null);
46
+ const [faceMimeType, setFaceMimeType] = useState<string>('image/jpeg');
47
+ const [mrzResult, setMrzResult] = useState<MRZInfo | null>(null);
48
+ const [mrzText, setMrzText] = useState('');
49
+ const [cameraOpen, setCameraOpen] = useState(false);
50
+ const [cameraMrzStatus, setCameraMrzStatus] = useState('');
51
+
52
+ const scrollRef = useRef<ScrollView>(null);
53
+ const cameraRef = useRef<TrustchexCameraHandle>(null);
54
+ const originalConsoleDebug = useRef<((...args: unknown[]) => void) | null>(
55
+ null
56
+ );
57
+
58
+ const addLog = useCallback((message: string, type: LogType = 'info') => {
59
+ const now = new Date();
60
+ const time = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}.${String(now.getMilliseconds()).padStart(3, '0')}`;
61
+ setLogs((prev) => [...prev, { time, message: String(message), type }]);
62
+ setTimeout(() => scrollRef.current?.scrollToEnd({ animated: false }), 50);
63
+ }, []);
64
+
65
+ const clearAll = useCallback(() => {
66
+ setLogs([]);
67
+ setFaceImage(null);
68
+ setMrzResult(null);
69
+ setProgress(0);
70
+ }, []);
71
+
72
+ const parseMRZText = useCallback((text: string) => {
73
+ setMrzText(text);
74
+ if (!text.trim()) return;
75
+ const result = mrzUtils.validateMRZWithCorrections(text);
76
+ if (result.valid && result.fields) {
77
+ const {
78
+ documentNumber: dn,
79
+ birthDate: bd,
80
+ expirationDate: ed,
81
+ } = result.fields;
82
+ if (dn) setDocumentNumber(dn);
83
+ if (bd) setDateOfBirth(bd);
84
+ if (ed) setDateOfExpiry(ed);
85
+ }
86
+ }, []);
87
+
88
+ const handleCameraFrame = useCallback(
89
+ (event: NativeSyntheticEvent<{ frame: Frame }>) => {
90
+ const frame = event.nativeEvent.frame;
91
+ if (!frame.resultText || !frame.resultText.includes('<')) {
92
+ setCameraMrzStatus('Point camera at MRZ zone...');
93
+ return;
94
+ }
95
+
96
+ let mrzOnlyText = '';
97
+ if (frame.textBlocks && frame.textBlocks.length > 0) {
98
+ const frameHeight = frame.height;
99
+ const bottomThreshold = frameHeight * 0.5;
100
+ const mrzBlocks = frame.textBlocks.filter((block) => {
101
+ const blockText = block.text || '';
102
+ return (
103
+ blockText.includes('<') &&
104
+ blockText.length >= 12 &&
105
+ (block.blockFrame ? block.blockFrame.y > bottomThreshold : false)
106
+ );
107
+ });
108
+ if (mrzBlocks.length > 0) {
109
+ mrzOnlyText = mrzBlocks.map((b) => b.text).join('\n');
110
+ }
111
+ }
112
+ if (!mrzOnlyText) mrzOnlyText = frame.resultText;
113
+
114
+ const fixedMrz = mrzUtils.fixMRZ(mrzOnlyText);
115
+ const result = mrzUtils.validateMRZWithCorrections(fixedMrz);
116
+
117
+ if (result.valid && result.fields) {
118
+ const {
119
+ documentNumber: dn,
120
+ birthDate: bd,
121
+ expirationDate: ed,
122
+ } = result.fields;
123
+ if (dn) setDocumentNumber(dn);
124
+ if (bd) setDateOfBirth(bd);
125
+ if (ed) setDateOfExpiry(ed);
126
+ setMrzText(fixedMrz);
127
+ setCameraOpen(false);
128
+ } else {
129
+ setCameraMrzStatus('Reading MRZ... Hold steady');
130
+ }
131
+ },
132
+ []
133
+ );
134
+
135
+ const argsToString = (args: unknown[]): string =>
136
+ args
137
+ .map((a) => {
138
+ if (a instanceof Error) return `[${a.name}] ${a.message}`;
139
+ if (typeof a === 'object' && a !== null) {
140
+ try {
141
+ return JSON.stringify(a);
142
+ } catch {
143
+ return String(a);
144
+ }
145
+ }
146
+ return String(a);
147
+ })
148
+ .join(' ');
149
+
150
+ const startScan = useCallback(async () => {
151
+ const docNum = documentNumber.trim();
152
+ const dob = dateOfBirth.trim();
153
+ const doe = dateOfExpiry.trim();
154
+
155
+ if (!docNum) {
156
+ addLog('Document number is required', 'error');
157
+ return;
158
+ }
159
+ if (dob.length !== 6 || !/^\d{6}$/.test(dob)) {
160
+ addLog('Date of Birth must be 6 digits (YYMMDD)', 'error');
161
+ return;
162
+ }
163
+ if (doe.length !== 6 || !/^\d{6}$/.test(doe)) {
164
+ addLog('Date of Expiry must be 6 digits (YYMMDD)', 'error');
165
+ return;
166
+ }
167
+
168
+ clearAll();
169
+ setIsScanning(true);
170
+
171
+ // Capture console.debug to show internal EIDReader logs
172
+ originalConsoleDebug.current = console.debug as (
173
+ ...args: unknown[]
174
+ ) => void;
175
+ (console as unknown as Record<string, unknown>).debug = (
176
+ ...args: unknown[]
177
+ ) => {
178
+ originalConsoleDebug.current?.(...args);
179
+ addLog(argsToString(args), 'debug');
180
+ };
181
+
182
+ addLog(`Doc: ${docNum} DOB: ${dob} DOE: ${doe}`, 'info');
183
+ addLog('Hold device near NFC chip on passport/ID…', 'info');
184
+
185
+ try {
186
+ const result = await eidReader(docNum, dob, doe, (p) => {
187
+ const pct = Math.round(p);
188
+ setProgress(pct);
189
+ addLog(`Progress: ${pct}%`, 'info');
190
+ });
191
+
192
+ if (result) {
193
+ addLog('Scan completed successfully', 'success');
194
+ if (result.faceImage) {
195
+ setFaceImage(result.faceImage);
196
+ setFaceMimeType(result.mimeType || 'image/jpeg');
197
+ addLog(
198
+ `Face image: ${result.faceImage.length} base64 chars (${result.mimeType})`,
199
+ 'success'
200
+ );
201
+ } else {
202
+ addLog('No face image in result', 'info');
203
+ }
204
+ if (result.mrz) {
205
+ setMrzResult(result.mrz);
206
+ addLog('MRZ data received', 'success');
207
+ } else {
208
+ addLog('No MRZ data in result', 'info');
209
+ }
210
+ } else {
211
+ addLog('eidReader returned undefined', 'error');
212
+ }
213
+ } catch (err) {
214
+ const msg = err instanceof Error ? err.message : String(err);
215
+ addLog(`Error: ${msg}`, 'error');
216
+ if (err instanceof Error && err.stack) {
217
+ addLog(err.stack, 'debug');
218
+ }
219
+ } finally {
220
+ if (originalConsoleDebug.current) {
221
+ (console as unknown as Record<string, unknown>).debug =
222
+ originalConsoleDebug.current;
223
+ originalConsoleDebug.current = null;
224
+ }
225
+ setIsScanning(false);
226
+ }
227
+ }, [documentNumber, dateOfBirth, dateOfExpiry, addLog, clearAll]);
228
+
229
+ const cancelScan = useCallback(async () => {
230
+ addLog('Cancelling NFC session…', 'info');
231
+ try {
232
+ await NFCManager.cancelTechnologyRequest();
233
+ } catch {
234
+ // ignore
235
+ }
236
+ }, [addLog]);
237
+
238
+ const logColor: Record<LogType, string> = {
239
+ info: '#cccccc',
240
+ debug: '#888888',
241
+ success: '#4caf50',
242
+ error: '#f44336',
243
+ };
244
+
245
+ return (
246
+ <SafeAreaView style={styles.container} edges={['top', 'bottom']}>
247
+ <StatusBar barStyle="light-content" backgroundColor="#111" />
248
+ <Text style={styles.header}>NFC Scan Debug</Text>
249
+
250
+ <KeyboardAvoidingView
251
+ behavior={Platform.OS === 'ios' ? 'padding' : undefined}
252
+ >
253
+ <View style={styles.inputSection}>
254
+ <View style={styles.row}>
255
+ <View style={styles.flexOne}>
256
+ <Text style={styles.label}>
257
+ Paste MRZ Text (auto-fills fields below)
258
+ </Text>
259
+ </View>
260
+ <TouchableOpacity
261
+ style={[styles.button, styles.scanMrzButton]}
262
+ onPress={() => {
263
+ setCameraMrzStatus('Point camera at MRZ zone...');
264
+ setCameraOpen(true);
265
+ }}
266
+ disabled={isScanning}
267
+ >
268
+ <Text style={styles.buttonText}>Scan MRZ</Text>
269
+ </TouchableOpacity>
270
+ </View>
271
+ <TextInput
272
+ style={[styles.input, styles.mrzInput]}
273
+ value={mrzText}
274
+ onChangeText={parseMRZText}
275
+ placeholder="P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<&#10;L898902C36UTO7408122F1204159<<<<<<<<<<<<<<"
276
+ placeholderTextColor="#555"
277
+ multiline
278
+ numberOfLines={3}
279
+ autoCapitalize="characters"
280
+ autoCorrect={false}
281
+ editable={!isScanning}
282
+ />
283
+
284
+ <Text style={styles.label}>Document Number</Text>
285
+ <TextInput
286
+ style={styles.input}
287
+ value={documentNumber}
288
+ onChangeText={setDocumentNumber}
289
+ placeholder="e.g. A12345678"
290
+ placeholderTextColor="#555"
291
+ autoCapitalize="characters"
292
+ autoCorrect={false}
293
+ editable={!isScanning}
294
+ />
295
+
296
+ <View style={styles.row}>
297
+ <View style={styles.halfColumn}>
298
+ <Text style={styles.label}>Date of Birth (YYMMDD)</Text>
299
+ <TextInput
300
+ style={styles.input}
301
+ value={dateOfBirth}
302
+ onChangeText={setDateOfBirth}
303
+ placeholder="e.g. 900115"
304
+ placeholderTextColor="#555"
305
+ keyboardType="numeric"
306
+ maxLength={6}
307
+ editable={!isScanning}
308
+ />
309
+ </View>
310
+ <View style={styles.halfColumn}>
311
+ <Text style={styles.label}>Date of Expiry (YYMMDD)</Text>
312
+ <TextInput
313
+ style={styles.input}
314
+ value={dateOfExpiry}
315
+ onChangeText={setDateOfExpiry}
316
+ placeholder="e.g. 280101"
317
+ placeholderTextColor="#555"
318
+ keyboardType="numeric"
319
+ maxLength={6}
320
+ editable={!isScanning}
321
+ />
322
+ </View>
323
+ </View>
324
+
325
+ <View style={styles.row}>
326
+ <TouchableOpacity
327
+ style={[
328
+ styles.button,
329
+ styles.scanButton,
330
+ isScanning && styles.buttonDisabled,
331
+ ]}
332
+ onPress={startScan}
333
+ disabled={isScanning}
334
+ >
335
+ <Text style={styles.buttonText}>
336
+ {isScanning
337
+ ? `Scanning… ${Math.round(progress)}%`
338
+ : 'Start NFC Scan'}
339
+ </Text>
340
+ </TouchableOpacity>
341
+
342
+ {isScanning ? (
343
+ <TouchableOpacity
344
+ style={[styles.button, styles.cancelButton]}
345
+ onPress={cancelScan}
346
+ >
347
+ <Text style={styles.buttonText}>Cancel</Text>
348
+ </TouchableOpacity>
349
+ ) : (
350
+ <TouchableOpacity
351
+ style={[styles.button, styles.clearButton]}
352
+ onPress={clearAll}
353
+ >
354
+ <Text style={styles.buttonText}>Clear</Text>
355
+ </TouchableOpacity>
356
+ )}
357
+ </View>
358
+ </View>
359
+ </KeyboardAvoidingView>
360
+
361
+ {(faceImage || mrzResult) && (
362
+ <View>
363
+ <Text style={styles.chipDataHeader}>NFC Chip Data</Text>
364
+ <View style={styles.resultSection}>
365
+ {faceImage && (
366
+ <Image
367
+ source={{ uri: `data:${faceMimeType};base64,${faceImage}` }}
368
+ style={styles.faceImage}
369
+ resizeMode="contain"
370
+ />
371
+ )}
372
+ {mrzResult && (
373
+ <View style={styles.mrzFields}>
374
+ <MRZRow label="Doc Code" value={mrzResult.getDocumentCode()} />
375
+ <MRZRow
376
+ label="Issuing State"
377
+ value={mrzResult.getIssuingState()}
378
+ />
379
+ <MRZRow
380
+ label="Surname"
381
+ value={mrzResult.getPrimaryIdentifier()}
382
+ />
383
+ <MRZRow
384
+ label="Given Names"
385
+ value={mrzResult.getSecondaryIdentifier()}
386
+ />
387
+ <MRZRow
388
+ label="Doc Number"
389
+ value={mrzResult.getDocumentNumber()}
390
+ />
391
+ <MRZRow
392
+ label="Nationality"
393
+ value={mrzResult.getNationality()}
394
+ />
395
+ <MRZRow
396
+ label="Date of Birth"
397
+ value={mrzResult.getDateOfBirth()}
398
+ />
399
+ <MRZRow
400
+ label="Date of Expiry"
401
+ value={mrzResult.getDateOfExpiry()}
402
+ />
403
+ <MRZRow
404
+ label="Gender"
405
+ value={mrzResult.getGender()?.getStrCode() ?? ''}
406
+ />
407
+ <MRZRow
408
+ label="Personal Number"
409
+ value={mrzResult.getPersonalNumber() ?? ''}
410
+ />
411
+ </View>
412
+ )}
413
+ </View>
414
+ </View>
415
+ )}
416
+
417
+ <Modal
418
+ visible={cameraOpen}
419
+ animationType="slide"
420
+ onRequestClose={() => setCameraOpen(false)}
421
+ >
422
+ <View style={styles.cameraContainer}>
423
+ <TrustchexCamera
424
+ ref={cameraRef}
425
+ style={StyleSheet.absoluteFill}
426
+ cameraType="back"
427
+ enableFrameProcessing={true}
428
+ enableFaceDetection={false}
429
+ enableTextRecognition={true}
430
+ enableBarcodeScanning={false}
431
+ includeBase64={false}
432
+ targetFps={10}
433
+ onFrameAvailable={handleCameraFrame}
434
+ />
435
+ <SafeAreaView style={styles.cameraOverlay} edges={['top', 'bottom']}>
436
+ <View style={styles.cameraTopBar}>
437
+ <Text style={styles.cameraTitle}>Scan MRZ</Text>
438
+ <TouchableOpacity
439
+ style={styles.cameraCloseButton}
440
+ onPress={() => setCameraOpen(false)}
441
+ >
442
+ <Text style={styles.cameraCloseText}>Close</Text>
443
+ </TouchableOpacity>
444
+ </View>
445
+ <View style={styles.cameraGuide}>
446
+ <View style={styles.cameraGuideBox} />
447
+ <Text style={styles.cameraGuideText}>{cameraMrzStatus}</Text>
448
+ </View>
449
+ </SafeAreaView>
450
+ </View>
451
+ </Modal>
452
+
453
+ <ScrollView
454
+ ref={scrollRef}
455
+ style={styles.logScroll}
456
+ contentContainerStyle={styles.logContent}
457
+ nestedScrollEnabled
458
+ >
459
+ {logs.length === 0 ? (
460
+ <Text style={styles.logPlaceholder}>Logs will appear here…</Text>
461
+ ) : (
462
+ logs.map((entry, i) => (
463
+ <Text
464
+ key={i}
465
+ style={[styles.logLine, { color: logColor[entry.type] }]}
466
+ >
467
+ <Text style={styles.logTime}>{entry.time} </Text>
468
+ {entry.message}
469
+ </Text>
470
+ ))
471
+ )}
472
+ </ScrollView>
473
+ </SafeAreaView>
474
+ );
475
+ };
476
+
477
+ const MRZRow = ({ label, value }: { label: string; value: string }) => (
478
+ <View style={styles.mrzRow}>
479
+ <Text style={styles.mrzLabel}>{label}:</Text>
480
+ <Text style={styles.mrzValue}>{value || '—'}</Text>
481
+ </View>
482
+ );
483
+
484
+ const styles = StyleSheet.create({
485
+ container: {
486
+ flex: 1,
487
+ backgroundColor: '#111',
488
+ },
489
+ header: {
490
+ color: '#fff',
491
+ fontSize: 16,
492
+ fontWeight: '700',
493
+ textAlign: 'center',
494
+ paddingVertical: 10,
495
+ backgroundColor: '#1a1a1a',
496
+ letterSpacing: 0.5,
497
+ },
498
+ inputSection: {
499
+ padding: 12,
500
+ backgroundColor: '#1a1a1a',
501
+ borderBottomWidth: 1,
502
+ borderBottomColor: '#333',
503
+ },
504
+ label: {
505
+ color: '#aaa',
506
+ fontSize: 11,
507
+ marginBottom: 3,
508
+ marginTop: 8,
509
+ textTransform: 'uppercase',
510
+ letterSpacing: 0.5,
511
+ },
512
+ input: {
513
+ backgroundColor: '#2a2a2a',
514
+ color: '#fff',
515
+ borderRadius: 6,
516
+ paddingHorizontal: 10,
517
+ paddingVertical: 8,
518
+ fontSize: 14,
519
+ borderWidth: 1,
520
+ borderColor: '#444',
521
+ fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
522
+ },
523
+ mrzInput: {
524
+ fontSize: 11,
525
+ minHeight: 54,
526
+ textAlignVertical: 'top',
527
+ },
528
+ row: {
529
+ flexDirection: 'row',
530
+ gap: 8,
531
+ marginTop: 4,
532
+ },
533
+ halfColumn: {
534
+ flex: 1,
535
+ },
536
+ button: {
537
+ paddingVertical: 10,
538
+ paddingHorizontal: 16,
539
+ borderRadius: 6,
540
+ alignItems: 'center',
541
+ justifyContent: 'center',
542
+ marginTop: 10,
543
+ },
544
+ scanButton: {
545
+ flex: 2,
546
+ backgroundColor: '#1565c0',
547
+ },
548
+ scanMrzButton: {
549
+ backgroundColor: '#e65100',
550
+ paddingVertical: 6,
551
+ paddingHorizontal: 12,
552
+ marginTop: 4,
553
+ },
554
+ cancelButton: {
555
+ flex: 1,
556
+ backgroundColor: '#b71c1c',
557
+ },
558
+ clearButton: {
559
+ flex: 1,
560
+ backgroundColor: '#333',
561
+ },
562
+ buttonDisabled: {
563
+ backgroundColor: '#333',
564
+ },
565
+ buttonText: {
566
+ color: '#fff',
567
+ fontSize: 13,
568
+ fontWeight: '600',
569
+ },
570
+ resultSection: {
571
+ flexDirection: 'row',
572
+ backgroundColor: '#1a1a1a',
573
+ padding: 8,
574
+ gap: 8,
575
+ },
576
+ chipDataHeader: {
577
+ color: '#4fc3f7',
578
+ fontSize: 13,
579
+ fontWeight: '700',
580
+ paddingHorizontal: 8,
581
+ paddingTop: 8,
582
+ paddingBottom: 4,
583
+ backgroundColor: '#1a1a1a',
584
+ borderBottomWidth: 0,
585
+ letterSpacing: 0.5,
586
+ },
587
+ faceImage: {
588
+ width: 72,
589
+ height: 96,
590
+ borderRadius: 4,
591
+ backgroundColor: '#2a2a2a',
592
+ },
593
+ mrzFields: {
594
+ flex: 1,
595
+ },
596
+ mrzRow: {
597
+ flexDirection: 'row',
598
+ marginBottom: 2,
599
+ },
600
+ mrzLabel: {
601
+ color: '#888',
602
+ fontSize: 11,
603
+ width: 90,
604
+ textTransform: 'uppercase',
605
+ },
606
+ mrzValue: {
607
+ color: '#e0e0e0',
608
+ fontSize: 11,
609
+ flex: 1,
610
+ fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
611
+ },
612
+ logScroll: {
613
+ flex: 1,
614
+ backgroundColor: '#0d0d0d',
615
+ },
616
+ logContent: {
617
+ padding: 8,
618
+ paddingBottom: 16,
619
+ },
620
+ logPlaceholder: {
621
+ color: '#444',
622
+ fontSize: 12,
623
+ fontStyle: 'italic',
624
+ textAlign: 'center',
625
+ marginTop: 20,
626
+ },
627
+ logLine: {
628
+ fontSize: 11,
629
+ lineHeight: 17,
630
+ fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
631
+ },
632
+ logTime: {
633
+ color: '#555',
634
+ fontSize: 10,
635
+ },
636
+ cameraContainer: {
637
+ flex: 1,
638
+ backgroundColor: '#000',
639
+ },
640
+ cameraOverlay: {
641
+ ...StyleSheet.absoluteFillObject,
642
+ justifyContent: 'space-between',
643
+ },
644
+ cameraTopBar: {
645
+ flexDirection: 'row',
646
+ justifyContent: 'space-between',
647
+ alignItems: 'center',
648
+ paddingHorizontal: 16,
649
+ paddingVertical: 10,
650
+ backgroundColor: 'rgba(0,0,0,0.5)',
651
+ },
652
+ cameraTitle: {
653
+ color: '#fff',
654
+ fontSize: 16,
655
+ fontWeight: '700',
656
+ },
657
+ cameraCloseButton: {
658
+ backgroundColor: 'rgba(255,255,255,0.2)',
659
+ paddingVertical: 6,
660
+ paddingHorizontal: 14,
661
+ borderRadius: 6,
662
+ },
663
+ cameraCloseText: {
664
+ color: '#fff',
665
+ fontSize: 13,
666
+ fontWeight: '600',
667
+ },
668
+ cameraGuide: {
669
+ alignItems: 'center',
670
+ paddingBottom: 40,
671
+ },
672
+ cameraGuideBox: {
673
+ width: '85%',
674
+ height: 80,
675
+ borderWidth: 2,
676
+ borderColor: '#FFA500',
677
+ borderRadius: 8,
678
+ backgroundColor: 'rgba(255,165,0,0.05)',
679
+ marginBottom: 12,
680
+ },
681
+ cameraGuideText: {
682
+ color: '#FFA500',
683
+ fontSize: 13,
684
+ fontWeight: '600',
685
+ textAlign: 'center',
686
+ },
687
+ flexOne: {
688
+ flex: 1,
689
+ },
690
+ });
691
+
692
+ export default NFCScanTestScreen;
@@ -199,12 +199,9 @@ const ContractAcceptanceScreen = () => {
199
199
  };
200
200
 
201
201
  const styles = StyleSheet.create({
202
- safeAreaContainer: {
203
- flex: 1,
204
- backgroundColor: 'white',
205
- },
206
202
  container: {
207
203
  flex: 1,
204
+ backgroundColor: 'white',
208
205
  },
209
206
  webViewContainer: {
210
207
  flex: 1,