[IMPROVE] - migrating the Passcode container

This commit is contained in:
AlexAlexandre 2021-07-26 14:35:37 -03:00
parent 60f659618d
commit 369b3f2ef6
14 changed files with 131 additions and 144 deletions

View File

@ -1,47 +0,0 @@
import React from 'react';
import { Text } from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles';
import { themes } from '../../../constants/colors';
import Touch from '../../../utils/touch';
import { CustomIcon } from '../../../lib/Icons';
const Button = React.memo(({
text, disabled, theme, onPress, icon
}) => {
const press = () => onPress && onPress(text);
return (
<Touch
style={[styles.buttonView, { backgroundColor: 'transparent' }]}
underlayColor={themes[theme].passcodeButtonActive}
rippleColor={themes[theme].passcodeButtonActive}
enabled={!disabled}
theme={theme}
onPress={press}
>
{
icon
? (
<CustomIcon name={icon} size={36} color={themes[theme].passcodePrimary} />
)
: (
<Text style={[styles.buttonText, { color: themes[theme].passcodePrimary }]}>
{text}
</Text>
)
}
</Touch>
);
});
Button.propTypes = {
text: PropTypes.string,
icon: PropTypes.string,
theme: PropTypes.string,
disabled: PropTypes.bool,
onPress: PropTypes.func
};
export default Button;

View File

@ -0,0 +1,44 @@
import React from 'react';
import { Text } from 'react-native';
import styles from './styles';
import { themes } from '../../../constants/colors';
import Touch from '../../../utils/touch';
import { CustomIcon } from '../../../lib/Icons';
interface IPasscodeButton {
text: string;
icon: string;
theme: string;
disabled: boolean;
onPress({}?): void;
}
const Button = React.memo(({ text, disabled, theme, onPress, icon }: Partial<IPasscodeButton>) => {
const press = () => onPress && onPress(text!);
return (
<Touch
style={[styles.buttonView, { backgroundColor: 'transparent' }]}
underlayColor={themes[theme!].passcodeButtonActive}
rippleColor={themes[theme!].passcodeButtonActive}
enabled={!disabled}
theme={theme}
onPress={press}
>
{
icon
? (
<CustomIcon name={icon} size={36} color={themes[theme!].passcodePrimary} />
)
: (
<Text style={[styles.buttonText, { color: themes[theme!].passcodePrimary }]}>
{text}
</Text>
)
}
</Touch>
);
});
export default Button;

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { View } from 'react-native'; import { View } from 'react-native';
import range from 'lodash/range'; import range from 'lodash/range';
import PropTypes from 'prop-types';
import styles from './styles'; import styles from './styles';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
@ -9,7 +8,13 @@ import { themes } from '../../../constants/colors';
const SIZE_EMPTY = 12; const SIZE_EMPTY = 12;
const SIZE_FULL = 16; const SIZE_FULL = 16;
const Dots = React.memo(({ passcode, theme, length }) => ( interface IPasscodeDots {
passcode: string;
theme: string;
length: number;
}
const Dots = React.memo(({ passcode, theme, length }: IPasscodeDots) => (
<View style={styles.dotsContainer}> <View style={styles.dotsContainer}>
{range(length).map((val) => { {range(length).map((val) => {
const lengthSup = (passcode.length >= val + 1); const lengthSup = (passcode.length >= val + 1);
@ -42,10 +47,4 @@ const Dots = React.memo(({ passcode, theme, length }) => (
</View> </View>
)); ));
Dots.propTypes = {
passcode: PropTypes.string,
theme: PropTypes.string,
length: PropTypes.string
};
export default Dots; export default Dots;

View File

@ -1,13 +1,12 @@
import React from 'react'; import React from 'react';
import { View } from 'react-native'; import { View } from 'react-native';
import { Row } from 'react-native-easy-grid'; import { Row } from 'react-native-easy-grid';
import PropTypes from 'prop-types';
import styles from './styles'; import styles from './styles';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
import { CustomIcon } from '../../../lib/Icons'; import { CustomIcon } from '../../../lib/Icons';
const LockIcon = React.memo(({ theme }) => ( const LockIcon = React.memo(({ theme }: {theme: string}) => (
<Row style={styles.row}> <Row style={styles.row}>
<View style={styles.iconView}> <View style={styles.iconView}>
<CustomIcon name='auth' size={40} color={themes[theme].passcodeLockIcon} /> <CustomIcon name='auth' size={40} color={themes[theme].passcodeLockIcon} />
@ -15,8 +14,4 @@ const LockIcon = React.memo(({ theme }) => (
</Row> </Row>
)); ));
LockIcon.propTypes = {
theme: PropTypes.string
};
export default LockIcon; export default LockIcon;

View File

@ -1,5 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { Grid } from 'react-native-easy-grid'; import { Grid } from 'react-native-easy-grid';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
@ -12,7 +11,18 @@ import Title from './Title';
import Subtitle from './Subtitle'; import Subtitle from './Subtitle';
import LockIcon from './LockIcon'; import LockIcon from './LockIcon';
const Timer = React.memo(({ time, theme, setStatus }) => { type TPasscodeTimer = {
time: string;
theme: string;
setStatus: Function;
};
interface IPasscodeLocked {
theme: string;
setStatus: Function;
}
const Timer = React.memo(({ time, theme, setStatus }: TPasscodeTimer) => {
const calcTimeLeft = () => { const calcTimeLeft = () => {
const diff = getDiff(time); const diff = getDiff(time);
if (diff > 0) { if (diff > 0) {
@ -20,7 +30,7 @@ const Timer = React.memo(({ time, theme, setStatus }) => {
} }
}; };
const [timeLeft, setTimeLeft] = useState(calcTimeLeft()); const [timeLeft, setTimeLeft] = useState<any>(calcTimeLeft());
useEffect(() => { useEffect(() => {
setTimeout(() => { setTimeout(() => {
@ -39,8 +49,8 @@ const Timer = React.memo(({ time, theme, setStatus }) => {
return <Subtitle text={I18n.t('Passcode_app_locked_subtitle', { timeLeft })} theme={theme} />; return <Subtitle text={I18n.t('Passcode_app_locked_subtitle', { timeLeft })} theme={theme} />;
}); });
const Locked = React.memo(({ theme, setStatus }) => { const Locked = React.memo(({ theme, setStatus }: IPasscodeLocked) => {
const [lockedUntil, setLockedUntil] = useState(null); const [lockedUntil, setLockedUntil] = useState<any>(null);
const readItemFromStorage = async() => { const readItemFromStorage = async() => {
const l = await getLockedUntil(); const l = await getLockedUntil();
@ -60,15 +70,4 @@ const Locked = React.memo(({ theme, setStatus }) => {
); );
}); });
Locked.propTypes = {
theme: PropTypes.string,
setStatus: PropTypes.func
};
Timer.propTypes = {
time: PropTypes.string,
theme: PropTypes.string,
setStatus: PropTypes.func
};
export default Locked; export default Locked;

View File

@ -1,12 +1,16 @@
import React from 'react'; import React from 'react';
import { View, Text } from 'react-native'; import { View, Text } from 'react-native';
import { Row } from 'react-native-easy-grid'; import { Row } from 'react-native-easy-grid';
import PropTypes from 'prop-types';
import styles from './styles'; import styles from './styles';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
const Subtitle = React.memo(({ text, theme }) => ( interface IPasscodeSubtitle {
text: string;
theme: string;
}
const Subtitle = React.memo(({ text, theme }: IPasscodeSubtitle) => (
<Row style={styles.row}> <Row style={styles.row}>
<View style={styles.subtitleView}> <View style={styles.subtitleView}>
<Text style={[styles.textSubtitle, { color: themes[theme].passcodeSecondary }]}>{text}</Text> <Text style={[styles.textSubtitle, { color: themes[theme].passcodeSecondary }]}>{text}</Text>
@ -14,9 +18,4 @@ const Subtitle = React.memo(({ text, theme }) => (
</Row> </Row>
)); ));
Subtitle.propTypes = {
text: PropTypes.string,
theme: PropTypes.string
};
export default Subtitle; export default Subtitle;

View File

@ -1,12 +1,16 @@
import React from 'react'; import React from 'react';
import { View, Text } from 'react-native'; import { View, Text } from 'react-native';
import { Row } from 'react-native-easy-grid'; import { Row } from 'react-native-easy-grid';
import PropTypes from 'prop-types';
import styles from './styles'; import styles from './styles';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
const Title = React.memo(({ text, theme }) => ( interface IPasscodeTitle {
text: string;
theme: string;
}
const Title = React.memo(({ text, theme }: IPasscodeTitle) => (
<Row style={styles.row}> <Row style={styles.row}>
<View style={styles.titleView}> <View style={styles.titleView}>
<Text style={[styles.textTitle, { color: themes[theme].passcodePrimary }]}>{text}</Text> <Text style={[styles.textTitle, { color: themes[theme].passcodePrimary }]}>{text}</Text>
@ -14,9 +18,4 @@ const Title = React.memo(({ text, theme }) => (
</Row> </Row>
)); ));
Title.propTypes = {
text: PropTypes.string,
theme: PropTypes.string
};
export default Title; export default Title;

View File

@ -1,9 +1,6 @@
import React, { import React, { useState, forwardRef, useImperativeHandle, useRef } from 'react';
useState, forwardRef, useImperativeHandle, useRef
} from 'react';
import { Col, Row, Grid } from 'react-native-easy-grid'; import { Col, Row, Grid } from 'react-native-easy-grid';
import range from 'lodash/range'; import range from 'lodash/range';
import PropTypes from 'prop-types';
import * as Animatable from 'react-native-animatable'; import * as Animatable from 'react-native-animatable';
import * as Haptics from 'expo-haptics'; import * as Haptics from 'expo-haptics';
@ -17,11 +14,23 @@ import LockIcon from './LockIcon';
import Title from './Title'; import Title from './Title';
import Subtitle from './Subtitle'; import Subtitle from './Subtitle';
interface IPasscodeBase {
theme: string;
type: string;
previousPasscode: string;
title: string;
subtitle: string;
showBiometry: string;
onEndProcess: Function;
onError: Function;
onBiometryPress(): void;
}
const Base = forwardRef(({ const Base = forwardRef(({
theme, type, onEndProcess, previousPasscode, title, subtitle, onError, showBiometry, onBiometryPress theme, type, onEndProcess, previousPasscode, title, subtitle, onError, showBiometry, onBiometryPress
}, ref) => { }: IPasscodeBase, ref) => {
const rootRef = useRef(); const rootRef = useRef<any>();
const dotsRef = useRef(); const dotsRef = useRef<any>();
const [passcode, setPasscode] = useState(''); const [passcode, setPasscode] = useState('');
const clearPasscode = () => setPasscode(''); const clearPasscode = () => setPasscode('');
@ -32,11 +41,11 @@ const Base = forwardRef(({
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
}; };
const animate = (animation, duration = 500) => { const animate = (animation: string, duration = 500) => {
rootRef?.current?.[animation](duration); rootRef?.current?.[animation](duration);
}; };
const onPressNumber = text => setPasscode((p) => { const onPressNumber = (text: string) => setPasscode((p) => {
const currentPasscode = p + text; const currentPasscode = p + text;
if (currentPasscode?.length === PASSCODE_LENGTH) { if (currentPasscode?.length === PASSCODE_LENGTH) {
switch (type) { switch (type) {
@ -77,28 +86,28 @@ const Base = forwardRef(({
<Grid style={[styles.grid, { backgroundColor: themes[theme].passcodeBackground }]}> <Grid style={[styles.grid, { backgroundColor: themes[theme].passcodeBackground }]}>
<LockIcon theme={theme} /> <LockIcon theme={theme} />
<Title text={title} theme={theme} /> <Title text={title} theme={theme} />
<Subtitle text={subtitle} theme={theme} /> <Subtitle text={subtitle!} theme={theme} />
<Row style={styles.row}> <Row style={styles.row}>
<Animatable.View ref={dotsRef}> <Animatable.View ref={dotsRef}>
<Dots passcode={passcode} theme={theme} length={PASSCODE_LENGTH} /> <Dots passcode={passcode} theme={theme} length={PASSCODE_LENGTH} />
</Animatable.View> </Animatable.View>
</Row> </Row>
<Row style={[styles.row, styles.buttonRow]}> <Row style={[styles.row, styles.buttonRow]}>
{range(1, 4).map(i => ( {range(1, 4).map((i: any) => (
<Col key={i} style={styles.colButton}> <Col key={i} style={styles.colButton}>
<Button text={i} theme={theme} onPress={onPressNumber} /> <Button text={i} theme={theme} onPress={onPressNumber} />
</Col> </Col>
))} ))}
</Row> </Row>
<Row style={[styles.row, styles.buttonRow]}> <Row style={[styles.row, styles.buttonRow]}>
{range(4, 7).map(i => ( {range(4, 7).map((i: any) => (
<Col key={i} style={styles.colButton}> <Col key={i} style={styles.colButton}>
<Button text={i} theme={theme} onPress={onPressNumber} /> <Button text={i} theme={theme} onPress={onPressNumber} />
</Col> </Col>
))} ))}
</Row> </Row>
<Row style={[styles.row, styles.buttonRow]}> <Row style={[styles.row, styles.buttonRow]}>
{range(7, 10).map(i => ( {range(7, 10).map((i: any) => (
<Col key={i} style={styles.colButton}> <Col key={i} style={styles.colButton}>
<Button text={i} theme={theme} onPress={onPressNumber} /> <Button text={i} theme={theme} onPress={onPressNumber} />
</Col> </Col>
@ -124,16 +133,4 @@ const Base = forwardRef(({
); );
}); });
Base.propTypes = {
theme: PropTypes.string,
type: PropTypes.string,
previousPasscode: PropTypes.string,
title: PropTypes.string,
subtitle: PropTypes.string,
showBiometry: PropTypes.string,
onEndProcess: PropTypes.func,
onError: PropTypes.func,
onBiometryPress: PropTypes.func
};
export default Base; export default Base;

View File

@ -1,5 +1,4 @@
import React, { useState, useRef } from 'react'; import React, { useState, useRef } from 'react';
import PropTypes from 'prop-types';
import * as Haptics from 'expo-haptics'; import * as Haptics from 'expo-haptics';
import { gestureHandlerRootHOC } from 'react-native-gesture-handler'; import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
@ -7,14 +6,20 @@ import Base from './Base';
import { TYPE } from './constants'; import { TYPE } from './constants';
import I18n from '../../i18n'; import I18n from '../../i18n';
const PasscodeChoose = ({ theme, finishProcess, force = false }) => { interface IPasscodeChoose {
const chooseRef = useRef(null); theme: string;
const confirmRef = useRef(null); force: boolean;
finishProcess: Function;
}
const PasscodeChoose = ({ theme, finishProcess, force = false }: IPasscodeChoose) => {
const chooseRef = useRef<any>(null);
const confirmRef = useRef<any>(null);
const [subtitle, setSubtitle] = useState(null); const [subtitle, setSubtitle] = useState(null);
const [status, setStatus] = useState(TYPE.CHOOSE); const [status, setStatus] = useState(TYPE.CHOOSE);
const [previousPasscode, setPreviouPasscode] = useState(null); const [previousPasscode, setPreviouPasscode] = useState<any>(null);
const firstStep = (p) => { const firstStep = (p: any) => {
setTimeout(() => { setTimeout(() => {
setStatus(TYPE.CONFIRM); setStatus(TYPE.CONFIRM);
setPreviouPasscode(p); setPreviouPasscode(p);
@ -22,7 +27,7 @@ const PasscodeChoose = ({ theme, finishProcess, force = false }) => {
}, 200); }, 200);
}; };
const changePasscode = p => finishProcess && finishProcess(p); const changePasscode = (p: string) => finishProcess && finishProcess(p);
const onError = () => { const onError = () => {
setTimeout(() => { setTimeout(() => {
@ -36,6 +41,7 @@ const PasscodeChoose = ({ theme, finishProcess, force = false }) => {
if (status === TYPE.CONFIRM) { if (status === TYPE.CONFIRM) {
return ( return (
// @ts-ignore
<Base <Base
ref={confirmRef} ref={confirmRef}
theme={theme} theme={theme}
@ -49,6 +55,7 @@ const PasscodeChoose = ({ theme, finishProcess, force = false }) => {
} }
return ( return (
// @ts-ignore
<Base <Base
ref={chooseRef} ref={chooseRef}
theme={theme} theme={theme}
@ -60,10 +67,4 @@ const PasscodeChoose = ({ theme, finishProcess, force = false }) => {
); );
}; };
PasscodeChoose.propTypes = {
theme: PropTypes.string,
force: PropTypes.bool,
finishProcess: PropTypes.func
};
export default gestureHandlerRootHOC(PasscodeChoose); export default gestureHandlerRootHOC(PasscodeChoose);

View File

@ -1,6 +1,5 @@
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { useAsyncStorage } from '@react-native-community/async-storage'; import { useAsyncStorage } from '@react-native-community/async-storage';
import PropTypes from 'prop-types';
import { gestureHandlerRootHOC } from 'react-native-gesture-handler'; import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import * as Haptics from 'expo-haptics'; import * as Haptics from 'expo-haptics';
import { sha256 } from 'js-sha256'; import { sha256 } from 'js-sha256';
@ -16,17 +15,24 @@ import { getLockedUntil, getDiff } from './utils';
import UserPreferences from '../../lib/userPreferences'; import UserPreferences from '../../lib/userPreferences';
import I18n from '../../i18n'; import I18n from '../../i18n';
const PasscodeEnter = ({ theme, hasBiometry, finishProcess }) => {
interface IPasscodePasscodeEnter {
theme: string;
hasBiometry: string;
finishProcess: Function;
}
const PasscodeEnter = ({ theme, hasBiometry, finishProcess }: IPasscodePasscodeEnter) => {
const ref = useRef(null); const ref = useRef(null);
let attempts = 0; let attempts: any = 0;
let lockedUntil = false; let lockedUntil: any = false;
const [passcode, setPasscode] = useState(null); const [passcode, setPasscode] = useState(null);
const [status, setStatus] = useState(null); const [status, setStatus] = useState(null);
const { getItem: getAttempts, setItem: setAttempts } = useAsyncStorage(ATTEMPTS_KEY); const { getItem: getAttempts, setItem: setAttempts } = useAsyncStorage(ATTEMPTS_KEY);
const { setItem: setLockedUntil } = useAsyncStorage(LOCKED_OUT_TIMER_KEY); const { setItem: setLockedUntil } = useAsyncStorage(LOCKED_OUT_TIMER_KEY);
const fetchPasscode = async() => { const fetchPasscode = async() => {
const p = await UserPreferences.getStringAsync(PASSCODE_KEY); const p: any = await UserPreferences.getStringAsync(PASSCODE_KEY);
setPasscode(p); setPasscode(p);
}; };
@ -61,7 +67,7 @@ const PasscodeEnter = ({ theme, hasBiometry, finishProcess }) => {
readStorage(); readStorage();
}, [status]); }, [status]);
const onEndProcess = (p) => { const onEndProcess = (p: any) => {
setTimeout(() => { setTimeout(() => {
if (sha256(p) === passcode) { if (sha256(p) === passcode) {
finishProcess(); finishProcess();
@ -72,6 +78,7 @@ const PasscodeEnter = ({ theme, hasBiometry, finishProcess }) => {
setLockedUntil(new Date().toISOString()); setLockedUntil(new Date().toISOString());
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
} else { } else {
// @ts-ignore
ref.current.wrongPasscode(); ref.current.wrongPasscode();
setAttempts(attempts?.toString()); setAttempts(attempts?.toString());
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning); Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning);
@ -97,10 +104,4 @@ const PasscodeEnter = ({ theme, hasBiometry, finishProcess }) => {
); );
}; };
PasscodeEnter.propTypes = {
theme: PropTypes.string,
hasBiometry: PropTypes.string,
finishProcess: PropTypes.func
};
export default gestureHandlerRootHOC(PasscodeEnter); export default gestureHandlerRootHOC(PasscodeEnter);

View File

@ -1,4 +1,4 @@
export const TYPE = { export const TYPE: any = {
CHOOSE: 'choose', CHOOSE: 'choose',
CONFIRM: 'confirm', CONFIRM: 'confirm',
ENTER: 'enter', ENTER: 'enter',

View File

@ -4,11 +4,11 @@ import moment from 'moment';
import { LOCKED_OUT_TIMER_KEY, TIME_TO_LOCK } from '../../constants/localAuthentication'; import { LOCKED_OUT_TIMER_KEY, TIME_TO_LOCK } from '../../constants/localAuthentication';
export const getLockedUntil = async() => { export const getLockedUntil = async() => {
const t = await AsyncStorage.getItem(LOCKED_OUT_TIMER_KEY); const t: any = await AsyncStorage.getItem(LOCKED_OUT_TIMER_KEY);
if (t) { if (t) {
return moment(t).add(TIME_TO_LOCK); return moment(t).add(TIME_TO_LOCK);
} }
return null; return null;
}; };
// @ts-ignore
export const getDiff = t => new Date(t) - new Date(); export const getDiff = t => new Date(t) - new Date();