From 498d5c74b3c15a4d473496a7433972e196c0b5c4 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 14 Jul 2023 18:01:01 +0200 Subject: [PATCH 01/23] refactor: wip - 2fa steps --- .../AppNavigator/ModalStackNavigators.js | 59 +++--- .../settings/Security/SecuritySettingsPage.js | 9 +- .../TwoFactorAuth/StepWrapper/StepWrapper.js | 27 +++ .../Security/TwoFactorAuth/Steps/CodesStep.js | 143 ++++++++++++++ .../TwoFactorAuth/Steps/DisableStep.js | 49 +++++ .../TwoFactorAuth/Steps/IsEnabledStep.js | 69 +++++++ .../TwoFactorAuth/Steps/SuccessStep.js | 35 ++++ .../TwoFactorAuth/Steps/VerifyStep.js | 176 ++++++++++++++++++ .../TwoFactorAuth/TwoFactorAuthPage.js | 49 +++++ 9 files changed, 579 insertions(+), 37 deletions(-) create mode 100644 src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.js create mode 100644 src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js create mode 100644 src/pages/settings/Security/TwoFactorAuth/Steps/DisableStep.js create mode 100644 src/pages/settings/Security/TwoFactorAuth/Steps/IsEnabledStep.js create mode 100644 src/pages/settings/Security/TwoFactorAuth/Steps/SuccessStep.js create mode 100644 src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.js create mode 100644 src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage.js diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index e12995db48ce..f0ba70f03ee7 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -620,41 +620,42 @@ const SettingsModalStackNavigator = createModalStackNavigator([ }, name: 'GetAssistance', }, + // TODO: remove unnecessary pages + // { + // getComponent: () => { + // const SettingsTwoFactorAuthIsEnabled = require('../../../pages/settings/Security/TwoFactorAuth/IsEnabledPage').default; + // return SettingsTwoFactorAuthIsEnabled; + // }, + // name: 'Settings_TwoFactorAuthIsEnabled', + // }, + // { + // getComponent: () => { + // const SettingsTwoFactorAuthDisable = require('../../../pages/settings/Security/TwoFactorAuth/DisablePage').default; + // return SettingsTwoFactorAuthDisable; + // }, + // name: 'Settings_TwoFactorAuthDisable', + // }, { getComponent: () => { - const SettingsTwoFactorAuthIsEnabled = require('../../../pages/settings/Security/TwoFactorAuth/IsEnabledPage').default; - return SettingsTwoFactorAuthIsEnabled; - }, - name: 'Settings_TwoFactorAuthIsEnabled', - }, - { - getComponent: () => { - const SettingsTwoFactorAuthDisable = require('../../../pages/settings/Security/TwoFactorAuth/DisablePage').default; - return SettingsTwoFactorAuthDisable; - }, - name: 'Settings_TwoFactorAuthDisable', - }, - { - getComponent: () => { - const SettingsTwoFactorAuthCodes = require('../../../pages/settings/Security/TwoFactorAuth/CodesPage').default; + const SettingsTwoFactorAuthCodes = require('../../../pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage').default; return SettingsTwoFactorAuthCodes; }, name: 'Settings_TwoFactorAuthCodes', }, - { - getComponent: () => { - const SettingsTwoFactorAuthVerify = require('../../../pages/settings/Security/TwoFactorAuth/VerifyPage').default; - return SettingsTwoFactorAuthVerify; - }, - name: 'Settings_TwoFactorAuthVerify', - }, - { - getComponent: () => { - const SettingsTwoFactorAuthSuccess = require('../../../pages/settings/Security/TwoFactorAuth/SuccessPage').default; - return SettingsTwoFactorAuthSuccess; - }, - name: 'Settings_TwoFactorAuthSuccess', - }, + // { + // getComponent: () => { + // const SettingsTwoFactorAuthVerify = require('../../../pages/settings/Security/TwoFactorAuth/VerifyPage').default; + // return SettingsTwoFactorAuthVerify; + // }, + // name: 'Settings_TwoFactorAuthVerify', + // }, + // { + // getComponent: () => { + // const SettingsTwoFactorAuthSuccess = require('../../../pages/settings/Security/TwoFactorAuth/SuccessPage').default; + // return SettingsTwoFactorAuthSuccess; + // }, + // name: 'Settings_TwoFactorAuthSuccess', + // }, ]); const EnablePaymentsStackNavigator = createModalStackNavigator([ diff --git a/src/pages/settings/Security/SecuritySettingsPage.js b/src/pages/settings/Security/SecuritySettingsPage.js index f86dee56d2c5..c4c229b6cdd3 100644 --- a/src/pages/settings/Security/SecuritySettingsPage.js +++ b/src/pages/settings/Security/SecuritySettingsPage.js @@ -36,14 +36,7 @@ function SecuritySettingsPage(props) { { translationKey: 'twoFactorAuth.headerTitle', icon: Expensicons.Shield, - action: () => { - if (props.account.requiresTwoFactorAuth) { - Navigation.navigate(ROUTES.SETTINGS_2FA_IS_ENABLED); - } else { - Session.toggleTwoFactorAuth(true); - Navigation.navigate(ROUTES.SETTINGS_2FA_CODES); - } - }, + action: () => Navigation.navigate(ROUTES.SETTINGS_2FA_CODES) }, { translationKey: 'passwordPage.changePassword', diff --git a/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.js b/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.js new file mode 100644 index 000000000000..ba23a6f5f2cd --- /dev/null +++ b/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.js @@ -0,0 +1,27 @@ +import React from 'react'; +import HeaderWithBackButton from '../../../../../components/HeaderWithBackButton'; +import ScreenWrapper from '../../../../../components/ScreenWrapper'; +import FullPageOfflineBlockingView from '../../../../../components/BlockingViews/FullPageOfflineBlockingView'; +import Navigation from "../../../../../libs/Navigation/Navigation"; +import ROUTES from "../../../../../ROUTES"; + +const defaultProps = {}; + +function StepWrapper({title, stepCounter, onBackButtonPress, children}) { + const navigateBackToSettings = () => Navigation.goBack(ROUTES.SETTINGS_SECURITY) + return ( + + + + {children} + + + ); +} + +export default StepWrapper; diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js b/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js new file mode 100644 index 000000000000..219f9826f9dc --- /dev/null +++ b/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js @@ -0,0 +1,143 @@ +import React, {useEffect, useState} from 'react'; +import {withOnyx} from 'react-native-onyx'; +import {ActivityIndicator, View} from 'react-native'; +import {ScrollView} from 'react-native-gesture-handler'; +import _ from 'underscore'; +import PropTypes from 'prop-types'; +import Navigation from '../../../../../libs/Navigation/Navigation'; +import * as Expensicons from '../../../../../components/Icon/Expensicons'; +import withLocalize, {withLocalizePropTypes} from '../../../../../components/withLocalize'; +import compose from '../../../../../libs/compose'; +import ROUTES from '../../../../../ROUTES'; +import * as Illustrations from '../../../../../components/Icon/Illustrations'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../../components/withWindowDimensions'; +import styles from '../../../../../styles/styles'; +import FixedFooter from '../../../../../components/FixedFooter'; +import Button from '../../../../../components/Button'; +import PressableWithDelayToggle from '../../../../../components/Pressable/PressableWithDelayToggle'; +import Text from '../../../../../components/Text'; +import Section from '../../../../../components/Section'; +import ONYXKEYS from '../../../../../ONYXKEYS'; +import Clipboard from '../../../../../libs/Clipboard'; +import themeColors from '../../../../../styles/themes/default'; +import localFileDownload from '../../../../../libs/localFileDownload'; +import * as TwoFactorAuthActions from '../../../../../libs/actions/TwoFactorAuthActions'; +import * as Session from "../../../../../libs/actions/Session"; +import StepWrapper from "../StepWrapper/StepWrapper"; + +const propTypes = { + ...withLocalizePropTypes, + account: PropTypes.shape({ + /** User recovery codes for setting up 2-FA */ + recoveryCodes: PropTypes.string, + + /** If recovery codes are loading */ + isLoading: PropTypes.bool, + }), +}; + +const defaultProps = { + account: { + recoveryCodes: '', + }, +}; + +function CodesStep({translate, setStep, account, ...props}) { + const [isNextButtonDisabled, setIsNextButtonDisabled] = useState(true); + + // Here, this eslint rule will make the unmount effect unreadable, possibly confusing with mount + // eslint-disable-next-line arrow-body-style + useEffect(() => { + Session.toggleTwoFactorAuth(true); + // return () => { + // TwoFactorAuthActions.clearTwoFactorAuthData(); + // }; + }, []); + + return ( + + +
+ + {translate('twoFactorAuth.codesLoseAccess')} + + + {account.isLoading ? ( + + + + ) : ( + <> + + {Boolean(account.recoveryCodes) && + _.map(account.recoveryCodes.split(', '), (code) => ( + + {code} + + ))} + + + { + Clipboard.setString(account.recoveryCodes); + setIsNextButtonDisabled(false); + }} + styles={[styles.button, styles.buttonMedium, styles.twoFactorAuthCodesButton]} + textStyles={[styles.buttonMediumText]} + /> + { + localFileDownload('two-factor-auth-codes', account.recoveryCodes); + setIsNextButtonDisabled(false); + }} + inline={false} + styles={[styles.button, styles.buttonMedium, styles.twoFactorAuthCodesButton]} + textStyles={[styles.buttonMediumText]} + /> + + + )} + +
+
+ +