From dfcc774a90346e4e46100184b54499f09cda0825 Mon Sep 17 00:00:00 2001 From: RohanSasne Date: Tue, 30 Jan 2024 13:02:58 +0530 Subject: [PATCH 01/21] Add isExtraSmallScreenWidth Prop --- src/hooks/useWindowDimensions/index.native.ts | 2 ++ src/hooks/useWindowDimensions/index.ts | 2 ++ src/hooks/useWindowDimensions/types.ts | 1 + 3 files changed, 5 insertions(+) diff --git a/src/hooks/useWindowDimensions/index.native.ts b/src/hooks/useWindowDimensions/index.native.ts index 5d556234aeb9..5b389e8aa2e1 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; return { windowWidth, @@ -20,5 +21,6 @@ export default function (): WindowDimensions { isSmallScreenWidth, isMediumScreenWidth, isLargeScreenWidth, + isExtraSmallScreenWidth, }; } diff --git a/src/hooks/useWindowDimensions/index.ts b/src/hooks/useWindowDimensions/index.ts index b0a29e9f901b..625bbc3c7845 100644 --- a/src/hooks/useWindowDimensions/index.ts +++ b/src/hooks/useWindowDimensions/index.ts @@ -14,6 +14,7 @@ export default function (): WindowDimensions { const isSmallScreenWidth = windowWidth <= variables.mobileResponsiveWidthBreakpoint; const isMediumScreenWidth = windowWidth > variables.mobileResponsiveWidthBreakpoint && windowWidth <= variables.tabletResponsiveWidthBreakpoint; const isLargeScreenWidth = windowWidth > variables.tabletResponsiveWidthBreakpoint; + const isExtraSmallScreenWidth = windowWidth <= variables.extraSmallMobileResponsiveWidthBreakpoint; return { windowWidth, @@ -22,5 +23,6 @@ export default function (): WindowDimensions { isSmallScreenWidth, isMediumScreenWidth, isLargeScreenWidth, + isExtraSmallScreenWidth, }; } diff --git a/src/hooks/useWindowDimensions/types.ts b/src/hooks/useWindowDimensions/types.ts index 9b59d4968935..5793e8f2c806 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; }; export default WindowDimensions; From e9ca05234e0f137629b11228622a0b706fddacb4 Mon Sep 17 00:00:00 2001 From: RohanSasne Date: Tue, 30 Jan 2024 13:14:46 +0530 Subject: [PATCH 02/21] Convert steps to typescriopt --- .../Steps/{CodesStep.js => CodesStep.tsx} | 63 ++++++++++-------- .../{DisabledStep.js => DisabledStep.tsx} | 0 .../Steps/{EnabledStep.js => EnabledStep.tsx} | 0 .../Steps/{SuccessStep.js => SuccessStep.tsx} | 22 +++---- .../Steps/{VerifyStep.js => VerifyStep.tsx} | 64 ++++++++----------- 5 files changed, 74 insertions(+), 75 deletions(-) rename src/pages/settings/Security/TwoFactorAuth/Steps/{CodesStep.js => CodesStep.tsx} (74%) rename src/pages/settings/Security/TwoFactorAuth/Steps/{DisabledStep.js => DisabledStep.tsx} (100%) rename src/pages/settings/Security/TwoFactorAuth/Steps/{EnabledStep.js => EnabledStep.tsx} (100%) rename src/pages/settings/Security/TwoFactorAuth/Steps/{SuccessStep.js => SuccessStep.tsx} (83%) rename src/pages/settings/Security/TwoFactorAuth/Steps/{VerifyStep.js => VerifyStep.tsx} (77%) diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js b/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.tsx similarity index 74% rename from src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js rename to src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.tsx index 420d976dcd26..f124565db39b 100644 --- a/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js +++ b/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.tsx @@ -1,7 +1,7 @@ -import React, {useEffect, useState} from 'react'; -import {ActivityIndicator, ScrollView, View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import React, { useEffect, useState } from 'react'; +import { ActivityIndicator, ScrollView, View } from 'react-native'; +import { withOnyx } from 'react-native-onyx'; +import type { Route } from '@src/ROUTES'; import Button from '@components/Button'; import FixedFooter from '@components/FixedFooter'; import FormHelpMessage from '@components/FormHelpMessage'; @@ -18,23 +18,29 @@ import Clipboard from '@libs/Clipboard'; import localFileDownload from '@libs/localFileDownload'; 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 TwoFactorAuthOnyxProps from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthPropTypes'; import * as Session from '@userActions/Session'; import * as TwoFactorAuthActions from '@userActions/TwoFactorAuthActions'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type { TranslationPaths } from '@src/languages/types'; -function CodesStep({account = defaultAccount, backTo}) { +type CodesStepProps = TwoFactorAuthOnyxProps & { + /** The route to go back to when the user cancels */ + backTo: Route | undefined; +} + +function CodesStep({ account, backTo }: CodesStepProps) { const theme = useTheme(); const styles = useThemeStyles(); - const {translate} = useLocalize(); - const {isExtraSmallScreenWidth, isSmallScreenWidth} = useWindowDimensions(); + const { translate } = useLocalize(); + const { isExtraSmallScreenWidth, isSmallScreenWidth } = useWindowDimensions(); const [error, setError] = useState(''); - const {setStep} = useTwoFactorAuthContext(); + const { setStep } = useTwoFactorAuthContext(); useEffect(() => { - if (account.requiresTwoFactorAuth || account.recoveryCodes) { + if (account?.requiresTwoFactorAuth ?? account?.recoveryCodes) { return; } Session.toggleTwoFactorAuth(true); @@ -62,23 +68,25 @@ 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) => ( {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,10 +126,10 @@ function CodesStep({account = defaultAccount, backTo}) { - {!_.isEmpty(error) && ( + {!error && ( )} @@ -123,7 +137,7 @@ function CodesStep({account = defaultAccount, backTo}) { success text={translate('common.next')} onPress={() => { - if (!account.codesAreCopied) { + if (!account?.codesAreCopied) { setError('twoFactorAuth.errorStepCodes'); return; } @@ -136,10 +150,9 @@ function CodesStep({account = defaultAccount, backTo}) { ); } -CodesStep.propTypes = TwoFactorAuthPropTypes; CodesStep.displayName = 'CodesStep'; -// eslint-disable-next-line rulesdir/onyx-props-must-have-default -export default withOnyx({ - account: {key: ONYXKEYS.ACCOUNT}, -})(CodesStep); +export default withOnyx({ + account: { key: ONYXKEYS.ACCOUNT }, + session: { key: ONYXKEYS.SESSION }, +})(CodesStep); \ No newline at end of file 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..9ffc25427a64 100644 --- a/src/pages/settings/Security/TwoFactorAuth/Steps/SuccessStep.js +++ b/src/pages/settings/Security/TwoFactorAuth/Steps/SuccessStep.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React from 'react'; import ConfirmationPage from '@components/ConfirmationPage'; import LottieAnimations from '@components/LottieAnimations'; @@ -8,17 +7,16 @@ import StepWrapper from '@pages/settings/Security/TwoFactorAuth/StepWrapper/Step import useTwoFactorAuthContext from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/useTwoFactorAuth'; import * as TwoFactorAuthActions from '@userActions/TwoFactorAuthActions'; import CONST from '@src/CONST'; +import type { Route } from '@src/ROUTES'; -const propTypes = { +type SuccessStepProps = { /** The route where user needs to be redirected after setting up 2FA */ - backTo: PropTypes.string, + backTo: string; }; -const defaultProps = { - backTo: '', -}; - -function SuccessStep({backTo}) { +function SuccessStep({ + backTo='' +}:SuccessStepProps) { const {setStep} = useTwoFactorAuthContext(); const {translate} = useLocalize(); @@ -29,6 +27,7 @@ function SuccessStep({backTo}) { stepCounter={{ step: 3, text: translate('twoFactorAuth.stepSuccess'), + total: 3, }} > @@ -49,7 +48,4 @@ function SuccessStep({backTo}) { ); } -SuccessStep.propTypes = propTypes; -SuccessStep.defaultProps = defaultProps; - -export default SuccessStep; +export default SuccessStep; \ No newline at end of file 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..4e2152c74fb3 100644 --- a/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.js +++ b/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.tsx @@ -1,5 +1,4 @@ -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'; @@ -16,25 +15,29 @@ 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 TwoFactorAuthOnyxProps from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthPropTypes'; 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, +type VerifyStepProps = TwoFactorAuthOnyxProps & { + /** Session of currently logged in user */ session: { - email: null, - }, + /** Email address */ + email: string; + }; }; -function VerifyStep({account, session}) { +function VerifyStep({ + account, + session, +}: VerifyStepProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const formRef = React.useRef(null); + const formRef = useRef(null);; const {setStep} = useTwoFactorAuthContext(); @@ -46,19 +49,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): string { if (secret.length !== 16) { return secret; } @@ -69,11 +69,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`; + function buildAuthenticatorUrl(): string { + return `otpauth://totp/Expensify:${account?.primaryLogin ?? session.email}?secret=${account?.twoFactorAuthSecretKey}&issuer=Expensify`; } return ( @@ -85,7 +83,7 @@ function VerifyStep({account, session}) { total: 3, }} onBackButtonPress={() => setStep(CONST.TWO_FACTOR_AUTH_STEPS.CODES, CONST.ANIMATION_DIRECTION.OUT)} - onEntryTransitionEnd={() => formRef.current && formRef.current.focus()} + onEntryTransitionEnd={() => formRef.current?.focus()} > {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} + tooltipText='' + tooltipTextChecked='' /> {translate('twoFactorAuth.enterCode')} @@ -127,12 +128,12 @@ function VerifyStep({account, session}) {