datakeen-session-react 1.1.199-rc.4 → 1.1.200-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/cjs/components/document-collection/DocumentCollection.js +2 -2
- package/dist/cjs/components/document-collection/DocumentError.js +4 -1
- package/dist/cjs/components/id-check/Photo.js +42 -36
- package/dist/cjs/components/jdi/JDIError.js +4 -1
- package/dist/cjs/components/session/DocumentCheck.js +2 -2
- package/dist/cjs/i18n/en.json.js +10 -0
- package/dist/cjs/i18n/es.json.js +10 -0
- package/dist/cjs/i18n/fr.json.js +10 -0
- package/dist/cjs/index.css.js +1 -1
- package/dist/cjs/services/cameraService.js +44 -23
- package/dist/cjs/services/sessionCredentials.js +2 -1
- package/dist/cjs/services/sessionService.js +16 -6
- package/dist/cjs/utils/cameraResolution.js +12 -0
- package/dist/esm/components/document-collection/DocumentCollection.js +2 -2
- package/dist/esm/components/document-collection/DocumentError.js +4 -1
- package/dist/esm/components/id-check/Photo.js +42 -36
- package/dist/esm/components/jdi/JDIError.js +4 -1
- package/dist/esm/components/session/DocumentCheck.js +2 -2
- package/dist/esm/i18n/en.json.js +10 -0
- package/dist/esm/i18n/es.json.js +10 -0
- package/dist/esm/i18n/fr.json.js +10 -0
- package/dist/esm/index.css.js +1 -1
- package/dist/esm/services/cameraService.js +44 -23
- package/dist/esm/services/sessionCredentials.js +2 -1
- package/dist/esm/services/sessionService.js +16 -7
- package/dist/esm/utils/cameraResolution.js +9 -0
- package/package.json +3 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var tslib_es6 = require('../node_modules/tslib/tslib.es6.js');
|
|
4
|
+
var cameraResolution = require('../utils/cameraResolution.js');
|
|
4
5
|
var professionalImageQuality = require('./professionalImageQuality.js');
|
|
5
6
|
|
|
6
7
|
// Permit to enable development mode to test without a camera
|
|
@@ -123,18 +124,18 @@ var CameraService = /** @class */ (function () {
|
|
|
123
124
|
};
|
|
124
125
|
CameraService.prototype._doStartCamera = function (videoElement, options) {
|
|
125
126
|
return tslib_es6.__awaiter(this, void 0, void 0, function () {
|
|
126
|
-
var facingMode, preferImageCapture, desiredWidth, desiredHeight, videoConstraints, stream, err_2, errorMessage;
|
|
127
|
-
var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
128
|
-
return tslib_es6.__generator(this, function (
|
|
129
|
-
switch (
|
|
127
|
+
var facingMode, preferImageCapture, desiredWidth, desiredHeight, videoConstraints, stream, retriedWithHd, err_2, errorMessage;
|
|
128
|
+
var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
129
|
+
return tslib_es6.__generator(this, function (_t) {
|
|
130
|
+
switch (_t.label) {
|
|
130
131
|
case 0:
|
|
131
132
|
facingMode = (_b = options.facingMode) !== null && _b !== void 0 ? _b : "environment";
|
|
132
133
|
preferImageCapture = (_c = options.preferImageCapture) !== null && _c !== void 0 ? _c : true;
|
|
133
134
|
desiredWidth = (_e = (_d = options.idealResolution) === null || _d === void 0 ? void 0 : _d.width) !== null && _e !== void 0 ? _e : DEFAULT_RESOLUTION.width;
|
|
134
135
|
desiredHeight = (_g = (_f = options.idealResolution) === null || _f === void 0 ? void 0 : _f.height) !== null && _g !== void 0 ? _g : DEFAULT_RESOLUTION.height;
|
|
135
|
-
|
|
136
|
+
_t.label = 1;
|
|
136
137
|
case 1:
|
|
137
|
-
|
|
138
|
+
_t.trys.push([1, 10, , 11]);
|
|
138
139
|
// Close any existing stream before opening a new one
|
|
139
140
|
if (this.stream) {
|
|
140
141
|
this.stream.getTracks().forEach(function (t) { return t.stop(); });
|
|
@@ -157,28 +158,45 @@ var CameraService = /** @class */ (function () {
|
|
|
157
158
|
video: videoConstraints,
|
|
158
159
|
})];
|
|
159
160
|
case 2:
|
|
160
|
-
stream =
|
|
161
|
+
stream = _t.sent();
|
|
161
162
|
this.stream = stream;
|
|
162
163
|
this.videoTrack = (_j = stream.getVideoTracks()[0]) !== null && _j !== void 0 ? _j : null;
|
|
163
164
|
this.lastTrackSettings = (_l = (_k = this.videoTrack) === null || _k === void 0 ? void 0 : _k.getSettings()) !== null && _l !== void 0 ? _l : null;
|
|
165
|
+
retriedWithHd = false;
|
|
166
|
+
if (!(cameraResolution.isBelowHd((_m = this.lastTrackSettings) !== null && _m !== void 0 ? _m : undefined) && desiredWidth / desiredHeight !== 16 / 9)) return [3 /*break*/, 4];
|
|
167
|
+
retriedWithHd = true;
|
|
168
|
+
stream.getTracks().forEach(function (track) { return track.stop(); });
|
|
169
|
+
this.stream = null;
|
|
170
|
+
this.videoTrack = null;
|
|
171
|
+
this.lastTrackSettings = null;
|
|
172
|
+
return [4 /*yield*/, navigator.mediaDevices.getUserMedia({
|
|
173
|
+
video: tslib_es6.__assign(tslib_es6.__assign({}, videoConstraints), { width: { ideal: 1920 }, height: { ideal: 1080 } }),
|
|
174
|
+
})];
|
|
175
|
+
case 3:
|
|
176
|
+
stream = _t.sent();
|
|
177
|
+
this.stream = stream;
|
|
178
|
+
this.videoTrack = (_o = stream.getVideoTracks()[0]) !== null && _o !== void 0 ? _o : null;
|
|
179
|
+
this.lastTrackSettings = (_q = (_p = this.videoTrack) === null || _p === void 0 ? void 0 : _p.getSettings()) !== null && _q !== void 0 ? _q : null;
|
|
180
|
+
_t.label = 4;
|
|
181
|
+
case 4:
|
|
164
182
|
console.debug("[CameraService] Camera opened", {
|
|
165
183
|
desired: { width: desiredWidth, height: desiredHeight },
|
|
166
184
|
actual: this.lastTrackSettings,
|
|
167
185
|
});
|
|
168
|
-
if (!this.videoTrack) return [3 /*break*/,
|
|
169
|
-
|
|
170
|
-
case
|
|
171
|
-
|
|
186
|
+
if (!this.videoTrack) return [3 /*break*/, 8];
|
|
187
|
+
_t.label = 5;
|
|
188
|
+
case 5:
|
|
189
|
+
_t.trys.push([5, 7, , 8]);
|
|
172
190
|
return [4 /*yield*/, this.videoTrack.applyConstraints({
|
|
173
191
|
advanced: [{ focusMode: "continuous" }],
|
|
174
192
|
})];
|
|
175
|
-
case 4:
|
|
176
|
-
_o.sent();
|
|
177
|
-
return [3 /*break*/, 6];
|
|
178
|
-
case 5:
|
|
179
|
-
_o.sent();
|
|
180
|
-
return [3 /*break*/, 6];
|
|
181
193
|
case 6:
|
|
194
|
+
_t.sent();
|
|
195
|
+
return [3 /*break*/, 8];
|
|
196
|
+
case 7:
|
|
197
|
+
_t.sent();
|
|
198
|
+
return [3 /*break*/, 8];
|
|
199
|
+
case 8:
|
|
182
200
|
if (supportsImageCapture() && this.videoTrack && preferImageCapture) {
|
|
183
201
|
try {
|
|
184
202
|
this.imageCapture = new ImageCapture(this.videoTrack);
|
|
@@ -196,15 +214,18 @@ var CameraService = /** @class */ (function () {
|
|
|
196
214
|
}
|
|
197
215
|
videoElement.srcObject = stream;
|
|
198
216
|
return [4 /*yield*/, this.waitForVideoReady(videoElement)];
|
|
199
|
-
case
|
|
200
|
-
|
|
217
|
+
case 9:
|
|
218
|
+
_t.sent();
|
|
201
219
|
return [2 /*return*/, {
|
|
202
220
|
success: true,
|
|
203
|
-
settings: (
|
|
221
|
+
settings: (_r = this.lastTrackSettings) !== null && _r !== void 0 ? _r : undefined,
|
|
204
222
|
captureMethod: this.captureMethod,
|
|
223
|
+
requested: { w: desiredWidth, h: desiredHeight },
|
|
224
|
+
retriedWithHd: retriedWithHd,
|
|
225
|
+
label: (_s = this.videoTrack) === null || _s === void 0 ? void 0 : _s.label,
|
|
205
226
|
}];
|
|
206
|
-
case
|
|
207
|
-
err_2 =
|
|
227
|
+
case 10:
|
|
228
|
+
err_2 = _t.sent();
|
|
208
229
|
console.error("Erreur lors de l'accès à la caméra:", err_2);
|
|
209
230
|
errorMessage = "Une erreur inconnue s'est produite lors de l'accès à la caméra.";
|
|
210
231
|
if (err_2 instanceof DOMException) {
|
|
@@ -220,7 +241,7 @@ var CameraService = /** @class */ (function () {
|
|
|
220
241
|
}
|
|
221
242
|
}
|
|
222
243
|
return [2 /*return*/, { success: false, error: errorMessage }];
|
|
223
|
-
case
|
|
244
|
+
case 11: return [2 /*return*/];
|
|
224
245
|
}
|
|
225
246
|
});
|
|
226
247
|
});
|
|
@@ -14,7 +14,8 @@ function getSessionCredential(sessionId) {
|
|
|
14
14
|
function credentialForRequest(url) {
|
|
15
15
|
if (/^[a-z]+:/i.test(url) || url.startsWith('//'))
|
|
16
16
|
return undefined;
|
|
17
|
-
|
|
17
|
+
// Routes SDK et biométrie Unissey : toutes adressent une seule session par son id.
|
|
18
|
+
var match = url.match(/^\/?session\/(?:sdk|unissey)\/([^/?]+)/);
|
|
18
19
|
return match ? credentials.get(decodeURIComponent(match[1])) : undefined;
|
|
19
20
|
}
|
|
20
21
|
|
|
@@ -5,12 +5,21 @@ var api = require('./api.js');
|
|
|
5
5
|
var auditTrailService = require('./auditTrailService.js');
|
|
6
6
|
var sessionMemoryStore = require('./sessionMemoryStore.js');
|
|
7
7
|
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
/** Reports camera diagnostics without updating local session state. */
|
|
9
|
+
function reportCaptureDiagnostics(sessionId, nodeId, diag) {
|
|
10
|
+
return tslib_es6.__awaiter(this, void 0, void 0, function () {
|
|
11
|
+
var _a;
|
|
12
|
+
return tslib_es6.__generator(this, function (_b) {
|
|
13
|
+
switch (_b.label) {
|
|
14
|
+
case 0: return [4 /*yield*/, api.apiService.patch("/session/sdk/".concat(sessionId), { captureDiagnostics: (_a = {}, _a[nodeId] = diag, _a) })];
|
|
15
|
+
case 1:
|
|
16
|
+
_b.sent();
|
|
17
|
+
return [2 /*return*/];
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** Fetches session data by ID from the Datakeen backend. */
|
|
14
23
|
var fetchSessionById = function (sessionId) { return tslib_es6.__awaiter(void 0, void 0, void 0, function () {
|
|
15
24
|
var response, error_1;
|
|
16
25
|
return tslib_es6.__generator(this, function (_a) {
|
|
@@ -518,6 +527,7 @@ exports.getRunPrefillData = getRunPrefillData;
|
|
|
518
527
|
exports.hasOutgoingEdgeForHandle = hasOutgoingEdgeForHandle;
|
|
519
528
|
exports.isSessionExpired = isSessionExpired;
|
|
520
529
|
exports.reconstructHistoryToStep = reconstructHistoryToStep;
|
|
530
|
+
exports.reportCaptureDiagnostics = reportCaptureDiagnostics;
|
|
521
531
|
exports.reportSelfieFailure = reportSelfieFailure;
|
|
522
532
|
exports.resetNfcScan = resetNfcScan;
|
|
523
533
|
exports.sendClientInfo = sendClientInfo;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var HD_MIN = { long: 1280, short: 720 };
|
|
4
|
+
function isBelowHd(settings) {
|
|
5
|
+
var _a = settings !== null && settings !== void 0 ? settings : {}, width = _a.width, height = _a.height;
|
|
6
|
+
if (!width || !height)
|
|
7
|
+
return false;
|
|
8
|
+
return Math.max(width, height) < HD_MIN.long || Math.min(width, height) < HD_MIN.short;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
exports.HD_MIN = HD_MIN;
|
|
12
|
+
exports.isBelowHd = isBelowHd;
|
|
@@ -304,7 +304,7 @@ var DocumentCollection = function (_a) {
|
|
|
304
304
|
case "before-recto":
|
|
305
305
|
return jsx(BeforePhoto, { setStep: function () { return setPhotoSubStep("recto"); }, isSingleSided: !requiresTwoSides_1, onBack: handleBack });
|
|
306
306
|
case "recto":
|
|
307
|
-
return jsx(Photo, { onCapture: function (img) { return handlePhotoCapture(img, "recto"); }, automaticCapture: false, maskType: "document" });
|
|
307
|
+
return jsx(Photo, { sessionId: sessionId, nodeId: node.id, onCapture: function (img) { return handlePhotoCapture(img, "recto"); }, automaticCapture: false, maskType: "document" });
|
|
308
308
|
case "preview-recto":
|
|
309
309
|
return (jsx(PhotoPreview, { imageUrl: capturedImages.recto, side: "recto", isSingleSided: !requiresTwoSides_1, onValidate: function () { return setPhotoSubStep(requiresTwoSides_1 ? "before-verso" : "confirmation"); }, onRetake: function () {
|
|
310
310
|
setCapturedImages(function (prev) { var next = __assign({}, prev); delete next.recto; return next; });
|
|
@@ -313,7 +313,7 @@ var DocumentCollection = function (_a) {
|
|
|
313
313
|
case "before-verso":
|
|
314
314
|
return jsx(BeforeVersoPhoto, { setStep: function () { return setPhotoSubStep("verso"); } });
|
|
315
315
|
case "verso":
|
|
316
|
-
return jsx(Photo, { onCapture: function (img) { return handlePhotoCapture(img, "verso"); }, automaticCapture: false, maskType: "document" });
|
|
316
|
+
return jsx(Photo, { sessionId: sessionId, nodeId: node.id, onCapture: function (img) { return handlePhotoCapture(img, "verso"); }, automaticCapture: false, maskType: "document" });
|
|
317
317
|
case "preview-verso":
|
|
318
318
|
return (jsx(PhotoPreview, { imageUrl: capturedImages.verso, side: "verso", onValidate: function () { return setPhotoSubStep("confirmation"); }, onRetake: function () {
|
|
319
319
|
setCapturedImages(function (prev) { var next = __assign({}, prev); delete next.verso; return next; });
|
|
@@ -10,6 +10,9 @@ import { isServiceErrorCode } from '../../utils/analysisErrors.js';
|
|
|
10
10
|
var DocumentError = function (_a) {
|
|
11
11
|
var documentType = _a.documentType, documentTypeLabel = _a.documentTypeLabel, onRetry = _a.onRetry, onContinueAnyway = _a.onContinueAnyway, onBackToUserInput = _a.onBackToUserInput, _b = _a.retryCount, retryCount = _b === void 0 ? 0 : _b, _c = _a.predictions, predictions = _c === void 0 ? [] : _c; _a.errorDetails; _a.allowResubmission; var maxResubmissionAttempts = _a.maxResubmissionAttempts, _d = _a.isRetryAllowed, isRetryAllowed = _d === void 0 ? true : _d, errorCode = _a.errorCode, _e = _a.blockContinue, blockContinue = _e === void 0 ? false : _e;
|
|
12
12
|
var t = useI18n().t;
|
|
13
|
+
// APP3-1955: a service refusal (no credits / quota) blocks the journey,
|
|
14
|
+
// whatever node.allowContinueOnFailure says.
|
|
15
|
+
var continueAnyway = isServiceErrorCode(errorCode) ? undefined : onContinueAnyway;
|
|
13
16
|
var _f = useState([]), validationPoints = _f[0], setValidationPoints = _f[1];
|
|
14
17
|
useEffect(function () {
|
|
15
18
|
// Use shared utility to get validation points
|
|
@@ -58,7 +61,7 @@ var DocumentError = function (_a) {
|
|
|
58
61
|
: t("errors.resubmission.no_resubmission_allowed") })] })), !blockContinue && getUserGuidance() && (jsxs("div", { className: "bg-yellow-50 border border-yellow-200 rounded-lg p-4", children: [jsx("h3", { className: "font-medium text-yellow-900 mb-2", children: isServiceErrorCode(errorCode)
|
|
59
62
|
? // Quota / out of credits: nothing for the end user to improve.
|
|
60
63
|
t("errors.service_refusal_title")
|
|
61
|
-
: "\uD83D\uDCA1 ".concat(t("errors.improvement_tips", "Conseils d'amélioration")) }), jsx("p", { className: "text-sm text-yellow-800", children: getUserGuidance() })] })), !blockContinue && isRetryAllowed && (jsxs("div", { className: "bg-blue-50 border border-blue-200 rounded-lg p-4", children: [jsx("h3", { className: "font-medium text-blue-900 mb-2", children: t("errors.next_steps", "Prochaines étapes") }), jsx("p", { className: "text-sm text-blue-800", children: t("errors.next_steps_description", "Veuillez reprendre les photos de votre document en suivant les conseils d'amélioration pour une meilleure qualité.") })] }))] }) }), jsx("div", { className: "sticky bottom-0 md:static bg-white border-t md:border-t-0 p-4 md:p-0 md:pb-8", children: jsxs("div", { className: "w-full max-w-md mx-auto", children: [jsx("div", { className: "flex flex-col space-y-3 md:hidden", children: blockContinue ? (jsx(ButtonDesktop, { onClick: onRetry, type: "continue", children: t("errors.retry_verification", "Recommencer la vérification") })) : (jsxs(Fragment, { children: [onBackToUserInput && (jsx(ButtonDesktop, { onClick: onBackToUserInput, type: "back", children: t("errors.back_to_user_input", "Retour aux informations d'identité") })), retryCount >= 1 && isRetryAllowed &&
|
|
64
|
+
: "\uD83D\uDCA1 ".concat(t("errors.improvement_tips", "Conseils d'amélioration")) }), jsx("p", { className: "text-sm text-yellow-800", children: getUserGuidance() })] })), !blockContinue && isRetryAllowed && (jsxs("div", { className: "bg-blue-50 border border-blue-200 rounded-lg p-4", children: [jsx("h3", { className: "font-medium text-blue-900 mb-2", children: t("errors.next_steps", "Prochaines étapes") }), jsx("p", { className: "text-sm text-blue-800", children: t("errors.next_steps_description", "Veuillez reprendre les photos de votre document en suivant les conseils d'amélioration pour une meilleure qualité.") })] }))] }) }), jsx("div", { className: "sticky bottom-0 md:static bg-white border-t md:border-t-0 p-4 md:p-0 md:pb-8", children: jsxs("div", { className: "w-full max-w-md mx-auto", children: [jsx("div", { className: "flex flex-col space-y-3 md:hidden", children: blockContinue ? (jsx(ButtonDesktop, { onClick: onRetry, type: "continue", children: t("errors.retry_verification", "Recommencer la vérification") })) : (jsxs(Fragment, { children: [onBackToUserInput && (jsx(ButtonDesktop, { onClick: onBackToUserInput, type: "back", children: t("errors.back_to_user_input", "Retour aux informations d'identité") })), retryCount >= 1 && isRetryAllowed && continueAnyway && (jsx(ButtonDesktop, { onClick: continueAnyway, type: "back", children: t("errors.continue_anyway", "Poursuivre tout de même") })), isRetryAllowed && (jsx(ButtonDesktop, { onClick: onRetry, type: "continue", children: t("errors.retry_verification", "Recommencer la vérification") })), !isRetryAllowed && continueAnyway && (jsx(ButtonDesktop, { onClick: continueAnyway, type: "back", children: t("errors.continue_anyway", "Poursuivre tout de même") }))] })) }), jsx("div", { className: "hidden md:flex flex-col space-y-3", children: jsx("div", { className: "flex justify-center space-x-3", children: blockContinue ? (jsx(ButtonDesktop, { onClick: onRetry, type: "continue", children: t("errors.retry_verification", "Recommencer la vérification") })) : (jsxs(Fragment, { children: [onBackToUserInput && (jsx(ButtonDesktop, { onClick: onBackToUserInput, type: "back", children: t("errors.back_to_user_input", "Retour aux informations d'identité") })), retryCount >= 1 && isRetryAllowed && continueAnyway && (jsx(ButtonDesktop, { onClick: continueAnyway, type: "back", children: t("errors.continue_anyway", "Poursuivre tout de même") })), isRetryAllowed && (jsx(ButtonDesktop, { onClick: onRetry, type: "continue", children: t("errors.retry_verification", "Recommencer la vérification") })), !isRetryAllowed && continueAnyway && (jsx(ButtonDesktop, { onClick: continueAnyway, type: "continue", children: t("errors.continue_anyway", "Poursuivre tout de même") }))] })) }) })] }) })] }));
|
|
62
65
|
};
|
|
63
66
|
|
|
64
67
|
export { DocumentError as default };
|
|
@@ -7,27 +7,31 @@ import CameraError from './CameraError.js';
|
|
|
7
7
|
import CameraMask from './CameraMask.js';
|
|
8
8
|
import OrientationToggle from './OrientationToggle.js';
|
|
9
9
|
import { useI18n } from '../../hooks/useI18n.js';
|
|
10
|
+
import { isBelowHd } from '../../utils/cameraResolution.js';
|
|
11
|
+
import { reportCaptureDiagnostics } from '../../services/sessionService.js';
|
|
10
12
|
import useIsMobile from '../../hooks/useIsMobile.js';
|
|
11
13
|
|
|
12
14
|
var Photo = function (_a) {
|
|
13
|
-
var onCapture = _a.onCapture, _b = _a.automaticCapture, automaticCapture = _b === void 0 ? false : _b, _c = _a.maskType, maskType = _c === void 0 ? "id" : _c;
|
|
15
|
+
var onCapture = _a.onCapture, sessionId = _a.sessionId, nodeId = _a.nodeId, _b = _a.automaticCapture, automaticCapture = _b === void 0 ? false : _b, _c = _a.maskType, maskType = _c === void 0 ? "id" : _c;
|
|
14
16
|
var isMobile = useIsMobile(768);
|
|
15
17
|
var videoRef = useRef(null);
|
|
16
|
-
var _d = useState(false),
|
|
17
|
-
var _e = useState(
|
|
18
|
-
var _f = useState(
|
|
19
|
-
var _g = useState(
|
|
20
|
-
var _h = useState(
|
|
21
|
-
var _j = useState(
|
|
22
|
-
var _k = useState(false),
|
|
23
|
-
var _l = useState(
|
|
24
|
-
var _m = useState(
|
|
25
|
-
var _o = useState(
|
|
26
|
-
var _p = useState(
|
|
27
|
-
var _q = useState(false),
|
|
18
|
+
var _d = useState(false), lowResolution = _d[0], setLowResolution = _d[1];
|
|
19
|
+
var _e = useState(0), cameraAttempt = _e[0], setCameraAttempt = _e[1];
|
|
20
|
+
var _f = useState(false), isDetecting = _f[0], setIsDetecting = _f[1];
|
|
21
|
+
var _g = useState("environment"), facingMode = _g[0], setFacingMode = _g[1];
|
|
22
|
+
var _h = useState(null), cameraError = _h[0], setCameraError = _h[1];
|
|
23
|
+
var _j = useState(window.matchMedia("(orientation: portrait)").matches), isPortrait = _j[0], setIsPortrait = _j[1];
|
|
24
|
+
var _k = useState(false), isCountingDown = _k[0], setIsCountingDown = _k[1];
|
|
25
|
+
var _l = useState(0), countdown = _l[0], setCountdown = _l[1];
|
|
26
|
+
var _m = useState(false), documentDetected = _m[0], setDocumentDetected = _m[1];
|
|
27
|
+
var _o = useState(undefined), qualityHint = _o[0], setQualityHint = _o[1];
|
|
28
|
+
var _p = useState(true), isInitializing = _p[0], setIsInitializing = _p[1];
|
|
29
|
+
var _q = useState(false), isProcessing = _q[0], setIsProcessing = _q[1];
|
|
30
|
+
var _r = useState(null), captureError = _r[0], setCaptureError = _r[1];
|
|
31
|
+
var _s = useState(false), showCaptureFlash = _s[0], setShowCaptureFlash = _s[1];
|
|
28
32
|
var t = useI18n().t;
|
|
29
|
-
var
|
|
30
|
-
var
|
|
33
|
+
var _t = useState(null); _t[0]; var setLastCaptureMeta = _t[1];
|
|
34
|
+
var _u = useState(null), maskRect = _u[0], setMaskRect = _u[1];
|
|
31
35
|
var countdownIntervalRef = useRef(null);
|
|
32
36
|
var detectionHandlerRef = useRef(null);
|
|
33
37
|
var flashTimeoutRef = useRef(null);
|
|
@@ -216,11 +220,13 @@ var Photo = function (_a) {
|
|
|
216
220
|
useEffect(function () {
|
|
217
221
|
var isCancelled = false;
|
|
218
222
|
var initCamera = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
219
|
-
var result;
|
|
220
|
-
|
|
221
|
-
|
|
223
|
+
var result, belowHd;
|
|
224
|
+
var _a, _b, _c, _d;
|
|
225
|
+
return __generator(this, function (_e) {
|
|
226
|
+
switch (_e.label) {
|
|
222
227
|
case 0:
|
|
223
228
|
setCameraError(null);
|
|
229
|
+
setLowResolution(false);
|
|
224
230
|
setIsInitializing(true);
|
|
225
231
|
return [4 /*yield*/, cameraService.startCamera(videoRef.current, {
|
|
226
232
|
facingMode: facingMode,
|
|
@@ -228,11 +234,25 @@ var Photo = function (_a) {
|
|
|
228
234
|
preferImageCapture: true,
|
|
229
235
|
})];
|
|
230
236
|
case 1:
|
|
231
|
-
result =
|
|
237
|
+
result = _e.sent();
|
|
232
238
|
if (isCancelled)
|
|
233
239
|
return [2 /*return*/];
|
|
234
240
|
setIsInitializing(false);
|
|
235
241
|
if (result.success) {
|
|
242
|
+
belowHd = isBelowHd(result.settings);
|
|
243
|
+
setLowResolution(belowHd);
|
|
244
|
+
if (sessionId && nodeId) {
|
|
245
|
+
void reportCaptureDiagnostics(sessionId, nodeId, {
|
|
246
|
+
requested: result.requested,
|
|
247
|
+
obtained: { w: (_a = result.settings) === null || _a === void 0 ? void 0 : _a.width, h: (_b = result.settings) === null || _b === void 0 ? void 0 : _b.height, frameRate: (_c = result.settings) === null || _c === void 0 ? void 0 : _c.frameRate },
|
|
248
|
+
retriedWithHd: (_d = result.retriedWithHd) !== null && _d !== void 0 ? _d : false,
|
|
249
|
+
devicePixelRatio: window.devicePixelRatio,
|
|
250
|
+
cameraLabel: result.label,
|
|
251
|
+
lowResolutionWarningShown: belowHd,
|
|
252
|
+
facingMode: facingMode,
|
|
253
|
+
at: new Date().toISOString(),
|
|
254
|
+
}).catch(function () { });
|
|
255
|
+
}
|
|
236
256
|
setLastCaptureMeta(null);
|
|
237
257
|
// Detection also runs in manual mode: it only drives the visual
|
|
238
258
|
// feedback on the mask (documentDetected), it never starts the
|
|
@@ -260,7 +280,7 @@ var Photo = function (_a) {
|
|
|
260
280
|
flashTimeoutRef.current = null;
|
|
261
281
|
}
|
|
262
282
|
};
|
|
263
|
-
}, [facingMode, clearCountdown, stopDetection]);
|
|
283
|
+
}, [facingMode, cameraAttempt, sessionId, nodeId, clearCountdown, stopDetection]);
|
|
264
284
|
useEffect(function () {
|
|
265
285
|
if (!isDetecting || !videoRef.current) {
|
|
266
286
|
return;
|
|
@@ -303,21 +323,7 @@ var Photo = function (_a) {
|
|
|
303
323
|
setIsInitializing(true);
|
|
304
324
|
stopDetection();
|
|
305
325
|
cameraService.stopCamera();
|
|
306
|
-
|
|
307
|
-
.startCamera(videoRef.current, {
|
|
308
|
-
facingMode: facingMode,
|
|
309
|
-
idealResolution: { width: 4096, height: 3072 },
|
|
310
|
-
preferImageCapture: true,
|
|
311
|
-
})
|
|
312
|
-
.then(function (result) {
|
|
313
|
-
setIsInitializing(false);
|
|
314
|
-
if (result.success) {
|
|
315
|
-
setIsDetecting(true);
|
|
316
|
-
}
|
|
317
|
-
else if (result.error) {
|
|
318
|
-
setCameraError(result.error);
|
|
319
|
-
}
|
|
320
|
-
});
|
|
326
|
+
setCameraAttempt(function (attempt) { return attempt + 1; });
|
|
321
327
|
};
|
|
322
328
|
var handleManualCapture = function () {
|
|
323
329
|
if (isProcessing) {
|
|
@@ -341,7 +347,7 @@ var Photo = function (_a) {
|
|
|
341
347
|
zIndex: isMobile ? 50 : "auto",
|
|
342
348
|
height: "100%",
|
|
343
349
|
width: "100%",
|
|
344
|
-
}, children: [jsx("div", { className: "flex-1 relative overflow-hidden bg-black", children: cameraError ? (jsx("div", { className: "flex items-center justify-center h-full px-4 py-6", children: jsx(CameraError, { errorMessage: cameraError, onRetry: handleRetry }) })) : (jsxs(Fragment, { children: [isInitializing && (jsxs("div", { "data-testid": "camera-initializing", className: "absolute inset-0 z-30 flex flex-col items-center justify-center gap-3 bg-black", children: [jsx("div", { className: "w-10 h-10 border-4 border-white border-t-transparent rounded-full animate-spin" }), jsx("span", { className: "text-white text-sm", children: t("id_check.photo.initializing", "Ouverture de la caméra…") })] })), isProcessing && (jsxs("div", { "data-testid": "camera-processing", className: "absolute inset-0 z-30 flex flex-col items-center justify-center gap-3 bg-black bg-opacity-70", children: [jsx("div", { className: "w-10 h-10 border-4 border-white border-t-transparent rounded-full animate-spin" }), jsx("span", { className: "text-white text-sm", children: t("id_check.photo.processing", "Traitement de l'image…") })] })), jsx("video", { ref: videoRef, autoPlay: true, playsInline: true, muted: true, className: "absolute inset-0 w-full h-full object-cover z-0 bg-black ".concat(facingMode === "user" ? "scale-x-[-1]" : "") }), jsx("div", { className: "absolute top-4 left-1/2 transform -translate-x-1/2 z-50", children: jsx("span", { className: "text-white z-50 text-sm font-semibold drop-shadow-lg", children: automaticCapture ? (jsx("span", { className: "animate-pulse", children: t("id_check.photo.mode_autocapture", "Autocapture activée") })) : (jsx("span", { className: "bg-orange-500 px-2 py-1 rounded hidden", children: t("id_check.photo.mode_manual", "Mode manuel") })) }) }), jsx(CameraMask, { isDetecting: isDetecting, isPortrait: isPortrait, facingMode: facingMode, isCountingDown: isCountingDown, countdown: countdown, maskType: maskType, documentDetected: documentDetected, qualityHint: qualityHint, onMaskRectChange: setMaskRect }), jsx("div", { className: "absolute bottom-20 left-0 right-0 z-20 pointer-events-none", children: jsx("div", { className: "text-center px-4", children: jsx("div", { className: "bg-black bg-opacity-60 text-white text-sm px-4 py-2 rounded-lg inline-block", children: t("id_check.photo.position_document", "Posez le document à plat ou tenez-le dans le cadre") }) }) }), jsx(OrientationToggle, { onCameraToggle: toggleCamera }), showCaptureFlash && (jsx("div", { "data-testid": "capture-flash", className: "absolute inset-0 z-40 bg-white pointer-events-none animate-pulse" }))] })) }), jsx("div", { className: "relative z-20 bg-black bg-opacity-80 p-4", children: jsxs("div", { className: "flex flex-col items-center space-y-4", children: [jsx("button", { onClick: handleManualCapture, className: "w-16 h-16 rounded-full border-4 border-white flex items-center justify-center transition-all duration-150 bg-transparent hover:bg-white hover:bg-opacity-20 cursor-pointer ".concat(showCaptureFlash ? "scale-90 ring-4 ring-[#11E5C5]" : ""), children: jsx("div", { className: "w-8 h-8 bg-white rounded-full" }) }), jsxs("div", { className: "text-white text-center text-sm", children: [jsxs("div", { className: "flex items-center justify-center space-x-2", children: [jsx("div", { className: "w-2 h-2 rounded-full ".concat(automaticCapture
|
|
350
|
+
}, children: [jsx("div", { className: "flex-1 relative overflow-hidden bg-black", children: cameraError ? (jsx("div", { className: "flex items-center justify-center h-full px-4 py-6", children: jsx(CameraError, { errorMessage: cameraError, onRetry: handleRetry }) })) : (jsxs(Fragment, { children: [isInitializing && (jsxs("div", { "data-testid": "camera-initializing", className: "absolute inset-0 z-30 flex flex-col items-center justify-center gap-3 bg-black", children: [jsx("div", { className: "w-10 h-10 border-4 border-white border-t-transparent rounded-full animate-spin" }), jsx("span", { className: "text-white text-sm", children: t("id_check.photo.initializing", "Ouverture de la caméra…") })] })), isProcessing && (jsxs("div", { "data-testid": "camera-processing", className: "absolute inset-0 z-30 flex flex-col items-center justify-center gap-3 bg-black bg-opacity-70", children: [jsx("div", { className: "w-10 h-10 border-4 border-white border-t-transparent rounded-full animate-spin" }), jsx("span", { className: "text-white text-sm", children: t("id_check.photo.processing", "Traitement de l'image…") })] })), jsx("video", { ref: videoRef, autoPlay: true, playsInline: true, muted: true, className: "absolute inset-0 w-full h-full object-cover z-0 bg-black ".concat(facingMode === "user" ? "scale-x-[-1]" : "") }), jsx("div", { className: "absolute top-4 left-1/2 transform -translate-x-1/2 z-50", children: jsx("span", { className: "text-white z-50 text-sm font-semibold drop-shadow-lg", children: automaticCapture ? (jsx("span", { className: "animate-pulse", children: t("id_check.photo.mode_autocapture", "Autocapture activée") })) : (jsx("span", { className: "bg-orange-500 px-2 py-1 rounded hidden", children: t("id_check.photo.mode_manual", "Mode manuel") })) }) }), jsx(CameraMask, { isDetecting: isDetecting, isPortrait: isPortrait, facingMode: facingMode, isCountingDown: isCountingDown, countdown: countdown, maskType: maskType, documentDetected: documentDetected, qualityHint: qualityHint, onMaskRectChange: setMaskRect }), jsx("div", { className: "absolute bottom-20 left-0 right-0 z-20 pointer-events-none", children: jsx("div", { className: "text-center px-4", children: jsx("div", { className: "bg-black bg-opacity-60 text-white text-sm px-4 py-2 rounded-lg inline-block", children: t("id_check.photo.position_document", "Posez le document à plat ou tenez-le dans le cadre") }) }) }), jsx(OrientationToggle, { onCameraToggle: toggleCamera }), showCaptureFlash && (jsx("div", { "data-testid": "capture-flash", className: "absolute inset-0 z-40 bg-white pointer-events-none animate-pulse" }))] })) }), lowResolution && !isInitializing && !cameraError && (jsxs("div", { role: "status", className: "relative z-20 shrink-0 border border-orange-200 bg-orange-50 p-4 text-sm text-orange-800", children: [jsx("h3", { className: "mb-2 font-medium text-orange-900", children: t("id_check.camera.low_resolution.title") }), jsx("p", { children: t("id_check.camera.low_resolution.body") }), jsx("p", { className: "mt-2", children: t("id_check.camera.low_resolution.tips") }), jsx("button", { type: "button", onClick: handleRetry, disabled: isProcessing, className: "mt-2 min-h-11 rounded border border-orange-800 px-4 py-2 font-medium hover:bg-orange-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 disabled:opacity-50", children: t("id_check.camera.low_resolution.retry") })] })), jsx("div", { className: "relative z-20 bg-black bg-opacity-80 p-4", children: jsxs("div", { className: "flex flex-col items-center space-y-4", children: [jsx("button", { onClick: handleManualCapture, className: "w-16 h-16 rounded-full border-4 border-white flex items-center justify-center transition-all duration-150 bg-transparent hover:bg-white hover:bg-opacity-20 cursor-pointer ".concat(showCaptureFlash ? "scale-90 ring-4 ring-[#11E5C5]" : ""), children: jsx("div", { className: "w-8 h-8 bg-white rounded-full" }) }), jsxs("div", { className: "text-white text-center text-sm", children: [jsxs("div", { className: "flex items-center justify-center space-x-2", children: [jsx("div", { className: "w-2 h-2 rounded-full ".concat(automaticCapture
|
|
345
351
|
? "bg-[#11E5C5] animate-pulse"
|
|
346
352
|
: "bg-orange-500") }), jsx("span", { children: statusMessage })] }), captureError && (jsx("p", { className: "text-red-400 text-xs mt-2 text-center", children: captureError }))] })] }) })] }));
|
|
347
353
|
};
|
|
@@ -14,6 +14,9 @@ var JDIError = function (_a) {
|
|
|
14
14
|
var _b;
|
|
15
15
|
_a.documentType; var onRetry = _a.onRetry, onContinueAnyway = _a.onContinueAnyway, onBackToUserInput = _a.onBackToUserInput, _c = _a.retryCount, retryCount = _c === void 0 ? 0 : _c, _d = _a.predictions, predictions = _d === void 0 ? DEFAULT_PREDICTIONS : _d, errorCode = _a.errorCode; _a.allowResubmission; var maxResubmissionAttempts = _a.maxResubmissionAttempts, _e = _a.isRetryAllowed, isRetryAllowed = _e === void 0 ? true : _e, _f = _a.blockContinue, blockContinue = _f === void 0 ? false : _f;
|
|
16
16
|
var t = useI18n().t;
|
|
17
|
+
// APP3-1955: a service refusal (no credits / quota) blocks the journey,
|
|
18
|
+
// whatever node.allowContinueOnFailure says.
|
|
19
|
+
var continueAnyway = isServiceErrorCode(errorCode) ? undefined : onContinueAnyway;
|
|
17
20
|
var _g = useState([]), validationPoints = _g[0], setValidationPoints = _g[1];
|
|
18
21
|
// Détail de mismatch d'identité (code 7.0), calculé côté backend et porté
|
|
19
22
|
// par la première prédiction. Absent pour tous les autres codes.
|
|
@@ -29,7 +32,7 @@ var JDIError = function (_a) {
|
|
|
29
32
|
var guidance = t("user_guidance.".concat(errorCode), "");
|
|
30
33
|
return guidance || null;
|
|
31
34
|
};
|
|
32
|
-
return (jsx(MobilePageLayout, { footer: jsxs("div", { className: "w-full max-w-md mx-auto", children: [jsx("div", { className: "flex flex-col space-y-3 md:hidden", children: blockContinue ? (jsx(Button, { onClick: onRetry, className: "w-full py-3", children: t("errors.retry_verification") })) : (jsxs(Fragment, { children: [onBackToUserInput && (jsx(ButtonDesktop, { onClick: onBackToUserInput, type: "back", className: "w-full text-black text-center font-poppins text-sm font-medium py-2", children: t("errors.back_to_user_input") })), retryCount >= 1 && isRetryAllowed &&
|
|
35
|
+
return (jsx(MobilePageLayout, { footer: jsxs("div", { className: "w-full max-w-md mx-auto", children: [jsx("div", { className: "flex flex-col space-y-3 md:hidden", children: blockContinue ? (jsx(Button, { onClick: onRetry, className: "w-full py-3", children: t("errors.retry_verification") })) : (jsxs(Fragment, { children: [onBackToUserInput && (jsx(ButtonDesktop, { onClick: onBackToUserInput, type: "back", className: "w-full text-black text-center font-poppins text-sm font-medium py-2", children: t("errors.back_to_user_input") })), retryCount >= 1 && isRetryAllowed && continueAnyway && (jsx(ButtonDesktop, { onClick: continueAnyway, type: "back", className: "w-full text-black text-center font-poppins text-sm font-medium py-2", children: t("errors.continue_anyway") })), isRetryAllowed && (jsx(Button, { onClick: onRetry, className: "w-full py-3", children: t("errors.retry_verification") })), !isRetryAllowed && continueAnyway && (jsx(Button, { onClick: continueAnyway, className: "w-full py-3", children: t("errors.continue_anyway") }))] })) }), jsx("div", { className: "hidden md:flex flex-col space-y-3", children: jsx("div", { className: "flex justify-center space-x-3", children: blockContinue ? (jsx(ButtonDesktop, { onClick: onRetry, type: "continue", children: t("errors.retry_verification") })) : (jsxs(Fragment, { children: [onBackToUserInput && (jsx(ButtonDesktop, { onClick: onBackToUserInput, type: "back", className: "text-black text-center font-poppins text-sm font-medium py-2", children: t("errors.back_to_user_input") })), retryCount >= 1 && isRetryAllowed && continueAnyway && (jsx(ButtonDesktop, { onClick: continueAnyway, type: "back", className: "text-black text-center font-poppins text-sm font-medium py-2", children: t("errors.continue_anyway") })), isRetryAllowed && (jsx(ButtonDesktop, { onClick: onRetry, type: "continue", children: t("errors.retry_verification") })), !isRetryAllowed && continueAnyway && (jsx(ButtonDesktop, { onClick: continueAnyway, type: "continue", children: t("errors.continue_anyway") }))] })) }) })] }), children: jsx("div", { className: "px-4 py-6 pt-11 md:px-8 md:py-8", children: jsxs("div", { className: "w-full max-w-md mx-auto space-y-6", children: [jsx("div", { className: "text-center", children: jsx("div", { className: "mx-auto w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mb-4", children: jsx("div", { className: "w-8 h-8 bg-red-500 rounded-full flex items-center justify-center", children: jsx("span", { className: "text-white text-lg", children: "\u2715" }) }) }) }), jsxs("div", { className: "text-center space-y-4", children: [jsx(Title, { className: "text-xl md:text-2xl lg:text-3xl text-red-600", children: blockContinue
|
|
33
36
|
? t("errors.upload_not_confirmed.title")
|
|
34
37
|
: t("errors.verification_failed") }), jsx(Subtitle, { className: "text-sm text-gray-600 leading-relaxed", children: blockContinue
|
|
35
38
|
? t("errors.upload_not_confirmed.subtitle")
|
|
@@ -760,13 +760,13 @@ var DocumentCheck = function (_a) {
|
|
|
760
760
|
return (jsx(BeforePhoto, { setStep: function () { return setCurrentPhotoStep("recto"); }, isSingleSided: isSingleSided, onBack: function () { return setDocStep(2); } }));
|
|
761
761
|
}
|
|
762
762
|
if (currentPhotoStep === "recto") {
|
|
763
|
-
return (jsx(Photo, { onCapture: function (imageData) { return handlePhotoCapture(imageData, "recto"); } }));
|
|
763
|
+
return (jsx(Photo, { sessionId: sessionId, nodeId: node.id, onCapture: function (imageData) { return handlePhotoCapture(imageData, "recto"); } }));
|
|
764
764
|
}
|
|
765
765
|
if (currentPhotoStep === "before-verso") {
|
|
766
766
|
return (jsx(BeforeVersoPhoto, { setStep: function () { return setCurrentPhotoStep("verso"); } }));
|
|
767
767
|
}
|
|
768
768
|
if (currentPhotoStep === "verso") {
|
|
769
|
-
return (jsx(Photo, { onCapture: function (imageData) { return handlePhotoCapture(imageData, "verso"); } }));
|
|
769
|
+
return (jsx(Photo, { sessionId: sessionId, nodeId: node.id, onCapture: function (imageData) { return handlePhotoCapture(imageData, "verso"); } }));
|
|
770
770
|
}
|
|
771
771
|
if (currentPhotoStep === "confirmation") {
|
|
772
772
|
return (jsx(PhotoConfirmation, { imageUrl: capturedImages.recto || "", versoImageUrl: capturedImages.verso, requiresTwoSides: !!capturedImages.verso, onConfirm: onConfirm, onRetry: function () { return setCurrentPhotoStep("before-recto"); }, onRetryAfterProcessing: function () { return setCurrentPhotoStep("before-recto"); }, fileUploaded: fileUploaded, isSingleSided: isSingleSided }));
|
package/dist/esm/i18n/en.json.js
CHANGED
|
@@ -780,11 +780,21 @@ var id_check = {
|
|
|
780
780
|
alt_verso: "Before taking the verso photo"
|
|
781
781
|
},
|
|
782
782
|
camera: {
|
|
783
|
+
low_resolution: {
|
|
784
|
+
title: "Limited camera resolution",
|
|
785
|
+
body: "Your camera resolution is below HD. The photo may be blurry, but you can continue.",
|
|
786
|
+
tips: "Close other apps using the camera, choose another camera or switch to mobile, and improve the lighting.",
|
|
787
|
+
retry: "Try again"
|
|
788
|
+
},
|
|
783
789
|
error_title: "Camera error",
|
|
784
790
|
retry: "Retry",
|
|
785
791
|
document_detected: "Document detected"
|
|
786
792
|
},
|
|
787
793
|
photo: {
|
|
794
|
+
hint: {
|
|
795
|
+
too_dark: "Too dark, find more light",
|
|
796
|
+
too_bright: "Too much glare, avoid direct light"
|
|
797
|
+
},
|
|
788
798
|
capture_failed: "Capture failed. Please try again.",
|
|
789
799
|
mode_autocapture: "Autocapture enabled",
|
|
790
800
|
mode_manual: "Manual mode",
|
package/dist/esm/i18n/es.json.js
CHANGED
|
@@ -780,11 +780,21 @@ var id_check = {
|
|
|
780
780
|
alt_verso: "Antes de tomar la foto del reverso"
|
|
781
781
|
},
|
|
782
782
|
camera: {
|
|
783
|
+
low_resolution: {
|
|
784
|
+
title: "Resolución de cámara limitada",
|
|
785
|
+
body: "La resolución de la cámara es inferior a HD. La foto puede salir borrosa, pero puede continuar.",
|
|
786
|
+
tips: "Cierre otras aplicaciones que usen la cámara, elija otra cámara o pase al móvil y mejore la iluminación.",
|
|
787
|
+
retry: "Reintentar"
|
|
788
|
+
},
|
|
783
789
|
error_title: "Error de cámara",
|
|
784
790
|
retry: "Reintentar",
|
|
785
791
|
document_detected: "Documento detectado"
|
|
786
792
|
},
|
|
787
793
|
photo: {
|
|
794
|
+
hint: {
|
|
795
|
+
too_dark: "Demasiado oscuro, busque más luz",
|
|
796
|
+
too_bright: "Demasiados reflejos, evite la luz directa"
|
|
797
|
+
},
|
|
788
798
|
capture_failed: "La captura ha fallado. Inténtalo de nuevo.",
|
|
789
799
|
mode_autocapture: "Captura automática activada",
|
|
790
800
|
mode_manual: "Modo manual",
|
package/dist/esm/i18n/fr.json.js
CHANGED
|
@@ -780,11 +780,21 @@ var id_check = {
|
|
|
780
780
|
alt_verso: "Avant de prendre la photo du verso"
|
|
781
781
|
},
|
|
782
782
|
camera: {
|
|
783
|
+
low_resolution: {
|
|
784
|
+
title: "Résolution de la caméra limitée",
|
|
785
|
+
body: "La résolution de votre caméra est inférieure à la HD. La photo risque d’être floue, mais vous pouvez continuer.",
|
|
786
|
+
tips: "Fermez les autres applications utilisant la caméra, choisissez une autre caméra ou passez sur mobile, et améliorez l’éclairage.",
|
|
787
|
+
retry: "Réessayer"
|
|
788
|
+
},
|
|
783
789
|
error_title: "Erreur de caméra",
|
|
784
790
|
retry: "Réessayer",
|
|
785
791
|
document_detected: "Document détecté"
|
|
786
792
|
},
|
|
787
793
|
photo: {
|
|
794
|
+
hint: {
|
|
795
|
+
too_dark: "Trop sombre, cherchez plus de lumière",
|
|
796
|
+
too_bright: "Trop de reflets, évitez la lumière directe"
|
|
797
|
+
},
|
|
788
798
|
capture_failed: "La capture a échoué. Veuillez réessayer.",
|
|
789
799
|
mode_autocapture: "Autocapture activée",
|
|
790
800
|
mode_manual: "Mode manuel",
|