mirror of
https://github.com/mosip/inji-wallet.git
synced 2026-01-09 13:38:01 -05:00
initial
This commit is contained in:
committed by
Nichole Martinez
parent
ce4f85c068
commit
179d3ce00a
128
components/Biometrics.tsx
Normal file
128
components/Biometrics.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { Camera } from 'expo-camera';
|
||||
import { BarCodeEvent, BarCodeScanner } from 'expo-barcode-scanner';
|
||||
import { Linking, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||
import { Colors } from './ui/styleUtils';
|
||||
import { Button, Text } from './ui';
|
||||
import { GlobalContext } from '../shared/GlobalContext';
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { selectIsActive } from '../machines/app';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
scannerContainer: {
|
||||
borderWidth: 4,
|
||||
borderColor: Colors.Black,
|
||||
borderRadius: 32,
|
||||
justifyContent: 'center',
|
||||
height: 300,
|
||||
width: 300,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
scanner: {
|
||||
height: 400,
|
||||
width: '100%',
|
||||
margin: 'auto'
|
||||
},
|
||||
buttonContainer: {
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
},
|
||||
flipButtonContainer: {
|
||||
position: 'absolute',
|
||||
width: '80%',
|
||||
top: '110%',
|
||||
},
|
||||
buttonStyle: {
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
bottom: -90,
|
||||
},
|
||||
button: {
|
||||
alignSelf: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export const QrScanner: React.FC<QrScannerProps> = (props) => {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const [hasPermission, setHasPermission] = useState(null);
|
||||
const [scanned, setScanned] = useState(false);
|
||||
const [type, setType] = useState(Camera.Constants.Type.back);
|
||||
|
||||
const isActive = useSelector(appService, selectIsActive);
|
||||
|
||||
const openSettings = () => {
|
||||
Linking.openSettings();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const response = await Camera.requestCameraPermissionsAsync();
|
||||
setHasPermission(response.granted);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isActive && hasPermission === false) {
|
||||
(async () => {
|
||||
const response = await Camera.requestCameraPermissionsAsync();
|
||||
setHasPermission(response.granted);
|
||||
})();
|
||||
}
|
||||
}, [isActive]);
|
||||
|
||||
if (hasPermission === null) {
|
||||
return <View />;
|
||||
}
|
||||
|
||||
if (hasPermission === false) {
|
||||
return (
|
||||
<View style={styles.buttonContainer}>
|
||||
<Text align="center">
|
||||
This app uses the camera to scan the QR code of another device.
|
||||
</Text>
|
||||
<View style={styles.buttonStyle}>
|
||||
<Button title="Allow access to camera" onPress={openSettings} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View style={styles.scannerContainer}>
|
||||
<Camera
|
||||
style={styles.scanner}
|
||||
barCodeScannerSettings={{
|
||||
barcodeTypes: [BarCodeScanner.Constants.BarCodeType.qr]
|
||||
}}
|
||||
onBarCodeScanned={scanned ? undefined : onBarcodeScanned}
|
||||
type={type}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.flipButtonContainer}>
|
||||
<TouchableOpacity
|
||||
style={styles.button}
|
||||
onPress={() => {
|
||||
setType(
|
||||
type === Camera.Constants.Type.back
|
||||
? Camera.Constants.Type.front
|
||||
: Camera.Constants.Type.back
|
||||
);
|
||||
}}>
|
||||
<Icon name="flip-camera-ios" color={Colors.Black} size={64} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
function onBarcodeScanned(event: BarCodeEvent) {
|
||||
props.onQrFound(event.data);
|
||||
setScanned(true);
|
||||
}
|
||||
};
|
||||
|
||||
interface QrScannerProps {
|
||||
onQrFound: (data: string) => void;
|
||||
}
|
||||
12917
package-lock.json
generated
12917
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,7 @@
|
||||
"expo-camera": "~12.1.2",
|
||||
"expo-constants": "~13.0.1",
|
||||
"expo-font": "~10.0.4",
|
||||
"expo-local-authentication": "~12.0.1",
|
||||
"expo-notifications": "~0.14.0",
|
||||
"expo-splash-screen": "~0.14.1",
|
||||
"expo-status-bar": "~1.2.0",
|
||||
@@ -56,6 +57,7 @@
|
||||
"react-native-simple-markdown": "^1.1.0",
|
||||
"react-native-svg": "12.1.1",
|
||||
"react-native-system-setting": "^1.7.6",
|
||||
"react-native-unimodules": "^0.14.10",
|
||||
"react-native-vector-icons": "^8.1.0",
|
||||
"xstate": "^4.26.0"
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
NativeStackScreenProps,
|
||||
} from '@react-navigation/native-stack';
|
||||
import { AuthScreen } from '../screens/AuthScreen';
|
||||
import { BiometricScreen } from '../screens/BiometricScreen';
|
||||
import { WelcomeScreen } from '../screens/WelcomeScreen';
|
||||
import { PasscodeScreen } from '../screens/PasscodeScreen';
|
||||
import { MainLayout } from '../screens/MainLayout';
|
||||
@@ -30,6 +31,10 @@ export const baseRoutes: Screen[] = [
|
||||
name: 'Passcode',
|
||||
component: PasscodeScreen,
|
||||
},
|
||||
{
|
||||
name: 'Biometric',
|
||||
component: BiometricScreen,
|
||||
},
|
||||
];
|
||||
|
||||
export const authRoutes: Screen[] = [
|
||||
@@ -52,6 +57,9 @@ export type RootStackParamList = {
|
||||
Passcode: {
|
||||
setup: boolean;
|
||||
};
|
||||
Biometric: {
|
||||
setup: boolean;
|
||||
};
|
||||
Main: undefined;
|
||||
Notifications: undefined;
|
||||
};
|
||||
@@ -68,3 +76,8 @@ export type PasscodeRouteProps = NativeStackScreenProps<
|
||||
RootStackParamList,
|
||||
'Passcode'
|
||||
>;
|
||||
|
||||
// export type BiometricRouteProps = NativeStackScreenProps<
|
||||
// RootStackParamList,
|
||||
// 'Biometric
|
||||
// >;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useAuthScreen } from './AuthScreenController';
|
||||
|
||||
export const AuthScreen: React.FC<RootRouteProps> = (props) => {
|
||||
const controller = useAuthScreen(props);
|
||||
|
||||
console.log('-------->CONTROLLER', controller)
|
||||
return (
|
||||
<Column fill padding="32" backgroundColor={Colors.White}>
|
||||
<Column>
|
||||
@@ -19,7 +19,7 @@ export const AuthScreen: React.FC<RootRouteProps> = (props) => {
|
||||
<Icon name="fingerprint" size={180} color={Colors.Orange} />
|
||||
</Centered>
|
||||
<Column>
|
||||
<Button title="Use biometrics" margin="0 0 8 0" disabled={true} />
|
||||
<Button title="Use biometrics" margin="0 0 8 0" disabled={!controller.hasBiometric} onPress={controller.useBiometrics} />
|
||||
<Button
|
||||
type="clear"
|
||||
title="I'd rather use a passcode"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { useContext } from 'react';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import * as LocalAuthentication from 'expo-local-authentication';
|
||||
import { selectSettingUp } from '../machines/auth';
|
||||
import { RootRouteProps } from '../routes';
|
||||
import { GlobalContext } from '../shared/GlobalContext';
|
||||
@@ -7,12 +8,30 @@ import { GlobalContext } from '../shared/GlobalContext';
|
||||
export function useAuthScreen(props: RootRouteProps) {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const authService = appService.children.get('auth');
|
||||
const [hasBiometric, setHasBiometric] = useState(null);
|
||||
|
||||
const isSettingUp = useSelector(authService, selectSettingUp);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const response = await LocalAuthentication.hasHardwareAsync();
|
||||
console.log('-------> biometrics response', response);
|
||||
setHasBiometric(response);
|
||||
const available = await LocalAuthentication.supportedAuthenticationTypesAsync();
|
||||
console.log('-------> biometrics available', available);
|
||||
const enrolled = await LocalAuthentication.isEnrolledAsync();
|
||||
console.log('-------> biometrics enrolled', enrolled);
|
||||
|
||||
})();
|
||||
}, [])
|
||||
|
||||
return {
|
||||
isSettingUp,
|
||||
|
||||
hasBiometric,
|
||||
useBiometrics: () => {
|
||||
props.navigation.navigate('Biometric', { setup: isSettingUp });
|
||||
},
|
||||
usePasscode: () => {
|
||||
props.navigation.navigate('Passcode', { setup: isSettingUp });
|
||||
},
|
||||
|
||||
93
screens/BiometricScreen.tsx
Normal file
93
screens/BiometricScreen.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { View, StyleSheet, TouchableOpacity, Alert, Platform } from 'react-native';
|
||||
import Constants from 'expo-constants';
|
||||
import { LocalAuthentication } from 'expo';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Column, Text } from '../components/ui';
|
||||
import { Colors } from '../components/ui/styleUtils';
|
||||
import { RootRouteProps } from '../routes';
|
||||
import { useBiometricScreen } from './BiometricScreenController';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-around',
|
||||
paddingTop: Constants.statusBarHeight,
|
||||
backgroundColor: '#ecf0f1',
|
||||
},
|
||||
text: {
|
||||
fontSize: 18,
|
||||
textAlign: 'center',
|
||||
},
|
||||
button: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: 150,
|
||||
height: 60,
|
||||
backgroundColor: '#056ecf',
|
||||
borderRadius: 5,
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: 30,
|
||||
color: '#fff',
|
||||
},
|
||||
});
|
||||
|
||||
export const BiometricScreen: React.FC<RootRouteProps> = (props) => {
|
||||
const controller = useBiometricScreen(props);
|
||||
const [savedFingerprints, setSavedFingerprints] = useState(null);
|
||||
const [results, setResults] = useState(null)
|
||||
|
||||
const handleBiometricAuth = async () => {
|
||||
const savedBiometrics = await LocalAuthentication.isEnrolledAsync();
|
||||
setSavedFingerprints(savedBiometrics)
|
||||
}
|
||||
|
||||
const scanFingerprint = async () => {
|
||||
let results = await LocalAuthentication.authenticateAsync();
|
||||
if (results.success) {
|
||||
alert("success!");
|
||||
} else {
|
||||
alert("fail");
|
||||
}
|
||||
};
|
||||
|
||||
const showAndroidAlert = () => {
|
||||
Alert.alert(
|
||||
'Fingerprint Scan',
|
||||
'Place your finger over the touch sensor and press scan.',
|
||||
[
|
||||
{
|
||||
text: 'Scan',
|
||||
onPress: () => {
|
||||
scanFingerprint();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Cancel',
|
||||
onPress: () => console.log('Cancel'),
|
||||
style: 'cancel',
|
||||
},
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Column fill padding="32" backgroundColor={Colors.White}>
|
||||
<Text>Scan your finger</Text>
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>
|
||||
Fingerprings Saved?{' '}
|
||||
{savedFingerprints === true ? 'True' : 'False'}
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
onPress={scanFingerprint}
|
||||
style={styles.button}>
|
||||
<Text style={styles.buttonText}>SCAN</Text>
|
||||
</TouchableOpacity>
|
||||
<Text>{results}</Text>
|
||||
</View>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
41
screens/BiometricScreenController.ts
Normal file
41
screens/BiometricScreenController.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { AuthEvents, selectAuthorized, selectBiometrics } from '../machines/auth';
|
||||
import { RouteProps } from '../routes';
|
||||
import { GlobalContext } from '../shared/GlobalContext';
|
||||
|
||||
export function useBiometricScreen(props: RouteProps) {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const authService = appService.children.get('auth');
|
||||
|
||||
const isAuthorized = useSelector(authService, selectAuthorized);
|
||||
|
||||
const [biometric, setBiometric] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuthorized) {
|
||||
props.navigation.reset({
|
||||
index: 0,
|
||||
routes: [{ name: 'Main' }],
|
||||
});
|
||||
}
|
||||
}, [isAuthorized]);
|
||||
|
||||
return {
|
||||
biometric,
|
||||
setBiometric,
|
||||
error,
|
||||
setError,
|
||||
|
||||
storedBiometric: useSelector(authService, selectBiometrics),
|
||||
|
||||
LOGIN: () => {
|
||||
authService.send(AuthEvents.LOGIN());
|
||||
},
|
||||
|
||||
SETUP_BIOMETRIC: () => {
|
||||
authService.send(AuthEvents.SETUP_BIOMETRIC(biometric));
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user