@rick427/react-native-liveness 0.1.7 → 0.1.9

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.
@@ -2,10 +2,12 @@
2
2
 
3
3
  import { useCallback, useEffect, useRef, useState } from 'react';
4
4
  import { Animated, StyleSheet, Text, View } from 'react-native';
5
- import { Camera, useCameraDevice, useCameraPermission } from 'react-native-vision-camera';
5
+ import { Camera, useCameraDevice, useCameraFormat, useCameraPermission } from 'react-native-vision-camera';
6
6
  import { Circle, Path, Svg } from 'react-native-svg';
7
7
  import { useLivenessCamera } from "./useLivenessCamera.js";
8
8
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
+ const DEFAULT_FONT = 'Baloo-Medium';
10
+
9
11
  // Circle diameter = 82 % of container width — large enough to fit any face
10
12
  // comfortably without the user needing to fiddle with distance.
11
13
  const CIRCLE_DIAMETER_RATIO = 0.82;
@@ -51,13 +53,9 @@ function CircleOverlay({
51
53
  }) {
52
54
  if (width === 0 || height === 0) return null;
53
55
  const cx = width / 2;
54
- // Centre the circle slightly above midpoint so the face sits naturally
55
56
  const cy = height * 0.42;
56
57
  const r = width * CIRCLE_DIAMETER_RATIO / 2;
57
58
  const color = getCircleColor(state, score);
58
-
59
- // Compound path: full-screen rect + circle cutout.
60
- // evenodd fill rule makes the circle area transparent.
61
59
  const scrimD = `M0 0H${width}V${height}H0Z ${circlePath(cx, cy, r)}`;
62
60
  return /*#__PURE__*/_jsxs(Svg, {
63
61
  style: StyleSheet.absoluteFill,
@@ -78,10 +76,9 @@ function CircleOverlay({
78
76
  });
79
77
  }
80
78
  function CountdownBubble({
81
- value
79
+ value,
80
+ fontFamily
82
81
  }) {
83
- // key={countdown} in the parent remounts this component on every tick,
84
- // so [] deps are correct — each mount runs a fresh animation.
85
82
  const scale = useRef(new Animated.Value(0)).current;
86
83
  const opacity = useRef(new Animated.Value(0)).current;
87
84
  useEffect(() => {
@@ -117,7 +114,9 @@ function CountdownBubble({
117
114
  }]
118
115
  }],
119
116
  children: /*#__PURE__*/_jsx(Text, {
120
- style: styles.countdownText,
117
+ style: [styles.countdownText, {
118
+ fontFamily
119
+ }],
121
120
  children: value
122
121
  })
123
122
  });
@@ -130,6 +129,7 @@ export function LivenessCamera({
130
129
  livenessThreshold = 0.75,
131
130
  confirmFrames = 10,
132
131
  soundEnabled = true,
132
+ fontFamily = DEFAULT_FONT,
133
133
  style
134
134
  }) {
135
135
  const {
@@ -137,6 +137,10 @@ export function LivenessCamera({
137
137
  requestPermission
138
138
  } = useCameraPermission();
139
139
  const device = useCameraDevice('front');
140
+ const format = useCameraFormat(device, [{
141
+ fps: 60
142
+ }]);
143
+ const fps = Math.min(format?.maxFps ?? 30, 60);
140
144
  const cameraRef = useRef(null);
141
145
  const [containerSize, setContainerSize] = useState({
142
146
  width: 0,
@@ -179,7 +183,9 @@ export function LivenessCamera({
179
183
  return /*#__PURE__*/_jsx(View, {
180
184
  style: [styles.root, style, styles.centered],
181
185
  children: /*#__PURE__*/_jsx(Text, {
182
- style: styles.permissionText,
186
+ style: [styles.permissionText, {
187
+ fontFamily
188
+ }],
183
189
  children: "Camera permission required"
184
190
  })
185
191
  });
@@ -188,7 +194,9 @@ export function LivenessCamera({
188
194
  return /*#__PURE__*/_jsx(View, {
189
195
  style: [styles.root, style, styles.centered],
190
196
  children: /*#__PURE__*/_jsx(Text, {
191
- style: styles.permissionText,
197
+ style: [styles.permissionText, {
198
+ fontFamily
199
+ }],
192
200
  children: "No front camera found"
193
201
  })
194
202
  });
@@ -204,7 +212,8 @@ export function LivenessCamera({
204
212
  frameProcessor: frameProcessor,
205
213
  photo: true,
206
214
  pixelFormat: "yuv",
207
- fps: 60
215
+ format: format,
216
+ fps: fps
208
217
  }), /*#__PURE__*/_jsx(CircleOverlay, {
209
218
  width: containerSize.width,
210
219
  height: containerSize.height,
@@ -213,13 +222,16 @@ export function LivenessCamera({
213
222
  }), livenessState !== 'done' && /*#__PURE__*/_jsx(View, {
214
223
  style: styles.feedbackContainer,
215
224
  children: /*#__PURE__*/_jsx(Text, {
216
- style: styles.feedbackText,
225
+ style: [styles.feedbackText, {
226
+ fontFamily
227
+ }],
217
228
  children: feedback
218
229
  })
219
230
  }), livenessState === 'countdown' && countdown !== null && /*#__PURE__*/_jsx(View, {
220
231
  style: styles.countdownContainer,
221
232
  children: /*#__PURE__*/_jsx(CountdownBubble, {
222
- value: countdown
233
+ value: countdown,
234
+ fontFamily: fontFamily
223
235
  }, countdown)
224
236
  }), livenessState === 'capturing' && /*#__PURE__*/_jsx(View, {
225
237
  style: styles.captureFlash,
@@ -254,7 +266,6 @@ const styles = StyleSheet.create({
254
266
  feedbackText: {
255
267
  color: '#fff',
256
268
  fontSize: 16,
257
- fontWeight: '600',
258
269
  textAlign: 'center',
259
270
  textShadowColor: 'rgba(0,0,0,0.8)',
260
271
  textShadowOffset: {
@@ -281,7 +292,6 @@ const styles = StyleSheet.create({
281
292
  countdownText: {
282
293
  color: '#fff',
283
294
  fontSize: 52,
284
- fontWeight: '700',
285
295
  lineHeight: 60
286
296
  },
287
297
  captureFlash: {
@@ -1 +1 @@
1
- {"version":3,"names":["useCallback","useEffect","useRef","useState","Animated","StyleSheet","Text","View","Camera","useCameraDevice","useCameraPermission","Circle","Path","Svg","useLivenessCamera","jsx","_jsx","jsxs","_jsxs","CIRCLE_DIAMETER_RATIO","STROKE_WIDTH","K","getCircleColor","state","score","circlePath","cx","cy","r","join","CircleOverlay","width","height","color","scrimD","style","absoluteFill","children","d","fill","fillRule","stroke","strokeWidth","CountdownBubble","value","scale","Value","current","opacity","parallel","sequence","spring","toValue","stiffness","damping","useNativeDriver","timing","duration","start","styles","countdownBubble","transform","countdownText","LivenessCamera","onCapture","onLivenessConfirmed","onError","countdownFrom","livenessThreshold","confirmFrames","soundEnabled","hasPermission","requestPermission","device","cameraRef","containerSize","setContainerSize","frameProcessor","livenessState","livenessScore","countdown","feedback","handleLayout","e","nativeEvent","layout","catch","Error","root","centered","permissionText","onLayout","ref","isActive","photo","pixelFormat","fps","feedbackContainer","feedbackText","countdownContainer","captureFlash","pointerEvents","create","flex","backgroundColor","overflow","justifyContent","alignItems","fontSize","textAlign","paddingHorizontal","position","bottom","left","right","fontWeight","textShadowColor","textShadowOffset","textShadowRadius","absoluteFillObject","borderRadius","borderWidth","borderColor","lineHeight"],"sourceRoot":"../../src","sources":["LivenessCamera.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useCallback","useEffect","useRef","useState","Animated","StyleSheet","Text","View","Camera","useCameraDevice","useCameraFormat","useCameraPermission","Circle","Path","Svg","useLivenessCamera","jsx","_jsx","jsxs","_jsxs","DEFAULT_FONT","CIRCLE_DIAMETER_RATIO","STROKE_WIDTH","K","getCircleColor","state","score","circlePath","cx","cy","r","join","CircleOverlay","width","height","color","scrimD","style","absoluteFill","children","d","fill","fillRule","stroke","strokeWidth","CountdownBubble","value","fontFamily","scale","Value","current","opacity","parallel","sequence","spring","toValue","stiffness","damping","useNativeDriver","timing","duration","start","styles","countdownBubble","transform","countdownText","LivenessCamera","onCapture","onLivenessConfirmed","onError","countdownFrom","livenessThreshold","confirmFrames","soundEnabled","hasPermission","requestPermission","device","format","fps","Math","min","maxFps","cameraRef","containerSize","setContainerSize","frameProcessor","livenessState","livenessScore","countdown","feedback","handleLayout","e","nativeEvent","layout","catch","Error","root","centered","permissionText","onLayout","ref","isActive","photo","pixelFormat","feedbackContainer","feedbackText","countdownContainer","captureFlash","pointerEvents","create","flex","backgroundColor","overflow","justifyContent","alignItems","fontSize","textAlign","paddingHorizontal","position","bottom","left","right","textShadowColor","textShadowOffset","textShadowRadius","absoluteFillObject","borderRadius","borderWidth","borderColor","lineHeight"],"sourceRoot":"../../src","sources":["LivenessCamera.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+
3
+ import { Modal, StyleSheet, TouchableOpacity, View } from 'react-native';
4
+ import { Path, Svg } from 'react-native-svg';
5
+ import { LivenessCamera } from "./LivenessCamera.js";
6
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
+ /** × icon drawn with SVG — no external icon library required. */
8
+ function CloseIcon({
9
+ color = '#fff',
10
+ size = 18
11
+ }) {
12
+ const pad = size * 0.1;
13
+ const end = size - pad;
14
+ return /*#__PURE__*/_jsx(Svg, {
15
+ width: size,
16
+ height: size,
17
+ viewBox: `0 0 ${size} ${size}`,
18
+ children: /*#__PURE__*/_jsx(Path, {
19
+ d: `M${pad} ${pad} L${end} ${end} M${end} ${pad} L${pad} ${end}`,
20
+ stroke: color,
21
+ strokeWidth: size * 0.14,
22
+ strokeLinecap: "round"
23
+ })
24
+ });
25
+ }
26
+
27
+ /**
28
+ * Drop-in liveness detection modal.
29
+ *
30
+ * ```tsx
31
+ * <LivenessCameraModal
32
+ * visible={showLiveness}
33
+ * onClose={() => setShowLiveness(false)}
34
+ * onCapture={(result) => console.log(result.photo.path)}
35
+ * animationType="slide"
36
+ * />
37
+ * ```
38
+ */
39
+ export function LivenessCameraModal({
40
+ visible,
41
+ onClose,
42
+ animationType = 'slide',
43
+ closeButtonStyle,
44
+ closeButtonIconColor = '#fff',
45
+ closeButtonIconSize = 18,
46
+ // forward all LivenessCamera props
47
+ onCapture,
48
+ onLivenessConfirmed,
49
+ onError,
50
+ countdownFrom,
51
+ livenessThreshold,
52
+ confirmFrames,
53
+ soundEnabled,
54
+ fontFamily
55
+ }) {
56
+ const handleCapture = result => {
57
+ onCapture(result);
58
+ // Modal stays open — consumer decides when to close via onCapture callback
59
+ };
60
+ return /*#__PURE__*/_jsx(Modal, {
61
+ visible: visible,
62
+ animationType: animationType,
63
+ statusBarTranslucent: true,
64
+ onRequestClose: onClose,
65
+ children: /*#__PURE__*/_jsxs(View, {
66
+ style: styles.container,
67
+ children: [/*#__PURE__*/_jsx(LivenessCamera, {
68
+ style: styles.camera,
69
+ onCapture: handleCapture,
70
+ onLivenessConfirmed: onLivenessConfirmed,
71
+ onError: onError,
72
+ countdownFrom: countdownFrom,
73
+ livenessThreshold: livenessThreshold,
74
+ confirmFrames: confirmFrames,
75
+ soundEnabled: soundEnabled,
76
+ fontFamily: fontFamily
77
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
78
+ style: [styles.closeButton, closeButtonStyle],
79
+ onPress: onClose,
80
+ activeOpacity: 0.7,
81
+ hitSlop: {
82
+ top: 8,
83
+ bottom: 8,
84
+ left: 8,
85
+ right: 8
86
+ },
87
+ children: /*#__PURE__*/_jsx(CloseIcon, {
88
+ color: closeButtonIconColor,
89
+ size: closeButtonIconSize
90
+ })
91
+ })]
92
+ })
93
+ });
94
+ }
95
+ const styles = StyleSheet.create({
96
+ container: {
97
+ flex: 1,
98
+ backgroundColor: '#000'
99
+ },
100
+ camera: {
101
+ flex: 1
102
+ },
103
+ closeButton: {
104
+ position: 'absolute',
105
+ top: 52,
106
+ left: 16,
107
+ width: 48,
108
+ height: 48,
109
+ borderRadius: 24,
110
+ justifyContent: 'center',
111
+ alignItems: 'center',
112
+ backgroundColor: 'rgba(0,0,0,0.5)',
113
+ borderWidth: 1,
114
+ borderColor: 'rgba(255,255,255,0.3)'
115
+ }
116
+ });
117
+ //# sourceMappingURL=LivenessCameraModal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Modal","StyleSheet","TouchableOpacity","View","Path","Svg","LivenessCamera","jsx","_jsx","jsxs","_jsxs","CloseIcon","color","size","pad","end","width","height","viewBox","children","d","stroke","strokeWidth","strokeLinecap","LivenessCameraModal","visible","onClose","animationType","closeButtonStyle","closeButtonIconColor","closeButtonIconSize","onCapture","onLivenessConfirmed","onError","countdownFrom","livenessThreshold","confirmFrames","soundEnabled","fontFamily","handleCapture","result","statusBarTranslucent","onRequestClose","style","styles","container","camera","closeButton","onPress","activeOpacity","hitSlop","top","bottom","left","right","create","flex","backgroundColor","position","borderRadius","justifyContent","alignItems","borderWidth","borderColor"],"sourceRoot":"../../src","sources":["LivenessCameraModal.tsx"],"mappings":"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","ignoreList":[]}
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
 
3
3
  export { LivenessCamera } from "./LivenessCamera.js";
4
+ export { LivenessCameraModal } from "./LivenessCameraModal.js";
4
5
  export { useLivenessCamera } from "./useLivenessCamera.js";
5
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["LivenessCamera","useLivenessCamera"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,cAAc,QAAQ,qBAAkB;AACjD,SAASC,iBAAiB,QAAQ,wBAAqB","ignoreList":[]}
1
+ {"version":3,"names":["LivenessCamera","LivenessCameraModal","useLivenessCamera"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,cAAc,QAAQ,qBAAkB;AACjD,SAASC,mBAAmB,QAAQ,0BAAuB;AAC3D,SAASC,iBAAiB,QAAQ,wBAAqB","ignoreList":[]}
@@ -1,3 +1,3 @@
1
1
  import type { LivenessCameraProps } from './types';
2
- export declare function LivenessCamera({ onCapture, onLivenessConfirmed, onError, countdownFrom, livenessThreshold, confirmFrames, soundEnabled, style, }: LivenessCameraProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function LivenessCamera({ onCapture, onLivenessConfirmed, onError, countdownFrom, livenessThreshold, confirmFrames, soundEnabled, fontFamily, style, }: LivenessCameraProps): import("react/jsx-runtime").JSX.Element;
3
3
  //# sourceMappingURL=LivenessCamera.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LivenessCamera.d.ts","sourceRoot":"","sources":["../../../src/LivenessCamera.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,SAAS,CAAC;AAoIlE,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,mBAAmB,EACnB,OAAO,EACP,aAAiB,EACjB,iBAAwB,EACxB,aAAkB,EAClB,YAAmB,EACnB,KAAK,GACN,EAAE,mBAAmB,2CAmFrB"}
1
+ {"version":3,"file":"LivenessCamera.d.ts","sourceRoot":"","sources":["../../../src/LivenessCamera.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,SAAS,CAAC;AAuIlE,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,mBAAmB,EACnB,OAAO,EACP,aAAiB,EACjB,iBAAwB,EACxB,aAAkB,EAClB,YAAmB,EACnB,UAAyB,EACzB,KAAK,GACN,EAAE,mBAAmB,2CA8FrB"}
@@ -0,0 +1,15 @@
1
+ import type { LivenessCameraModalProps } from './types';
2
+ /**
3
+ * Drop-in liveness detection modal.
4
+ *
5
+ * ```tsx
6
+ * <LivenessCameraModal
7
+ * visible={showLiveness}
8
+ * onClose={() => setShowLiveness(false)}
9
+ * onCapture={(result) => console.log(result.photo.path)}
10
+ * animationType="slide"
11
+ * />
12
+ * ```
13
+ */
14
+ export declare function LivenessCameraModal({ visible, onClose, animationType, closeButtonStyle, closeButtonIconColor, closeButtonIconSize, onCapture, onLivenessConfirmed, onError, countdownFrom, livenessThreshold, confirmFrames, soundEnabled, fontFamily, }: LivenessCameraModalProps): import("react/jsx-runtime").JSX.Element;
15
+ //# sourceMappingURL=LivenessCameraModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LivenessCameraModal.d.ts","sourceRoot":"","sources":["../../../src/LivenessCameraModal.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAwBxD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,OAAO,EACP,aAAuB,EACvB,gBAAgB,EAChB,oBAA6B,EAC7B,mBAAwB,EAExB,SAAS,EACT,mBAAmB,EACnB,OAAO,EACP,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,UAAU,GACX,EAAE,wBAAwB,2CAqC1B"}
@@ -1,4 +1,5 @@
1
1
  export { LivenessCamera } from './LivenessCamera';
2
+ export { LivenessCameraModal } from './LivenessCameraModal';
2
3
  export { useLivenessCamera } from './useLivenessCamera';
3
- export type { CaptureResult, FaceData, FeedbackMessage, LivenessCameraProps, LivenessState, } from './types';
4
+ export type { CaptureResult, FaceData, FeedbackMessage, LivenessCameraModalProps, LivenessCameraProps, LivenessState, } from './types';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EACV,aAAa,EACb,QAAQ,EACR,eAAe,EACf,mBAAmB,EACnB,aAAa,GACd,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EACV,aAAa,EACb,QAAQ,EACR,eAAe,EACf,wBAAwB,EACxB,mBAAmB,EACnB,aAAa,GACd,MAAM,SAAS,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { ViewStyle } from 'react-native';
1
+ import type { ModalProps, ViewStyle } from 'react-native';
2
2
  import type { PhotoFile } from 'react-native-vision-camera';
3
3
  export type FaceData = {
4
4
  detected: boolean;
@@ -53,9 +53,41 @@ export type LivenessCameraProps = {
53
53
  */
54
54
  style?: ViewStyle;
55
55
  /**
56
- * Whether to play a shutter sound on capture. Requires react-native-sound
57
- * to be installed. Defaults to true.
56
+ * Whether to play a shutter sound on capture. Defaults to true.
58
57
  */
59
58
  soundEnabled?: boolean;
59
+ /**
60
+ * Font family applied to all text inside the component.
61
+ * Defaults to 'Baloo-Medium'. Set to undefined to use the system font.
62
+ */
63
+ fontFamily?: string;
64
+ };
65
+ export type LivenessCameraModalProps = Omit<LivenessCameraProps, 'style'> & {
66
+ /**
67
+ * Controls modal visibility — pass your own boolean state.
68
+ */
69
+ visible: boolean;
70
+ /**
71
+ * Called when the close button is pressed or the Android back button fires.
72
+ * Use this to set your visible state to false.
73
+ */
74
+ onClose: () => void;
75
+ /**
76
+ * Modal entrance/exit animation. Defaults to 'slide'.
77
+ */
78
+ animationType?: ModalProps['animationType'];
79
+ /**
80
+ * Override styles on the close button container.
81
+ * Useful for adjusting position or size to match your design system.
82
+ */
83
+ closeButtonStyle?: ViewStyle;
84
+ /**
85
+ * Colour of the × icon inside the close button. Defaults to '#fff'.
86
+ */
87
+ closeButtonIconColor?: string;
88
+ /**
89
+ * Size of the × icon in dp. Defaults to 18.
90
+ */
91
+ closeButtonIconSize?: number;
60
92
  };
61
93
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE;QACN,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,uBAAuB,EAAE,MAAM,CAAC;IAChC,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,MAAM,eAAe,GACvB,kCAAkC,GAClC,aAAa,GACb,mBAAmB,GACnB,qBAAqB,GACrB,eAAe,GACf,YAAY,GACZ,gBAAgB,GAChB,oBAAoB,GACpB,EAAE,CAAC;AAEP,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,SAAS,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAE3C;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IAEjC;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE;QACN,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,uBAAuB,EAAE,MAAM,CAAC;IAChC,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,MAAM,eAAe,GACvB,kCAAkC,GAClC,aAAa,GACb,mBAAmB,GACnB,qBAAqB,GACrB,eAAe,GACf,YAAY,GACZ,gBAAgB,GAChB,oBAAoB,GACpB,EAAE,CAAC;AAEP,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,SAAS,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAE3C;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IAEjC;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,GAAG;IAC1E;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB;;OAEG;IACH,aAAa,CAAC,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC;IAE5C;;;OAGG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAE7B;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rick427/react-native-liveness",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Liveness detection library for React Native using Vision Camera v4 and ML Kit",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -3,12 +3,15 @@ import { Animated, StyleSheet, Text, View } from 'react-native';
3
3
  import {
4
4
  Camera,
5
5
  useCameraDevice,
6
+ useCameraFormat,
6
7
  useCameraPermission,
7
8
  } from 'react-native-vision-camera';
8
9
  import { Circle, Path, Svg } from 'react-native-svg';
9
10
  import { useLivenessCamera } from './useLivenessCamera';
10
11
  import type { LivenessCameraProps, LivenessState } from './types';
11
12
 
13
+ const DEFAULT_FONT = 'Baloo-Medium';
14
+
12
15
  // Circle diameter = 82 % of container width — large enough to fit any face
13
16
  // comfortably without the user needing to fiddle with distance.
14
17
  const CIRCLE_DIAMETER_RATIO = 0.82;
@@ -68,13 +71,10 @@ function CircleOverlay({
68
71
  if (width === 0 || height === 0) return null;
69
72
 
70
73
  const cx = width / 2;
71
- // Centre the circle slightly above midpoint so the face sits naturally
72
74
  const cy = height * 0.42;
73
75
  const r = (width * CIRCLE_DIAMETER_RATIO) / 2;
74
76
  const color = getCircleColor(state, score);
75
77
 
76
- // Compound path: full-screen rect + circle cutout.
77
- // evenodd fill rule makes the circle area transparent.
78
78
  const scrimD = `M0 0H${width}V${height}H0Z ${circlePath(cx, cy, r)}`;
79
79
 
80
80
  return (
@@ -92,9 +92,13 @@ function CircleOverlay({
92
92
  );
93
93
  }
94
94
 
95
- function CountdownBubble({ value }: { value: number }) {
96
- // key={countdown} in the parent remounts this component on every tick,
97
- // so [] deps are correct — each mount runs a fresh animation.
95
+ function CountdownBubble({
96
+ value,
97
+ fontFamily,
98
+ }: {
99
+ value: number;
100
+ fontFamily: string;
101
+ }) {
98
102
  const scale = useRef(new Animated.Value(0)).current;
99
103
  const opacity = useRef(new Animated.Value(0)).current;
100
104
 
@@ -134,7 +138,7 @@ function CountdownBubble({ value }: { value: number }) {
134
138
  <Animated.View
135
139
  style={[styles.countdownBubble, { opacity, transform: [{ scale }] }]}
136
140
  >
137
- <Text style={styles.countdownText}>{value}</Text>
141
+ <Text style={[styles.countdownText, { fontFamily }]}>{value}</Text>
138
142
  </Animated.View>
139
143
  );
140
144
  }
@@ -147,10 +151,13 @@ export function LivenessCamera({
147
151
  livenessThreshold = 0.75,
148
152
  confirmFrames = 10,
149
153
  soundEnabled = true,
154
+ fontFamily = DEFAULT_FONT,
150
155
  style,
151
156
  }: LivenessCameraProps) {
152
157
  const { hasPermission, requestPermission } = useCameraPermission();
153
158
  const device = useCameraDevice('front');
159
+ const format = useCameraFormat(device, [{ fps: 60 }]);
160
+ const fps = Math.min(format?.maxFps ?? 30, 60);
154
161
  const cameraRef = useRef<Camera>(null);
155
162
  const [containerSize, setContainerSize] = useState({ width: 0, height: 0 });
156
163
 
@@ -185,7 +192,9 @@ export function LivenessCamera({
185
192
  if (!hasPermission) {
186
193
  return (
187
194
  <View style={[styles.root, style, styles.centered]}>
188
- <Text style={styles.permissionText}>Camera permission required</Text>
195
+ <Text style={[styles.permissionText, { fontFamily }]}>
196
+ Camera permission required
197
+ </Text>
189
198
  </View>
190
199
  );
191
200
  }
@@ -193,7 +202,9 @@ export function LivenessCamera({
193
202
  if (!device) {
194
203
  return (
195
204
  <View style={[styles.root, style, styles.centered]}>
196
- <Text style={styles.permissionText}>No front camera found</Text>
205
+ <Text style={[styles.permissionText, { fontFamily }]}>
206
+ No front camera found
207
+ </Text>
197
208
  </View>
198
209
  );
199
210
  }
@@ -208,7 +219,8 @@ export function LivenessCamera({
208
219
  frameProcessor={frameProcessor}
209
220
  photo
210
221
  pixelFormat="yuv"
211
- fps={60}
222
+ format={format}
223
+ fps={fps}
212
224
  />
213
225
  <CircleOverlay
214
226
  width={containerSize.width}
@@ -218,12 +230,16 @@ export function LivenessCamera({
218
230
  />
219
231
  {livenessState !== 'done' && (
220
232
  <View style={styles.feedbackContainer}>
221
- <Text style={styles.feedbackText}>{feedback}</Text>
233
+ <Text style={[styles.feedbackText, { fontFamily }]}>{feedback}</Text>
222
234
  </View>
223
235
  )}
224
236
  {livenessState === 'countdown' && countdown !== null && (
225
237
  <View style={styles.countdownContainer}>
226
- <CountdownBubble key={countdown} value={countdown} />
238
+ <CountdownBubble
239
+ key={countdown}
240
+ value={countdown}
241
+ fontFamily={fontFamily}
242
+ />
227
243
  </View>
228
244
  )}
229
245
  {livenessState === 'capturing' && (
@@ -260,7 +276,6 @@ const styles = StyleSheet.create({
260
276
  feedbackText: {
261
277
  color: '#fff',
262
278
  fontSize: 16,
263
- fontWeight: '600',
264
279
  textAlign: 'center',
265
280
  textShadowColor: 'rgba(0,0,0,0.8)',
266
281
  textShadowOffset: { width: 0, height: 1 },
@@ -284,7 +299,6 @@ const styles = StyleSheet.create({
284
299
  countdownText: {
285
300
  color: '#fff',
286
301
  fontSize: 52,
287
- fontWeight: '700',
288
302
  lineHeight: 60,
289
303
  },
290
304
  captureFlash: {
@@ -0,0 +1,116 @@
1
+ import { Modal, StyleSheet, TouchableOpacity, View } from 'react-native';
2
+ import { Path, Svg } from 'react-native-svg';
3
+ import { LivenessCamera } from './LivenessCamera';
4
+ import type { LivenessCameraModalProps } from './types';
5
+
6
+ /** × icon drawn with SVG — no external icon library required. */
7
+ function CloseIcon({
8
+ color = '#fff',
9
+ size = 18,
10
+ }: {
11
+ color?: string;
12
+ size?: number;
13
+ }) {
14
+ const pad = size * 0.1;
15
+ const end = size - pad;
16
+ return (
17
+ <Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
18
+ <Path
19
+ d={`M${pad} ${pad} L${end} ${end} M${end} ${pad} L${pad} ${end}`}
20
+ stroke={color}
21
+ strokeWidth={size * 0.14}
22
+ strokeLinecap="round"
23
+ />
24
+ </Svg>
25
+ );
26
+ }
27
+
28
+ /**
29
+ * Drop-in liveness detection modal.
30
+ *
31
+ * ```tsx
32
+ * <LivenessCameraModal
33
+ * visible={showLiveness}
34
+ * onClose={() => setShowLiveness(false)}
35
+ * onCapture={(result) => console.log(result.photo.path)}
36
+ * animationType="slide"
37
+ * />
38
+ * ```
39
+ */
40
+ export function LivenessCameraModal({
41
+ visible,
42
+ onClose,
43
+ animationType = 'slide',
44
+ closeButtonStyle,
45
+ closeButtonIconColor = '#fff',
46
+ closeButtonIconSize = 18,
47
+ // forward all LivenessCamera props
48
+ onCapture,
49
+ onLivenessConfirmed,
50
+ onError,
51
+ countdownFrom,
52
+ livenessThreshold,
53
+ confirmFrames,
54
+ soundEnabled,
55
+ fontFamily,
56
+ }: LivenessCameraModalProps) {
57
+ const handleCapture: LivenessCameraModalProps['onCapture'] = (result) => {
58
+ onCapture(result);
59
+ // Modal stays open — consumer decides when to close via onCapture callback
60
+ };
61
+
62
+ return (
63
+ <Modal
64
+ visible={visible}
65
+ animationType={animationType}
66
+ statusBarTranslucent
67
+ onRequestClose={onClose}
68
+ >
69
+ <View style={styles.container}>
70
+ <LivenessCamera
71
+ style={styles.camera}
72
+ onCapture={handleCapture}
73
+ onLivenessConfirmed={onLivenessConfirmed}
74
+ onError={onError}
75
+ countdownFrom={countdownFrom}
76
+ livenessThreshold={livenessThreshold}
77
+ confirmFrames={confirmFrames}
78
+ soundEnabled={soundEnabled}
79
+ fontFamily={fontFamily}
80
+ />
81
+
82
+ <TouchableOpacity
83
+ style={[styles.closeButton, closeButtonStyle]}
84
+ onPress={onClose}
85
+ activeOpacity={0.7}
86
+ hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
87
+ >
88
+ <CloseIcon color={closeButtonIconColor} size={closeButtonIconSize} />
89
+ </TouchableOpacity>
90
+ </View>
91
+ </Modal>
92
+ );
93
+ }
94
+
95
+ const styles = StyleSheet.create({
96
+ container: {
97
+ flex: 1,
98
+ backgroundColor: '#000',
99
+ },
100
+ camera: {
101
+ flex: 1,
102
+ },
103
+ closeButton: {
104
+ position: 'absolute',
105
+ top: 52,
106
+ left: 16,
107
+ width: 48,
108
+ height: 48,
109
+ borderRadius: 24,
110
+ justifyContent: 'center',
111
+ alignItems: 'center',
112
+ backgroundColor: 'rgba(0,0,0,0.5)',
113
+ borderWidth: 1,
114
+ borderColor: 'rgba(255,255,255,0.3)',
115
+ },
116
+ });
package/src/index.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  export { LivenessCamera } from './LivenessCamera';
2
+ export { LivenessCameraModal } from './LivenessCameraModal';
2
3
  export { useLivenessCamera } from './useLivenessCamera';
3
4
  export type {
4
5
  CaptureResult,
5
6
  FaceData,
6
7
  FeedbackMessage,
8
+ LivenessCameraModalProps,
7
9
  LivenessCameraProps,
8
10
  LivenessState,
9
11
  } from './types';
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ViewStyle } from 'react-native';
1
+ import type { ModalProps, ViewStyle } from 'react-native';
2
2
  import type { PhotoFile } from 'react-native-vision-camera';
3
3
 
4
4
  export type FaceData = {
@@ -81,8 +81,47 @@ export type LivenessCameraProps = {
81
81
  style?: ViewStyle;
82
82
 
83
83
  /**
84
- * Whether to play a shutter sound on capture. Requires react-native-sound
85
- * to be installed. Defaults to true.
84
+ * Whether to play a shutter sound on capture. Defaults to true.
86
85
  */
87
86
  soundEnabled?: boolean;
87
+
88
+ /**
89
+ * Font family applied to all text inside the component.
90
+ * Defaults to 'Baloo-Medium'. Set to undefined to use the system font.
91
+ */
92
+ fontFamily?: string;
93
+ };
94
+
95
+ export type LivenessCameraModalProps = Omit<LivenessCameraProps, 'style'> & {
96
+ /**
97
+ * Controls modal visibility — pass your own boolean state.
98
+ */
99
+ visible: boolean;
100
+
101
+ /**
102
+ * Called when the close button is pressed or the Android back button fires.
103
+ * Use this to set your visible state to false.
104
+ */
105
+ onClose: () => void;
106
+
107
+ /**
108
+ * Modal entrance/exit animation. Defaults to 'slide'.
109
+ */
110
+ animationType?: ModalProps['animationType'];
111
+
112
+ /**
113
+ * Override styles on the close button container.
114
+ * Useful for adjusting position or size to match your design system.
115
+ */
116
+ closeButtonStyle?: ViewStyle;
117
+
118
+ /**
119
+ * Colour of the × icon inside the close button. Defaults to '#fff'.
120
+ */
121
+ closeButtonIconColor?: string;
122
+
123
+ /**
124
+ * Size of the × icon in dp. Defaults to 18.
125
+ */
126
+ closeButtonIconSize?: number;
88
127
  };