@trustchex/react-native-sdk 1.493.1 → 1.494.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 (34) hide show
  1. package/lib/module/Shared/Components/DiagnosticReportButton.js +118 -29
  2. package/lib/module/Shared/Components/NavigationManager.js +12 -0
  3. package/lib/module/Shared/Libs/diagnosticPow.js +56 -3
  4. package/lib/module/Shared/Libs/diagnosticReport.js +8 -1
  5. package/lib/module/Shared/Libs/diagnostics.js +61 -2
  6. package/lib/module/Shared/Libs/sendDiagnosticReport.js +20 -3
  7. package/lib/module/Translation/Resources/en.js +5 -2
  8. package/lib/module/Translation/Resources/tr.js +5 -2
  9. package/lib/module/version.js +1 -1
  10. package/lib/typescript/src/Shared/Components/DiagnosticReportButton.d.ts +5 -4
  11. package/lib/typescript/src/Shared/Components/DiagnosticReportButton.d.ts.map +1 -1
  12. package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
  13. package/lib/typescript/src/Shared/Libs/diagnosticPow.d.ts +26 -3
  14. package/lib/typescript/src/Shared/Libs/diagnosticPow.d.ts.map +1 -1
  15. package/lib/typescript/src/Shared/Libs/diagnosticReport.d.ts.map +1 -1
  16. package/lib/typescript/src/Shared/Libs/diagnostics.d.ts +39 -1
  17. package/lib/typescript/src/Shared/Libs/diagnostics.d.ts.map +1 -1
  18. package/lib/typescript/src/Shared/Libs/sendDiagnosticReport.d.ts +9 -0
  19. package/lib/typescript/src/Shared/Libs/sendDiagnosticReport.d.ts.map +1 -1
  20. package/lib/typescript/src/Translation/Resources/en.d.ts +3 -0
  21. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  22. package/lib/typescript/src/Translation/Resources/tr.d.ts +3 -0
  23. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  24. package/lib/typescript/src/version.d.ts +1 -1
  25. package/package.json +1 -1
  26. package/src/Shared/Components/DiagnosticReportButton.tsx +128 -34
  27. package/src/Shared/Components/NavigationManager.tsx +12 -0
  28. package/src/Shared/Libs/diagnosticPow.ts +71 -3
  29. package/src/Shared/Libs/diagnosticReport.ts +21 -3
  30. package/src/Shared/Libs/diagnostics.ts +77 -2
  31. package/src/Shared/Libs/sendDiagnosticReport.ts +37 -3
  32. package/src/Translation/Resources/en.ts +6 -2
  33. package/src/Translation/Resources/tr.ts +6 -2
  34. package/src/version.ts +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Libs/diagnostics.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Libs/diagnostics.ts"],"names":[],"mappings":"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"}
@@ -14,10 +14,19 @@ export interface SendDiagnosticParams {
14
14
  sdkVersion: string;
15
15
  /** base64 images to attach (MRZ side / front / face / NFC chip face). */
16
16
  images?: DiagnosticImage[];
17
+ /**
18
+ * Progress callback for the proof-of-work step (0..1). The PoW solve is the
19
+ * only slow part; the UI uses this to show a progress bar instead of freezing.
20
+ */
21
+ onPowProgress?: (fraction: number) => void;
22
+ /** Polled during the solve; return true to cancel. */
23
+ shouldCancel?: () => boolean;
17
24
  }
18
25
  export interface SendResult {
19
26
  /** True when the report was uploaded and stored. */
20
27
  uploaded: boolean;
28
+ /** True when the user cancelled before completion. */
29
+ cancelled?: boolean;
21
30
  }
22
31
  /**
23
32
  * Build the report and upload it (over HTTPS) to the demo-diagnostics endpoint.
@@ -1 +1 @@
1
- {"version":3,"file":"sendDiagnosticReport.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Libs/sendDiagnosticReport.ts"],"names":[],"mappings":"AAqBA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAI5B,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,iBAAiB,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAKD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,oBAAoB,KAC3B,OAAO,CAAC,UAAU,CAsFpB,CAAC"}
1
+ {"version":3,"file":"sendDiagnosticReport.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Libs/sendDiagnosticReport.ts"],"names":[],"mappings":"AAqBA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAQ5B,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,iBAAiB,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,QAAQ,EAAE,OAAO,CAAC;IAClB,sDAAsD;IACtD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAKD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,oBAAoB,KAC3B,OAAO,CAAC,UAAU,CA2GpB,CAAC"}
@@ -48,6 +48,9 @@ declare const _default: {
48
48
  'diagnosticReport.noticeBody': string;
49
49
  'diagnosticReport.continue': string;
50
50
  'diagnosticReport.cancel': string;
51
+ 'diagnosticReport.sending': string;
52
+ 'diagnosticReport.sendingPreparing': string;
53
+ 'diagnosticReport.sendingProgress': string;
51
54
  'diagnosticReport.sentTitle': string;
52
55
  'diagnosticReport.sentBody': string;
53
56
  'diagnosticReport.failedTitle': string;
@@ -1 +1 @@
1
- {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/en.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAgRE"}
1
+ {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/en.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAoRE"}
@@ -48,6 +48,9 @@ declare const _default: {
48
48
  'diagnosticReport.noticeBody': string;
49
49
  'diagnosticReport.continue': string;
50
50
  'diagnosticReport.cancel': string;
51
+ 'diagnosticReport.sending': string;
52
+ 'diagnosticReport.sendingPreparing': string;
53
+ 'diagnosticReport.sendingProgress': string;
51
54
  'diagnosticReport.sentTitle': string;
52
55
  'diagnosticReport.sentBody': string;
53
56
  'diagnosticReport.failedTitle': string;
@@ -1 +1 @@
1
- {"version":3,"file":"tr.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/tr.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAmRE"}
1
+ {"version":3,"file":"tr.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/tr.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAuRE"}
@@ -1,2 +1,2 @@
1
- export declare const SDK_VERSION = "1.493.1";
1
+ export declare const SDK_VERSION = "1.494.0";
2
2
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trustchex/react-native-sdk",
3
- "version": "1.493.1",
3
+ "version": "1.494.0",
4
4
  "description": "Trustchex mobile app react native SDK for android or ios devices",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -1,5 +1,13 @@
1
- import React, { useState } from 'react';
2
- import { Alert } from 'react-native';
1
+ import React, { useRef, useState } from 'react';
2
+ import {
3
+ ActivityIndicator,
4
+ Alert,
5
+ Modal,
6
+ StyleSheet,
7
+ Text,
8
+ TouchableOpacity,
9
+ View,
10
+ } from 'react-native';
3
11
  import { useTranslation } from 'react-i18next';
4
12
  import StyledButton from './StyledButton';
5
13
  import {
@@ -20,10 +28,11 @@ export interface DiagnosticReportButtonProps {
20
28
  /**
21
29
  * "Report a scanning issue" button for the result screen.
22
30
  *
23
- * Shows a brief notice describing what will be shared (acting as the user's
24
- * consent step), then uploads the diagnostic bundle (technical signals +
25
- * scanned data + captured images) to the backend, encrypted in transit and at
26
- * rest. Operators view and download the reports from the dashboard.
31
+ * Uploads the diagnostic bundle to the backend (proof-of-work authenticated,
32
+ * encrypted in transit + at rest). The PoW solve is the only slow step, so while
33
+ * sending we show a NON-DISMISSIBLE modal overlay with progress the user can't
34
+ * dismiss it accidentally (tap-outside / Android-back do nothing), but can
35
+ * explicitly Cancel, which aborts the solve. A thank-you shows on success.
27
36
  *
28
37
  * Hidden when there's no session/baseUrl to upload to (e.g. before init).
29
38
  */
@@ -35,11 +44,43 @@ const DiagnosticReportButton: React.FC<DiagnosticReportButtonProps> = ({
35
44
  }) => {
36
45
  const { t } = useTranslation();
37
46
  const [sending, setSending] = useState(false);
47
+ const [progress, setProgress] = useState(0);
48
+ // Ref so the running solve sees cancellation immediately (no stale closure).
49
+ const cancelledRef = useRef(false);
38
50
 
39
51
  if (!baseUrl || !sessionId) {
40
52
  return null;
41
53
  }
42
54
 
55
+ const doSend = async () => {
56
+ cancelledRef.current = false;
57
+ setProgress(0);
58
+ setSending(true);
59
+ try {
60
+ const { uploaded, cancelled } = await sendDiagnosticReport({
61
+ baseUrl,
62
+ sessionId,
63
+ scan,
64
+ sdkVersion: SDK_VERSION,
65
+ images,
66
+ onPowProgress: setProgress,
67
+ shouldCancel: () => cancelledRef.current,
68
+ });
69
+ setSending(false);
70
+ if (cancelled) return; // user cancelled — no alert
71
+ Alert.alert(
72
+ uploaded
73
+ ? t('diagnosticReport.sentTitle')
74
+ : t('diagnosticReport.failedTitle'),
75
+ uploaded
76
+ ? t('diagnosticReport.sentBody')
77
+ : t('diagnosticReport.failedBody')
78
+ );
79
+ } catch {
80
+ setSending(false);
81
+ }
82
+ };
83
+
43
84
  const onPress = () => {
44
85
  if (sending) return;
45
86
  Alert.alert(
@@ -47,40 +88,93 @@ const DiagnosticReportButton: React.FC<DiagnosticReportButtonProps> = ({
47
88
  t('diagnosticReport.noticeBody'),
48
89
  [
49
90
  { text: t('diagnosticReport.cancel'), style: 'cancel' },
50
- {
51
- text: t('diagnosticReport.continue'),
52
- onPress: async () => {
53
- setSending(true);
54
- try {
55
- const { uploaded } = await sendDiagnosticReport({
56
- baseUrl,
57
- sessionId,
58
- scan,
59
- sdkVersion: SDK_VERSION,
60
- images,
61
- });
62
- Alert.alert(
63
- uploaded
64
- ? t('diagnosticReport.sentTitle')
65
- : t('diagnosticReport.failedTitle'),
66
- uploaded
67
- ? t('diagnosticReport.sentBody')
68
- : t('diagnosticReport.failedBody')
69
- );
70
- } finally {
71
- setSending(false);
72
- }
73
- },
74
- },
91
+ { text: t('diagnosticReport.continue'), onPress: doSend },
75
92
  ]
76
93
  );
77
94
  };
78
95
 
96
+ const pct = Math.round(progress * 100);
97
+
79
98
  return (
80
- <StyledButton mode="outlined" onPress={onPress} disabled={sending}>
81
- {t('diagnosticReport.button')}
82
- </StyledButton>
99
+ <>
100
+ <StyledButton mode="outlined" onPress={onPress}>
101
+ {t('diagnosticReport.button')}
102
+ </StyledButton>
103
+
104
+ <Modal
105
+ visible={sending}
106
+ transparent
107
+ animationType="fade"
108
+ // Block accidental dismissal: Android hardware-back does nothing while
109
+ // sending. There is no tap-outside-to-close (we render our own backdrop).
110
+ onRequestClose={() => {}}
111
+ >
112
+ <View style={styles.backdrop}>
113
+ <View style={styles.card}>
114
+ <ActivityIndicator size="large" />
115
+ <Text style={styles.title}>{t('diagnosticReport.sending')}</Text>
116
+ <Text style={styles.progress}>
117
+ {pct > 0
118
+ ? t('diagnosticReport.sendingProgress', { pct })
119
+ : t('diagnosticReport.sendingPreparing')}
120
+ </Text>
121
+ <TouchableOpacity
122
+ accessibilityRole="button"
123
+ style={styles.cancelBtn}
124
+ onPress={() => {
125
+ cancelledRef.current = true;
126
+ }}
127
+ >
128
+ <Text style={styles.cancelText}>
129
+ {t('diagnosticReport.cancel')}
130
+ </Text>
131
+ </TouchableOpacity>
132
+ </View>
133
+ </View>
134
+ </Modal>
135
+ </>
83
136
  );
84
137
  };
85
138
 
139
+ const styles = StyleSheet.create({
140
+ backdrop: {
141
+ flex: 1,
142
+ backgroundColor: 'rgba(0,0,0,0.45)',
143
+ alignItems: 'center',
144
+ justifyContent: 'center',
145
+ padding: 24,
146
+ },
147
+ card: {
148
+ width: '100%',
149
+ maxWidth: 320,
150
+ backgroundColor: 'white',
151
+ borderRadius: 16,
152
+ paddingVertical: 24,
153
+ paddingHorizontal: 20,
154
+ alignItems: 'center',
155
+ gap: 12,
156
+ },
157
+ title: {
158
+ fontSize: 16,
159
+ fontWeight: '600',
160
+ color: '#111827',
161
+ textAlign: 'center',
162
+ },
163
+ progress: {
164
+ fontSize: 14,
165
+ color: '#6B7280',
166
+ textAlign: 'center',
167
+ },
168
+ cancelBtn: {
169
+ marginTop: 8,
170
+ paddingVertical: 10,
171
+ paddingHorizontal: 24,
172
+ },
173
+ cancelText: {
174
+ fontSize: 15,
175
+ fontWeight: '600',
176
+ color: '#DC2626',
177
+ },
178
+ });
179
+
86
180
  export default DiagnosticReportButton;
@@ -16,6 +16,7 @@ import { useTranslation } from 'react-i18next';
16
16
  import i18n from '../../Translation';
17
17
  import StyledButton from './StyledButton';
18
18
  import { analyticsService } from '../Services/AnalyticsService';
19
+ import { diagnostics } from '../Libs/diagnostics';
19
20
 
20
21
  // Navigation lock - use a ref-like pattern so unmounting resets state.
21
22
  // The module-level object is shared across all instances but the
@@ -87,12 +88,23 @@ const NavigationManager = forwardRef(
87
88
  ? workflowSteps[currentStepIndex + 1]
88
89
  : null;
89
90
 
91
+ // Diagnostics: advancing past the current step means it completed. Record
92
+ // its outcome (success — the user moved on) and start-time the next step,
93
+ // so the report captures the WHOLE flow, not just the NFC read.
94
+ const now = Date.now();
95
+ if (currentWorkFlowStep?.type) {
96
+ diagnostics.stepEnded(currentWorkFlowStep.type, 'success', now);
97
+ }
98
+
90
99
  if (!nextStep) {
91
100
  appContext.currentWorkflowStep = undefined;
92
101
  return routes.RESULT;
93
102
  }
94
103
 
95
104
  appContext.currentWorkflowStep = nextStep;
105
+ if (nextStep.type) {
106
+ diagnostics.stepStarted(nextStep.type, now);
107
+ }
96
108
 
97
109
  if (nextStep.type === 'CONTRACT_ACCEPTANCE') {
98
110
  return routes.DYNAMIC_ROUTES.CONTRACT_ACCEPTANCE;
@@ -34,9 +34,11 @@ const leadingZeroBits = (bytes: Uint8Array): number => {
34
34
  };
35
35
 
36
36
  /**
37
- * Solve the PoW for a challenge. Returns the solution nonce as a string.
38
- * Bounded by `maxIterations` so a pathological difficulty can't hang the app;
39
- * returns null if unsolved within the bound.
37
+ * Solve the PoW for a challenge (synchronous). Returns the solution nonce as a
38
+ * string. Bounded by `maxIterations` so a pathological difficulty can't hang the
39
+ * app; returns null if unsolved within the bound.
40
+ *
41
+ * Prefer `solvePowAsync` on the UI thread — this blocks until solved.
40
42
  */
41
43
  export const solvePow = (
42
44
  challenge: string,
@@ -52,3 +54,69 @@ export const solvePow = (
52
54
  }
53
55
  return null;
54
56
  };
57
+
58
+ /** Thrown by solvePowAsync / sendDiagnosticReport when the user cancels. */
59
+ export class PowCancelledError extends Error {
60
+ constructor() {
61
+ super('Proof-of-work cancelled');
62
+ this.name = 'PowCancelledError';
63
+ }
64
+ }
65
+
66
+ export interface SolvePowAsyncOptions {
67
+ onProgress?: (fraction: number) => void;
68
+ /** Polled between chunks; return true to abort. Throws PowCancelledError. */
69
+ shouldCancel?: () => boolean;
70
+ maxIterations?: number;
71
+ chunkSize?: number;
72
+ }
73
+
74
+ /**
75
+ * Async, UI-friendly PoW solver. Hashes in small chunks and yields to the event
76
+ * loop between chunks so the JS thread (and the UI) stays responsive instead of
77
+ * freezing during the solve. Reports coarse progress (0..1, best-effort against
78
+ * the expected work for the difficulty) via `onProgress`, and aborts (throwing
79
+ * PowCancelledError) when `shouldCancel` returns true.
80
+ *
81
+ * Returns the solution nonce, or null if unsolved within `maxIterations`.
82
+ */
83
+ export const solvePowAsync = async (
84
+ challenge: string,
85
+ difficulty: number,
86
+ options: SolvePowAsyncOptions = {}
87
+ ): Promise<string | null> => {
88
+ const {
89
+ onProgress,
90
+ shouldCancel,
91
+ maxIterations = 8_000_000,
92
+ // Hash a large chunk between yields: the per-yield setTimeout(0) round-trip
93
+ // on Hermes costs ~ms, so yielding every 2k hashes dominated wall-clock.
94
+ // 25k keeps the UI responsive + cancel snappy (a chunk is a few tens of ms)
95
+ // while spending almost all time hashing, not bouncing through the event loop.
96
+ chunkSize = 25_000,
97
+ } = options;
98
+ // Expected hashes ≈ 2^difficulty; used only to render a smooth-ish bar.
99
+ const expected = Math.max(1, Math.pow(2, difficulty));
100
+ // Encode the fixed "challenge." prefix once; per iteration we only encode the
101
+ // small varying nonce and concat, instead of re-UTF-8-encoding the whole
102
+ // string (the challenge is 32+ chars) on every hash.
103
+ const prefix = Buffer.from(`${challenge}.`, 'utf-8');
104
+ for (let i = 0; i < maxIterations; i++) {
105
+ const digest = sha256(
106
+ Buffer.concat([prefix, Buffer.from(i.toString(), 'utf-8')])
107
+ );
108
+ if (leadingZeroBits(digest) >= difficulty) {
109
+ onProgress?.(1);
110
+ return i.toString();
111
+ }
112
+ if (i % chunkSize === chunkSize - 1) {
113
+ if (shouldCancel?.()) {
114
+ throw new PowCancelledError();
115
+ }
116
+ onProgress?.(Math.min(0.99, i / expected));
117
+ // Yield so React can paint the progress and the UI stays responsive.
118
+ await new Promise<void>((resolve) => setTimeout(resolve, 0));
119
+ }
120
+ }
121
+ return null;
122
+ };
@@ -155,15 +155,23 @@ export const buildDiagnosticReport = (params: {
155
155
  // Set the Turkish-letter-prefix flag from the final accepted MRZ (the collector
156
156
  // can't know it during scanning; the report does, from the MRZ text).
157
157
  const turkishLetterPrefixApplied =
158
- diag.mrz.turkishLetterPrefixApplied || computed?.turkishLetterPrefix === true;
158
+ diag.mrz.turkishLetterPrefixApplied ||
159
+ computed?.turkishLetterPrefix === true;
159
160
 
160
161
  // --- diagnostics.json (technical, low-PII) ---
161
162
  const diagnosticsObj = {
162
163
  schemaVersion: diag.schemaVersion,
163
164
  generatedAt: diag.generatedAt,
164
165
  sessionId: sessionId ?? null,
165
- sdk: { platform: device.platform, sdkVersion, buildNumber: device.buildNumber },
166
+ sdk: {
167
+ platform: device.platform,
168
+ sdkVersion,
169
+ buildNumber: device.buildNumber,
170
+ },
166
171
  device,
172
+ // Whole-flow, per-step record (contract → document → eID → liveness →
173
+ // consent → video). Diagnostics are NOT just the NFC read.
174
+ steps: diag.steps,
167
175
  scan: {
168
176
  documentType: scan.documentType ?? diag.documentType ?? null,
169
177
  dataSource: scan.dataSource ?? diag.dataSource ?? null,
@@ -208,11 +216,18 @@ export const buildDiagnosticReport = (params: {
208
216
  .join(' ')
209
217
  : 'n/a';
210
218
 
219
+ // Compact per-step line, e.g. "Steps: CONTRACT_ACCEPTANCE:success
220
+ // IDENTITY_DOCUMENT_SCAN:success LIVENESS_CHECK:failed".
221
+ const stepsLine = diag.steps.length
222
+ ? diag.steps.map((s) => `${s.step}:${s.outcome}`).join(' ')
223
+ : 'none recorded';
224
+
211
225
  const body = [
212
226
  'TRUSTCHEX SCAN REPORT',
213
227
  `Device: ${device.brand} ${device.model} · ${device.systemName} ${device.systemVersion}${device.apiLevel ? ` (API ${device.apiLevel})` : ''}`,
214
228
  `SDK: ${sdkVersion} (build ${device.buildNumber}) · ${device.manufacturer} · ${device.deviceId}`,
215
229
  `Document: ${scanDataObj.documentType ?? 'unknown'} · source: ${scanDataObj.dataSource ?? 'unknown'}`,
230
+ `Steps: ${stepsLine}`,
216
231
  `MRZ: ${diag.mrz.reachedStable ? 'stable' : 'NOT stable'} after ${diag.mrz.framesProcessed} frames · checks: ${checksLine}`,
217
232
  `Camera: ${diag.camera.frames} frames · brightness ${diag.camera.avgBrightness} (low:${yesNo(diag.camera.hadLowBrightness)} blur:${yesNo(diag.camera.hadBlurryFrames)})`,
218
233
  `NFC: ${nfcLine}`,
@@ -231,7 +246,10 @@ export const buildDiagnosticReport = (params: {
231
246
  return {
232
247
  diagnosticsJson: JSON.stringify(diagnosticsObj, null, 2),
233
248
  scanDataJson: JSON.stringify(scanDataObj, null, 2),
234
- body: body.length > MAX_BODY_BYTES ? body.slice(0, MAX_BODY_BYTES - 1) + '…' : body,
249
+ body:
250
+ body.length > MAX_BODY_BYTES
251
+ ? body.slice(0, MAX_BODY_BYTES - 1) + '…'
252
+ : body,
235
253
  subject,
236
254
  };
237
255
  };
@@ -95,12 +95,33 @@ export interface MrzDiagnostics {
95
95
  turkishLetterPrefixApplied: boolean;
96
96
  }
97
97
 
98
+ /**
99
+ * A single workflow step's outcome. Diagnostics are NOT just about NFC — every
100
+ * step of the verification flow (contract, document scan, eID, liveness, verbal
101
+ * consent, video call) records one of these so a support/AI reader sees the
102
+ * whole journey: what ran, how long it took, whether it succeeded, and a small
103
+ * non-PII `details` bag specific to the step.
104
+ */
105
+ export interface WorkflowStepRecord {
106
+ /** Step type, e.g. 'CONTRACT_ACCEPTANCE' | 'IDENTITY_DOCUMENT_SCAN' |
107
+ * 'IDENTITY_DOCUMENT_EID_SCAN' | 'LIVENESS_CHECK' | 'VERBAL_CONSENT' |
108
+ * 'VIDEO_CALL'. */
109
+ step: string;
110
+ /** 'success' | 'failed' | 'skipped' | 'cancelled' | 'started' (still running). */
111
+ outcome: 'success' | 'failed' | 'skipped' | 'cancelled' | 'started';
112
+ durationMs: number;
113
+ /** Coarse, non-PII per-step signals (counts, flags, retries — never PII). */
114
+ details?: Record<string, string | number | boolean | null>;
115
+ }
116
+
98
117
  export interface DiagnosticsSnapshot {
99
118
  schemaVersion: number;
100
119
  generatedAt: string;
101
120
  documentType?: string;
102
121
  dataSource?: string;
103
122
  totalDurationMs: number;
123
+ /** Per-step record of the WHOLE flow, in the order steps started. */
124
+ steps: WorkflowStepRecord[];
104
125
  camera: CameraDiagnostics;
105
126
  mrz: MrzDiagnostics;
106
127
  nfc: NfcDiagnostics;
@@ -149,9 +170,28 @@ export class DiagnosticsCollector {
149
170
  private mrz = emptyMrz();
150
171
  private nfc = emptyNfc();
151
172
  private errors: DiagnosticsSnapshot['errors'] = [];
173
+ private steps: WorkflowStepRecord[] = [];
174
+ // Open steps keyed by step name → start time, for duration on stepEnded.
175
+ private openSteps = new Map<string, number>();
152
176
 
153
- /** Begin a fresh session — call when a scan flow starts. */
177
+ /**
178
+ * Reset the per-SCAN signals (camera/MRZ/NFC) — called when a scan flow
179
+ * starts. Deliberately PRESERVES the workflow `steps` log and accumulated
180
+ * errors: those span the whole verification journey (contract → … → result),
181
+ * and a scan starting mid-flow must not erase steps already completed. Use
182
+ * `resetSession` to clear everything for a brand-new session.
183
+ */
154
184
  reset(nowMs: number): void {
185
+ if (!this.startedAtMs) this.startedAtMs = nowMs;
186
+ this.documentType = undefined;
187
+ this.dataSource = undefined;
188
+ this.camera = emptyCamera();
189
+ this.mrz = emptyMrz();
190
+ this.nfc = emptyNfc();
191
+ }
192
+
193
+ /** Clear the ENTIRE session (steps, errors, timings) — new verification. */
194
+ resetSession(nowMs: number): void {
155
195
  this.startedAtMs = nowMs;
156
196
  this.documentType = undefined;
157
197
  this.dataSource = undefined;
@@ -159,6 +199,8 @@ export class DiagnosticsCollector {
159
199
  this.mrz = emptyMrz();
160
200
  this.nfc = emptyNfc();
161
201
  this.errors = [];
202
+ this.steps = [];
203
+ this.openSteps.clear();
162
204
  }
163
205
 
164
206
  setDocument(documentType?: string, dataSource?: string): void {
@@ -166,6 +208,35 @@ export class DiagnosticsCollector {
166
208
  if (dataSource) this.dataSource = dataSource;
167
209
  }
168
210
 
211
+ // ---- Workflow steps (whole-flow, not just NFC) ----
212
+
213
+ /** Mark a workflow step as started (records start time for duration). */
214
+ stepStarted(step: string, nowMs: number): void {
215
+ this.openSteps.set(step, nowMs);
216
+ }
217
+
218
+ /**
219
+ * Record a step's outcome. Pairs with `stepStarted` for duration; if the step
220
+ * was never started, durationMs is 0. `details` is a small non-PII bag of
221
+ * step-specific signals (counts, flags, retries).
222
+ */
223
+ stepEnded(
224
+ step: string,
225
+ outcome: WorkflowStepRecord['outcome'],
226
+ nowMs: number,
227
+ details?: WorkflowStepRecord['details']
228
+ ): void {
229
+ const startedAt = this.openSteps.get(step);
230
+ this.openSteps.delete(step);
231
+ this.steps.push({
232
+ step,
233
+ outcome,
234
+ // `startedAt` can legitimately be 0 (session t0), so check for undefined.
235
+ durationMs: startedAt !== undefined ? Math.max(0, nowMs - startedAt) : 0,
236
+ details: details && Object.keys(details).length ? details : undefined,
237
+ });
238
+ }
239
+
169
240
  /** Merge a camera snapshot (called periodically as frames arrive). */
170
241
  setCamera(c: Partial<CameraDiagnostics>): void {
171
242
  this.camera = { ...this.camera, ...c };
@@ -256,13 +327,17 @@ export class DiagnosticsCollector {
256
327
  /** Build the serialisable snapshot. */
257
328
  snapshot(nowMs: number, isoNow: string): DiagnosticsSnapshot {
258
329
  return {
259
- schemaVersion: 1,
330
+ schemaVersion: 2,
260
331
  generatedAt: isoNow,
261
332
  documentType: this.documentType,
262
333
  dataSource: this.dataSource,
263
334
  totalDurationMs: this.startedAtMs
264
335
  ? Math.max(0, nowMs - this.startedAtMs)
265
336
  : 0,
337
+ steps: this.steps.map((s) => ({
338
+ ...s,
339
+ details: s.details ? { ...s.details } : undefined,
340
+ })),
266
341
  camera: { ...this.camera },
267
342
  mrz: { ...this.mrz },
268
343
  nfc: { ...this.nfc, steps: this.nfc.steps.map((s) => ({ ...s })) },
@@ -23,7 +23,11 @@ import {
23
23
  buildDiagnosticReport,
24
24
  type ScanDataForReport,
25
25
  } from './diagnosticReport';
26
- import { solvePow, type PowChallenge } from './diagnosticPow';
26
+ import {
27
+ solvePowAsync,
28
+ PowCancelledError,
29
+ type PowChallenge,
30
+ } from './diagnosticPow';
27
31
  import { logError } from './debug.utils';
28
32
 
29
33
  export interface DiagnosticImage {
@@ -42,11 +46,20 @@ export interface SendDiagnosticParams {
42
46
  sdkVersion: string;
43
47
  /** base64 images to attach (MRZ side / front / face / NFC chip face). */
44
48
  images?: DiagnosticImage[];
49
+ /**
50
+ * Progress callback for the proof-of-work step (0..1). The PoW solve is the
51
+ * only slow part; the UI uses this to show a progress bar instead of freezing.
52
+ */
53
+ onPowProgress?: (fraction: number) => void;
54
+ /** Polled during the solve; return true to cancel. */
55
+ shouldCancel?: () => boolean;
45
56
  }
46
57
 
47
58
  export interface SendResult {
48
59
  /** True when the report was uploaded and stored. */
49
60
  uploaded: boolean;
61
+ /** True when the user cancelled before completion. */
62
+ cancelled?: boolean;
50
63
  }
51
64
 
52
65
  const mimeToType = (mime: 'jpg' | 'png'): string =>
@@ -60,7 +73,15 @@ const mimeToType = (mime: 'jpg' | 'png'): string =>
60
73
  export const sendDiagnosticReport = async (
61
74
  params: SendDiagnosticParams
62
75
  ): Promise<SendResult> => {
63
- const { baseUrl, sessionId, scan, sdkVersion, images = [] } = params;
76
+ const {
77
+ baseUrl,
78
+ sessionId,
79
+ scan,
80
+ sdkVersion,
81
+ images = [],
82
+ onPowProgress,
83
+ shouldCancel,
84
+ } = params;
64
85
  if (!baseUrl) {
65
86
  return { uploaded: false };
66
87
  }
@@ -121,10 +142,20 @@ export const sendDiagnosticReport = async (
121
142
  return { uploaded: false };
122
143
  }
123
144
  const challenge = (await challengeRes.json()) as PowChallenge;
124
- const solution = solvePow(challenge.challenge, challenge.difficulty);
145
+ const solution = await solvePowAsync(
146
+ challenge.challenge,
147
+ challenge.difficulty,
148
+ {
149
+ onProgress: onPowProgress,
150
+ shouldCancel,
151
+ }
152
+ );
125
153
  if (!solution) {
126
154
  return { uploaded: false };
127
155
  }
156
+ if (shouldCancel?.()) {
157
+ return { uploaded: false, cancelled: true };
158
+ }
128
159
 
129
160
  const response = await fetch(`${baseUrl}/api/app/mobile/diagnostics`, {
130
161
  method: 'POST',
@@ -138,6 +169,9 @@ export const sendDiagnosticReport = async (
138
169
 
139
170
  return { uploaded: response.ok };
140
171
  } catch (e) {
172
+ if (e instanceof PowCancelledError) {
173
+ return { uploaded: false, cancelled: true };
174
+ }
141
175
  logError(
142
176
  'Diagnostics',
143
177
  'Failed to upload diagnostic report',