diff --git a/src/pages/signin/LoginForm/BaseLoginForm.js b/src/pages/signin/LoginForm/BaseLoginForm.js index de2f2900c58d..1ac12dca0a09 100644 --- a/src/pages/signin/LoginForm/BaseLoginForm.js +++ b/src/pages/signin/LoginForm/BaseLoginForm.js @@ -245,6 +245,15 @@ function LoginForm(props) { isInputFocused() { return input.current && input.current.isFocused(); }, + clearDataAndFocus(clearLogin = true) { + if (!input.current) { + return; + } + if (clearLogin) { + Session.clearSignInData(); + } + input.current.focus(); + }, })); const formErrorText = useMemo(() => (formError ? translate(formError) : ''), [formError, translate]); diff --git a/src/pages/signin/LoginForm/index.js b/src/pages/signin/LoginForm/index.js index 91aba70a866f..e861100c25fe 100644 --- a/src/pages/signin/LoginForm/index.js +++ b/src/pages/signin/LoginForm/index.js @@ -1,18 +1,24 @@ import PropTypes from 'prop-types'; import React from 'react'; +import refPropTypes from '@components/refPropTypes'; import BaseLoginForm from './BaseLoginForm'; const propTypes = { /** Function used to scroll to the top of the page */ scrollPageToTop: PropTypes.func, + + /** A reference so we can expose clearDataAndFocus */ + innerRef: refPropTypes, }; const defaultProps = { scrollPageToTop: undefined, + innerRef: () => {}, }; -function LoginForm(props) { +function LoginForm({innerRef, ...props}) { return ( @@ -23,4 +29,14 @@ LoginForm.displayName = 'LoginForm'; LoginForm.propTypes = propTypes; LoginForm.defaultProps = defaultProps; -export default LoginForm; +const LoginFormWithRef = React.forwardRef((props, ref) => ( + +)); + +LoginFormWithRef.displayName = 'LoginFormWithRef'; + +export default LoginFormWithRef; diff --git a/src/pages/signin/LoginForm/index.native.js b/src/pages/signin/LoginForm/index.native.js index 87258e69165f..3187faac8127 100644 --- a/src/pages/signin/LoginForm/index.native.js +++ b/src/pages/signin/LoginForm/index.native.js @@ -1,17 +1,23 @@ import PropTypes from 'prop-types'; import React, {useEffect, useRef} from 'react'; +import _ from 'underscore'; +import refPropTypes from '@components/refPropTypes'; import AppStateMonitor from '@libs/AppStateMonitor'; import BaseLoginForm from './BaseLoginForm'; const propTypes = { /** Function used to scroll to the top of the page */ scrollPageToTop: PropTypes.func, + + /** A reference so we can expose clearDataAndFocus */ + innerRef: refPropTypes, }; const defaultProps = { scrollPageToTop: undefined, + innerRef: () => {}, }; -function LoginForm(props) { +function LoginForm({innerRef, ...props}) { const loginFormRef = useRef(); const {scrollPageToTop} = props; @@ -36,7 +42,15 @@ function LoginForm(props) { (loginFormRef.current = ref)} + ref={(ref) => { + loginFormRef.current = ref; + if (typeof innerRef === 'function') { + innerRef(ref); + } else if (innerRef && _.has(innerRef, 'current')) { + // eslint-disable-next-line no-param-reassign + innerRef.current = ref; + } + }} /> ); } @@ -45,4 +59,14 @@ LoginForm.displayName = 'LoginForm'; LoginForm.propTypes = propTypes; LoginForm.defaultProps = defaultProps; -export default LoginForm; +const LoginFormWithRef = React.forwardRef((props, ref) => ( + +)); + +LoginFormWithRef.displayName = 'LoginFormWithRef'; + +export default LoginFormWithRef; diff --git a/src/pages/signin/SignInPage.js b/src/pages/signin/SignInPage.js index 8cb0ef9907af..9d5b51d667ff 100644 --- a/src/pages/signin/SignInPage.js +++ b/src/pages/signin/SignInPage.js @@ -140,6 +140,7 @@ function SignInPageInner({credentials, account, isInModal, activeClients, prefer const shouldShowSmallScreen = isSmallScreenWidth || isInModal; const safeAreaInsets = useSafeAreaInsets(); const signInPageLayoutRef = useRef(); + const loginFormRef = useRef(); /** This state is needed to keep track of if user is using recovery code instead of 2fa code, * and we need it here since welcome text(`welcomeText`) also depends on it */ const [isUsingRecoveryCode, setIsUsingRecoveryCode] = useState(false); @@ -242,6 +243,11 @@ function SignInPageInner({credentials, account, isInModal, activeClients, prefer Log.warn('SignInPage in unexpected state!'); } + const navigateFocus = () => { + signInPageLayoutRef.current.scrollPageToTop(); + loginFormRef.current.clearDataAndFocus(); + }; + return ( // Bottom SafeAreaView is removed so that login screen svg displays correctly on mobile. // The SVG should flow under the Home Indicator on iOS. @@ -253,10 +259,12 @@ function SignInPageInner({credentials, account, isInModal, activeClients, prefer shouldShowWelcomeText={shouldShowWelcomeText} ref={signInPageLayoutRef} shouldShowSmallScreen={shouldShowSmallScreen} + navigateFocus={navigateFocus} > {/* LoginForm must use the isVisible prop. This keeps it mounted, but visually hidden so that password managers can access the values. Conditionally rendering this component will break this feature. */} { - scrollPageToTop(); - - // We need to clear sign in data in case the user is already in the ValidateCodeForm or PasswordForm pages - Session.clearSignInData(); -}; - -const columns = ({scrollPageToTop}) => [ +const columns = ({navigateFocus}) => [ { translationPath: 'footer.features', rows: [ @@ -135,11 +127,11 @@ const columns = ({scrollPageToTop}) => [ translationPath: 'footer.getStarted', rows: [ { - onPress: () => navigateHome(scrollPageToTop), + onPress: () => navigateFocus(), translationPath: 'footer.createAccount', }, { - onPress: () => navigateHome(scrollPageToTop), + onPress: () => navigateFocus(), translationPath: 'footer.logIn', }, ], @@ -172,7 +164,7 @@ function Footer(props) { ) : null} - {_.map(columns({scrollPageToTop: props.scrollPageToTop}), (column, i) => ( + {_.map(columns({navigateFocus: props.navigateFocus}), (column, i) => ( -