This commit is contained in:
Danica Erediano
2022-02-28 16:57:03 +08:00
committed by Nichole Martinez
parent ce4f85c068
commit 179d3ce00a
8 changed files with 8858 additions and 4361 deletions

128
components/Biometrics.tsx Normal file
View 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

File diff suppressed because it is too large Load Diff

View File

@@ -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"
},

View File

@@ -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
// >;

View File

@@ -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"

View File

@@ -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 });
},

View 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>
);
};

View 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));
},
};
}