diff --git a/src/components/AnimatedStep/index.tsx b/src/components/AnimatedStep/index.tsx
index 66d2108ca5d8..2fb3e3167ff8 100644
--- a/src/components/AnimatedStep/index.tsx
+++ b/src/components/AnimatedStep/index.tsx
@@ -15,7 +15,7 @@ type AnimatedStepProps = ChildrenProps & {
direction: AnimationDirection;
/** Callback to fire when the animation ends */
- onAnimationEnd: () => void;
+ onAnimationEnd?: () => void;
};
function AnimatedStep({onAnimationEnd, direction = CONST.ANIMATION_DIRECTION.IN, style, children}: AnimatedStepProps) {
diff --git a/src/hooks/useWindowDimensions/index.native.ts b/src/hooks/useWindowDimensions/index.native.ts
index f727a40b0c3b..8b420412cf24 100644
--- a/src/hooks/useWindowDimensions/index.native.ts
+++ b/src/hooks/useWindowDimensions/index.native.ts
@@ -12,6 +12,7 @@ export default function (): WindowDimensions {
const isSmallScreenWidth = true;
const isMediumScreenWidth = false;
const isLargeScreenWidth = false;
+ const isExtraSmallScreenWidth = windowWidth <= variables.extraSmallMobileResponsiveWidthBreakpoint;
const isSmallScreen = true;
return {
@@ -21,6 +22,7 @@ export default function (): WindowDimensions {
isSmallScreenWidth,
isMediumScreenWidth,
isLargeScreenWidth,
+ isExtraSmallScreenWidth,
isSmallScreen,
};
}
diff --git a/src/hooks/useWindowDimensions/index.ts b/src/hooks/useWindowDimensions/index.ts
index 6e68e696ba89..e257b03e2865 100644
--- a/src/hooks/useWindowDimensions/index.ts
+++ b/src/hooks/useWindowDimensions/index.ts
@@ -24,6 +24,7 @@ export default function (useCachedViewportHeight = false): WindowDimensions {
const isSmallScreenWidth = windowWidth <= variables.mobileResponsiveWidthBreakpoint;
const isMediumScreenWidth = windowWidth > variables.mobileResponsiveWidthBreakpoint && windowWidth <= variables.tabletResponsiveWidthBreakpoint;
const isLargeScreenWidth = windowWidth > variables.tabletResponsiveWidthBreakpoint;
+ const isExtraSmallScreenWidth = windowWidth <= variables.extraSmallMobileResponsiveWidthBreakpoint;
const lowerScreenDimmension = Math.min(windowWidth, windowHeight);
const isSmallScreen = lowerScreenDimmension <= variables.mobileResponsiveWidthBreakpoint;
@@ -88,6 +89,7 @@ export default function (useCachedViewportHeight = false): WindowDimensions {
isSmallScreenWidth,
isMediumScreenWidth,
isLargeScreenWidth,
+ isExtraSmallScreenWidth,
isSmallScreen,
};
}
diff --git a/src/hooks/useWindowDimensions/types.ts b/src/hooks/useWindowDimensions/types.ts
index d7a1fe41d187..fa74e03f185c 100644
--- a/src/hooks/useWindowDimensions/types.ts
+++ b/src/hooks/useWindowDimensions/types.ts
@@ -5,6 +5,7 @@ type WindowDimensions = {
isSmallScreenWidth: boolean;
isMediumScreenWidth: boolean;
isLargeScreenWidth: boolean;
+ isExtraSmallScreenWidth: boolean;
isSmallScreen: boolean;
};
diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts
index 6ffbb3a358b0..6790dd5f8f10 100644
--- a/src/libs/Navigation/types.ts
+++ b/src/libs/Navigation/types.ts
@@ -100,6 +100,10 @@ type WorkspaceSwitcherNavigatorParamList = {
[SCREENS.WORKSPACE_SWITCHER.ROOT]: undefined;
};
+type BackToParams = {
+ backTo?: Routes;
+};
+
type SettingsNavigatorParamList = {
[SCREENS.SETTINGS.ROOT]: undefined;
[SCREENS.SETTINGS.SHARE_CODE]: undefined;
@@ -203,7 +207,7 @@ type SettingsNavigatorParamList = {
[SCREENS.GET_ASSISTANCE]: {
backTo: Routes;
};
- [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: undefined;
+ [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: BackToParams;
[SCREENS.SETTINGS.REPORT_CARD_LOST_OR_DAMAGED]: undefined;
[SCREENS.KEYBOARD_SHORTCUTS]: undefined;
[SCREENS.SETTINGS.EXIT_SURVEY.REASON]: undefined;
@@ -632,4 +636,5 @@ export type {
WorkspaceSwitcherNavigatorParamList,
OnboardEngagementNavigatorParamList,
SwitchPolicyIDParams,
+ BackToParams,
};
diff --git a/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.js b/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.tsx
similarity index 68%
rename from src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.js
rename to src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.tsx
index ba899a0e2d20..146bdbfff667 100644
--- a/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.js
+++ b/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.tsx
@@ -6,19 +6,35 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import useThemeStyles from '@hooks/useThemeStyles';
import * as TwoFactorAuthActions from '@userActions/TwoFactorAuthActions';
-import StepWrapperPropTypes from './StepWrapperPropTypes';
+import type {StepCounterParams} from '@src/languages/types';
+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 = null,
+ stepCounter,
onBackButtonPress = () => TwoFactorAuthActions.quitAndNavigateBack(),
children = null,
shouldEnableKeyboardAvoidingView = true,
onEntryTransitionEnd,
-}) {
+}: StepWrapperProps) {
const styles = useThemeStyles();
- const shouldShowStepCounter = Boolean(stepCounter);
-
const {animationDirection} = useAnimatedStepContext();
return (
@@ -34,7 +50,6 @@ function StepWrapper({
>
@@ -44,7 +59,6 @@ function StepWrapper({
);
}
-StepWrapper.propTypes = StepWrapperPropTypes;
StepWrapper.displayName = 'StepWrapper';
export default StepWrapper;
diff --git a/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapperPropTypes.js b/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapperPropTypes.js
deleted file mode 100644
index 3c06cc7bca52..000000000000
--- a/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapperPropTypes.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import PropTypes from 'prop-types';
-
-export default {
- /** Title of the Header */
- title: PropTypes.string,
-
- /** Data to display a step counter in the header */
- stepCounter: PropTypes.shape({
- /** Current step */
- step: PropTypes.number,
- /** Total number of steps */
- total: PropTypes.number,
- /** Text to display next to the step counter */
- text: PropTypes.string,
- }),
-
- /** Method to trigger when pressing back button of the header */
- onBackButtonPress: PropTypes.func,
-
- /** Called when navigated Screen's transition is finished. It does not fire when user exits the page. */
- onEntryTransitionEnd: PropTypes.func,
-
- /** Children components */
- children: PropTypes.node,
-
- /** Flag to indicate if the keyboard avoiding view should be enabled */
- shouldEnableKeyboardAvoidingView: PropTypes.bool,
-};
diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js b/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.tsx
similarity index 83%
rename from src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js
rename to src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.tsx
index aafa144e769f..d6c7a1abcd4f 100644
--- a/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js
+++ b/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.tsx
@@ -1,7 +1,6 @@
import React, {useEffect, useState} from 'react';
import {ActivityIndicator, ScrollView, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
-import _ from 'underscore';
import Button from '@components/Button';
import FixedFooter from '@components/FixedFooter';
import FormHelpMessage from '@components/FormHelpMessage';
@@ -16,15 +15,18 @@ import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import Clipboard from '@libs/Clipboard';
import localFileDownload from '@libs/localFileDownload';
+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 {defaultAccount, TwoFactorAuthPropTypes} from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthPropTypes';
+import type {BaseTwoFactorAuthFormOnyxProps} from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/types';
import * as Session from '@userActions/Session';
import * as TwoFactorAuthActions from '@userActions/TwoFactorAuthActions';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
-function CodesStep({account = defaultAccount, backTo}) {
+type CodesStepProps = BaseTwoFactorAuthFormOnyxProps & BackToParams;
+
+function CodesStep({account, backTo}: CodesStepProps) {
const theme = useTheme();
const styles = useThemeStyles();
const {translate} = useLocalize();
@@ -34,7 +36,8 @@ function CodesStep({account = defaultAccount, backTo}) {
const {setStep} = useTwoFactorAuthContext();
useEffect(() => {
- if (account.requiresTwoFactorAuth || account.recoveryCodes) {
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ if (account?.requiresTwoFactorAuth || account?.recoveryCodes) {
return;
}
Session.toggleTwoFactorAuth(true);
@@ -63,15 +66,15 @@ function CodesStep({account = defaultAccount, backTo}) {
{translate('twoFactorAuth.codesLoseAccess')}
- {account.isLoading ? (
+ {account?.isLoading ? (
) : (
<>
- {Boolean(account.recoveryCodes) &&
- _.map(account.recoveryCodes.split(', '), (code) => (
+ {Boolean(account?.recoveryCodes) &&
+ account?.recoveryCodes?.split(', ').map((code) => (
{
- Clipboard.setString(account.recoveryCodes);
+ Clipboard.setString(account?.recoveryCodes ?? '');
setError('');
TwoFactorAuthActions.setCodesAreCopied();
}}
styles={[styles.button, styles.buttonMedium, styles.twoFactorAuthCodesButton]}
textStyles={[styles.buttonMediumText]}
+ accessible={false}
+ tooltipText=""
+ tooltipTextChecked=""
/>
{
- localFileDownload('two-factor-auth-codes', account.recoveryCodes);
+ localFileDownload('two-factor-auth-codes', account?.recoveryCodes ?? '');
setError('');
TwoFactorAuthActions.setCodesAreCopied();
}}
inline={false}
styles={[styles.button, styles.buttonMedium, styles.twoFactorAuthCodesButton]}
textStyles={[styles.buttonMediumText]}
+ accessible={false}
+ tooltipText=""
+ tooltipTextChecked=""
/>
>
@@ -112,7 +121,7 @@ function CodesStep({account = defaultAccount, backTo}) {
- {!_.isEmpty(error) && (
+ {Boolean(error) && (
{
- if (!account.codesAreCopied) {
+ if (!account?.codesAreCopied) {
setError('twoFactorAuth.errorStepCodes');
return;
}
@@ -136,10 +145,8 @@ function CodesStep({account = defaultAccount, backTo}) {
);
}
-CodesStep.propTypes = TwoFactorAuthPropTypes;
CodesStep.displayName = 'CodesStep';
-// eslint-disable-next-line rulesdir/onyx-props-must-have-default
-export default withOnyx({
+export default withOnyx({
account: {key: ONYXKEYS.ACCOUNT},
})(CodesStep);
diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/DisabledStep.js b/src/pages/settings/Security/TwoFactorAuth/Steps/DisabledStep.tsx
similarity index 100%
rename from src/pages/settings/Security/TwoFactorAuth/Steps/DisabledStep.js
rename to src/pages/settings/Security/TwoFactorAuth/Steps/DisabledStep.tsx
diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/EnabledStep.js b/src/pages/settings/Security/TwoFactorAuth/Steps/EnabledStep.tsx
similarity index 100%
rename from src/pages/settings/Security/TwoFactorAuth/Steps/EnabledStep.js
rename to src/pages/settings/Security/TwoFactorAuth/Steps/EnabledStep.tsx
diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/SuccessStep.js b/src/pages/settings/Security/TwoFactorAuth/Steps/SuccessStep.tsx
similarity index 83%
rename from src/pages/settings/Security/TwoFactorAuth/Steps/SuccessStep.js
rename to src/pages/settings/Security/TwoFactorAuth/Steps/SuccessStep.tsx
index de36888f30b8..5e92fb8649ef 100644
--- a/src/pages/settings/Security/TwoFactorAuth/Steps/SuccessStep.js
+++ b/src/pages/settings/Security/TwoFactorAuth/Steps/SuccessStep.tsx
@@ -1,24 +1,15 @@
-import PropTypes from 'prop-types';
import React from 'react';
import ConfirmationPage from '@components/ConfirmationPage';
import LottieAnimations from '@components/LottieAnimations';
import useLocalize from '@hooks/useLocalize';
+import type {BackToParams} from '@libs/Navigation/types';
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 TwoFactorAuthActions from '@userActions/TwoFactorAuthActions';
import CONST from '@src/CONST';
-const propTypes = {
- /** The route where user needs to be redirected after setting up 2FA */
- backTo: PropTypes.string,
-};
-
-const defaultProps = {
- backTo: '',
-};
-
-function SuccessStep({backTo}) {
+function SuccessStep({backTo}: BackToParams) {
const {setStep} = useTwoFactorAuthContext();
const {translate} = useLocalize();
@@ -49,7 +40,4 @@ function SuccessStep({backTo}) {
);
}
-SuccessStep.propTypes = propTypes;
-SuccessStep.defaultProps = defaultProps;
-
export default SuccessStep;
diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.js b/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.tsx
similarity index 77%
rename from src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.js
rename to src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.tsx
index e5f809204bd6..d9998c777f3b 100644
--- a/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.js
+++ b/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.tsx
@@ -1,11 +1,11 @@
-import PropTypes from 'prop-types';
-import React, {useEffect} from 'react';
+import React, {useEffect, useRef} from 'react';
import {ScrollView, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import expensifyLogo from '@assets/images/expensify-logo-round-transparent.png';
import Button from '@components/Button';
import FixedFooter from '@components/FixedFooter';
import * as Expensicons from '@components/Icon/Expensicons';
+import {useSession} from '@components/OnyxProvider';
import PressableWithDelayToggle from '@components/Pressable/PressableWithDelayToggle';
import QRCode from '@components/QRCode';
import Text from '@components/Text';
@@ -16,25 +16,21 @@ import Clipboard from '@libs/Clipboard';
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 {defaultAccount, TwoFactorAuthPropTypes} from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthPropTypes';
+import type {BaseTwoFactorAuthFormOnyxProps, BaseTwoFactorAuthFormRef} from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/types';
import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
const TROUBLESHOOTING_LINK = 'https://community.expensify.com/discussion/7736/faq-troubleshooting-two-factor-authentication-issues/p1?new=1';
-const defaultProps = {
- account: defaultAccount,
- session: {
- email: null,
- },
-};
+type VerifyStepProps = BaseTwoFactorAuthFormOnyxProps;
-function VerifyStep({account, session}) {
+function VerifyStep({account}: VerifyStepProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
+ const session = useSession();
- const formRef = React.useRef(null);
+ const formRef = useRef(null);
const {setStep} = useTwoFactorAuthContext();
@@ -46,19 +42,16 @@ function VerifyStep({account, session}) {
}, []);
useEffect(() => {
- if (!account.requiresTwoFactorAuth) {
+ if (!account?.requiresTwoFactorAuth) {
return;
}
setStep(CONST.TWO_FACTOR_AUTH_STEPS.SUCCESS);
- }, [account.requiresTwoFactorAuth, setStep]);
+ }, [account?.requiresTwoFactorAuth, setStep]);
/**
* Splits the two-factor auth secret key in 4 chunks
- *
- * @param {String} secret
- * @returns {string}
*/
- function splitSecretInChunks(secret) {
+ function splitSecretInChunks(secret: string) {
if (secret.length !== 16) {
return secret;
}
@@ -69,11 +62,9 @@ function VerifyStep({account, session}) {
/**
* Builds the URL string to generate the QRCode, using the otpauth:// protocol,
* so it can be detected by authenticator apps
- *
- * @returns {string}
*/
function buildAuthenticatorUrl() {
- return `otpauth://totp/Expensify:${account.primaryLogin || session.email}?secret=${account.twoFactorAuthSecretKey}&issuer=Expensify`;
+ return `otpauth://totp/Expensify:${account?.primaryLogin ?? session?.email}?secret=${account?.twoFactorAuthSecretKey}&issuer=Expensify`;
}
return (
@@ -106,15 +97,18 @@ function VerifyStep({account, session}) {
{translate('twoFactorAuth.addKey')}
- {Boolean(account.twoFactorAuthSecretKey) && {splitSecretInChunks(account.twoFactorAuthSecretKey)}}
+ {Boolean(account?.twoFactorAuthSecretKey) && {splitSecretInChunks(account?.twoFactorAuthSecretKey ?? '')}}
Clipboard.setString(account.twoFactorAuthSecretKey)}
+ onPress={() => Clipboard.setString(account?.twoFactorAuthSecretKey ?? '')}
styles={[styles.button, styles.buttonMedium, styles.twoFactorAuthCopyCodeButton]}
textStyles={[styles.buttonMediumText]}
+ accessible={false}
/>
{translate('twoFactorAuth.enterCode')}
@@ -127,7 +121,7 @@ function VerifyStep({account, session}) {