diff --git a/src/CONST.ts b/src/CONST.ts index 1538ae6b3c16..2bd5cc15d553 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -4789,12 +4789,12 @@ const CONST = { CUSTOM: 'custom', }, TWO_FACTOR_AUTH_STEPS: { - CODES: 'CODES', + COPY_CODES: 'COPY_CODES', VERIFY: 'VERIFY', SUCCESS: 'SUCCESS', ENABLED: 'ENABLED', DISABLED: 'DISABLED', - GETCODE: 'GETCODE', + DISABLE: 'DISABLE', }, DELEGATE_ROLE: { ALL: 'all', diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 1d0100add00f..e446f1e725a9 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -234,7 +234,7 @@ function Expensify() { if (!isAuthenticated) { return; } - setCrashlyticsUserId(session?.accountID ?? -1); + setCrashlyticsUserId(session?.accountID ?? CONST.DEFAULT_NUMBER_ID); }, [isAuthenticated, session?.accountID]); // Display a blank page until the onyx migration completes @@ -275,7 +275,7 @@ function Expensify() { { setShouldShowRequire2FAModal(false); - Navigation.navigate(ROUTES.SETTINGS_2FA.getRoute(ROUTES.HOME)); + Navigation.navigate(ROUTES.SETTINGS_2FA_ROOT.getRoute(ROUTES.HOME)); }} isVisible description={translate('twoFactorAuth.twoFactorAuthIsRequiredForAdminsDescription')} diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 5a15717dcecd..da6e019e7b62 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -241,11 +241,25 @@ const ROUTES = { route: 'settings/profile/contact-methods/new', getRoute: (backTo?: string) => getUrlWithBackToParam('settings/profile/contact-methods/new', backTo), }, - SETTINGS_2FA: { + + SETTINGS_2FA_ROOT: { route: 'settings/security/two-factor-auth', getRoute: (backTo?: string, forwardTo?: string) => getUrlWithBackToParam(forwardTo ? `settings/security/two-factor-auth?forwardTo=${encodeURIComponent(forwardTo)}` : 'settings/security/two-factor-auth', backTo), }, + SETTINGS_2FA_VERIFY: { + route: 'settings/security/two-factor-auth/verify', + getRoute: (backTo?: string, forwardTo?: string) => + getUrlWithBackToParam(forwardTo ? `settings/security/two-factor-auth/verify?forwardTo=${encodeURIComponent(forwardTo)}` : 'settings/security/two-factor-auth/verify', backTo), + }, + SETTINGS_2FA_SUCCESS: { + route: 'settings/security/two-factor-auth/success', + getRoute: (backTo?: string, forwardTo?: string) => + getUrlWithBackToParam(forwardTo ? `settings/security/two-factor-auth/success?forwardTo=${encodeURIComponent(forwardTo)}` : 'settings/security/two-factor-auth/success', backTo), + }, + SETTINGS_2FA_DISABLED: 'settings/security/two-factor-auth/disabled', + SETTINGS_2FA_DISABLE: 'settings/security/two-factor-auth/disable', + SETTINGS_STATUS: 'settings/profile/status', SETTINGS_STATUS_CLEAR_AFTER: 'settings/profile/status/clear-after', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 05f5a1c17c3b..8fc6dfca0972 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -75,7 +75,6 @@ const SCREENS = { ADD_BANK_ACCOUNT: 'Settings_Add_Bank_Account', ADD_US_BANK_ACCOUNT: 'Settings_Add_US_Bank_Account', CLOSE: 'Settings_Close', - TWO_FACTOR_AUTH: 'Settings_TwoFactorAuth', REPORT_CARD_LOST_OR_DAMAGED: 'Settings_ReportCardLostOrDamaged', TROUBLESHOOT: 'Settings_Troubleshoot', CONSOLE: 'Settings_Console', @@ -145,6 +144,13 @@ const SCREENS = { UPDATE_DELEGATE_ROLE: 'Settings_Delegate_Update_Role', }, }, + TWO_FACTOR_AUTH: { + ROOT: 'Settings_TwoFactorAuth_Root', + VERIFY: 'Settings_TwoFactorAuth_Verify', + SUCCESS: 'Settings_TwoFactorAuth_Success', + DISABLED: 'Settings_TwoFactorAuth_Disabled', + DISABLE: 'Settings_TwoFactorAuth_Disable', + }, SAVE_THE_WORLD: { ROOT: 'SaveTheWorld_Root', }, @@ -153,6 +159,7 @@ const SCREENS = { }, RIGHT_MODAL: { SETTINGS: 'Settings', + TWO_FACTOR_AUTH: 'TwoFactorAuth', NEW_CHAT: 'NewChat', DETAILS: 'Details', PROFILE: 'Profile', diff --git a/src/components/AnimatedStep/AnimatedStepContext.ts b/src/components/AnimatedStep/AnimatedStepContext.ts deleted file mode 100644 index 14dba4b27cc2..000000000000 --- a/src/components/AnimatedStep/AnimatedStepContext.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type React from 'react'; -import {createContext} from 'react'; -import type {ValueOf} from 'type-fest'; -import type CONST from '@src/CONST'; - -type AnimationDirection = ValueOf; - -type StepContext = { - animationDirection: AnimationDirection; - setAnimationDirection: React.Dispatch>; -}; - -const AnimatedStepContext = createContext(null); - -export default AnimatedStepContext; -export type {StepContext, AnimationDirection}; diff --git a/src/components/AnimatedStep/AnimatedStepProvider.tsx b/src/components/AnimatedStep/AnimatedStepProvider.tsx deleted file mode 100644 index ea268e1d52cb..000000000000 --- a/src/components/AnimatedStep/AnimatedStepProvider.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React, {useMemo, useState} from 'react'; -import CONST from '@src/CONST'; -import type ChildrenProps from '@src/types/utils/ChildrenProps'; -import type {AnimationDirection} from './AnimatedStepContext'; -import AnimatedStepContext from './AnimatedStepContext'; - -function AnimatedStepProvider({children}: ChildrenProps): React.ReactNode { - const [animationDirection, setAnimationDirection] = useState(CONST.ANIMATION_DIRECTION.IN); - const contextValue = useMemo(() => ({animationDirection, setAnimationDirection}), [animationDirection, setAnimationDirection]); - - return {children}; -} - -AnimatedStepProvider.displayName = 'AnimatedStepProvider'; -export default AnimatedStepProvider; diff --git a/src/components/AnimatedStep/index.tsx b/src/components/AnimatedStep/index.tsx deleted file mode 100644 index e9f99b6fc17c..000000000000 --- a/src/components/AnimatedStep/index.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React, {useMemo} from 'react'; -import type {StyleProp, ViewStyle} from 'react-native'; -// eslint-disable-next-line no-restricted-imports -- will be removed in the future PR -import * as Animatable from 'react-native-animatable'; -import useThemeStyles from '@hooks/useThemeStyles'; -import CONST from '@src/CONST'; -import type ChildrenProps from '@src/types/utils/ChildrenProps'; -import type {AnimationDirection} from './AnimatedStepContext'; - -type AnimatedStepProps = ChildrenProps & { - /** Styles to be assigned to Container */ - style: StyleProp; - - /** Whether we're animating the step in or out */ - direction: AnimationDirection; - - /** Callback to fire when the animation ends */ - onAnimationEnd?: () => void; -}; - -function AnimatedStep({onAnimationEnd, direction = CONST.ANIMATION_DIRECTION.IN, style, children}: AnimatedStepProps) { - const styles = useThemeStyles(); - - const animationStyle = useMemo(() => { - const transitionValue = direction === 'in' ? CONST.ANIMATED_TRANSITION_FROM_VALUE : -CONST.ANIMATED_TRANSITION_FROM_VALUE; - - return styles.makeSlideInTranslation('translateX', transitionValue); - }, [direction, styles]); - - return ( - { - if (!onAnimationEnd) { - return; - } - onAnimationEnd(); - }} - duration={CONST.ANIMATED_TRANSITION} - animation={animationStyle} - style={style} - > - {children} - - ); -} - -AnimatedStep.displayName = 'AnimatedStep'; -export default AnimatedStep; diff --git a/src/components/AnimatedStep/useAnimatedStepContext.ts b/src/components/AnimatedStep/useAnimatedStepContext.ts deleted file mode 100644 index 2adde8fd576e..000000000000 --- a/src/components/AnimatedStep/useAnimatedStepContext.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {useContext} from 'react'; -import type {StepContext} from './AnimatedStepContext'; -import AnimatedStepContext from './AnimatedStepContext'; - -function useAnimatedStepContext(): StepContext { - const context = useContext(AnimatedStepContext); - if (!context) { - throw new Error('useAnimatedStepContext must be used within an AnimatedStepContextProvider'); - } - return context; -} - -export default useAnimatedStepContext; diff --git a/src/components/ConnectToXeroFlow/index.native.tsx b/src/components/ConnectToXeroFlow/index.native.tsx index 6c9adfe8dbd7..5cda933a7ebe 100644 --- a/src/components/ConnectToXeroFlow/index.native.tsx +++ b/src/components/ConnectToXeroFlow/index.native.tsx @@ -43,7 +43,7 @@ function ConnectToXeroFlow({policyID}: ConnectToXeroFlowProps) { { setIsRequire2FAModalOpen(false); - Navigation.navigate(ROUTES.SETTINGS_2FA.getRoute(ROUTES.POLICY_ACCOUNTING.getRoute(policyID), getXeroSetupLink(policyID))); + Navigation.navigate(ROUTES.SETTINGS_2FA_ROOT.getRoute(ROUTES.POLICY_ACCOUNTING.getRoute(policyID), getXeroSetupLink(policyID))); }} onCancel={() => setIsRequire2FAModalOpen(false)} isVisible={isRequire2FAModalOpen} diff --git a/src/components/ConnectToXeroFlow/index.tsx b/src/components/ConnectToXeroFlow/index.tsx index 7ed2c73ba348..08ba20f71cdc 100644 --- a/src/components/ConnectToXeroFlow/index.tsx +++ b/src/components/ConnectToXeroFlow/index.tsx @@ -5,7 +5,7 @@ import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; import {getXeroSetupLink} from '@libs/actions/connections/Xero'; import Navigation from '@libs/Navigation/Navigation'; -import * as Link from '@userActions/Link'; +import {openLink} from '@userActions/Link'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {ConnectToXeroFlowProps} from './types'; @@ -24,7 +24,7 @@ function ConnectToXeroFlow({policyID}: ConnectToXeroFlowProps) { setIsRequire2FAModalOpen(true); return; } - Link.openLink(getXeroSetupLink(policyID), environmentURL); + openLink(getXeroSetupLink(policyID), environmentURL); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, []); @@ -33,7 +33,7 @@ function ConnectToXeroFlow({policyID}: ConnectToXeroFlowProps) { { setIsRequire2FAModalOpen(false); - Navigation.navigate(ROUTES.SETTINGS_2FA.getRoute(ROUTES.POLICY_ACCOUNTING.getRoute(policyID), getXeroSetupLink(policyID))); + Navigation.navigate(ROUTES.SETTINGS_2FA_ROOT.getRoute(ROUTES.POLICY_ACCOUNTING.getRoute(policyID), getXeroSetupLink(policyID))); }} onCancel={() => { setIsRequire2FAModalOpen(false); diff --git a/src/components/TabSelector/TabSelectorItem.tsx b/src/components/TabSelector/TabSelectorItem.tsx index ea5ffc00d81d..b5d067d410c3 100644 --- a/src/components/TabSelector/TabSelectorItem.tsx +++ b/src/components/TabSelector/TabSelectorItem.tsx @@ -57,7 +57,7 @@ function TabSelectorItem({ > setIsHovered(true)} diff --git a/src/languages/en.ts b/src/languages/en.ts index 361251ea1a96..9524986210a8 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1186,6 +1186,7 @@ const translations = { securityPage: { title: 'Security options', subtitle: 'Enable two-factor authentication to keep your account safe.', + goToSecurity: 'Go back to security page', }, shareCodePage: { title: 'Your code', diff --git a/src/languages/es.ts b/src/languages/es.ts index f82b8fd7d064..bd2e41ee7ccf 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1184,6 +1184,7 @@ const translations = { securityPage: { title: 'Opciones de seguridad', subtitle: 'Activa la autenticación de dos factores para mantener tu cuenta segura.', + goToSecurity: 'Volver a la página de seguridad', }, shareCodePage: { title: 'Tu código', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 98a542451028..c16fc4e164db 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -386,7 +386,6 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/accounting/qbd/import/QuickbooksDesktopItemsPage').default, [SCREENS.REIMBURSEMENT_ACCOUNT]: () => require('../../../../pages/ReimbursementAccount/ReimbursementAccountPage').default, [SCREENS.GET_ASSISTANCE]: () => require('../../../../pages/GetAssistancePage').default, - [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: () => require('../../../../pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage').default, [SCREENS.SETTINGS.REPORT_CARD_LOST_OR_DAMAGED]: () => require('../../../../pages/settings/Wallet/ReportCardLostPage').default, [SCREENS.KEYBOARD_SHORTCUTS]: () => require('../../../../pages/KeyboardShortcutsPage').default, [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: () => require('../../../../pages/settings/ExitSurvey/ExitSurveyReasonPage').default, @@ -613,6 +612,14 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/perDiem/EditPerDiemCurrencyPage').default, }); +const TwoFactorAuthenticatorStackNavigator = createModalStackNavigator({ + [SCREENS.TWO_FACTOR_AUTH.ROOT]: () => require('../../../../pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage').default, + [SCREENS.TWO_FACTOR_AUTH.VERIFY]: () => require('../../../../pages/settings/Security/TwoFactorAuth/VerifyPage').default, + [SCREENS.TWO_FACTOR_AUTH.DISABLED]: () => require('../../../../pages/settings/Security/TwoFactorAuth/DisabledPage').default, + [SCREENS.TWO_FACTOR_AUTH.DISABLE]: () => require('../../../../pages/settings/Security/TwoFactorAuth/DisablePage').default, + [SCREENS.TWO_FACTOR_AUTH.SUCCESS]: () => require('../../../../pages/settings/Security/TwoFactorAuth/SuccessPage').default, +}); + const EnablePaymentsStackNavigator = createModalStackNavigator({ [SCREENS.ENABLE_PAYMENTS_ROOT]: () => require('../../../../pages/EnablePayments/EnablePaymentsPage').default, }); @@ -738,6 +745,7 @@ export { ReportSettingsModalStackNavigator, RoomMembersModalStackNavigator, SettingsModalStackNavigator, + TwoFactorAuthenticatorStackNavigator, SignInModalStackNavigator, CategoriesModalStackNavigator, TagsModalStackNavigator, diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx index ac53ad3b64d2..2d83cb3fc2b5 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx @@ -5,6 +5,7 @@ import NoDropZone from '@components/DragAndDrop/NoDropZone'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import {abandonReviewDuplicateTransactions} from '@libs/actions/Transaction'; +import {clearTwoFactorAuthData} from '@libs/actions/TwoFactorAuthActions'; import {isSafari} from '@libs/Browser'; import hideKeyboardOnSwipe from '@libs/Navigation/AppNavigator/hideKeyboardOnSwipe'; import * as ModalStackNavigators from '@libs/Navigation/AppNavigator/ModalStackNavigators'; @@ -86,6 +87,15 @@ function RightModalNavigator({navigation, route}: RightModalNavigatorProps) { name={SCREENS.RIGHT_MODAL.SETTINGS} component={ModalStackNavigators.SettingsModalStackNavigator} /> + { + InteractionManager.runAfterInteractions(clearTwoFactorAuthData); + }, + }} + /> ['config'] = { path: ROUTES.SETTINGS_ADDRESS_STATE.route, exact: true, }, - [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: { - path: ROUTES.SETTINGS_2FA.route, - exact: true, - }, [SCREENS.SETTINGS.DELEGATE.ADD_DELEGATE]: { path: ROUTES.SETTINGS_ADD_DELEGATE, exact: true, @@ -978,6 +974,30 @@ const config: LinkingOptions['config'] = { }, }, }, + [SCREENS.RIGHT_MODAL.TWO_FACTOR_AUTH]: { + screens: { + [SCREENS.TWO_FACTOR_AUTH.ROOT]: { + path: ROUTES.SETTINGS_2FA_ROOT.route, + exact: true, + }, + [SCREENS.TWO_FACTOR_AUTH.VERIFY]: { + path: ROUTES.SETTINGS_2FA_VERIFY.route, + exact: true, + }, + [SCREENS.TWO_FACTOR_AUTH.SUCCESS]: { + path: ROUTES.SETTINGS_2FA_SUCCESS.route, + exact: true, + }, + [SCREENS.TWO_FACTOR_AUTH.DISABLED]: { + path: ROUTES.SETTINGS_2FA_DISABLED, + exact: true, + }, + [SCREENS.TWO_FACTOR_AUTH.DISABLE]: { + path: ROUTES.SETTINGS_2FA_DISABLE, + exact: true, + }, + }, + }, [SCREENS.RIGHT_MODAL.PRIVATE_NOTES]: { screens: { [SCREENS.PRIVATE_NOTES.LIST]: ROUTES.PRIVATE_NOTES_LIST.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index f008bcaf435d..615ff86ecb6b 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -760,10 +760,8 @@ type SettingsNavigatorParamList = { [SCREENS.GET_ASSISTANCE]: { backTo: Routes; }; - [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: { - backTo?: Routes; - forwardTo?: string; - }; + [SCREENS.TWO_FACTOR_AUTH.DISABLED]: undefined; + [SCREENS.TWO_FACTOR_AUTH.DISABLE]: undefined; [SCREENS.SETTINGS.DELEGATE.ADD_DELEGATE]: undefined; [SCREENS.SETTINGS.DELEGATE.DELEGATE_ROLE]: { login: string; @@ -973,6 +971,23 @@ type SettingsNavigatorParamList = { }; } & ReimbursementAccountNavigatorParamList; +type TwoFactorAuthNavigatorParamList = { + [SCREENS.TWO_FACTOR_AUTH.ROOT]: { + backTo?: Routes; + forwardTo?: string; + }; + [SCREENS.TWO_FACTOR_AUTH.VERIFY]: { + backTo?: Routes; + forwardTo?: string; + }; + [SCREENS.TWO_FACTOR_AUTH.SUCCESS]: { + backTo?: Routes; + forwardTo?: string; + }; + [SCREENS.TWO_FACTOR_AUTH.DISABLE]: undefined; + [SCREENS.TWO_FACTOR_AUTH.DISABLED]: undefined; +}; + type NewChatNavigatorParamList = { [SCREENS.NEW_CHAT.ROOT]: undefined; [SCREENS.NEW_CHAT.NEW_CHAT_CONFIRM]: undefined; @@ -1478,6 +1493,7 @@ type LeftModalNavigatorParamList = { type RightModalNavigatorParamList = { [SCREENS.RIGHT_MODAL.SETTINGS]: NavigatorScreenParams; + [SCREENS.RIGHT_MODAL.TWO_FACTOR_AUTH]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.NEW_CHAT]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.DETAILS]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.PROFILE]: NavigatorScreenParams; @@ -1936,4 +1952,5 @@ export type { WorkspaceSplitNavigatorParamList, MigratedUserModalNavigatorParamList, WorkspaceConfirmationNavigatorParamList, + TwoFactorAuthNavigatorParamList, }; diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 512019b6db6e..093208cbc2a7 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -1048,9 +1048,6 @@ function toggleTwoFactorAuth(enable: boolean, twoFactorAuthCode = '') { key: ONYXKEYS.ACCOUNT, value: { isLoading: false, - - // When disabling 2FA, the user needs to end up on the step that confirms the setting was disabled - twoFactorAuthStep: enable ? undefined : CONST.TWO_FACTOR_AUTH_STEPS.DISABLED, }, }, ]; diff --git a/src/libs/actions/TwoFactorAuthActions.ts b/src/libs/actions/TwoFactorAuthActions.ts index 7c875b886e0b..5e205954607a 100644 --- a/src/libs/actions/TwoFactorAuthActions.ts +++ b/src/libs/actions/TwoFactorAuthActions.ts @@ -1,17 +1,14 @@ +import {InteractionManager} from 'react-native'; import Onyx from 'react-native-onyx'; import Navigation from '@libs/Navigation/Navigation'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; -import type {TwoFactorAuthStep} from '@src/types/onyx/Account'; /** * Clear 2FA data if the flow is interrupted without finishing */ function clearTwoFactorAuthData() { - Onyx.merge(ONYXKEYS.ACCOUNT, {recoveryCodes: null, twoFactorAuthSecretKey: null, twoFactorAuthStep: null, codesAreCopied: false}); -} -function setTwoFactorAuthStep(twoFactorAuthStep: TwoFactorAuthStep) { - Onyx.merge(ONYXKEYS.ACCOUNT, {twoFactorAuthStep}); + Onyx.merge(ONYXKEYS.ACCOUNT, {recoveryCodes: null, twoFactorAuthSecretKey: null, codesAreCopied: false}); } function setCodesAreCopied() { @@ -19,8 +16,8 @@ function setCodesAreCopied() { } function quitAndNavigateBack(backTo?: Route) { - clearTwoFactorAuthData(); Navigation.goBack(backTo); + InteractionManager.runAfterInteractions(clearTwoFactorAuthData); } -export {clearTwoFactorAuthData, setTwoFactorAuthStep, quitAndNavigateBack, setCodesAreCopied}; +export {clearTwoFactorAuthData, quitAndNavigateBack, setCodesAreCopied}; diff --git a/src/pages/ReimbursementAccount/ConnectBankAccount/components/Enable2FACard.tsx b/src/pages/ReimbursementAccount/ConnectBankAccount/components/Enable2FACard.tsx index 27f73c64232f..8c497aaf3697 100644 --- a/src/pages/ReimbursementAccount/ConnectBankAccount/components/Enable2FACard.tsx +++ b/src/pages/ReimbursementAccount/ConnectBankAccount/components/Enable2FACard.tsx @@ -27,7 +27,7 @@ function Enable2FACard({policyID}: Enable2FACardProps) { { title: translate('validationStep.secureYourAccount'), onPress: () => { - Navigation.navigate(ROUTES.SETTINGS_2FA.getRoute(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute(policyID))); + Navigation.navigate(ROUTES.SETTINGS_2FA_ROOT.getRoute(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute(policyID))); }, icon: Expensicons.Shield, shouldShowRightIcon: true, diff --git a/src/pages/ReimbursementAccount/NonUSD/Finish/index.tsx b/src/pages/ReimbursementAccount/NonUSD/Finish/index.tsx index 0821f100814a..b4e16663a144 100644 --- a/src/pages/ReimbursementAccount/NonUSD/Finish/index.tsx +++ b/src/pages/ReimbursementAccount/NonUSD/Finish/index.tsx @@ -65,7 +65,7 @@ function Finish() { { title: translate('finishStep.secure'), onPress: () => { - Navigation.navigate(ROUTES.SETTINGS_2FA.getRoute(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute(policyID))); + Navigation.navigate(ROUTES.SETTINGS_2FA_ROOT.getRoute(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute(policyID))); }, icon: Expensicons.Shield, shouldShowRightIcon: true, diff --git a/src/pages/settings/Security/SecuritySettingsPage.tsx b/src/pages/settings/Security/SecuritySettingsPage.tsx index ea6207b562dd..0b92bc6f00f1 100644 --- a/src/pages/settings/Security/SecuritySettingsPage.tsx +++ b/src/pages/settings/Security/SecuritySettingsPage.tsx @@ -28,13 +28,13 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; import useWindowDimensions from '@hooks/useWindowDimensions'; import {clearDelegateErrorsByField, removeDelegate} from '@libs/actions/Delegate'; -import * as ErrorUtils from '@libs/ErrorUtils'; +import {getLatestError} from '@libs/ErrorUtils'; import getClickedTargetLocation from '@libs/getClickedTargetLocation'; import {formatPhoneNumber} from '@libs/LocalePhoneNumber'; import Navigation from '@libs/Navigation/Navigation'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import type {AnchorPosition} from '@styles/index'; -import * as Modal from '@userActions/Modal'; +import {close as modalClose} from '@userActions/Modal'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -115,7 +115,7 @@ function SecuritySettingsPage() { { translationKey: 'twoFactorAuth.headerTitle', icon: Expensicons.Shield, - action: isActingAsDelegate ? showDelegateNoAccessMenu : waitForNavigate(() => Navigation.navigate(ROUTES.SETTINGS_2FA.getRoute())), + action: isActingAsDelegate ? showDelegateNoAccessMenu : waitForNavigate(() => Navigation.navigate(ROUTES.SETTINGS_2FA_ROOT.getRoute())), }, { translationKey: 'closeAccountPage.closeAccount', @@ -142,7 +142,7 @@ function SecuritySettingsPage() { .map(({email, role, pendingAction, pendingFields}) => { const personalDetail = getPersonalDetailByEmail(email); const addDelegateErrors = errorFields?.addDelegate?.[email]; - const error = ErrorUtils.getLatestError(addDelegateErrors); + const error = getLatestError(addDelegateErrors); const onPress = (e: GestureResponderEvent | KeyboardEvent) => { if (isEmptyObject(pendingAction)) { @@ -213,7 +213,7 @@ function SecuritySettingsPage() { icon: Expensicons.Pencil, onPress: () => { if (isActingAsDelegate) { - Modal.close(() => setIsNoDelegateAccessMenuVisible(true)); + modalClose(() => setIsNoDelegateAccessMenuVisible(true)); return; } Navigation.navigate(ROUTES.SETTINGS_UPDATE_DELEGATE_ROLE.getRoute(selectedDelegate?.email ?? '', selectedDelegate?.role ?? '')); @@ -227,10 +227,10 @@ function SecuritySettingsPage() { icon: Expensicons.Trashcan, onPress: () => { if (isActingAsDelegate) { - Modal.close(() => setIsNoDelegateAccessMenuVisible(true)); + modalClose(() => setIsNoDelegateAccessMenuVisible(true)); return; } - Modal.close(() => { + modalClose(() => { setShouldShowDelegatePopoverMenu(false); setShouldShowRemoveDelegateModal(true); setSelectedEmail(undefined); diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.tsx b/src/pages/settings/Security/TwoFactorAuth/CopyCodesPage.tsx similarity index 78% rename from src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.tsx rename to src/pages/settings/Security/TwoFactorAuth/CopyCodesPage.tsx index 67b47cb8598d..8ef051e119d7 100644 --- a/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.tsx +++ b/src/pages/settings/Security/TwoFactorAuth/CopyCodesPage.tsx @@ -1,4 +1,3 @@ -import {useRoute} from '@react-navigation/native'; import React, {useEffect, useMemo, useState} from 'react'; import {ActivityIndicator, View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; @@ -19,23 +18,21 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {READ_COMMANDS} from '@libs/API/types'; import Clipboard from '@libs/Clipboard'; -import * as ErrorUtils from '@libs/ErrorUtils'; +import {getEarliestErrorField, getLatestErrorField} from '@libs/ErrorUtils'; import localFileDownload from '@libs/localFileDownload'; -import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; -import type {BackToParams, SettingsNavigatorParamList} from '@libs/Navigation/types'; -import StepWrapper from '@pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper'; -import useTwoFactorAuthContext from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/useTwoFactorAuth'; -import * as Session from '@userActions/Session'; -import * as TwoFactorAuthActions from '@userActions/TwoFactorAuthActions'; -import * as User from '@userActions/User'; +import Navigation from '@libs/Navigation/Navigation'; +import {toggleTwoFactorAuth} from '@userActions/Session'; +import {quitAndNavigateBack, setCodesAreCopied} from '@userActions/TwoFactorAuthActions'; +import {clearContactMethodErrors, requestValidateCodeAction, validateSecondaryLogin} from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type SCREENS from '@src/SCREENS'; +import ROUTES from '@src/ROUTES'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; +import type {TwoFactorAuthPageProps} from './TwoFactorAuthPage'; +import TwoFactorAuthWrapper from './TwoFactorAuthWrapper'; -type CodesStepProps = BackToParams; - -function CodesStep({backTo}: CodesStepProps) { +function CopyCodesPage({route}: TwoFactorAuthPageProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -44,47 +41,45 @@ function CodesStep({backTo}: CodesStepProps) { const {isExtraSmallScreenWidth, isSmallScreenWidth} = useResponsiveLayout(); const [error, setError] = useState(''); - const [account] = useOnyx(ONYXKEYS.ACCOUNT); + const [account, accountMetadata] = useOnyx(ONYXKEYS.ACCOUNT); const [user] = useOnyx(ONYXKEYS.USER); const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); const [validateCodeAction] = useOnyx(ONYXKEYS.VALIDATE_ACTION_CODE); const isUserValidated = user?.validated; const contactMethod = account?.primaryLogin ?? ''; - const route = useRoute>(); const loginData = useMemo(() => loginList?.[contactMethod], [loginList, contactMethod]); - const validateLoginError = ErrorUtils.getEarliestErrorField(loginData, 'validateLogin'); + const validateLoginError = getEarliestErrorField(loginData, 'validateLogin'); const hasMagicCodeBeenSent = !!validateCodeAction?.validateCodeSent; const [isValidateModalVisible, setIsValidateModalVisible] = useState(!isUserValidated); - const {setStep} = useTwoFactorAuthContext(); - useEffect(() => { setIsValidateModalVisible(!isUserValidated); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (account?.requiresTwoFactorAuth || account?.recoveryCodes || !isUserValidated) { + if (isLoadingOnyxValue(accountMetadata) || account?.requiresTwoFactorAuth || account?.recoveryCodes || !isUserValidated) { return; } - Session.toggleTwoFactorAuth(true); + toggleTwoFactorAuth(true); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps -- We want to run this when component mounts - }, [isUserValidated]); + }, [isUserValidated, accountMetadata]); useBeforeRemove(() => setIsValidateModalVisible(false)); return ( - TwoFactorAuthActions.quitAndNavigateBack(route?.params?.forwardTo?.includes(READ_COMMANDS.CONNECT_POLICY_TO_XERO) ? '' : backTo)} + onBackButtonPress={() => quitAndNavigateBack(route?.params?.forwardTo?.includes(READ_COMMANDS.CONNECT_POLICY_TO_XERO) ? undefined : route?.params?.backTo)} > {!!isUserValidated && ( @@ -124,7 +119,7 @@ function CodesStep({backTo}: CodesStepProps) { onPress={() => { Clipboard.setString(account?.recoveryCodes ?? ''); setError(''); - TwoFactorAuthActions.setCodesAreCopied(); + setCodesAreCopied(); }} styles={[styles.button, styles.buttonMedium, styles.twoFactorAuthCodesButton]} textStyles={[styles.buttonMediumText]} @@ -138,7 +133,7 @@ function CodesStep({backTo}: CodesStepProps) { onPress={() => { localFileDownload('two-factor-auth-codes', account?.recoveryCodes ?? ''); setError(''); - TwoFactorAuthActions.setCodesAreCopied(); + setCodesAreCopied(); }} inline={false} styles={[styles.button, styles.buttonMedium, styles.twoFactorAuthCodesButton]} @@ -168,10 +163,9 @@ function CodesStep({backTo}: CodesStepProps) { text={translate('common.next')} onPress={() => { if (!account?.codesAreCopied) { - setError(translate('twoFactorAuth.errorStepCodes')); - return; + return setError(translate('twoFactorAuth.errorStepCodes')); } - setStep(CONST.TWO_FACTOR_AUTH_STEPS.VERIFY); + Navigation.navigate(ROUTES.SETTINGS_2FA_VERIFY.getRoute(route.params?.backTo, route.params?.forwardTo)); }} /> @@ -183,20 +177,20 @@ function CodesStep({backTo}: CodesStepProps) { isVisible={isValidateModalVisible} hasMagicCodeBeenSent={hasMagicCodeBeenSent} validatePendingAction={loginData?.pendingFields?.validateCodeSent} - sendValidateCode={() => User.requestValidateCodeAction()} - handleSubmitForm={(validateCode) => User.validateSecondaryLogin(loginList, contactMethod, validateCode, true)} - validateError={!isEmptyObject(validateLoginError) ? validateLoginError : ErrorUtils.getLatestErrorField(loginData, 'validateCodeSent')} - clearError={() => User.clearContactMethodErrors(contactMethod, !isEmptyObject(validateLoginError) ? 'validateLogin' : 'validateCodeSent')} + sendValidateCode={() => requestValidateCodeAction()} + handleSubmitForm={(validateCode) => validateSecondaryLogin(loginList, contactMethod, validateCode, true)} + validateError={!isEmptyObject(validateLoginError) ? validateLoginError : getLatestErrorField(loginData, 'validateCodeSent')} + clearError={() => clearContactMethodErrors(contactMethod, !isEmptyObject(validateLoginError) ? 'validateLogin' : 'validateCodeSent')} onModalHide={() => {}} onClose={() => { setIsValidateModalVisible(false); - TwoFactorAuthActions.quitAndNavigateBack(backTo); + quitAndNavigateBack(); }} /> - + ); } -CodesStep.displayName = 'CodesStep'; +CopyCodesPage.displayName = 'CopyCodesPage'; -export default CodesStep; +export default CopyCodesPage; diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/GetCode.tsx b/src/pages/settings/Security/TwoFactorAuth/DisablePage.tsx similarity index 56% rename from src/pages/settings/Security/TwoFactorAuth/Steps/GetCode.tsx rename to src/pages/settings/Security/TwoFactorAuth/DisablePage.tsx index a36f2e8c7c2f..7ff3d7893965 100644 --- a/src/pages/settings/Security/TwoFactorAuth/Steps/GetCode.tsx +++ b/src/pages/settings/Security/TwoFactorAuth/DisablePage.tsx @@ -1,35 +1,39 @@ -import React, {useRef} from 'react'; +import React, {useEffect, useRef} from 'react'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import FixedFooter from '@components/FixedFooter'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {BackToParams} from '@libs/Navigation/types'; -import StepWrapper from '@pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper'; -import useTwoFactorAuthContext from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/useTwoFactorAuth'; -import TwoFactorAuthForm from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm'; -import type {BaseTwoFactorAuthFormOnyxProps, BaseTwoFactorAuthFormRef} from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/types'; +import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import TwoFactorAuthForm from './TwoFactorAuthForm'; +import type {BaseTwoFactorAuthFormRef} from './TwoFactorAuthForm/types'; +import TwoFactorAuthWrapper from './TwoFactorAuthWrapper'; -type GetCodeProps = BaseTwoFactorAuthFormOnyxProps & BackToParams; - -function GetCode({account}: GetCodeProps) { +function DisablePage() { const styles = useThemeStyles(); const {translate} = useLocalize(); + const [account] = useOnyx(ONYXKEYS.ACCOUNT); const formRef = useRef(null); - const {setStep} = useTwoFactorAuthContext(); + useEffect(() => { + if (account?.requiresTwoFactorAuth) { + return; + } + + Navigation.navigate(ROUTES.SETTINGS_2FA_DISABLED); + }, [account?.requiresTwoFactorAuth]); return ( - setStep(CONST.TWO_FACTOR_AUTH_STEPS.ENABLED, CONST.ANIMATION_DIRECTION.OUT)} - onEntryTransitionEnd={() => formRef.current && formRef.current.focus()} > @@ -52,20 +56,14 @@ function GetCode({account}: GetCodeProps) { if (!formRef.current) { return; } - // eslint-disable-next-line @typescript-eslint/no-unsafe-call formRef.current.validateAndSubmitForm(); }} /> - + ); } -GetCode.displayName = 'GetCode'; +DisablePage.displayName = 'DisablePage'; -export default withOnyx({ - account: {key: ONYXKEYS.ACCOUNT}, - user: { - key: ONYXKEYS.USER, - }, -})(GetCode); +export default DisablePage; diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/DisabledStep.tsx b/src/pages/settings/Security/TwoFactorAuth/DisabledPage.tsx similarity index 64% rename from src/pages/settings/Security/TwoFactorAuth/Steps/DisabledStep.tsx rename to src/pages/settings/Security/TwoFactorAuth/DisabledPage.tsx index 588c21defee3..423581af2bac 100644 --- a/src/pages/settings/Security/TwoFactorAuth/Steps/DisabledStep.tsx +++ b/src/pages/settings/Security/TwoFactorAuth/DisabledPage.tsx @@ -5,16 +5,21 @@ import FixedFooter from '@components/FixedFooter'; import * as Illustrations from '@components/Icon/Illustrations'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import StepWrapper from '@pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper'; import variables from '@styles/variables'; -import * as TwoFactorAuthActions from '@userActions/TwoFactorAuthActions'; +import {quitAndNavigateBack} from '@userActions/TwoFactorAuthActions'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; +import TwoFactorAuthWrapper from './TwoFactorAuthWrapper'; -function DisabledStep() { +function DisabledPage() { const styles = useThemeStyles(); const {translate} = useLocalize(); return ( - + TwoFactorAuthActions.quitAndNavigateBack()} + onPress={() => quitAndNavigateBack(ROUTES.SETTINGS_SECURITY)} /> - + ); } -DisabledStep.displayName = 'DisabledStep'; +DisabledPage.displayName = 'DisabledPage'; -export default DisabledStep; +export default DisabledPage; diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/EnabledStep.tsx b/src/pages/settings/Security/TwoFactorAuth/EnabledPage.tsx similarity index 82% rename from src/pages/settings/Security/TwoFactorAuth/Steps/EnabledStep.tsx rename to src/pages/settings/Security/TwoFactorAuth/EnabledPage.tsx index c11ad6295e5c..2fee9626fb7a 100644 --- a/src/pages/settings/Security/TwoFactorAuth/Steps/EnabledStep.tsx +++ b/src/pages/settings/Security/TwoFactorAuth/EnabledPage.tsx @@ -10,17 +10,17 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; import {hasPolicyWithXeroConnection} from '@libs/PolicyUtils'; -import StepWrapper from '@pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper'; -import useTwoFactorAuthContext from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/useTwoFactorAuth'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import TwoFactorAuthWrapper from './TwoFactorAuthWrapper'; -function EnabledStep() { +function EnabledPage() { const theme = useTheme(); const styles = useThemeStyles(); const [isVisible, setIsVisible] = useState(false); - const {setStep} = useTwoFactorAuthContext(); const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email}); const {translate} = useLocalize(); @@ -30,7 +30,11 @@ function EnabledStep() { }, []); return ( - +
- + ); } -EnabledStep.displayName = 'EnabledStep'; +EnabledPage.displayName = 'EnabledPage'; -export default EnabledStep; +export default EnabledPage; diff --git a/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.tsx b/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.tsx deleted file mode 100644 index 2883605b915d..000000000000 --- a/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from 'react'; -import AnimatedStep from '@components/AnimatedStep'; -import useAnimatedStepContext from '@components/AnimatedStep/useAnimatedStepContext'; -import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import useThemeStyles from '@hooks/useThemeStyles'; -import * as TwoFactorAuthActions from '@userActions/TwoFactorAuthActions'; -import type {StepCounterParams} from '@src/languages/params'; -import type ChildrenProps from '@src/types/utils/ChildrenProps'; - -type StepWrapperProps = ChildrenProps & { - /** Title of the Header */ - title?: string; - - /** Data to display a step counter in the header */ - stepCounter?: StepCounterParams; - - /** Method to trigger when pressing back button of the header */ - onBackButtonPress?: () => void; - - /** Called when navigated Screen's transition is finished. It does not fire when user exits the page. */ - onEntryTransitionEnd?: () => void; - - /** Flag to indicate if the keyboard avoiding view should be enabled */ - shouldEnableKeyboardAvoidingView?: boolean; -}; - -function StepWrapper({ - title = '', - stepCounter, - onBackButtonPress = () => TwoFactorAuthActions.quitAndNavigateBack(), - children = null, - shouldEnableKeyboardAvoidingView = true, - onEntryTransitionEnd, -}: StepWrapperProps) { - const styles = useThemeStyles(); - const {animationDirection} = useAnimatedStepContext(); - - return ( - - - - {children} - - - ); -} - -StepWrapper.displayName = 'StepWrapper'; - -export default StepWrapper; diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/SuccessStep.tsx b/src/pages/settings/Security/TwoFactorAuth/Steps/SuccessStep.tsx deleted file mode 100644 index ad68d7cf7a0c..000000000000 --- a/src/pages/settings/Security/TwoFactorAuth/Steps/SuccessStep.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react'; -import ConfirmationPage from '@components/ConfirmationPage'; -import LottieAnimations from '@components/LottieAnimations'; -import useEnvironment from '@hooks/useEnvironment'; -import useLocalize from '@hooks/useLocalize'; -import Navigation from '@navigation/Navigation'; -import StepWrapper from '@pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper'; -import useTwoFactorAuthContext from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/useTwoFactorAuth'; -import * as Link from '@userActions/Link'; -import * as TwoFactorAuthActions from '@userActions/TwoFactorAuthActions'; -import CONST from '@src/CONST'; -import type {Route} from '@src/ROUTES'; - -type SuccessStepParams = { - backTo?: Route; - forwardTo?: string; -}; - -function SuccessStep({backTo, forwardTo}: SuccessStepParams) { - const {setStep} = useTwoFactorAuthContext(); - - const {translate} = useLocalize(); - const {environmentURL} = useEnvironment(); - - return ( - - { - TwoFactorAuthActions.clearTwoFactorAuthData(); - setStep(CONST.TWO_FACTOR_AUTH_STEPS.ENABLED); - if (backTo) { - Navigation.goBack(backTo); - } - if (forwardTo) { - Link.openLink(forwardTo, environmentURL); - } - }} - /> - - ); -} - -export default SuccessStep; diff --git a/src/pages/settings/Security/TwoFactorAuth/SuccessPage.tsx b/src/pages/settings/Security/TwoFactorAuth/SuccessPage.tsx new file mode 100644 index 000000000000..dd0153f92d8e --- /dev/null +++ b/src/pages/settings/Security/TwoFactorAuth/SuccessPage.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import ConfirmationPage from '@components/ConfirmationPage'; +import LottieAnimations from '@components/LottieAnimations'; +import useEnvironment from '@hooks/useEnvironment'; +import useLocalize from '@hooks/useLocalize'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {TwoFactorAuthNavigatorParamList} from '@libs/Navigation/types'; +import {openLink} from '@userActions/Link'; +import {quitAndNavigateBack} from '@userActions/TwoFactorAuthActions'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; +import TwoFactorAuthWrapper from './TwoFactorAuthWrapper'; + +type SuccessPageProps = PlatformStackScreenProps; + +function SuccessPage({route}: SuccessPageProps) { + const {translate} = useLocalize(); + const {environmentURL} = useEnvironment(); + + return ( + + { + quitAndNavigateBack(route.params?.backTo ?? ROUTES.SETTINGS_2FA_ROOT.getRoute()); + + if (route.params?.forwardTo) { + openLink(route.params.forwardTo, environmentURL); + } + }} + /> + + ); +} + +SuccessPage.displayName = 'SuccessPage'; + +export default SuccessPage; diff --git a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/index.tsx b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/index.tsx deleted file mode 100644 index 5b6ca64c7036..000000000000 --- a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/index.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import {createContext} from 'react'; -import type {AnimationDirection} from '@components/AnimatedStep/AnimatedStepContext'; -import type {TwoFactorAuthStep} from '@src/types/onyx/Account'; - -type TwoFactorAuthContextType = { - setStep: (step: TwoFactorAuthStep, animationDirection?: AnimationDirection) => void; -}; - -const TwoFactorAuthContext = createContext({ - setStep: () => {}, -}); - -export default TwoFactorAuthContext; diff --git a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/useTwoFactorAuth.tsx b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/useTwoFactorAuth.tsx deleted file mode 100644 index c0e45a21b050..000000000000 --- a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/useTwoFactorAuth.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import {useContext} from 'react'; -import TwoFactorAuthContext from './index'; - -export default function useTwoFactorAuthContext() { - return useContext(TwoFactorAuthContext); -} diff --git a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/BaseTwoFactorAuthForm.tsx b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/BaseTwoFactorAuthForm.tsx index fe37aa9b228a..577429901221 100644 --- a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/BaseTwoFactorAuthForm.tsx +++ b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/BaseTwoFactorAuthForm.tsx @@ -1,16 +1,16 @@ import React, {forwardRef, useCallback, useImperativeHandle, useRef, useState} from 'react'; -import type {ForwardedRef, RefAttributes} from 'react'; -import {withOnyx} from 'react-native-onyx'; +import type {ForwardedRef} from 'react'; +import {useOnyx} from 'react-native-onyx'; import type {AutoCompleteVariant, MagicCodeInputHandle} from '@components/MagicCodeInput'; import MagicCodeInput from '@components/MagicCodeInput'; import useLocalize from '@hooks/useLocalize'; -import * as ErrorUtils from '@libs/ErrorUtils'; -import * as ValidationUtils from '@libs/ValidationUtils'; -import * as Session from '@userActions/Session'; +import {getLatestErrorMessage} from '@libs/ErrorUtils'; +import {isValidTwoFactorCode} from '@libs/ValidationUtils'; +import {clearAccountMessages, toggleTwoFactorAuth, validateTwoFactorAuth} from '@userActions/Session'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {BaseTwoFactorAuthFormOnyxProps, BaseTwoFactorAuthFormRef} from './types'; +import type {BaseTwoFactorAuthFormRef} from './types'; -type BaseTwoFactorAuthFormProps = BaseTwoFactorAuthFormOnyxProps & { +type BaseTwoFactorAuthFormProps = { autoComplete: AutoCompleteVariant; // Set this to true in order to call the validateTwoFactorAuth action which is used when setting up 2FA for the first time. @@ -18,9 +18,10 @@ type BaseTwoFactorAuthFormProps = BaseTwoFactorAuthFormOnyxProps & { validateInsteadOfDisable?: boolean; }; -function BaseTwoFactorAuthForm({account, autoComplete, validateInsteadOfDisable}: BaseTwoFactorAuthFormProps, ref: ForwardedRef) { +function BaseTwoFactorAuthForm({autoComplete, validateInsteadOfDisable}: BaseTwoFactorAuthFormProps, ref: ForwardedRef) { const {translate} = useLocalize(); const [formError, setFormError] = useState<{twoFactorAuthCode?: string}>({}); + const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [twoFactorAuthCode, setTwoFactorAuthCode] = useState(''); const inputRef = useRef(null); const shouldClearData = account?.needsTwoFactorAuthSetup ?? false; @@ -34,7 +35,7 @@ function BaseTwoFactorAuthForm({account, autoComplete, validateInsteadOfDisable} setFormError({}); if (account?.errors) { - Session.clearAccountMessages(); + clearAccountMessages(); } }, [account?.errors], @@ -52,7 +53,7 @@ function BaseTwoFactorAuthForm({account, autoComplete, validateInsteadOfDisable} return; } - if (!ValidationUtils.isValidTwoFactorCode(twoFactorAuthCode)) { + if (!isValidTwoFactorCode(twoFactorAuthCode)) { setFormError({twoFactorAuthCode: translate('twoFactorAuthForm.error.incorrect2fa')}); return; } @@ -60,10 +61,10 @@ function BaseTwoFactorAuthForm({account, autoComplete, validateInsteadOfDisable} setFormError({}); if (validateInsteadOfDisable !== false) { - Session.validateTwoFactorAuth(twoFactorAuthCode, shouldClearData); + validateTwoFactorAuth(twoFactorAuthCode, shouldClearData); return; } - Session.toggleTwoFactorAuth(false, twoFactorAuthCode); + toggleTwoFactorAuth(false, twoFactorAuthCode); }, [twoFactorAuthCode, validateInsteadOfDisable, translate, shouldClearData]); useImperativeHandle(ref, () => ({ @@ -85,13 +86,11 @@ function BaseTwoFactorAuthForm({account, autoComplete, validateInsteadOfDisable} value={twoFactorAuthCode} onChangeText={onTextInput} onFulfill={validateAndSubmitForm} - errorText={formError.twoFactorAuthCode ?? ErrorUtils.getLatestErrorMessage(account)} + errorText={formError.twoFactorAuthCode ?? getLatestErrorMessage(account)} ref={inputRef} - autoFocus={false} + shouldDelayFocus /> ); } -export default withOnyx, BaseTwoFactorAuthFormOnyxProps>({ - account: {key: ONYXKEYS.ACCOUNT}, -})(forwardRef(BaseTwoFactorAuthForm)); +export default forwardRef(BaseTwoFactorAuthForm); diff --git a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/types.ts b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/types.ts index 14b5efa1feeb..1f999089977f 100644 --- a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/types.ts +++ b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/types.ts @@ -1,11 +1,4 @@ import type {ForwardedRef} from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; -import type {Account, User} from '@src/types/onyx'; - -type BaseTwoFactorAuthFormOnyxProps = { - account: OnyxEntry; - user?: OnyxEntry; -}; type BaseTwoFactorAuthFormRef = { validateAndSubmitForm: () => void; @@ -20,4 +13,4 @@ type TwoFactorAuthFormProps = { validateInsteadOfDisable?: boolean; }; -export type {BaseTwoFactorAuthFormOnyxProps, TwoFactorAuthFormProps, BaseTwoFactorAuthFormRef}; +export type {TwoFactorAuthFormProps, BaseTwoFactorAuthFormRef}; diff --git a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage.tsx b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage.tsx index bc3dc588c1b1..b8d2d9e2d076 100644 --- a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage.tsx +++ b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage.tsx @@ -1,32 +1,24 @@ import React from 'react'; import {useOnyx} from 'react-native-onyx'; -import AnimatedStepProvider from '@components/AnimatedStep/AnimatedStepProvider'; -import DelegateNoAccessWrapper from '@components/DelegateNoAccessWrapper'; -import ScreenWrapper from '@components/ScreenWrapper'; -import CONST from '@src/CONST'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {TwoFactorAuthNavigatorParamList} from '@libs/Navigation/types'; import ONYXKEYS from '@src/ONYXKEYS'; -import TwoFactorAuthSteps from './TwoFactorAuthSteps'; +import type SCREENS from '@src/SCREENS'; +import CopyCodesPage from './CopyCodesPage'; +import EnabledPage from './EnabledPage'; -function TwoFactorAuthPage() { - const [isActingAsDelegate] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => !!account?.delegatedAccess?.delegate}); - if (isActingAsDelegate) { - return ( - - - - ); +type TwoFactorAuthPageProps = PlatformStackScreenProps; + +function TwoFactorAuthPage(props: TwoFactorAuthPageProps) { + const [account] = useOnyx(ONYXKEYS.ACCOUNT); + + if (account?.requiresTwoFactorAuth) { + return ; } - return ( - - - - ); -} -TwoFactorAuthPage.displayName = 'TwoFactorAuthPage'; + // eslint-disable-next-line react/jsx-props-no-spreading + return ; +} export default TwoFactorAuthPage; +export type {TwoFactorAuthPageProps}; diff --git a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthSteps.tsx b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthSteps.tsx deleted file mode 100644 index 9c70371e76a3..000000000000 --- a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthSteps.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import {useRoute} from '@react-navigation/native'; -import React, {useCallback, useEffect, useMemo} from 'react'; -import {withOnyx} from 'react-native-onyx'; -import type {AnimationDirection} from '@components/AnimatedStep/AnimatedStepContext'; -import useAnimatedStepContext from '@components/AnimatedStep/useAnimatedStepContext'; -import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; -import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; -import * as TwoFactorAuthActions from '@userActions/TwoFactorAuthActions'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type SCREENS from '@src/SCREENS'; -import type {TwoFactorAuthStep} from '@src/types/onyx/Account'; -import CodesStep from './Steps/CodesStep'; -import DisabledStep from './Steps/DisabledStep'; -import EnabledStep from './Steps/EnabledStep'; -import GetCodeStep from './Steps/GetCode'; -import SuccessStep from './Steps/SuccessStep'; -import VerifyStep from './Steps/VerifyStep'; -import TwoFactorAuthContext from './TwoFactorAuthContext'; -import type {BaseTwoFactorAuthFormOnyxProps} from './TwoFactorAuthForm/types'; - -type TwoFactorAuthStepProps = BaseTwoFactorAuthFormOnyxProps; - -function TwoFactorAuthSteps({account}: TwoFactorAuthStepProps) { - const route = useRoute>(); - const backTo = route.params?.backTo ?? ''; - const forwardTo = route.params?.forwardTo ?? ''; - - const currentStep = useMemo(() => { - if (account?.twoFactorAuthStep) { - return account.twoFactorAuthStep; - } - return account?.requiresTwoFactorAuth ? CONST.TWO_FACTOR_AUTH_STEPS.ENABLED : CONST.TWO_FACTOR_AUTH_STEPS.CODES; - }, [account?.requiresTwoFactorAuth, account?.twoFactorAuthStep]); - - const {setAnimationDirection} = useAnimatedStepContext(); - - useEffect(() => () => TwoFactorAuthActions.clearTwoFactorAuthData(), []); - - const handleSetStep = useCallback( - (step: TwoFactorAuthStep, animationDirection: AnimationDirection = CONST.ANIMATION_DIRECTION.IN) => { - setAnimationDirection(animationDirection); - TwoFactorAuthActions.setTwoFactorAuthStep(step); - }, - [setAnimationDirection], - ); - const contextValue = useMemo(() => ({setStep: handleSetStep}), [handleSetStep]); - - const renderStep = () => { - switch (currentStep) { - case CONST.TWO_FACTOR_AUTH_STEPS.CODES: - return ; - case CONST.TWO_FACTOR_AUTH_STEPS.VERIFY: - return ; - case CONST.TWO_FACTOR_AUTH_STEPS.SUCCESS: - return ( - - ); - case CONST.TWO_FACTOR_AUTH_STEPS.ENABLED: - return ; - case CONST.TWO_FACTOR_AUTH_STEPS.DISABLED: - return ; - case CONST.TWO_FACTOR_AUTH_STEPS.GETCODE: - return ; - default: - return ; - } - }; - - return {renderStep()}; -} - -export default withOnyx({ - account: {key: ONYXKEYS.ACCOUNT}, -})(TwoFactorAuthSteps); diff --git a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthWrapper.tsx b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthWrapper.tsx new file mode 100644 index 000000000000..d0c4af7be356 --- /dev/null +++ b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthWrapper.tsx @@ -0,0 +1,100 @@ +import React, {useMemo} from 'react'; +import {useOnyx} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; +import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; +import DelegateNoAccessWrapper from '@components/DelegateNoAccessWrapper'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import {quitAndNavigateBack} from '@libs/actions/TwoFactorAuthActions'; +import CONST from '@src/CONST'; +import type {StepCounterParams} from '@src/languages/params'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type ChildrenProps from '@src/types/utils/ChildrenProps'; + +type TwoFactorAuthWrapperProps = ChildrenProps & { + /** Name of the step */ + stepName: ValueOf; + + /** Title of the Header */ + title: string; + + /** Data to display a step counter in the header */ + stepCounter?: StepCounterParams; + + /** Method to trigger when pressing back button of the header */ + onBackButtonPress?: () => void; + + /** Flag to indicate if the keyboard avoiding view should be enabled */ + shouldEnableKeyboardAvoidingView?: boolean; +}; + +function TwoFactorAuthWrapper({stepName, title, stepCounter, onBackButtonPress, shouldEnableKeyboardAvoidingView = true, children}: TwoFactorAuthWrapperProps) { + const [account] = useOnyx(ONYXKEYS.ACCOUNT); + const isActingAsDelegate = !!account?.delegatedAccess?.delegate; + + // eslint-disable-next-line rulesdir/no-negated-variables + const shouldShowNotFound = useMemo(() => { + if (!account) { + return true; + } + + const is2FAEnabled = !!account.requiresTwoFactorAuth; + + switch (stepName) { + case CONST.TWO_FACTOR_AUTH_STEPS.COPY_CODES: + case CONST.TWO_FACTOR_AUTH_STEPS.ENABLED: + case CONST.TWO_FACTOR_AUTH_STEPS.DISABLE: + return false; + case CONST.TWO_FACTOR_AUTH_STEPS.VERIFY: + return !account.codesAreCopied; + case CONST.TWO_FACTOR_AUTH_STEPS.SUCCESS: + return !is2FAEnabled; + case CONST.TWO_FACTOR_AUTH_STEPS.DISABLED: + return is2FAEnabled; + default: + return false; + } + }, [account, stepName]); + + if (isActingAsDelegate) { + return ( + + + + ); + } + + const defaultGoBack = () => quitAndNavigateBack(ROUTES.SETTINGS_SECURITY); + + return ( + + + + {children} + + + ); +} + +TwoFactorAuthWrapper.displayName = 'TwoFactorAuthWrapper'; + +export default TwoFactorAuthWrapper; diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.tsx b/src/pages/settings/Security/TwoFactorAuth/VerifyPage.tsx similarity index 77% rename from src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.tsx rename to src/pages/settings/Security/TwoFactorAuth/VerifyPage.tsx index 29f832ccaea8..afa5313ef47e 100644 --- a/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.tsx +++ b/src/pages/settings/Security/TwoFactorAuth/VerifyPage.tsx @@ -13,39 +13,43 @@ import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Clipboard from '@libs/Clipboard'; -import * as UserUtils from '@libs/UserUtils'; -import StepWrapper from '@pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper'; -import useTwoFactorAuthContext from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/useTwoFactorAuth'; -import TwoFactorAuthForm from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm'; -import type {BaseTwoFactorAuthFormRef} from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/types'; -import * as Session from '@userActions/Session'; +import Navigation from '@libs/Navigation/Navigation'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {TwoFactorAuthNavigatorParamList} from '@libs/Navigation/types'; +import {getContactMethod} from '@libs/UserUtils'; +import {clearAccountMessages} from '@userActions/Session'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; +import TwoFactorAuthForm from './TwoFactorAuthForm'; +import type {BaseTwoFactorAuthFormRef} from './TwoFactorAuthForm/types'; +import TwoFactorAuthWrapper from './TwoFactorAuthWrapper'; const TROUBLESHOOTING_LINK = 'https://help.expensify.com/articles/new-expensify/settings/Enable-Two-Factor-Authentication'; -function VerifyStep() { +type VerifyPageProps = PlatformStackScreenProps; + +function VerifyPage({route}: VerifyPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const contactMethod = UserUtils.getContactMethod(); + const contactMethod = getContactMethod(); const [account] = useOnyx(ONYXKEYS.ACCOUNT); const formRef = useRef(null); - const {setStep} = useTwoFactorAuthContext(); - useEffect(() => { - Session.clearAccountMessages(); + clearAccountMessages(); return () => { - Session.clearAccountMessages(); + clearAccountMessages(); }; }, []); useEffect(() => { - if (!account?.requiresTwoFactorAuth) { + if (!account?.requiresTwoFactorAuth || !account.codesAreCopied) { return; } - setStep(CONST.TWO_FACTOR_AUTH_STEPS.SUCCESS); - }, [account?.requiresTwoFactorAuth, setStep]); + Navigation.navigate(ROUTES.SETTINGS_2FA_SUCCESS.getRoute(route.params?.backTo, route.params?.forwardTo)); + }, [account?.codesAreCopied, account?.requiresTwoFactorAuth, route.params?.backTo, route.params?.forwardTo]); /** * Splits the two-factor auth secret key in 4 chunks @@ -67,15 +71,16 @@ function VerifyStep() { } return ( - setStep(CONST.TWO_FACTOR_AUTH_STEPS.CODES, CONST.ANIMATION_DIRECTION.OUT)} - onEntryTransitionEnd={() => formRef.current && formRef.current.focus()} + onBackButtonPress={Navigation.goBack} > - + ); } -VerifyStep.displayName = 'VerifyStep'; +VerifyPage.displayName = 'VerifyPage'; -export default VerifyStep; +export default VerifyPage; diff --git a/src/styles/index.ts b/src/styles/index.ts index 5661e0eb40e4..02136a68dd75 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5,8 +5,6 @@ import type {LineLayer} from 'react-map-gl'; // eslint-disable-next-line no-restricted-imports import type {Animated, ImageStyle, TextStyle, ViewStyle} from 'react-native'; import {Platform, StyleSheet} from 'react-native'; -// eslint-disable-next-line no-restricted-imports -- will be removed in the future PR -import type {CustomAnimation} from 'react-native-animatable'; import type {PickerStyle} from 'react-native-picker-select'; import {interpolate} from 'react-native-reanimated'; import type {SharedValue} from 'react-native-reanimated'; @@ -74,8 +72,6 @@ type OverlayStylesParams = {progress: Animated.AnimatedInterpolation; type MapDirectionStyle = Pick; @@ -92,7 +88,7 @@ type Styles = Record< | MapDirectionStyle | MapDirectionLayerStyle // eslint-disable-next-line @typescript-eslint/no-explicit-any - | ((...args: any[]) => ViewStyle | TextStyle | ImageStyle | AnchorPosition | CustomAnimation | CustomPickerStyle) + | ((...args: any[]) => ViewStyle | TextStyle | ImageStyle | AnchorPosition | CustomPickerStyle) >; // touchCallout is an iOS safari only property that controls the display of the callout information when you touch and hold a target @@ -3444,16 +3440,6 @@ const styles = (theme: ThemeColors) => }; }, - makeSlideInTranslation: (translationType: Translation, fromValue: number) => - ({ - from: { - [translationType]: fromValue, - }, - to: { - [translationType]: 0, - }, - } satisfies CustomAnimation), - growlNotificationBox: { backgroundColor: theme.inverse, borderRadius: variables.componentBorderRadiusNormal, @@ -4438,8 +4424,8 @@ const styles = (theme: ThemeColors) => fontSize: variables.fontSizeLabel, } satisfies TextStyle), - animatedTabBackground: (hovered: boolean, isFocused: boolean, background: string | Animated.AnimatedInterpolation) => ({ - backgroundColor: hovered && !isFocused ? theme.highlightBG : background, + tabBackground: (hovered: boolean, isFocused: boolean, background: string | Animated.AnimatedInterpolation) => ({ + backgroundColor: hovered && !isFocused ? theme.highlightBG : (background as string), }), tabOpacity: ( diff --git a/src/types/onyx/Account.ts b/src/types/onyx/Account.ts index 90f8b9b8e2c2..97fa83dc8670 100644 --- a/src/types/onyx/Account.ts +++ b/src/types/onyx/Account.ts @@ -3,9 +3,6 @@ import type CONST from '@src/CONST'; import type DismissedReferralBanners from './DismissedReferralBanners'; import type * as OnyxCommon from './OnyxCommon'; -/** Two factor authentication steps */ -type TwoFactorAuthStep = ValueOf | ''; - /** The role of the delegate */ type DelegateRole = ValueOf; @@ -146,9 +143,6 @@ type Account = { /** Whether the two factor authentication codes were copied */ codesAreCopied?: boolean; - /** Current two factor authentication step */ - twoFactorAuthStep?: TwoFactorAuthStep; - /** Referral banners that the user dismissed */ dismissedReferralBanners?: DismissedReferralBanners; @@ -183,4 +177,4 @@ type Account = { }; export default Account; -export type {TwoFactorAuthStep, DelegateRole, DelegatedAccess, Delegate}; +export type {DelegateRole, DelegatedAccess, Delegate};