@kernaq/verify-react-native 2.0.1 → 2.0.2
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 +1 -1
- package/dist/index.esm.js +63 -22
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +63 -22
- package/dist/index.js.map +1 -1
- package/package.json +2 -9
- package/src/KernaqVerify.tsx +43 -16
- package/src/steps/LivenessStep.tsx +15 -8
package/dist/index.js
CHANGED
|
@@ -956,6 +956,13 @@ function LivenessStep({ theme, locale, duration, taskCount = 2, onCapture }) {
|
|
|
956
956
|
;
|
|
957
957
|
const [complete, setComplete] = React.useState(false);
|
|
958
958
|
const [recording, setRecording] = React.useState(false);
|
|
959
|
+
// Keep a ref in sync with taskIdx so setTimeout/setInterval closures always
|
|
960
|
+
// read the latest value and don't stall on a stale snapshot.
|
|
961
|
+
const taskIdxRef = React.useRef(0);
|
|
962
|
+
const setTaskIdxSynced = (n)=>{
|
|
963
|
+
taskIdxRef.current = n;
|
|
964
|
+
setTaskIdx(n);
|
|
965
|
+
};
|
|
959
966
|
const progressTimer = React.useRef(null);
|
|
960
967
|
const taskTimer = React.useRef(null);
|
|
961
968
|
const pulseAnim = React.useRef(new reactNative.Animated.Value(1)).current;
|
|
@@ -984,13 +991,12 @@ function LivenessStep({ theme, locale, duration, taskCount = 2, onCapture }) {
|
|
|
984
991
|
]);
|
|
985
992
|
const startTask = ()=>{
|
|
986
993
|
if (!recording) {
|
|
987
|
-
// Begin recording when first task starts
|
|
988
994
|
startRecording((video)=>{
|
|
989
995
|
onCapture(video.uri);
|
|
990
996
|
}, ()=>{});
|
|
991
997
|
setRecording(true);
|
|
992
998
|
}
|
|
993
|
-
const task = tasks[
|
|
999
|
+
const task = tasks[taskIdxRef.current];
|
|
994
1000
|
if (!task) return;
|
|
995
1001
|
setStarted(true);
|
|
996
1002
|
setProgress(0);
|
|
@@ -1007,16 +1013,16 @@ function LivenessStep({ theme, locale, duration, taskCount = 2, onCapture }) {
|
|
|
1007
1013
|
}, 80);
|
|
1008
1014
|
};
|
|
1009
1015
|
const nextTask = ()=>{
|
|
1010
|
-
const next =
|
|
1016
|
+
const next = taskIdxRef.current + 1;
|
|
1011
1017
|
setTaskDone((prev)=>prev + 1);
|
|
1012
1018
|
if (next >= tasks.length) {
|
|
1013
1019
|
finishLiveness();
|
|
1014
1020
|
return;
|
|
1015
1021
|
}
|
|
1016
|
-
|
|
1022
|
+
setTaskIdxSynced(next);
|
|
1017
1023
|
setStarted(false);
|
|
1018
1024
|
setProgress(0);
|
|
1019
|
-
//
|
|
1025
|
+
// Use the ref-based startTask via the timer so it reads the fresh taskIdx
|
|
1020
1026
|
taskTimer.current = setTimeout(()=>startTask(), 500);
|
|
1021
1027
|
};
|
|
1022
1028
|
const finishLiveness = ()=>{
|
|
@@ -1492,6 +1498,16 @@ function KernaqVerify({ visible, apiKey, backendUrl, country = 'KEN', reference,
|
|
|
1492
1498
|
const [capture, setCapture] = React.useState({});
|
|
1493
1499
|
const [result, setResult] = React.useState(null);
|
|
1494
1500
|
const [error, setError] = React.useState(null);
|
|
1501
|
+
// Mirror capture in a ref so submit() always reads the latest value even
|
|
1502
|
+
// when called from inside a stale closure (e.g. goto → submit chain).
|
|
1503
|
+
const captureRef = React.useRef({});
|
|
1504
|
+
const setCaptureSync = (updater)=>{
|
|
1505
|
+
setCapture((prev)=>{
|
|
1506
|
+
const next = updater(prev);
|
|
1507
|
+
captureRef.current = next;
|
|
1508
|
+
return next;
|
|
1509
|
+
});
|
|
1510
|
+
};
|
|
1495
1511
|
const theme = React.useMemo(()=>resolveTheme(themeProp), [
|
|
1496
1512
|
themeProp
|
|
1497
1513
|
]);
|
|
@@ -1540,30 +1556,55 @@ function KernaqVerify({ visible, apiKey, backendUrl, country = 'KEN', reference,
|
|
|
1540
1556
|
}
|
|
1541
1557
|
setStep(s);
|
|
1542
1558
|
if (s === 'processing') submit();
|
|
1559
|
+
// submit is stable (no deps that change); locale/requestPermissions are stable refs
|
|
1543
1560
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1544
1561
|
}, [
|
|
1545
|
-
capture,
|
|
1546
1562
|
locale
|
|
1547
1563
|
]);
|
|
1548
1564
|
const reset = React.useCallback(()=>{
|
|
1549
1565
|
setStep('intro');
|
|
1566
|
+
captureRef.current = {};
|
|
1550
1567
|
setCapture({});
|
|
1551
1568
|
setResult(null);
|
|
1552
1569
|
setError(null);
|
|
1553
1570
|
}, []);
|
|
1554
1571
|
const submit = React.useCallback(async ()=>{
|
|
1555
|
-
const { docFrontUri, docBackUri, selfieUri, videoUri } =
|
|
1572
|
+
const { docFrontUri, docBackUri, selfieUri, videoUri } = captureRef.current;
|
|
1573
|
+
const steps = stepsProp ?? [
|
|
1574
|
+
'document',
|
|
1575
|
+
'selfie',
|
|
1576
|
+
'liveness'
|
|
1577
|
+
];
|
|
1578
|
+
if (steps.includes('document') && !docFrontUri) {
|
|
1579
|
+
setError('Document capture is missing — please restart.');
|
|
1580
|
+
setStep('result');
|
|
1581
|
+
onError?.({
|
|
1582
|
+
code: 'capture_missing',
|
|
1583
|
+
message: 'Document capture missing'
|
|
1584
|
+
});
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
if (steps.includes('selfie') && !selfieUri) {
|
|
1588
|
+
setError('Selfie capture is missing — please restart.');
|
|
1589
|
+
setStep('result');
|
|
1590
|
+
onError?.({
|
|
1591
|
+
code: 'capture_missing',
|
|
1592
|
+
message: 'Selfie capture missing'
|
|
1593
|
+
});
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1556
1596
|
const base = (backendUrl ?? 'https://api.identity.kernaq.com/v1').replace(/\/$/, '');
|
|
1597
|
+
const endpoint = sandbox ? '/verify/sandbox' : '/verify';
|
|
1557
1598
|
const form = new FormData();
|
|
1558
1599
|
if (docFrontUri) {
|
|
1559
|
-
form.append('
|
|
1600
|
+
form.append('doc_front', {
|
|
1560
1601
|
uri: docFrontUri,
|
|
1561
1602
|
name: 'doc_front.jpg',
|
|
1562
1603
|
type: 'image/jpeg'
|
|
1563
1604
|
});
|
|
1564
1605
|
}
|
|
1565
1606
|
if (docBackUri) {
|
|
1566
|
-
form.append('
|
|
1607
|
+
form.append('doc_back', {
|
|
1567
1608
|
uri: docBackUri,
|
|
1568
1609
|
name: 'doc_back.jpg',
|
|
1569
1610
|
type: 'image/jpeg'
|
|
@@ -1586,13 +1627,14 @@ function KernaqVerify({ visible, apiKey, backendUrl, country = 'KEN', reference,
|
|
|
1586
1627
|
form.append('document_type', documentType);
|
|
1587
1628
|
form.append('country', country);
|
|
1588
1629
|
form.append('reference', reference ?? `kq_${Date.now()}`);
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1630
|
+
// sandbox is handled via URL routing above — no form field needed
|
|
1631
|
+
// Do NOT set Content-Type manually — RN fetch will set multipart/form-data with
|
|
1632
|
+
// the correct boundary automatically. Setting it manually omits the boundary and
|
|
1633
|
+
// causes the server to reject the request.
|
|
1634
|
+
const headers = {};
|
|
1593
1635
|
if (apiKey) headers['X-API-Key'] = apiKey;
|
|
1594
1636
|
try {
|
|
1595
|
-
const res = await fetch(`${base}
|
|
1637
|
+
const res = await fetch(`${base}${endpoint}`, {
|
|
1596
1638
|
method: 'POST',
|
|
1597
1639
|
headers,
|
|
1598
1640
|
body: form
|
|
@@ -1622,9 +1664,8 @@ function KernaqVerify({ visible, apiKey, backendUrl, country = 'KEN', reference,
|
|
|
1622
1664
|
});
|
|
1623
1665
|
}
|
|
1624
1666
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1625
|
-
}, [
|
|
1626
|
-
|
|
1627
|
-
]);
|
|
1667
|
+
}, []) // no deps — always reads from captureRef.current, never from stale closure
|
|
1668
|
+
;
|
|
1628
1669
|
return /*#__PURE__*/ React.createElement(reactNative.Modal, {
|
|
1629
1670
|
visible: visible,
|
|
1630
1671
|
animationType: "slide",
|
|
@@ -1651,7 +1692,7 @@ function KernaqVerify({ visible, apiKey, backendUrl, country = 'KEN', reference,
|
|
|
1651
1692
|
locale: locale,
|
|
1652
1693
|
side: "front",
|
|
1653
1694
|
onCapture: (uri)=>{
|
|
1654
|
-
|
|
1695
|
+
setCaptureSync((c)=>({
|
|
1655
1696
|
...c,
|
|
1656
1697
|
docFrontUri: uri
|
|
1657
1698
|
}));
|
|
@@ -1663,14 +1704,14 @@ function KernaqVerify({ visible, apiKey, backendUrl, country = 'KEN', reference,
|
|
|
1663
1704
|
side: "back",
|
|
1664
1705
|
frontUri: capture.docFrontUri,
|
|
1665
1706
|
onCapture: (uri)=>{
|
|
1666
|
-
|
|
1707
|
+
setCaptureSync((c)=>({
|
|
1667
1708
|
...c,
|
|
1668
1709
|
docBackUri: uri
|
|
1669
1710
|
}));
|
|
1670
1711
|
next('doc-back');
|
|
1671
1712
|
},
|
|
1672
1713
|
onRetakeFront: ()=>{
|
|
1673
|
-
|
|
1714
|
+
setCaptureSync((c)=>({
|
|
1674
1715
|
...c,
|
|
1675
1716
|
docFrontUri: undefined
|
|
1676
1717
|
}));
|
|
@@ -1680,7 +1721,7 @@ function KernaqVerify({ visible, apiKey, backendUrl, country = 'KEN', reference,
|
|
|
1680
1721
|
theme: theme,
|
|
1681
1722
|
locale: locale,
|
|
1682
1723
|
onCapture: (uri)=>{
|
|
1683
|
-
|
|
1724
|
+
setCaptureSync((c)=>({
|
|
1684
1725
|
...c,
|
|
1685
1726
|
selfieUri: uri
|
|
1686
1727
|
}));
|
|
@@ -1692,7 +1733,7 @@ function KernaqVerify({ visible, apiKey, backendUrl, country = 'KEN', reference,
|
|
|
1692
1733
|
duration: livenessDuration,
|
|
1693
1734
|
taskCount: livenessTaskCount,
|
|
1694
1735
|
onCapture: (uri)=>{
|
|
1695
|
-
|
|
1736
|
+
setCaptureSync((c)=>({
|
|
1696
1737
|
...c,
|
|
1697
1738
|
videoUri: uri
|
|
1698
1739
|
}));
|