From 05bccf0703b44d6d340e874b78d31762d456414e Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Fri, 28 Apr 2023 16:25:47 +0100 Subject: [PATCH 01/30] chore: add IsEnabledPage and DisablePage --- ios/Podfile.lock | 6 +- src/ROUTES.js | 5 + src/languages/en.js | 13 ++- src/languages/es.js | 9 +- .../AppNavigator/ModalStackNavigators.js | 36 ++++++ src/libs/Navigation/linkingConfig.js | 20 ++++ src/pages/settings/PasswordPage.js | 2 +- .../settings/Security/SecuritySettingsPage.js | 38 ++++++- .../Security/TwoFactorAuth/CodesPage.js | 0 .../Security/TwoFactorAuth/DisablePage.js | 67 ++++++++++++ .../Security/TwoFactorAuth/IsEnabledPage.js | 103 ++++++++++++++++++ .../Security/TwoFactorAuth/SuccessPage.js | 0 .../Security/TwoFactorAuth/VerifyPage.js | 0 13 files changed, 290 insertions(+), 9 deletions(-) create mode 100644 src/pages/settings/Security/TwoFactorAuth/CodesPage.js create mode 100644 src/pages/settings/Security/TwoFactorAuth/DisablePage.js create mode 100644 src/pages/settings/Security/TwoFactorAuth/IsEnabledPage.js create mode 100644 src/pages/settings/Security/TwoFactorAuth/SuccessPage.js create mode 100644 src/pages/settings/Security/TwoFactorAuth/VerifyPage.js diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e81f2d2c640c..326ccb879fbb 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1014,7 +1014,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Airship: c70eed50e429f97f5adb285423c7291fb7a032ae AirshipFrameworkProxy: 2eefb77bb77b5120b0f48814b0d44439aa3ad415 - boost: a7c83b31436843459a1961bfd74b96033dc77234 + boost: 57d2868c099736d80fcd648bf211b4431e51a558 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 FBLazyVector: ff54429f0110d3c722630a98096ba689c39f6d5f @@ -1057,7 +1057,7 @@ SPEC CHECKSUMS: Permission-LocationWhenInUse: 3ba99e45c852763f730eabecec2870c2382b7bd4 Plaid: 7d340abeadb46c7aa1a91f896c5b22395a31fcf2 PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef - RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda + RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 RCTRequired: e9e7b8b45aa9bedb2fdad71740adf07a7265b9be RCTTypeSafety: 9ae0e9206625e995f0df4d5b9ddc94411929fb30 React: a71c8e1380f07e01de721ccd52bcf9c03e81867d @@ -1129,4 +1129,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: cd132e281e9e3d7e6ec2c99c08e6ec32b37886f8 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.0 diff --git a/src/ROUTES.js b/src/ROUTES.js index d3fd21c34f32..4112a0b02d24 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -54,6 +54,11 @@ export default { SETTINGS_CONTACT_METHOD_DETAILS: `${SETTINGS_CONTACT_METHODS}/:contactMethod/details`, getEditContactMethodRoute: contactMethod => `${SETTINGS_CONTACT_METHODS}/${encodeURIComponent(contactMethod)}/details`, SETTINGS_NEW_CONTACT_METHOD: `${SETTINGS_CONTACT_METHODS}/new`, + SETTINGS_TWO_FACTOR_IS_ENABLED: 'settings/security/two-factor-auth/enabled', + SETTINGS_TWO_FACTOR_DISABLE: 'settings/security/two-factor-auth/disable', + SETTINGS_TWO_FACTOR_CODES: 'settings/security/two-factor-auth/codes', + SETTINGS_TWO_FACTOR_VERIFY: 'settings/security/two-factor-auth/verify', + SETTINGS_TWO_FACTOR_SUCCESS: 'settings/security/two-factor-auth/success', NEW_GROUP: 'new/group', NEW_CHAT: 'new/chat', NEW_TASK, diff --git a/src/languages/en.js b/src/languages/en.js index 5422adba1af9..4ad2e94a35b3 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -8,6 +8,7 @@ export default { yes: 'Yes', no: 'No', ok: 'OK', + buttonConfirm: 'Got it', attachment: 'Attachment', to: 'To', optional: 'Optional', @@ -477,10 +478,19 @@ export default { newPassword: 'Your password must have at least 8 characters, 1 capital letter, 1 lowercase letter, and 1 number.', }, }, + twoFactorAuth: { + headerTitle: 'Two-factor authentication', + twoFactorAuthEnabled: 'Two-factor authentication enabled', + whatIsTwoFactorAuth: 'Two-factor authentication (2FA) helps keep your account safe. When logging in, you’ll need to enter a code generated by your preferred authenticator app.', + disableTwoFactorAuth: 'Disable two-factor authentication', + disableTwoFactorAuthConfirmation: 'Two-factor authentication keeps your account more secure. Are you sure you want to disable it?', + disabled: 'Two-factor authentication is now disabled', + noAuthenticatorApp: 'You’ll no longer require an authenticator app to log into Expensify.', + + }, passwordConfirmationScreen: { passwordUpdated: 'Password updated!', allSet: 'You’re all set. Keep your new password safe.', - gotIt: 'Got it', }, addPayPalMePage: { enterYourUsernameToGetPaidViaPayPal: 'Get paid back via PayPal.', @@ -721,7 +731,6 @@ export default { validateAccountError: 'In order to finish setting up your bank account, you must validate your account. Please check your email to validate your account, and return here to finish up!', hasPhoneLoginError: 'To add a verified bank account please ensure your primary login is a valid email and try again. You can add your phone number as a secondary login.', hasBeenThrottledError: 'There was an error adding your bank account. Please wait a few minutes and try again.', - buttonConfirm: 'Got it', error: { noBankAccountAvailable: 'Sorry, no bank account is available', noBankAccountSelected: 'Please choose an account', diff --git a/src/languages/es.js b/src/languages/es.js index 1fb5b1c1b826..d7fbef00369f 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -7,6 +7,7 @@ export default { yes: 'Sí', no: 'No', ok: 'OK', + buttonConfirm: 'Ok, entendido', attachment: 'Archivo adjunto', to: 'A', optional: 'Opcional', @@ -476,10 +477,15 @@ export default { newPassword: 'Su contraseña debe tener al menos 8 caracteres, 1 letra mayúscula, 1 letra minúscula y 1 número.', }, }, + twoFactorAuth: { + headerTitle: '.......', + twoFactorAuthEnabled: '.......', + disableTwoFactorAuth: '.......', + whatIsTwoFactorAuth: '.......', + }, passwordConfirmationScreen: { passwordUpdated: 'Contraseña actualizada!', allSet: 'Todo está listo. Guarda tu contraseña en un lugar seguro.', - gotIt: 'Ok, entendido', }, addPayPalMePage: { enterYourUsernameToGetPaidViaPayPal: 'Recibe pagos vía PayPal.', @@ -720,7 +726,6 @@ export default { validateAccountError: 'Para terminar de configurar tu cuenta bancaria, debes validar tu cuenta de Expensify. Por favor, revisa tu correo electrónico para validar tu cuenta y vuelve aquí para continuar.', hasPhoneLoginError: 'Para agregar una cuenta bancaria verificada, asegúrate de que tu nombre de usuario principal sea un correo electrónico válido y vuelve a intentarlo. Puedes agregar tu número de teléfono como nombre de usuario secundario.', hasBeenThrottledError: 'Se produjo un error al intentar agregar tu cuenta bancaria. Por favor, espera unos minutos e inténtalo de nuevo.', - buttonConfirm: 'OK', error: { noBankAccountAvailable: 'Lo sentimos, no hay ninguna cuenta bancaria disponible', noBankAccountSelected: 'Por favor, elige una cuenta bancaria', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 3b58d67a2670..4343ee6443b8 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -522,6 +522,42 @@ const SettingsModalStackNavigator = createModalStackNavigator([ }, name: 'GetAssistance', }, + { + 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; + 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', + }, + ]); const EnablePaymentsStackNavigator = createModalStackNavigator([{ diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index ddfcdaf411e0..68a9b6d680e0 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -148,6 +148,26 @@ export default { path: ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS, exact: true, }, + Settings_TwoFactorAuthIsEnabled: { + path: ROUTES.SETTINGS_TWO_FACTOR_IS_ENABLED, + exact: true, + }, + Settings_TwoFactorAuthDisable: { + path: ROUTES.SETTINGS_TWO_FACTOR_DISABLE, + exact: true, + }, + Settings_TwoFactorAuthCodes: { + path: ROUTES.SETTINGS_TWO_FACTOR_CODES, + exact: true, + }, + Settings_TwoFactorAuthVerify: { + path: ROUTES.SETTINGS_TWO_FACTOR_VERIFY, + exact: true, + }, + Settings_TwoFactorAuthSuccess: { + path: ROUTES.SETTINGS_TWO_FACTOR_SUCCESS, + exact: true, + }, Workspace_Initial: { path: ROUTES.WORKSPACE_INITIAL, }, diff --git a/src/pages/settings/PasswordPage.js b/src/pages/settings/PasswordPage.js index dce9a5e82cc5..acb1940316d5 100755 --- a/src/pages/settings/PasswordPage.js +++ b/src/pages/settings/PasswordPage.js @@ -158,7 +158,7 @@ class PasswordPage extends Component { heading={this.props.translate('passwordConfirmationScreen.passwordUpdated')} shouldShowButton onButtonPress={Navigation.goBack} - buttonText={this.props.translate('passwordConfirmationScreen.gotIt')} + buttonText={this.props.translate('common.buttonConfirm')} description={this.props.translate('passwordConfirmationScreen.allSet')} /> ) : ( diff --git a/src/pages/settings/Security/SecuritySettingsPage.js b/src/pages/settings/Security/SecuritySettingsPage.js index 72e9df3cebfb..d2d5defa5245 100644 --- a/src/pages/settings/Security/SecuritySettingsPage.js +++ b/src/pages/settings/Security/SecuritySettingsPage.js @@ -1,6 +1,8 @@ import _ from 'underscore'; import React from 'react'; import {View, ScrollView} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import PropTypes from 'prop-types'; import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; import Navigation from '../../../libs/Navigation/Navigation'; import ROUTES from '../../../ROUTES'; @@ -9,13 +11,41 @@ import * as Expensicons from '../../../components/Icon/Expensicons'; import ScreenWrapper from '../../../components/ScreenWrapper'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; import MenuItem from '../../../components/MenuItem'; +import compose from '../../../libs/compose'; +import ONYXKEYS from '../../../ONYXKEYS'; const propTypes = { + /* Onyx Props */ + + /** Holds information about the users account that is logging in */ + account: PropTypes.shape({ + /** Whether this account has 2-FA enabled or not */ + requiresTwoFactorAuth: PropTypes.bool, + }), + ...withLocalizePropTypes, }; +const defaultProps = { + account: {}, +}; + const SecuritySettingsPage = (props) => { const menuItems = [ + { + translationKey: 'twoFactorAuth.headerTitle', + icon: Expensicons.Lock, + action: () => { + // TODO: REMOVE + Navigation.navigate(ROUTES.SETTINGS_TWO_FACTOR_IS_ENABLED); + + // if (props.account.requiresTwoFactorAuth) { + // Navigation.navigate(ROUTES.SETTINGS_TWO_FACTOR_IS_ENABLED); + // } else { + // Navigation.navigate(ROUTES.SETTINGS_TWO_FACTOR_CODES); + // } + }, + }, { translationKey: 'passwordPage.changePassword', icon: Expensicons.Key, @@ -65,6 +95,12 @@ const SecuritySettingsPage = (props) => { }; SecuritySettingsPage.propTypes = propTypes; +SecuritySettingsPage.defaultProps = defaultProps; SecuritySettingsPage.displayName = 'SettingSecurityPage'; -export default withLocalize(SecuritySettingsPage); +export default compose( + withLocalize, + withOnyx({ + account: {key: ONYXKEYS.ACCOUNT}, + }), +)(SecuritySettingsPage); diff --git a/src/pages/settings/Security/TwoFactorAuth/CodesPage.js b/src/pages/settings/Security/TwoFactorAuth/CodesPage.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/pages/settings/Security/TwoFactorAuth/DisablePage.js b/src/pages/settings/Security/TwoFactorAuth/DisablePage.js new file mode 100644 index 000000000000..c285aa45f1a8 --- /dev/null +++ b/src/pages/settings/Security/TwoFactorAuth/DisablePage.js @@ -0,0 +1,67 @@ +import React, {Component} from 'react'; +import {withOnyx} from 'react-native-onyx'; +import HeaderWithCloseButton from '../../../../components/HeaderWithCloseButton'; +import Navigation from '../../../../libs/Navigation/Navigation'; +import ScreenWrapper from '../../../../components/ScreenWrapper'; +import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; +import compose from '../../../../libs/compose'; +import ROUTES from '../../../../ROUTES'; +import FullPageOfflineBlockingView from '../../../../components/BlockingViews/FullPageOfflineBlockingView'; +import * as Illustrations from '../../../../components/Icon/Illustrations'; +import styles from '../../../../styles/styles'; +import BlockingView from '../../../../components/BlockingViews/BlockingView'; +import FixedFooter from '../../../../components/FixedFooter'; +import Button from '../../../../components/Button'; + +const propTypes = { + ...withLocalizePropTypes, +}; + +const defaultProps = { +}; + +class DisablePage extends Component { + componentDidMount() { + // TODO: TO BE IMPLEMENTED + // Session.toggleTwoFactorAuth(false); + } + + render() { + return ( + + Navigation.navigate(ROUTES.SETTINGS_SECURITY)} + onCloseButtonPress={() => Navigation.dismissModal(true)} + /> + + + + + + + + + {this.props.translate('twoFactorAuth.enterCode')} + + + + {/* TODO: NUMBER KEYBOARD */} +
Session.validateTwoFactorAuth()} + > + + +
+ + + + + - + {this.props.translate('twoFactorAuth.enterCode')} @@ -162,7 +204,7 @@ class VerifyPage extends Component { validate={this.validate} onSubmit={this.submit} draftValues={{verifyTwoFactorAuth: ''}} - style={[styles.mt5, styles.mh5]} + style={[styles.mt3, styles.mh5]} > + + + {/* TODO: Button is disabled but has no disabled styles? */} + + + + + {this.props.translate('twoFactorAuth.enterCode')} + - - {this.props.translate('twoFactorAuth.addKey')} - - - - {Boolean(this.props.account.twoFactorAuthSecretKey) && ( - - {this.splitSecretInChunks(this.props.account.twoFactorAuthSecretKey)} - - )} - + + - - - {this.props.translate('twoFactorAuth.enterCode')} - - - - - - + - - {this.props.translate('twoFactorAuth.enterCode')} - + {this.props.translate('twoFactorAuth.enterCode')} From 6c23b160e2ef8d0184e8a3c574b5e117270a5085 Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Wed, 10 May 2023 16:50:15 +0100 Subject: [PATCH 15/30] chore: address pr comments --- src/components/TextFileLink/index.android.js | 14 +- src/components/TextFileLink/index.ios.js | 14 +- src/components/TextFileLink/index.js | 14 +- .../TextFileLink/textFileLinkPropTypes.js | 17 ++ .../settings/Security/SecuritySettingsPage.js | 8 +- .../Security/TwoFactorAuth/CodesPage.js | 157 ++++++++---------- .../Security/TwoFactorAuth/DisablePage.js | 61 ++++--- .../Security/TwoFactorAuth/IsEnabledPage.js | 122 ++++++-------- .../Security/TwoFactorAuth/SuccessPage.js | 7 +- .../BaseTwoFactorAuthForm.js | 93 +++++------ .../Security/TwoFactorAuth/VerifyPage.js | 153 +++++++---------- 11 files changed, 278 insertions(+), 382 deletions(-) create mode 100644 src/components/TextFileLink/textFileLinkPropTypes.js diff --git a/src/components/TextFileLink/index.android.js b/src/components/TextFileLink/index.android.js index 738a34d552a0..cadb55c87bf7 100644 --- a/src/components/TextFileLink/index.android.js +++ b/src/components/TextFileLink/index.android.js @@ -1,18 +1,6 @@ -import PropTypes from 'prop-types'; import RNFetchBlob from 'react-native-blob-util'; import * as FileUtils from '../../libs/fileDownload/FileUtils'; - -const propTypes = { - fileName: PropTypes.string, - textContent: PropTypes.string, - children: PropTypes.func, -}; - -const defaultProps = { - fileName: '', - textContent: '', - children: () => {}, -}; +import {propTypes, defaultProps} from './textFileLinkPropTypes'; const TextFileLink = (props) => { const downloadFile = () => { diff --git a/src/components/TextFileLink/index.ios.js b/src/components/TextFileLink/index.ios.js index b7ac7e6c79d5..a74ae584c660 100644 --- a/src/components/TextFileLink/index.ios.js +++ b/src/components/TextFileLink/index.ios.js @@ -1,19 +1,7 @@ import {Share} from 'react-native'; -import PropTypes from 'prop-types'; import RNFetchBlob from 'react-native-blob-util'; import * as FileUtils from '../../libs/fileDownload/FileUtils'; - -const propTypes = { - fileName: PropTypes.string, - textContent: PropTypes.string, - children: PropTypes.func, -}; - -const defaultProps = { - fileName: '', - textContent: '', - children: () => {}, -}; +import {propTypes, defaultProps} from './textFileLinkPropTypes'; const TextFileLink = (props) => { const downloadFile = () => { diff --git a/src/components/TextFileLink/index.js b/src/components/TextFileLink/index.js index 0262cf89e519..4a0b2b420d3b 100644 --- a/src/components/TextFileLink/index.js +++ b/src/components/TextFileLink/index.js @@ -1,16 +1,4 @@ -import PropTypes from 'prop-types'; - -const propTypes = { - fileName: PropTypes.string, - textContent: PropTypes.string, - children: PropTypes.func, -}; - -const defaultProps = { - fileName: '', - textContent: '', - children: () => {}, -}; +import {propTypes, defaultProps} from './textFileLinkPropTypes'; const TextFileLink = (props) => { const downloadFile = () => { diff --git a/src/components/TextFileLink/textFileLinkPropTypes.js b/src/components/TextFileLink/textFileLinkPropTypes.js new file mode 100644 index 000000000000..a8ff67a29979 --- /dev/null +++ b/src/components/TextFileLink/textFileLinkPropTypes.js @@ -0,0 +1,17 @@ +import PropTypes from 'prop-types'; + +const propTypes = { + /** Name to the file that will be downloaded */ + fileName: PropTypes.string.isRequired, + /** Text content to the file that will be downloaded */ + textContent: PropTypes.string.isRequired, + children: PropTypes.func, +}; + +const defaultProps = { + fileName: 'download', + textContent: '', + children: () => {}, +}; + +export {propTypes, defaultProps}; diff --git a/src/pages/settings/Security/SecuritySettingsPage.js b/src/pages/settings/Security/SecuritySettingsPage.js index 01f39b753aa9..eb6d3f7d60d6 100644 --- a/src/pages/settings/Security/SecuritySettingsPage.js +++ b/src/pages/settings/Security/SecuritySettingsPage.js @@ -16,6 +16,8 @@ import ONYXKEYS from '../../../ONYXKEYS'; import * as Session from '../../../libs/actions/Session'; const propTypes = { + ...withLocalizePropTypes, + /* Onyx Props */ /** Holds information about the users account that is logging in */ @@ -23,15 +25,13 @@ const propTypes = { /** Whether this account has 2-FA enabled or not */ requiresTwoFactorAuth: PropTypes.bool, }), - - ...withLocalizePropTypes, }; const defaultProps = { account: {}, }; -const SecuritySettingsPage = (props) => { +function SecuritySettingsPage(props) { const menuItems = [ { translationKey: 'twoFactorAuth.headerTitle', @@ -85,7 +85,7 @@ const SecuritySettingsPage = (props) => {
); -}; +} SecuritySettingsPage.propTypes = propTypes; SecuritySettingsPage.defaultProps = defaultProps; diff --git a/src/pages/settings/Security/TwoFactorAuth/CodesPage.js b/src/pages/settings/Security/TwoFactorAuth/CodesPage.js index 5dba4634d233..f3cf6a1315a5 100644 --- a/src/pages/settings/Security/TwoFactorAuth/CodesPage.js +++ b/src/pages/settings/Security/TwoFactorAuth/CodesPage.js @@ -1,4 +1,4 @@ -import React, {Component} from 'react'; +import React, {useState} from 'react'; import {withOnyx} from 'react-native-onyx'; import {View} from 'react-native'; import _ from 'underscore'; @@ -21,11 +21,11 @@ import ONYXKEYS from '../../../../ONYXKEYS'; import Clipboard from '../../../../libs/Clipboard'; const propTypes = { + ...withLocalizePropTypes, account: PropTypes.shape({ /** User recovery codes for setting up 2-FA */ recoveryCodes: PropTypes.string, }), - ...withLocalizePropTypes, }; const defaultProps = { @@ -34,92 +34,79 @@ const defaultProps = { }, }; -class CodesPage extends Component { - constructor(props) { - super(props); - - this.state = { - isNextButtonDisabled: true, - }; - } - - render() { - return ( - - Navigation.navigate(ROUTES.SETTINGS_SECURITY)} - onCloseButtonPress={() => Navigation.dismissModal(true)} - /> +function CodesPage(props) { + const [isNextButtonDisabled, setIsNextButtonDisabled] = useState(true); - -
- - {this.props.translate('twoFactorAuth.codesLoseAccess')} + return ( + + Navigation.navigate(ROUTES.SETTINGS_SECURITY)} + onCloseButtonPress={() => Navigation.dismissModal(true)} + /> + +
+ + {props.translate('twoFactorAuth.codesLoseAccess')} + + + + {Boolean(props.account.recoveryCodes) && + _.map(props.account.recoveryCodes.split(', '), (code) => ( + + {code} + + ))} - - - - {Boolean(this.props.account.recoveryCodes) && - _.map(this.props.account.recoveryCodes.split(', '), (code) => ( - - {code} - - ))} - - - -
- - -
+ + - - - {this.props.translate('twoFactorAuth.enterCode')} + return ( + + Navigation.navigate(ROUTES.SETTINGS_TWO_FACTOR_CODES)} + onCloseButtonPress={() => Navigation.dismissModal(true)} + /> + + + + + {props.translate('twoFactorAuth.scanCode')} + + {' '} + {props.translate('twoFactorAuth.authenticatorApp')} + + . + + + - - - + {props.translate('twoFactorAuth.addKey')} + + {Boolean(props.account.twoFactorAuthSecretKey) && {splitSecretInChunks(props.account.twoFactorAuthSecretKey)}} + - - - - {props.translate('twoFactorAuth.enterCode')} From 2675466e77b7194034441fc9819fb7e6aa572ff1 Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Thu, 11 May 2023 16:47:57 +0100 Subject: [PATCH 18/30] chore: tweak stepCounter to accept text --- src/components/HeaderWithCloseButton.js | 1 + src/languages/en.js | 19 +++++++++++++++---- src/languages/es.js | 19 +++++++++++++++---- .../Security/TwoFactorAuth/CodesPage.js | 6 +++++- .../Security/TwoFactorAuth/SuccessPage.js | 6 +++++- .../Security/TwoFactorAuth/VerifyPage.js | 6 +++++- 6 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/components/HeaderWithCloseButton.js b/src/components/HeaderWithCloseButton.js index e1c87c0c4728..78f9d9a04d85 100755 --- a/src/components/HeaderWithCloseButton.js +++ b/src/components/HeaderWithCloseButton.js @@ -77,6 +77,7 @@ const propTypes = { stepCounter: PropTypes.shape({ step: PropTypes.number, total: PropTypes.number, + text: PropTypes.string, }), /** Whether we should show an avatar */ diff --git a/src/languages/en.js b/src/languages/en.js index 39874e925b97..a47d5568ae20 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -490,16 +490,16 @@ export default { disableTwoFactorAuthConfirmation: 'Two-factor authentication keeps your account more secure. Are you sure you want to disable it?', disabled: 'Two-factor authentication is now disabled', noAuthenticatorApp: 'You’ll no longer require an authenticator app to log into Expensify.', - stepCodes: 'Step 1: Recovery codes', + stepCodes: 'Recovery codes', keepCodesSafe: 'Keep these recovery codes safe!', codesLoseAccess: 'If you lose access to your authenticator app and don’t have these codes, you will lose access to your account. \n\nNote: Setting up two factor authentication will log you out of all other active sessions.', - stepVerify: 'Step 2: Verify', + stepVerify: 'Verify', scanCode: 'Scan the QR code using your', authenticatorApp: 'authenticator app', addKey: 'Or add this secret key to your authenticator app:', enterCode: 'Then enter the six digit code generated from your authenticator app.', - stepSuccess: 'Step 3: Finished', + stepSuccess: 'Finished', enabled: 'Two-factor authentication is now enabled!', congrats: 'Congrats, now you’ve got that extra security.', copyCodes: 'Copy codes', @@ -737,7 +737,18 @@ export default { setPasswordLinkInvalid: 'This set password link is invalid or has expired. A new one is waiting for you in your email inbox!', validateAccount: 'Verify account', }, - stepCounter: ({step, total}) => `Step ${step} of ${total}`, + stepCounter: ({step, total, text}) => { + let result = `Step ${step}`; + + if (total) { + result = `${result} of ${total}`; + } + + if (text) { + result = `${result}: ${text}`; + } + return result; + }, bankAccount: { accountNumber: 'Account number', routingNumber: 'Routing number', diff --git a/src/languages/es.js b/src/languages/es.js index daadcc46db5f..6cc52ccc28d8 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -490,16 +490,16 @@ export default { disableTwoFactorAuthConfirmation: 'La autenticación de dos factores mantiene tu cuenta más segura. ¿Estás seguro de que quieres desactivarla?', disabled: 'La autenticación de dos factores ahora está deshabilitada', noAuthenticatorApp: 'Ya no necesitarás una aplicación de autenticación para iniciar sesión en Expensify.', - stepCodes: 'Paso 1: Códigos de recuperación', + stepCodes: 'Códigos de recuperación', keepCodesSafe: '¡Guarda los códigos de recuperación en un lugar seguro!', codesLoseAccess: 'Si pierdes el acceso a tu aplicación de autenticación y no tienes estos códigos, perderás el acceso a tu cuenta. \n\nNota: Configurar la autenticación de dos factores cerrará la sesión de todas las demás sesiones activas.', - stepVerify: 'Paso 2: Verificar', + stepVerify: 'Verificar', scanCode: 'Escanea el código QR usando tu', authenticatorApp: 'aplicación de autenticación', addKey: 'O agrega esta clave secreta a su aplicación de autenticación:', enterCode: 'Luego ingresa el código de seis dígitos generado por su aplicación de autenticación.', - stepSuccess: 'Paso 3: Terminado', + stepSuccess: 'Finalizado', enabled: '¡La autenticación de dos factores ahora está habilitada!', congrats: 'Felicidades, ahora tienes esa seguridad adicional.', copyCodes: 'Copiar códigos', @@ -738,7 +738,18 @@ export default { setPasswordLinkInvalid: 'El enlace para configurar tu contraseña ha expirado. Te hemos enviado un nuevo enlace a tu correo.', validateAccount: 'Verificar cuenta', }, - stepCounter: ({step, total}) => `Paso ${step} de ${total}`, + stepCounter: ({step, total, text}) => { + let result = `Paso ${step}`; + + if (total) { + result = `${result} de ${total}`; + } + + if (text) { + result = `${result}: ${text}`; + } + return result; + }, bankAccount: { accountNumber: 'Número de cuenta', routingNumber: 'Número de ruta', diff --git a/src/pages/settings/Security/TwoFactorAuth/CodesPage.js b/src/pages/settings/Security/TwoFactorAuth/CodesPage.js index d2e77276b875..9ad0f5d3a11e 100644 --- a/src/pages/settings/Security/TwoFactorAuth/CodesPage.js +++ b/src/pages/settings/Security/TwoFactorAuth/CodesPage.js @@ -41,7 +41,11 @@ function CodesPage(props) { Navigation.navigate(ROUTES.SETTINGS_SECURITY)} onCloseButtonPress={() => Navigation.dismissModal(true)} diff --git a/src/pages/settings/Security/TwoFactorAuth/SuccessPage.js b/src/pages/settings/Security/TwoFactorAuth/SuccessPage.js index 699ded2bbd99..e1b1bdde7fd0 100644 --- a/src/pages/settings/Security/TwoFactorAuth/SuccessPage.js +++ b/src/pages/settings/Security/TwoFactorAuth/SuccessPage.js @@ -15,7 +15,11 @@ function SuccessPage(props) { Navigation.navigate(ROUTES.SETTINGS_SECURITY)} onCloseButtonPress={() => Navigation.dismissModal(true)} diff --git a/src/pages/settings/Security/TwoFactorAuth/VerifyPage.js b/src/pages/settings/Security/TwoFactorAuth/VerifyPage.js index f940db25c0c4..8c783334a642 100644 --- a/src/pages/settings/Security/TwoFactorAuth/VerifyPage.js +++ b/src/pages/settings/Security/TwoFactorAuth/VerifyPage.js @@ -86,7 +86,11 @@ function VerifyPage(props) { Navigation.navigate(ROUTES.SETTINGS_TWO_FACTOR_CODES)} onCloseButtonPress={() => Navigation.dismissModal(true)} From 93d836792e55f16612485be8ac7f1ad7c0db34d8 Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Fri, 12 May 2023 09:37:33 +0100 Subject: [PATCH 19/30] chore: refactor route names --- src/ROUTES.js | 10 +++++----- src/libs/Navigation/linkingConfig.js | 10 +++++----- src/pages/settings/Security/SecuritySettingsPage.js | 4 ++-- src/pages/settings/Security/TwoFactorAuth/CodesPage.js | 2 +- .../settings/Security/TwoFactorAuth/IsEnabledPage.js | 2 +- .../settings/Security/TwoFactorAuth/SuccessPage.js | 2 +- .../settings/Security/TwoFactorAuth/VerifyPage.js | 4 ++-- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/ROUTES.js b/src/ROUTES.js index 9599b5c6dc10..eac4cf7da32f 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -54,11 +54,11 @@ export default { SETTINGS_CONTACT_METHOD_DETAILS: `${SETTINGS_CONTACT_METHODS}/:contactMethod/details`, getEditContactMethodRoute: (contactMethod) => `${SETTINGS_CONTACT_METHODS}/${encodeURIComponent(contactMethod)}/details`, SETTINGS_NEW_CONTACT_METHOD: `${SETTINGS_CONTACT_METHODS}/new`, - SETTINGS_TWO_FACTOR_IS_ENABLED: 'settings/security/two-factor-auth/enabled', - SETTINGS_TWO_FACTOR_DISABLE: 'settings/security/two-factor-auth/disable', - SETTINGS_TWO_FACTOR_CODES: 'settings/security/two-factor-auth/codes', - SETTINGS_TWO_FACTOR_VERIFY: 'settings/security/two-factor-auth/verify', - SETTINGS_TWO_FACTOR_SUCCESS: 'settings/security/two-factor-auth/success', + SETTINGS_2FA_IS_ENABLED: 'settings/security/two-factor-auth/enabled', + SETTINGS_2FA_DISABLE: 'settings/security/two-factor-auth/disable', + SETTINGS_2FA_CODES: 'settings/security/two-factor-auth/codes', + SETTINGS_2FA_VERIFY: 'settings/security/two-factor-auth/verify', + SETTINGS_2FA_SUCCESS: 'settings/security/two-factor-auth/success', NEW_GROUP: 'new/group', NEW_CHAT: 'new/chat', NEW_TASK, diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 720b2289139f..0152e041adde 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -142,23 +142,23 @@ export default { exact: true, }, Settings_TwoFactorAuthIsEnabled: { - path: ROUTES.SETTINGS_TWO_FACTOR_IS_ENABLED, + path: ROUTES.SETTINGS_2FA_IS_ENABLED, exact: true, }, Settings_TwoFactorAuthDisable: { - path: ROUTES.SETTINGS_TWO_FACTOR_DISABLE, + path: ROUTES.SETTINGS_2FA_DISABLE, exact: true, }, Settings_TwoFactorAuthCodes: { - path: ROUTES.SETTINGS_TWO_FACTOR_CODES, + path: ROUTES.SETTINGS_2FA_CODES, exact: true, }, Settings_TwoFactorAuthVerify: { - path: ROUTES.SETTINGS_TWO_FACTOR_VERIFY, + path: ROUTES.SETTINGS_2FA_VERIFY, exact: true, }, Settings_TwoFactorAuthSuccess: { - path: ROUTES.SETTINGS_TWO_FACTOR_SUCCESS, + path: ROUTES.SETTINGS_2FA_SUCCESS, exact: true, }, Workspace_Initial: { diff --git a/src/pages/settings/Security/SecuritySettingsPage.js b/src/pages/settings/Security/SecuritySettingsPage.js index 7532f8582d1b..76c40b252b83 100644 --- a/src/pages/settings/Security/SecuritySettingsPage.js +++ b/src/pages/settings/Security/SecuritySettingsPage.js @@ -38,10 +38,10 @@ function SecuritySettingsPage(props) { icon: Expensicons.Lock, action: () => { if (props.account.requiresTwoFactorAuth) { - Navigation.navigate(ROUTES.SETTINGS_TWO_FACTOR_IS_ENABLED); + Navigation.navigate(ROUTES.SETTINGS_2FA_IS_ENABLED); } else { Session.toggleTwoFactorAuth(true); - Navigation.navigate(ROUTES.SETTINGS_TWO_FACTOR_CODES); + Navigation.navigate(ROUTES.SETTINGS_2FA_CODES); } }, }, diff --git a/src/pages/settings/Security/TwoFactorAuth/CodesPage.js b/src/pages/settings/Security/TwoFactorAuth/CodesPage.js index 9ad0f5d3a11e..8e440d64ced3 100644 --- a/src/pages/settings/Security/TwoFactorAuth/CodesPage.js +++ b/src/pages/settings/Security/TwoFactorAuth/CodesPage.js @@ -104,7 +104,7 @@ function CodesPage(props) { + medium + /> {props.translate('twoFactorAuth.enterCode')} From 465c78aeb7405fb37cb2af265d0ad9bc78c6d817 Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Fri, 12 May 2023 16:11:51 +0100 Subject: [PATCH 22/30] chore: fix design comments --- src/components/Section.js | 7 +- .../Security/TwoFactorAuth/CodesPage.js | 80 +++++++++++-------- src/styles/styles.js | 8 ++ src/styles/utilities/spacing.js | 4 + 4 files changed, 66 insertions(+), 33 deletions(-) diff --git a/src/components/Section.js b/src/components/Section.js index fe0a4419c033..3849e50c7126 100644 --- a/src/components/Section.js +++ b/src/components/Section.js @@ -26,6 +26,10 @@ const propTypes = { /** Customize the Section container */ // eslint-disable-next-line react/forbid-prop-types containerStyles: PropTypes.arrayOf(PropTypes.object), + + /** Customize the Icon container */ + // eslint-disable-next-line react/forbid-prop-types + iconContainerStyles: PropTypes.arrayOf(PropTypes.object), }; const defaultProps = { @@ -34,6 +38,7 @@ const defaultProps = { icon: null, IconComponent: null, containerStyles: [], + iconContainerStyles: [], }; const Section = (props) => { @@ -45,7 +50,7 @@ const Section = (props) => { {props.title} - + {Boolean(props.icon) && ( {props.translate('twoFactorAuth.codesLoseAccess')} - - {Boolean(props.account.recoveryCodes) && - _.map(props.account.recoveryCodes.split(', '), (code) => ( - - {code} - - ))} - - -