datakeen-session-react 1.1.200-dev.2 → 1.1.200-dev.3

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.
@@ -18,6 +18,12 @@ var Title = require('../ui/Title.js');
18
18
  var Subtitle = require('../ui/Subtitle.js');
19
19
  var NfcSuccessAnimation = require('./NfcSuccessAnimation.js');
20
20
 
21
+ // App native NFC iOS « ID Scan ». Safari n'ouvre jamais l'app via une redirection JS ni via un
22
+ // Universal Link du même domaine que la page (Apple TN3155) : on passe par le schéma custom déclaré
23
+ // par l'app, sur un geste utilisateur. L'hôte du lien = backend Datakeen, que l'app utilise pour
24
+ // choisir l'environnement (même rôle que `intent://<host>/scan/<id>` sur Android).
25
+ var IOS_APP_SCHEME = "datakeenscannerswift";
26
+ var IOS_APP_STORE_URL = "https://apps.apple.com/app/id6762505375";
21
27
  var NfcScanStep = function (_a) {
22
28
  var sessionId = _a.sessionId, nodeId = _a.nodeId, stepObject = _a.stepObject, template = _a.template, pageTitle = _a.pageTitle, pageDescription = _a.pageDescription, onBack = _a.onBack, onContinueWithoutChip = _a.onContinueWithoutChip;
23
29
  var t = useI18n.useI18n().t;
@@ -27,11 +33,26 @@ var NfcScanStep = function (_a) {
27
33
  var _d = React.useState(false), isResetting = _d[0], setIsResetting = _d[1];
28
34
  // Sur mobile, on ouvre directement l'app de scan au lieu d'afficher un QR code inutile.
29
35
  var isMobile = React.useMemo(function () { return isMobileDevice.isMobileDevice(); }, []);
36
+ var isIOS = React.useMemo(function () { return isMobileDevice.isIOSDevice(); }, []);
30
37
  var nfcUrl = React.useMemo(function () {
31
38
  var origin = typeof window !== "undefined" ? window.location.origin : "";
32
39
  return "".concat(origin, "/scan/").concat(sessionId);
33
40
  }, [sessionId]);
34
41
  var apiBaseUrl = React.useMemo(function () { return api.getBaseURL(); }, []);
42
+ var iosAppLinks = React.useMemo(function () {
43
+ var host = new URL(apiBaseUrl, window.location.href).host;
44
+ return {
45
+ open: "".concat(IOS_APP_SCHEME, "://").concat(host, "/scan/").concat(sessionId),
46
+ // Lu par le bouton « Reprendre ma session » de l'app au premier lancement après installation.
47
+ resume: "https://".concat(host, "/scan/").concat(sessionId),
48
+ };
49
+ }, [apiBaseUrl, sessionId]);
50
+ // Best effort : l'App Store ne transmet aucun contexte à l'app installée (pas d'équivalent du
51
+ // Play Install Referrer), le presse-papier sert de relais. Échec silencieux (permission refusée).
52
+ var copyResumeLink = function () {
53
+ var _a;
54
+ (_a = navigator.clipboard) === null || _a === void 0 ? void 0 : _a.writeText(iosAppLinks.resume).catch(function () { return undefined; });
55
+ };
35
56
  var status = useNfcSseStatus.useNfcSseStatus({
36
57
  sessionId: sessionId,
37
58
  apiBaseUrl: apiBaseUrl,
@@ -50,13 +71,15 @@ var NfcScanStep = function (_a) {
50
71
  // Sur mobile : ouvrir directement l'application de scan (pas de QR à se scanner soi-même).
51
72
  // Une seule fois, tant que le scan n'a pas progressé. Le SSE reste actif pour faire avancer
52
73
  // le parcours quand la lecture est terminée.
74
+ // Pas sur iOS : la redirection resterait dans Safari et quitterait la page (SSE fermé, parcours
75
+ // bloqué). On y affiche des boutons à la place, et la page reste ouverte pendant le scan.
53
76
  var hasRedirectedRef = React.useRef(false);
54
77
  React.useEffect(function () {
55
- if (!isMobile || status !== "pending" || hasRedirectedRef.current)
78
+ if (!isMobile || isIOS || status !== "pending" || hasRedirectedRef.current)
56
79
  return;
57
80
  hasRedirectedRef.current = true;
58
81
  window.location.assign(nfcUrl);
59
- }, [isMobile, status, nfcUrl]);
82
+ }, [isMobile, isIOS, status, nfcUrl]);
60
83
  React.useEffect(function () {
61
84
  if (isMobile)
62
85
  return; // pas de QR sur mobile (redirection directe)
@@ -141,7 +164,7 @@ var NfcScanStep = function (_a) {
141
164
  return (jsxRuntime.jsx(MobilePageLayout.default, { footer: footer, children: jsxRuntime.jsx("div", { className: "px-4 py-6 pt-11 md:px-8 md:py-8", children: jsxRuntime.jsxs("div", { className: "w-full max-w-md mx-auto space-y-6", children: [jsxRuntime.jsxs("div", { className: "text-center space-y-4 mt-16 md:mt-20", children: [jsxRuntime.jsx(Title.default, { className: "text-xl md:text-2xl lg:text-3xl", children: title }), jsxRuntime.jsx(Subtitle.default, { className: "text-sm md:text-base text-gray-600 leading-relaxed whitespace-pre-line", children: subtitle })] }), jsxRuntime.jsx("div", { className: "flex-1 flex flex-col justify-center items-center gap-4", children: status === "success" ? (jsxRuntime.jsx(NfcSuccessAnimation.default, {})) : status === "completed" ? (jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4 p-8", children: [jsxRuntime.jsx("div", { className: "animate-spin w-10 h-10 border-2 border-gray-300 border-t-blue-500 rounded-full" }), jsxRuntime.jsx("p", { className: "text-sm text-gray-600", children: t("nfc_scan.verifying", "Vérification en cours…") })] })) : status === "failed" ? (jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-2 p-8 text-center", children: [jsxRuntime.jsx("p", { className: "text-base font-semibold text-red-600", children: t("nfc_scan.failed_title", "Le scan n'a pas pu être vérifié") }), jsxRuntime.jsx("p", { className: "text-sm text-gray-600 leading-relaxed", children: t("nfc_scan.failed_description", "Après plusieurs tentatives, la lecture de votre document n'a pas pu être validée.") }), resetError && (jsxRuntime.jsx("p", { className: "text-sm text-red-600", role: "alert", children: resetError }))] })) : (
142
165
  // Contenu principal QR/mobile — reste monté pour "pending" ET "opened", pour ne
143
166
  // jamais démonter le canvas déjà généré.
144
- jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [qrError ? (jsxRuntime.jsx("div", { className: "text-red-500 text-center p-4", children: qrError })) : isMobile ? (jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4 p-8 text-center", children: [jsxRuntime.jsx("div", { className: "animate-spin w-10 h-10 border-2 border-gray-300 border-t-blue-500 rounded-full" }), jsxRuntime.jsx("p", { className: "text-sm text-gray-600", children: t("nfc_scan.opening_app", "Ouverture de l'application…") }), jsxRuntime.jsx("a", { href: nfcUrl, className: "text-sm font-medium text-blue-600 underline", children: t("nfc_scan.open_app_link", "Ouvrir l'application") })] })) : (jsxRuntime.jsx("div", { className: "bg-white p-4 rounded-lg shadow-lg border-2 border-gray-200", children: jsxRuntime.jsx("canvas", { ref: canvasRef }) })), status === "opened" && (jsxRuntime.jsxs("div", { className: "text-center space-y-1", children: [jsxRuntime.jsx("p", { className: "text-sm font-semibold", style: { color: "var(--uni-secondary-color, #0a9983)" }, children: t("nfc_scan.qr_opened_title", "QR code scanné") }), jsxRuntime.jsx("p", { className: "text-xs text-gray-500 leading-relaxed", children: t("nfc_scan.qr_opened_description", "L'expérience NFC est ouverte sur votre smartphone. Continuez le scan sur votre téléphone.") }), jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2 text-xs text-gray-500 pt-1", children: [jsxRuntime.jsx("span", { className: "h-2 w-2 rounded-full animate-pulse", style: { backgroundColor: "var(--uni-primary-color, #11e5c5)" } }), t("nfc_scan.waiting_completion", "En attente de la lecture NFC")] })] }))] })) })] }) }) }));
167
+ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [qrError ? (jsxRuntime.jsx("div", { className: "text-red-500 text-center p-4", children: qrError })) : isIOS ? (jsxRuntime.jsxs("div", { className: "flex w-full flex-col items-center gap-3 p-4 text-center", children: [jsxRuntime.jsx("a", { href: iosAppLinks.open, className: "flex h-[48px] w-full items-center justify-center rounded-[12px] text-sm font-medium hover:brightness-110 active:brightness-90", style: { backgroundColor: "var(--dk-btn-bg)", color: "var(--dk-btn-text)" }, children: t("nfc_scan.open_app_link", "Ouvrir l'application") }), jsxRuntime.jsx("a", { href: IOS_APP_STORE_URL, onClick: copyResumeLink, className: "flex h-[48px] w-full items-center justify-center rounded-[12px] border border-gray-300 text-sm font-medium text-gray-600 hover:bg-black/5", children: t("nfc_scan.install_app_link", "Installer l'application") }), jsxRuntime.jsx("p", { className: "text-xs text-gray-500 leading-relaxed", children: t("nfc_scan.install_app_hint", "Après l'installation, revenez sur cette page et appuyez sur « Ouvrir l'application ».") })] })) : isMobile ? (jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4 p-8 text-center", children: [jsxRuntime.jsx("div", { className: "animate-spin w-10 h-10 border-2 border-gray-300 border-t-blue-500 rounded-full" }), jsxRuntime.jsx("p", { className: "text-sm text-gray-600", children: t("nfc_scan.opening_app", "Ouverture de l'application…") }), jsxRuntime.jsx("a", { href: nfcUrl, className: "text-sm font-medium text-blue-600 underline", children: t("nfc_scan.open_app_link", "Ouvrir l'application") })] })) : (jsxRuntime.jsx("div", { className: "bg-white p-4 rounded-lg shadow-lg border-2 border-gray-200", children: jsxRuntime.jsx("canvas", { ref: canvasRef }) })), status === "opened" && (jsxRuntime.jsxs("div", { className: "text-center space-y-1", children: [jsxRuntime.jsx("p", { className: "text-sm font-semibold", style: { color: "var(--uni-secondary-color, #0a9983)" }, children: t("nfc_scan.qr_opened_title", "QR code scanné") }), jsxRuntime.jsx("p", { className: "text-xs text-gray-500 leading-relaxed", children: t("nfc_scan.qr_opened_description", "L'expérience NFC est ouverte sur votre smartphone. Continuez le scan sur votre téléphone.") }), jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2 text-xs text-gray-500 pt-1", children: [jsxRuntime.jsx("span", { className: "h-2 w-2 rounded-full animate-pulse", style: { backgroundColor: "var(--uni-primary-color, #11e5c5)" } }), t("nfc_scan.waiting_completion", "En attente de la lecture NFC")] })] }))] })) })] }) }) }));
145
168
  };
146
169
 
147
170
  exports.default = NfcScanStep;
@@ -1025,6 +1025,9 @@ var signature = {
1025
1025
  token_error: "Error loading signature session."
1026
1026
  };
1027
1027
  var nfc_scan = {
1028
+ open_app_link: "Open the app",
1029
+ install_app_link: "Install the app",
1030
+ install_app_hint: "Once installed, come back to this page and tap “Open the app”.",
1028
1031
  page_title: "NFC scan of your document",
1029
1032
  page_description: "Scan the QR code below with your smartphone to read the electronic chip of your identity document.",
1030
1033
  verifying: "Verifying…",
@@ -1025,6 +1025,9 @@ var signature = {
1025
1025
  token_error: "Error al cargar la sesión de firma."
1026
1026
  };
1027
1027
  var nfc_scan = {
1028
+ open_app_link: "Abrir la aplicación",
1029
+ install_app_link: "Instalar la aplicación",
1030
+ install_app_hint: "Después de instalarla, vuelve a esta página y pulsa «Abrir la aplicación».",
1028
1031
  page_title: "Escaneo NFC de tu documento",
1029
1032
  page_description: "Escanea el código QR siguiente con tu smartphone para leer el chip electrónico de tu documento de identidad.",
1030
1033
  verifying: "Verificando…",
@@ -1025,6 +1025,9 @@ var signature = {
1025
1025
  token_error: "Erreur lors du chargement de la session de signature."
1026
1026
  };
1027
1027
  var nfc_scan = {
1028
+ open_app_link: "Ouvrir l'application",
1029
+ install_app_link: "Installer l'application",
1030
+ install_app_hint: "Après l'installation, revenez sur cette page et appuyez sur « Ouvrir l'application ».",
1028
1031
  page_title: "Scan NFC de votre document",
1029
1032
  page_description: "Scannez le QR code ci-dessous avec votre smartphone pour lire la puce électronique de votre document d'identité.",
1030
1033
  verifying: "Vérification en cours…",
@@ -12,5 +12,16 @@ var isMobileDevice = function () {
12
12
  return false;
13
13
  return /Android|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i.test(navigator.userAgent);
14
14
  };
15
+ /**
16
+ * iPhone / iPad (UA historique) / iPod. Sur iOS, Safari n'ouvre pas l'app NFC via une redirection
17
+ * JS ni via un Universal Link du même domaine : l'étape NFC doit proposer un bouton (geste
18
+ * utilisateur) vers le schéma custom de l'app.
19
+ */
20
+ var isIOSDevice = function () {
21
+ if (typeof navigator === "undefined")
22
+ return false;
23
+ return /iPhone|iPad|iPod/i.test(navigator.userAgent);
24
+ };
15
25
 
26
+ exports.isIOSDevice = isIOSDevice;
16
27
  exports.isMobileDevice = isMobileDevice;
@@ -6,7 +6,7 @@ import { useI18n } from '../../hooks/useI18n.js';
6
6
  import { useNfcSseStatus } from '../../hooks/useNfcSseStatus.js';
7
7
  import { getBaseURL } from '../../services/api.js';
8
8
  import { resetNfcScan } from '../../services/sessionService.js';
9
- import { isMobileDevice } from '../../utils/isMobileDevice.js';
9
+ import { isMobileDevice, isIOSDevice } from '../../utils/isMobileDevice.js';
10
10
  import MobilePageLayout from '../ui/MobilePageLayout.js';
11
11
  import Button from '../ui/Button.js';
12
12
  import PageActions from '../ui/PageActions.js';
@@ -14,6 +14,12 @@ import Title from '../ui/Title.js';
14
14
  import Subtitle from '../ui/Subtitle.js';
15
15
  import NfcSuccessAnimation from './NfcSuccessAnimation.js';
16
16
 
17
+ // App native NFC iOS « ID Scan ». Safari n'ouvre jamais l'app via une redirection JS ni via un
18
+ // Universal Link du même domaine que la page (Apple TN3155) : on passe par le schéma custom déclaré
19
+ // par l'app, sur un geste utilisateur. L'hôte du lien = backend Datakeen, que l'app utilise pour
20
+ // choisir l'environnement (même rôle que `intent://<host>/scan/<id>` sur Android).
21
+ var IOS_APP_SCHEME = "datakeenscannerswift";
22
+ var IOS_APP_STORE_URL = "https://apps.apple.com/app/id6762505375";
17
23
  var NfcScanStep = function (_a) {
18
24
  var sessionId = _a.sessionId, nodeId = _a.nodeId, stepObject = _a.stepObject, template = _a.template, pageTitle = _a.pageTitle, pageDescription = _a.pageDescription, onBack = _a.onBack, onContinueWithoutChip = _a.onContinueWithoutChip;
19
25
  var t = useI18n().t;
@@ -23,11 +29,26 @@ var NfcScanStep = function (_a) {
23
29
  var _d = useState(false), isResetting = _d[0], setIsResetting = _d[1];
24
30
  // Sur mobile, on ouvre directement l'app de scan au lieu d'afficher un QR code inutile.
25
31
  var isMobile = useMemo(function () { return isMobileDevice(); }, []);
32
+ var isIOS = useMemo(function () { return isIOSDevice(); }, []);
26
33
  var nfcUrl = useMemo(function () {
27
34
  var origin = typeof window !== "undefined" ? window.location.origin : "";
28
35
  return "".concat(origin, "/scan/").concat(sessionId);
29
36
  }, [sessionId]);
30
37
  var apiBaseUrl = useMemo(function () { return getBaseURL(); }, []);
38
+ var iosAppLinks = useMemo(function () {
39
+ var host = new URL(apiBaseUrl, window.location.href).host;
40
+ return {
41
+ open: "".concat(IOS_APP_SCHEME, "://").concat(host, "/scan/").concat(sessionId),
42
+ // Lu par le bouton « Reprendre ma session » de l'app au premier lancement après installation.
43
+ resume: "https://".concat(host, "/scan/").concat(sessionId),
44
+ };
45
+ }, [apiBaseUrl, sessionId]);
46
+ // Best effort : l'App Store ne transmet aucun contexte à l'app installée (pas d'équivalent du
47
+ // Play Install Referrer), le presse-papier sert de relais. Échec silencieux (permission refusée).
48
+ var copyResumeLink = function () {
49
+ var _a;
50
+ (_a = navigator.clipboard) === null || _a === void 0 ? void 0 : _a.writeText(iosAppLinks.resume).catch(function () { return undefined; });
51
+ };
31
52
  var status = useNfcSseStatus({
32
53
  sessionId: sessionId,
33
54
  apiBaseUrl: apiBaseUrl,
@@ -46,13 +67,15 @@ var NfcScanStep = function (_a) {
46
67
  // Sur mobile : ouvrir directement l'application de scan (pas de QR à se scanner soi-même).
47
68
  // Une seule fois, tant que le scan n'a pas progressé. Le SSE reste actif pour faire avancer
48
69
  // le parcours quand la lecture est terminée.
70
+ // Pas sur iOS : la redirection resterait dans Safari et quitterait la page (SSE fermé, parcours
71
+ // bloqué). On y affiche des boutons à la place, et la page reste ouverte pendant le scan.
49
72
  var hasRedirectedRef = useRef(false);
50
73
  useEffect(function () {
51
- if (!isMobile || status !== "pending" || hasRedirectedRef.current)
74
+ if (!isMobile || isIOS || status !== "pending" || hasRedirectedRef.current)
52
75
  return;
53
76
  hasRedirectedRef.current = true;
54
77
  window.location.assign(nfcUrl);
55
- }, [isMobile, status, nfcUrl]);
78
+ }, [isMobile, isIOS, status, nfcUrl]);
56
79
  useEffect(function () {
57
80
  if (isMobile)
58
81
  return; // pas de QR sur mobile (redirection directe)
@@ -137,7 +160,7 @@ var NfcScanStep = function (_a) {
137
160
  return (jsx(MobilePageLayout, { footer: footer, 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: [jsxs("div", { className: "text-center space-y-4 mt-16 md:mt-20", children: [jsx(Title, { className: "text-xl md:text-2xl lg:text-3xl", children: title }), jsx(Subtitle, { className: "text-sm md:text-base text-gray-600 leading-relaxed whitespace-pre-line", children: subtitle })] }), jsx("div", { className: "flex-1 flex flex-col justify-center items-center gap-4", children: status === "success" ? (jsx(NfcSuccessAnimation, {})) : status === "completed" ? (jsxs("div", { className: "flex flex-col items-center gap-4 p-8", children: [jsx("div", { className: "animate-spin w-10 h-10 border-2 border-gray-300 border-t-blue-500 rounded-full" }), jsx("p", { className: "text-sm text-gray-600", children: t("nfc_scan.verifying", "Vérification en cours…") })] })) : status === "failed" ? (jsxs("div", { className: "flex flex-col items-center gap-2 p-8 text-center", children: [jsx("p", { className: "text-base font-semibold text-red-600", children: t("nfc_scan.failed_title", "Le scan n'a pas pu être vérifié") }), jsx("p", { className: "text-sm text-gray-600 leading-relaxed", children: t("nfc_scan.failed_description", "Après plusieurs tentatives, la lecture de votre document n'a pas pu être validée.") }), resetError && (jsx("p", { className: "text-sm text-red-600", role: "alert", children: resetError }))] })) : (
138
161
  // Contenu principal QR/mobile — reste monté pour "pending" ET "opened", pour ne
139
162
  // jamais démonter le canvas déjà généré.
140
- jsxs(Fragment, { children: [qrError ? (jsx("div", { className: "text-red-500 text-center p-4", children: qrError })) : isMobile ? (jsxs("div", { className: "flex flex-col items-center gap-4 p-8 text-center", children: [jsx("div", { className: "animate-spin w-10 h-10 border-2 border-gray-300 border-t-blue-500 rounded-full" }), jsx("p", { className: "text-sm text-gray-600", children: t("nfc_scan.opening_app", "Ouverture de l'application…") }), jsx("a", { href: nfcUrl, className: "text-sm font-medium text-blue-600 underline", children: t("nfc_scan.open_app_link", "Ouvrir l'application") })] })) : (jsx("div", { className: "bg-white p-4 rounded-lg shadow-lg border-2 border-gray-200", children: jsx("canvas", { ref: canvasRef }) })), status === "opened" && (jsxs("div", { className: "text-center space-y-1", children: [jsx("p", { className: "text-sm font-semibold", style: { color: "var(--uni-secondary-color, #0a9983)" }, children: t("nfc_scan.qr_opened_title", "QR code scanné") }), jsx("p", { className: "text-xs text-gray-500 leading-relaxed", children: t("nfc_scan.qr_opened_description", "L'expérience NFC est ouverte sur votre smartphone. Continuez le scan sur votre téléphone.") }), jsxs("div", { className: "flex items-center justify-center gap-2 text-xs text-gray-500 pt-1", children: [jsx("span", { className: "h-2 w-2 rounded-full animate-pulse", style: { backgroundColor: "var(--uni-primary-color, #11e5c5)" } }), t("nfc_scan.waiting_completion", "En attente de la lecture NFC")] })] }))] })) })] }) }) }));
163
+ jsxs(Fragment, { children: [qrError ? (jsx("div", { className: "text-red-500 text-center p-4", children: qrError })) : isIOS ? (jsxs("div", { className: "flex w-full flex-col items-center gap-3 p-4 text-center", children: [jsx("a", { href: iosAppLinks.open, className: "flex h-[48px] w-full items-center justify-center rounded-[12px] text-sm font-medium hover:brightness-110 active:brightness-90", style: { backgroundColor: "var(--dk-btn-bg)", color: "var(--dk-btn-text)" }, children: t("nfc_scan.open_app_link", "Ouvrir l'application") }), jsx("a", { href: IOS_APP_STORE_URL, onClick: copyResumeLink, className: "flex h-[48px] w-full items-center justify-center rounded-[12px] border border-gray-300 text-sm font-medium text-gray-600 hover:bg-black/5", children: t("nfc_scan.install_app_link", "Installer l'application") }), jsx("p", { className: "text-xs text-gray-500 leading-relaxed", children: t("nfc_scan.install_app_hint", "Après l'installation, revenez sur cette page et appuyez sur « Ouvrir l'application ».") })] })) : isMobile ? (jsxs("div", { className: "flex flex-col items-center gap-4 p-8 text-center", children: [jsx("div", { className: "animate-spin w-10 h-10 border-2 border-gray-300 border-t-blue-500 rounded-full" }), jsx("p", { className: "text-sm text-gray-600", children: t("nfc_scan.opening_app", "Ouverture de l'application…") }), jsx("a", { href: nfcUrl, className: "text-sm font-medium text-blue-600 underline", children: t("nfc_scan.open_app_link", "Ouvrir l'application") })] })) : (jsx("div", { className: "bg-white p-4 rounded-lg shadow-lg border-2 border-gray-200", children: jsx("canvas", { ref: canvasRef }) })), status === "opened" && (jsxs("div", { className: "text-center space-y-1", children: [jsx("p", { className: "text-sm font-semibold", style: { color: "var(--uni-secondary-color, #0a9983)" }, children: t("nfc_scan.qr_opened_title", "QR code scanné") }), jsx("p", { className: "text-xs text-gray-500 leading-relaxed", children: t("nfc_scan.qr_opened_description", "L'expérience NFC est ouverte sur votre smartphone. Continuez le scan sur votre téléphone.") }), jsxs("div", { className: "flex items-center justify-center gap-2 text-xs text-gray-500 pt-1", children: [jsx("span", { className: "h-2 w-2 rounded-full animate-pulse", style: { backgroundColor: "var(--uni-primary-color, #11e5c5)" } }), t("nfc_scan.waiting_completion", "En attente de la lecture NFC")] })] }))] })) })] }) }) }));
141
164
  };
142
165
 
143
166
  export { NfcScanStep as default };
@@ -1021,6 +1021,9 @@ var signature = {
1021
1021
  token_error: "Error loading signature session."
1022
1022
  };
1023
1023
  var nfc_scan = {
1024
+ open_app_link: "Open the app",
1025
+ install_app_link: "Install the app",
1026
+ install_app_hint: "Once installed, come back to this page and tap “Open the app”.",
1024
1027
  page_title: "NFC scan of your document",
1025
1028
  page_description: "Scan the QR code below with your smartphone to read the electronic chip of your identity document.",
1026
1029
  verifying: "Verifying…",
@@ -1021,6 +1021,9 @@ var signature = {
1021
1021
  token_error: "Error al cargar la sesión de firma."
1022
1022
  };
1023
1023
  var nfc_scan = {
1024
+ open_app_link: "Abrir la aplicación",
1025
+ install_app_link: "Instalar la aplicación",
1026
+ install_app_hint: "Después de instalarla, vuelve a esta página y pulsa «Abrir la aplicación».",
1024
1027
  page_title: "Escaneo NFC de tu documento",
1025
1028
  page_description: "Escanea el código QR siguiente con tu smartphone para leer el chip electrónico de tu documento de identidad.",
1026
1029
  verifying: "Verificando…",
@@ -1021,6 +1021,9 @@ var signature = {
1021
1021
  token_error: "Erreur lors du chargement de la session de signature."
1022
1022
  };
1023
1023
  var nfc_scan = {
1024
+ open_app_link: "Ouvrir l'application",
1025
+ install_app_link: "Installer l'application",
1026
+ install_app_hint: "Après l'installation, revenez sur cette page et appuyez sur « Ouvrir l'application ».",
1024
1027
  page_title: "Scan NFC de votre document",
1025
1028
  page_description: "Scannez le QR code ci-dessous avec votre smartphone pour lire la puce électronique de votre document d'identité.",
1026
1029
  verifying: "Vérification en cours…",
@@ -10,5 +10,15 @@ var isMobileDevice = function () {
10
10
  return false;
11
11
  return /Android|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i.test(navigator.userAgent);
12
12
  };
13
+ /**
14
+ * iPhone / iPad (UA historique) / iPod. Sur iOS, Safari n'ouvre pas l'app NFC via une redirection
15
+ * JS ni via un Universal Link du même domaine : l'étape NFC doit proposer un bouton (geste
16
+ * utilisateur) vers le schéma custom de l'app.
17
+ */
18
+ var isIOSDevice = function () {
19
+ if (typeof navigator === "undefined")
20
+ return false;
21
+ return /iPhone|iPad|iPod/i.test(navigator.userAgent);
22
+ };
13
23
 
14
- export { isMobileDevice };
24
+ export { isIOSDevice, isMobileDevice };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datakeen-session-react",
3
- "version": "1.1.200-dev.2",
3
+ "version": "1.1.200-dev.3",
4
4
  "description": "React SDK component to manage and render Datakeen session experiences easily.",
5
5
  "publishConfig": {
6
6
  "access": "public",