This commit is contained in:
Diego Mello 2020-04-23 15:28:40 -03:00
parent 738fabf11d
commit 94fdf44260
5 changed files with 285 additions and 76 deletions

View File

@ -0,0 +1,122 @@
import React, { useState, forwardRef, useImperativeHandle } from 'react';
import { View } from 'react-native';
import { Col, Row, Grid } from 'react-native-easy-grid';
import _ from 'lodash';
import styles from './styles';
import Button from './Button';
import Dots from './Dots';
import Title from './Title';
import Subtitle from './Subtitle';
import { TYPE } from './constants';
const PASSCODE_LENGTH = 6;
const Base = forwardRef(({
theme, type, onEndProcess, previousPasscode
}, ref) => {
const [passcode, setPasscode] = useState('');
const handleEnd = () => {
alert('END')
};
const wrongPasscode = () => {
setPasscode('');
console.log('TODO: wrong animation and vibration');
};
const onPressNumber = text => setPasscode((p) => {
const currentPasscode = p + text;
if (currentPasscode?.length === PASSCODE_LENGTH) {
switch (type) {
case TYPE.CHOOSE:
// if (this.props.validationRegex && this.props.validationRegex.test(currentPasscode)) {
// this.showError(true);
// } else {
// // this.endProcess(currentPasscode);
onEndProcess(currentPasscode);
// }
break;
case TYPE.CONFIRM:
if (currentPasscode !== previousPasscode) {
// this.showError();
alert('SHOW ERROR');
} else {
// this.endProcess(currentPasscode);
onEndProcess(currentPasscode);
}
break;
case TYPE.ENTER:
// this.props.endProcess(currentPasscode);
onEndProcess(currentPasscode);
// await delay(300);
break;
default:
break;
}
return '';
}
return currentPasscode;
});
const onPressDelete = () => setPasscode((p) => {
if (p?.length > 0) {
const newPasscode = p.slice(0, -1);
return newPasscode;
}
return '';
});
useImperativeHandle(ref, () => ({
wrongPasscode
}));
return (
<View style={styles.container}>
<View style={styles.container}>
<View style={styles.viewTitle}>
<Title theme={theme} />
<Subtitle theme={theme} />
</View>
<View style={styles.flexCirclePasscode}>
<Dots passcode={passcode} theme={theme} length={PASSCODE_LENGTH} />
</View>
<Grid style={styles.grid}>
<Row style={styles.row}>
{_.range(1, 4).map(i => (
<Col key={i} style={styles.colButtonCircle}>
<Button text={i} theme={theme} onPress={onPressNumber} />
</Col>
))}
</Row>
<Row style={styles.row}>
{_.range(4, 7).map(i => (
<Col key={i} style={styles.colButtonCircle}>
<Button text={i} theme={theme} onPress={onPressNumber} />
</Col>
))}
</Row>
<Row style={styles.row}>
{_.range(7, 10).map(i => (
<Col key={i} style={styles.colButtonCircle}>
<Button text={i} theme={theme} onPress={onPressNumber} />
</Col>
))}
</Row>
<Row style={styles.row}>
<Col style={styles.colButtonCircle} />
<Col style={styles.colButtonCircle}>
<Button text='0' theme={theme} onPress={onPressNumber} />
</Col>
<Col style={styles.colButtonCircle}>
<Button text='X' theme={theme} onPress={onPressDelete} />
</Col>
</Row>
</Grid>
</View>
</View>
);
});
export default Base;

View File

@ -0,0 +1,94 @@
import React, { useEffect, useState } from 'react';
import {
View, StyleSheet, Text
} from 'react-native';
import PropTypes from 'prop-types';
import moment from 'moment';
import { useAsyncStorage } from '@react-native-community/async-storage';
import sharedStyles from '../../views/Styles';
import { themes } from '../../constants/colors';
import {
PASSCODE_KEY, PASSCODE_LENGTH, LOCAL_AUTHENTICATE_EMITTER, LOCKED_OUT_TIMER_KEY, ATTEMPTS_KEY
} from '../../constants/localAuthentication';
import { resetAttempts } from '../../utils/localAuthentication';
import { TYPE } from './constants';
const TIME_TO_LOCK = 10000;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
width: '100%'
},
title: {
...sharedStyles.textRegular,
fontSize: 20,
fontWeight: '400',
marginBottom: 10,
textAlign: 'center'
},
subtitle: {
...sharedStyles.textRegular,
fontSize: 16,
fontWeight: '400',
textAlign: 'center'
}
});
const getLockedUntil = t => moment(t).add(TIME_TO_LOCK);
const getDiff = t => new Date(t) - new Date();
const Timer = ({ time, theme, setStatus }) => {
const calcTimeLeft = () => {
const diff = getDiff(time);
if (diff > 0) {
return Math.floor((diff / 1000) % 60);
}
};
const [timeLeft, setTimeLeft] = useState(calcTimeLeft());
useEffect(() => {
setTimeout(() => {
setTimeLeft(calcTimeLeft());
if (timeLeft <= 1) {
resetAttempts();
setStatus(TYPE.ENTER);
}
}, 1000);
});
if (!timeLeft) {
return null;
}
return (
<Text style={[styles.subtitle, { color: themes[theme].bodyText }]}>Try again in {timeLeft} seconds</Text>
);
};
const Locked = ({ theme, setStatus }) => {
const [lockedUntil, setLockedUntil] = useState(null);
const { getItem } = useAsyncStorage(LOCKED_OUT_TIMER_KEY);
const readItemFromStorage = async() => {
const item = await getItem();
setLockedUntil(getLockedUntil(item));
};
useEffect(() => {
readItemFromStorage();
}, []);
return (
<View style={[styles.container, { backgroundColor: themes[theme].auxiliaryBackground }]}>
<Text style={[styles.title, { color: themes[theme].titleText }]}>App locked</Text>
<Timer theme={theme} time={lockedUntil} setStatus={setStatus} />
</View>
);
};
export default Locked;

View File

@ -0,0 +1,6 @@
export const TYPE = {
CHOOSE: 'choose',
CONFIRM: 'confirm',
ENTER: 'enter',
LOCKED: 'locked'
};

View File

@ -1,85 +1,71 @@
import React, { useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { View } from 'react-native'; import { useAsyncStorage } from '@react-native-community/async-storage';
import { Col, Row, Grid } from 'react-native-easy-grid'; import RNUserDefaults from 'rn-user-defaults';
import _ from 'lodash';
import styles from './styles'; import Base from './Base';
import Button from './Button'; import Locked from './Locked';
import Dots from './Dots'; import { TYPE } from './constants';
import Title from './Title'; import { ATTEMPTS_KEY, LOCKED_OUT_TIMER_KEY, PASSCODE_KEY } from '../../constants/localAuthentication';
import Subtitle from './Subtitle'; console.log('LOCKED_OUT_TIMER_KEY', LOCKED_OUT_TIMER_KEY);
const PASSCODE_LENGTH = 6; const MAX_ATTEMPTS = 2;
const Passcode = ({ theme }) => { const PasscodeEnter = ({
const [passcode, setPasscode] = useState(''); theme, type, finishProcess
}) => {
const ref = useRef(null);
let attempts = 0;
let isLocked = false;
let passcode = null;
const [status, setStatus] = useState(type);
console.log('status', status);
// const [attempts, setAttempts] = useState(null);
// console.log('PasscodeEnter -> attempts', attempts);
// const [isLocked, setIsLocked] = useState(null);
// console.log('PasscodeEnter -> isLocked', isLocked);
// const [passcode, setPasscode] = useState('');
// console.log('passcode', passcode);
const { getItem: getAttempts, setItem: setAttempts } = useAsyncStorage(ATTEMPTS_KEY);
const { getItem: getIsLocked, setItem: setIsLocked } = useAsyncStorage(LOCKED_OUT_TIMER_KEY);
const handleEnd = () => { const fetchPasscode = async() => {
alert('END') passcode = await RNUserDefaults.get(PASSCODE_KEY);
}; };
const onPressNumber = text => setPasscode((p) => { const readStorage = async() => {
const newPasscode = p + text; isLocked = await getIsLocked();
if (newPasscode?.length === PASSCODE_LENGTH) { attempts = await getAttempts();
handleEnd(); fetchPasscode();
return ''; };
}
return newPasscode;
});
const onPressDelete = () => setPasscode((p) => { useEffect(() => {
if (p?.length > 0) { readStorage();
const newPasscode = p.slice(0, -1); }, []);
return newPasscode;
}
return '';
});
return ( const onEndProcess = (p) => {
<View style={styles.container}> if (p === passcode) {
<View style={styles.container}> finishProcess();
<View style={styles.viewTitle}> } else {
<Title theme={theme} /> attempts += 1;
<Subtitle theme={theme} /> if (attempts >= MAX_ATTEMPTS) {
</View> setStatus(TYPE.LOCKED);
<View style={styles.flexCirclePasscode}> setIsLocked(new Date().toISOString());
<Dots passcode={passcode} theme={theme} length={PASSCODE_LENGTH} /> } else {
</View> ref.current.wrongPasscode();
<Grid style={styles.grid}> setAttempts(attempts?.toString());
<Row style={styles.row}> }
{_.range(1, 4).map(i => ( }
<Col key={i} style={styles.colButtonCircle}> };
<Button text={i} theme={theme} onPress={onPressNumber} />
</Col> finishProcess = () => {
))} alert('faz submit')
</Row> }
<Row style={styles.row}>
{_.range(4, 7).map(i => ( if (status === TYPE.LOCKED) {
<Col key={i} style={styles.colButtonCircle}> return <Locked theme={theme} setStatus={setStatus} />;
<Button text={i} theme={theme} onPress={onPressNumber} /> }
</Col>
))} return <Base ref={ref} theme={theme} type={TYPE.ENTER} onEndProcess={onEndProcess} />;
</Row>
<Row style={styles.row}>
{_.range(7, 10).map(i => (
<Col key={i} style={styles.colButtonCircle}>
<Button text={i} theme={theme} onPress={onPressNumber} />
</Col>
))}
</Row>
<Row style={styles.row}>
<Col style={styles.colButtonCircle} />
<Col style={styles.colButtonCircle}>
<Button text='0' theme={theme} onPress={onPressNumber} />
</Col>
<Col style={styles.colButtonCircle}>
<Button text='X' theme={theme} onPress={onPressDelete} />
</Col>
</Row>
</Grid>
</View>
</View>
);
}; };
export default Passcode; export default PasscodeEnter;

View File

@ -24,6 +24,7 @@ import { resetAttempts } from '../utils/localAuthentication';
import { isTablet } from '../utils/deviceInfo'; import { isTablet } from '../utils/deviceInfo';
import Orientation from 'react-native-orientation-locker'; import Orientation from 'react-native-orientation-locker';
import Passcode from '../containers/Passcode'; import Passcode from '../containers/Passcode';
import { TYPE } from '../containers/Passcode/constants';
const MAX_ATTEMPTS = 6; const MAX_ATTEMPTS = 6;
const TIME_TO_LOCK = 30000; const TIME_TO_LOCK = 30000;
@ -201,7 +202,7 @@ const ScreenLockedView = ({ theme }) => {
pinAttemptsAsyncStorageName={ATTEMPTS_KEY} pinAttemptsAsyncStorageName={ATTEMPTS_KEY}
lockedPage={<AppLocked />} lockedPage={<AppLocked />}
/> */} /> */}
<Passcode theme={theme} /> <Passcode theme={theme} type={TYPE.ENTER} />
</View> </View>
</Modal> </Modal>
); );