UNPKG

react-native-biometric-verifier

Version:

A React Native module for biometric verification with face recognition and QR code scanning

162 lines (145 loc) 4.37 kB
import React, { useEffect, useRef } from 'react'; import { Animated, Text, Platform, StyleSheet } from 'react-native'; import Icon from 'react-native-vector-icons/MaterialIcons'; import PropTypes from 'prop-types'; import { Global } from '../utils/Global'; export const Notification = ({ notification, fadeAnim, slideAnim }) => { if (!notification || typeof notification !== 'object') { console.warn('Notification: Invalid or missing notification object'); return null; } const { visible, type = 'info', message = '' } = notification; if (!visible) return null; // Icon and color mapping const iconMap = { success: { name: 'check-circle', color: Global.AppTheme.success }, error: { name: 'error', color: Global.AppTheme.error }, info: { name: 'info', color: Global.AppTheme.info }, }; const { name: iconName, color: iconColor } = iconMap[type] || iconMap.info; // Heartbeat animation (scale in/out) const scaleAnim = useRef(new Animated.Value(1)).current; // Shake animation (rotation wiggle) const shakeAnim = useRef(new Animated.Value(0)).current; useEffect(() => { const heartbeat = Animated.loop( Animated.sequence([ Animated.timing(scaleAnim, { toValue: 1.2, duration: 300, useNativeDriver: true, }), Animated.timing(scaleAnim, { toValue: 1, duration: 300, useNativeDriver: true, }), ]) ); const shake = Animated.loop( Animated.sequence([ Animated.timing(shakeAnim, { toValue: 1, duration: 80, useNativeDriver: true, }), Animated.timing(shakeAnim, { toValue: -1, duration: 80, useNativeDriver: true, }), Animated.timing(shakeAnim, { toValue: 0, duration: 80, useNativeDriver: true, }), ]) ); if (visible) { heartbeat.start(); if (type === 'error') shake.start(); } else { heartbeat.stop(); shake.stop(); scaleAnim.setValue(1); shakeAnim.setValue(0); } return () => { heartbeat.stop(); shake.stop(); }; }, [visible, type, scaleAnim, shakeAnim]); // Shake rotation mapping (small wiggle) const shakeInterpolate = shakeAnim.interpolate({ inputRange: [-1, 1], outputRange: ['-10deg', '10deg'], }); return ( <Animated.View style={[ styles.container, { opacity: fadeAnim instanceof Animated.Value ? fadeAnim : 1, transform: [ { translateY: slideAnim instanceof Animated.Value ? slideAnim : 0 }, ], }, ]} > <Animated.View style={{ transform: [ { scale: scaleAnim }, ...(type === 'error' ? [{ rotate: shakeInterpolate }] : []), ], }} > <Icon name={iconName} size={50} color={iconColor} style={styles.icon} /> </Animated.View> <Text style={styles.message}> {message || 'No message provided'} </Text> </Animated.View> ); }; const styles = StyleSheet.create({ container: { padding: 15, borderRadius: 12, marginVertical: 10, width: '100%', flexDirection: 'row', alignItems: 'center', backgroundColor: Global.AppTheme.dark, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 2, }, icon: { marginRight: 10, }, message: { fontSize: 14, color: Global.AppTheme.light, fontWeight: '500', flex: 1, fontFamily: Platform.OS === 'ios' ? 'Helvetica Neue' : 'sans-serif', }, }); Notification.propTypes = { notification: PropTypes.shape({ visible: PropTypes.bool.isRequired, type: PropTypes.oneOf(['success', 'error', 'info']), message: PropTypes.string, }), fadeAnim: PropTypes.instanceOf(Animated.Value), slideAnim: PropTypes.instanceOf(Animated.Value), }; Notification.defaultProps = { notification: { visible: false, type: 'info', message: '' }, fadeAnim: new Animated.Value(1), slideAnim: new Animated.Value(0), }; export default Notification;