From bbbbc7c5a720c08ca13712e6a858cbe69ede2f84 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Fri, 27 Jan 2023 10:13:16 -0700
Subject: [PATCH 01/23] add error to textinput

---
 src/pages/signin/PasswordForm.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index d984e6fd27b3..6c376ca749eb 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -151,6 +151,7 @@ class PasswordForm extends React.Component {
     }
 
     render() {
+        const hasError = this.state.formError || (this.props.account && !_.isEmpty(this.props.account.errors));
         return (
             <>
                 <View style={[styles.mv3]}>
@@ -166,6 +167,7 @@ class PasswordForm extends React.Component {
                         onChangeText={text => this.setState({password: text})}
                         onSubmitEditing={this.validateAndSubmitForm}
                         blurOnSubmit={false}
+                        hasError={hasError}
                     />
                     <View style={[styles.changeExpensifyLoginLinkContainer]}>
                         <TouchableOpacity
@@ -192,6 +194,7 @@ class PasswordForm extends React.Component {
                             keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
                             blurOnSubmit={false}
                             maxLength={CONST.TFA_CODE_LENGTH}
+                            hasError={hasError}
                         />
                     </View>
                 )}

From 56c3d7db44406647c9e04033e876a789672531fd Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Fri, 27 Jan 2023 10:15:16 -0700
Subject: [PATCH 02/23] add error for email field

---
 src/pages/signin/LoginForm.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/pages/signin/LoginForm.js b/src/pages/signin/LoginForm.js
index e64705de8239..1e7f32f7cf0a 100755
--- a/src/pages/signin/LoginForm.js
+++ b/src/pages/signin/LoginForm.js
@@ -183,6 +183,7 @@ class LoginForm extends React.Component {
                         autoCapitalize="none"
                         autoCorrect={false}
                         keyboardType={CONST.KEYBOARD_TYPE.EMAIL_ADDRESS}
+                        hasError={Boolean(error)}
                     />
                 </View>
                 {!_.isEmpty(this.props.account.success) && (

From 99de66834bddca04b72d735d6e1a2f2c4a7edb4b Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Fri, 27 Jan 2023 14:07:29 -0700
Subject: [PATCH 03/23] rm hasError

---
 src/pages/signin/PasswordForm.js | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index 6c376ca749eb..d984e6fd27b3 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -151,7 +151,6 @@ class PasswordForm extends React.Component {
     }
 
     render() {
-        const hasError = this.state.formError || (this.props.account && !_.isEmpty(this.props.account.errors));
         return (
             <>
                 <View style={[styles.mv3]}>
@@ -167,7 +166,6 @@ class PasswordForm extends React.Component {
                         onChangeText={text => this.setState({password: text})}
                         onSubmitEditing={this.validateAndSubmitForm}
                         blurOnSubmit={false}
-                        hasError={hasError}
                     />
                     <View style={[styles.changeExpensifyLoginLinkContainer]}>
                         <TouchableOpacity
@@ -194,7 +192,6 @@ class PasswordForm extends React.Component {
                             keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
                             blurOnSubmit={false}
                             maxLength={CONST.TFA_CODE_LENGTH}
-                            hasError={hasError}
                         />
                     </View>
                 )}

From 62f05efd931ec7ea4e4676014a355f93d16b89c2 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Fri, 27 Jan 2023 14:13:50 -0700
Subject: [PATCH 04/23] import form

---
 src/pages/signin/PasswordForm.js | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index d984e6fd27b3..444de9c21d51 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -24,6 +24,7 @@ import * as ErrorUtils from '../../libs/ErrorUtils';
 import {withNetwork} from '../../components/OnyxProvider';
 import networkPropTypes from '../../components/networkPropTypes';
 import OfflineIndicator from '../../components/OfflineIndicator';
+import Form from '../../components/Form';
 
 const propTypes = {
     /* Onyx Props */
@@ -152,7 +153,12 @@ class PasswordForm extends React.Component {
 
     render() {
         return (
-            <>
+            <Form
+                formID="PasswordForm"
+                submitButtonText={this.props.translate('common.saveAndContinue')}
+                onSubmit={this.validateAndSubmitForm}
+                validate={this.validateAndSubmitForm}
+            >
                 <View style={[styles.mv3]}>
                     <TextInput
                         ref={el => this.inputPassword = el}
@@ -219,7 +225,7 @@ class PasswordForm extends React.Component {
                     <ChangeExpensifyLoginLink onPress={this.clearSignInData} />
                 </View>
                 <OfflineIndicator containerStyles={[styles.mv5]} />
-            </>
+            </Form>
         );
     }
 }

From ad1a7a599c9b655863dfecbfad5333ea1e4f5751 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Fri, 27 Jan 2023 14:18:50 -0700
Subject: [PATCH 05/23] fix layout

---
 src/pages/signin/PasswordForm.js | 124 +++++++++++++++----------------
 1 file changed, 58 insertions(+), 66 deletions(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index 444de9c21d51..0f1997382b90 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -153,79 +153,71 @@ class PasswordForm extends React.Component {
 
     render() {
         return (
-            <Form
-                formID="PasswordForm"
-                submitButtonText={this.props.translate('common.saveAndContinue')}
-                onSubmit={this.validateAndSubmitForm}
-                validate={this.validateAndSubmitForm}
-            >
-                <View style={[styles.mv3]}>
-                    <TextInput
-                        ref={el => this.inputPassword = el}
-                        label={this.props.translate('common.password')}
-                        secureTextEntry
-                        autoCompleteType={ComponentUtils.PASSWORD_AUTOCOMPLETE_TYPE}
-                        textContentType="password"
-                        nativeID="password"
-                        name="password"
-                        value={this.state.password}
-                        onChangeText={text => this.setState({password: text})}
-                        onSubmitEditing={this.validateAndSubmitForm}
-                        blurOnSubmit={false}
-                    />
-                    <View style={[styles.changeExpensifyLoginLinkContainer]}>
-                        <TouchableOpacity
-                            style={[styles.mt2]}
-                            onPress={this.resetPassword}
-                        >
-                            <Text style={[styles.link]}>
-                                {this.props.translate('passwordForm.forgot')}
-                            </Text>
-                        </TouchableOpacity>
-                    </View>
-                </View>
-
-                {this.props.account.requiresTwoFactorAuth && (
+            <>
+                <Form
+                    formID="PasswordForm"
+                    submitButtonText={this.props.translate('common.saveAndContinue')}
+                    onSubmit={this.validateAndSubmitForm}
+                    validate={this.validateAndSubmitForm}
+                >
                     <View style={[styles.mv3]}>
                         <TextInput
-                            ref={el => this.input2FA = el}
-                            label={this.props.translate('passwordForm.twoFactorCode')}
-                            value={this.state.twoFactorAuthCode}
-                            placeholder={this.props.translate('passwordForm.requiredWhen2FAEnabled')}
-                            placeholderTextColor={themeColors.placeholderText}
-                            onChangeText={text => this.setState({twoFactorAuthCode: text})}
+                            ref={el => this.inputPassword = el}
+                            label={this.props.translate('common.password')}
+                            secureTextEntry
+                            autoCompleteType={ComponentUtils.PASSWORD_AUTOCOMPLETE_TYPE}
+                            textContentType="password"
+                            nativeID="password"
+                            name="password"
+                            value={this.state.password}
+                            onChangeText={text => this.setState({password: text})}
                             onSubmitEditing={this.validateAndSubmitForm}
-                            keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
                             blurOnSubmit={false}
-                            maxLength={CONST.TFA_CODE_LENGTH}
                         />
+                        <View style={[styles.changeExpensifyLoginLinkContainer]}>
+                            <TouchableOpacity
+                                style={[styles.mt2]}
+                                onPress={this.resetPassword}
+                            >
+                                <Text style={[styles.link]}>
+                                    {this.props.translate('passwordForm.forgot')}
+                                </Text>
+                            </TouchableOpacity>
+                        </View>
                     </View>
-                )}
-
-                {!this.state.formError && this.props.account && !_.isEmpty(this.props.account.errors) && (
-                    <Text style={[styles.formError]}>
-                        {ErrorUtils.getLatestErrorMessage(this.props.account)}
-                    </Text>
-                )}
-
-                {this.state.formError && (
-                    <Text style={[styles.formError]}>
-                        {this.props.translate(this.state.formError)}
-                    </Text>
-                )}
-                <View>
-                    <Button
-                        isDisabled={this.props.network.isOffline}
-                        success
-                        style={[styles.mv3]}
-                        text={this.props.translate('common.signIn')}
-                        isLoading={this.props.account.isLoading}
-                        onPress={this.validateAndSubmitForm}
-                    />
-                    <ChangeExpensifyLoginLink onPress={this.clearSignInData} />
-                </View>
+
+                    {this.props.account.requiresTwoFactorAuth && (
+                        <View style={[styles.mv3]}>
+                            <TextInput
+                                ref={el => this.input2FA = el}
+                                label={this.props.translate('passwordForm.twoFactorCode')}
+                                value={this.state.twoFactorAuthCode}
+                                placeholder={this.props.translate('passwordForm.requiredWhen2FAEnabled')}
+                                placeholderTextColor={themeColors.placeholderText}
+                                onChangeText={text => this.setState({twoFactorAuthCode: text})}
+                                onSubmitEditing={this.validateAndSubmitForm}
+                                keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
+                                blurOnSubmit={false}
+                                maxLength={CONST.TFA_CODE_LENGTH}
+                            />
+                        </View>
+                    )}
+
+                    {!this.state.formError && this.props.account && !_.isEmpty(this.props.account.errors) && (
+                        <Text style={[styles.formError]}>
+                            {ErrorUtils.getLatestErrorMessage(this.props.account)}
+                        </Text>
+                    )}
+
+                    {this.state.formError && (
+                        <Text style={[styles.formError]}>
+                            {this.props.translate(this.state.formError)}
+                        </Text>
+                    )}
+                </Form>
+                <ChangeExpensifyLoginLink onPress={this.clearSignInData} />
                 <OfflineIndicator containerStyles={[styles.mv5]} />
-            </Form>
+            </>
         );
     }
 }

From 90abcf672f4622142675fef3e165962eb2cdf43b Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Fri, 27 Jan 2023 14:19:13 -0700
Subject: [PATCH 06/23] update submit button text

---
 src/pages/signin/PasswordForm.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index 0f1997382b90..448e79522eb3 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -156,7 +156,7 @@ class PasswordForm extends React.Component {
             <>
                 <Form
                     formID="PasswordForm"
-                    submitButtonText={this.props.translate('common.saveAndContinue')}
+                    submitButtonText={this.props.translate('common.signIn')}
                     onSubmit={this.validateAndSubmitForm}
                     validate={this.validateAndSubmitForm}
                 >

From 39505dde8dc85837a09142b0cfc8899c3b36dc13 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Fri, 27 Jan 2023 14:24:55 -0700
Subject: [PATCH 07/23] rm input props

---
 src/pages/signin/PasswordForm.js | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index 448e79522eb3..3e38688e3f43 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -162,6 +162,7 @@ class PasswordForm extends React.Component {
                 >
                     <View style={[styles.mv3]}>
                         <TextInput
+                            inputID="password"
                             ref={el => this.inputPassword = el}
                             label={this.props.translate('common.password')}
                             secureTextEntry
@@ -169,9 +170,6 @@ class PasswordForm extends React.Component {
                             textContentType="password"
                             nativeID="password"
                             name="password"
-                            value={this.state.password}
-                            onChangeText={text => this.setState({password: text})}
-                            onSubmitEditing={this.validateAndSubmitForm}
                             blurOnSubmit={false}
                         />
                         <View style={[styles.changeExpensifyLoginLinkContainer]}>
@@ -189,13 +187,11 @@ class PasswordForm extends React.Component {
                     {this.props.account.requiresTwoFactorAuth && (
                         <View style={[styles.mv3]}>
                             <TextInput
+                                inputID="twoFactorCode"
                                 ref={el => this.input2FA = el}
                                 label={this.props.translate('passwordForm.twoFactorCode')}
-                                value={this.state.twoFactorAuthCode}
                                 placeholder={this.props.translate('passwordForm.requiredWhen2FAEnabled')}
                                 placeholderTextColor={themeColors.placeholderText}
-                                onChangeText={text => this.setState({twoFactorAuthCode: text})}
-                                onSubmitEditing={this.validateAndSubmitForm}
                                 keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
                                 blurOnSubmit={false}
                                 maxLength={CONST.TFA_CODE_LENGTH}

From c75b744b14773b22a434514281df6245313ea047 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Fri, 27 Jan 2023 14:35:58 -0700
Subject: [PATCH 08/23] create validate function

---
 src/pages/signin/PasswordForm.js | 28 ++++++++++++++++++++++++++--
 1 file changed, 26 insertions(+), 2 deletions(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index 3e38688e3f43..e45fc3bde8b9 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -53,6 +53,7 @@ class PasswordForm extends React.Component {
     constructor(props) {
         super(props);
         this.validateAndSubmitForm = this.validateAndSubmitForm.bind(this);
+        this.validate = this.validate.bind(this);
         this.resetPassword = this.resetPassword.bind(this);
         this.clearSignInData = this.clearSignInData.bind(this);
 
@@ -111,6 +112,29 @@ class PasswordForm extends React.Component {
         Session.clearSignInData();
     }
 
+    /**
+     * @param {Object} values - form input values passed by the Form component
+     * @returns {Boolean}
+     */
+    validate(values) {
+        const requiresTwoFactorAuth = this.props.account.requiresTwoFactorAuth;
+        const errors = {};
+
+        if (!values.password || !values.password.trim()) {
+            errors.password = this.props.translate('passwordForm.pleaseFillPassword');
+        } else if (!ValidationUtils.isValidPassword(values.password.trim())) {
+            errors.password = this.props.translate('passwordForm.error.incorrectPassword');
+        }
+
+        if (requiresTwoFactorAuth && !values.twoFactorAuthCode.trim()) {
+            errors.twoFactorAuthCode = this.props.translate('passwordForm.pleaseFillTwoFactorAuth');
+        } else if (requiresTwoFactorAuth && !ValidationUtils.isValidTwoFactorCode(values.twoFactorAuthCode.trim())) {
+            errors.twoFactorAuthCode = this.props.translate('passwordForm.error.incorrect2fa');
+        }
+
+        return errors;
+    }
+
     /**
      * Check that all the form fields are valid, then trigger the submit callback
      */
@@ -157,8 +181,8 @@ class PasswordForm extends React.Component {
                 <Form
                     formID="PasswordForm"
                     submitButtonText={this.props.translate('common.signIn')}
+                    validate={this.validate}
                     onSubmit={this.validateAndSubmitForm}
-                    validate={this.validateAndSubmitForm}
                 >
                     <View style={[styles.mv3]}>
                         <TextInput
@@ -187,7 +211,7 @@ class PasswordForm extends React.Component {
                     {this.props.account.requiresTwoFactorAuth && (
                         <View style={[styles.mv3]}>
                             <TextInput
-                                inputID="twoFactorCode"
+                                inputID="twoFactorAuthCode"
                                 ref={el => this.input2FA = el}
                                 label={this.props.translate('passwordForm.twoFactorCode')}
                                 placeholder={this.props.translate('passwordForm.requiredWhen2FAEnabled')}

From 0d57d900426e0c59cef54e1161029bdce6172646 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Fri, 27 Jan 2023 14:40:12 -0700
Subject: [PATCH 09/23] update submit function

---
 src/pages/signin/PasswordForm.js | 44 ++++----------------------------
 1 file changed, 5 insertions(+), 39 deletions(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index e45fc3bde8b9..b66a1ffc7b46 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -6,7 +6,6 @@ import PropTypes from 'prop-types';
 import {withOnyx} from 'react-native-onyx';
 import _ from 'underscore';
 import styles from '../../styles/styles';
-import Button from '../../components/Button';
 import Text from '../../components/Text';
 import themeColors from '../../styles/themes/default';
 import * as Session from '../../libs/actions/Session';
@@ -52,7 +51,7 @@ const defaultProps = {
 class PasswordForm extends React.Component {
     constructor(props) {
         super(props);
-        this.validateAndSubmitForm = this.validateAndSubmitForm.bind(this);
+        this.submit = this.submit.bind(this);
         this.validate = this.validate.bind(this);
         this.resetPassword = this.resetPassword.bind(this);
         this.clearSignInData = this.clearSignInData.bind(this);
@@ -136,43 +135,10 @@ class PasswordForm extends React.Component {
     }
 
     /**
-     * Check that all the form fields are valid, then trigger the submit callback
+     * @param {Object} values - form input values passed by the Form component
      */
-    validateAndSubmitForm() {
-        const password = this.state.password.trim();
-        const twoFactorCode = this.state.twoFactorAuthCode.trim();
-        const requiresTwoFactorAuth = this.props.account.requiresTwoFactorAuth;
-
-        if (!password && requiresTwoFactorAuth && !twoFactorCode) {
-            this.setState({formError: 'passwordForm.pleaseFillOutAllFields'});
-            return;
-        }
-
-        if (!password) {
-            this.setState({formError: 'passwordForm.pleaseFillPassword'});
-            return;
-        }
-
-        if (!ValidationUtils.isValidPassword(password)) {
-            this.setState({formError: 'passwordForm.error.incorrectPassword'});
-            return;
-        }
-
-        if (requiresTwoFactorAuth && !twoFactorCode) {
-            this.setState({formError: 'passwordForm.pleaseFillTwoFactorAuth'});
-            return;
-        }
-
-        if (requiresTwoFactorAuth && !ValidationUtils.isValidTwoFactorCode(twoFactorCode)) {
-            this.setState({formError: 'passwordForm.error.incorrect2fa'});
-            return;
-        }
-
-        this.setState({
-            formError: null,
-        });
-
-        Session.signIn(password, twoFactorCode);
+    submit(values) {
+        Session.signIn(values.password.trim(), values.twoFactorAuthCode.trim());
     }
 
     render() {
@@ -182,7 +148,7 @@ class PasswordForm extends React.Component {
                     formID="PasswordForm"
                     submitButtonText={this.props.translate('common.signIn')}
                     validate={this.validate}
-                    onSubmit={this.validateAndSubmitForm}
+                    onSubmit={this.submit}
                 >
                     <View style={[styles.mv3]}>
                         <TextInput

From 88b835f89cdd547ed69d1e3396de1327a15befc2 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Fri, 27 Jan 2023 14:41:13 -0700
Subject: [PATCH 10/23] rm errors

---
 src/pages/signin/PasswordForm.js | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index b66a1ffc7b46..1e5128411023 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -188,18 +188,6 @@ class PasswordForm extends React.Component {
                             />
                         </View>
                     )}
-
-                    {!this.state.formError && this.props.account && !_.isEmpty(this.props.account.errors) && (
-                        <Text style={[styles.formError]}>
-                            {ErrorUtils.getLatestErrorMessage(this.props.account)}
-                        </Text>
-                    )}
-
-                    {this.state.formError && (
-                        <Text style={[styles.formError]}>
-                            {this.props.translate(this.state.formError)}
-                        </Text>
-                    )}
                 </Form>
                 <ChangeExpensifyLoginLink onPress={this.clearSignInData} />
                 <OfflineIndicator containerStyles={[styles.mv5]} />

From c4f3823d8d34d6ace0279eba10d64f84d6272f23 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Fri, 27 Jan 2023 14:42:35 -0700
Subject: [PATCH 11/23] rm formError state

---
 src/pages/signin/PasswordForm.js | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index 1e5128411023..35a4abb97489 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -19,7 +19,6 @@ import * as ComponentUtils from '../../libs/ComponentUtils';
 import * as ValidationUtils from '../../libs/ValidationUtils';
 import withToggleVisibilityView, {toggleVisibilityViewPropTypes} from '../../components/withToggleVisibilityView';
 import canFocusInputOnScreenFocus from '../../libs/canFocusInputOnScreenFocus';
-import * as ErrorUtils from '../../libs/ErrorUtils';
 import {withNetwork} from '../../components/OnyxProvider';
 import networkPropTypes from '../../components/networkPropTypes';
 import OfflineIndicator from '../../components/OfflineIndicator';
@@ -57,7 +56,6 @@ class PasswordForm extends React.Component {
         this.clearSignInData = this.clearSignInData.bind(this);
 
         this.state = {
-            formError: false,
             password: '',
             twoFactorAuthCode: '',
         };
@@ -99,7 +97,6 @@ class PasswordForm extends React.Component {
         if (this.input2FA) {
             this.setState({twoFactorAuthCode: ''}, this.input2FA.clear);
         }
-        this.setState({formError: false});
         Session.resetPassword();
     }
 
@@ -107,7 +104,6 @@ class PasswordForm extends React.Component {
     * Clears local and Onyx sign in states
     */
     clearSignInData() {
-        this.setState({twoFactorAuthCode: '', formError: false});
         Session.clearSignInData();
     }
 

From f4e58b9af07ef4315604fc9e391cc486446833fd Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Mon, 30 Jan 2023 17:15:47 -0700
Subject: [PATCH 12/23] revert form changes

---
 src/pages/signin/PasswordForm.js | 160 ++++++++++++++++++-------------
 1 file changed, 96 insertions(+), 64 deletions(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index 35a4abb97489..4d656f883bbd 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -6,6 +6,7 @@ import PropTypes from 'prop-types';
 import {withOnyx} from 'react-native-onyx';
 import _ from 'underscore';
 import styles from '../../styles/styles';
+import Button from '../../components/Button';
 import Text from '../../components/Text';
 import themeColors from '../../styles/themes/default';
 import * as Session from '../../libs/actions/Session';
@@ -19,10 +20,10 @@ import * as ComponentUtils from '../../libs/ComponentUtils';
 import * as ValidationUtils from '../../libs/ValidationUtils';
 import withToggleVisibilityView, {toggleVisibilityViewPropTypes} from '../../components/withToggleVisibilityView';
 import canFocusInputOnScreenFocus from '../../libs/canFocusInputOnScreenFocus';
+import * as ErrorUtils from '../../libs/ErrorUtils';
 import {withNetwork} from '../../components/OnyxProvider';
 import networkPropTypes from '../../components/networkPropTypes';
 import OfflineIndicator from '../../components/OfflineIndicator';
-import Form from '../../components/Form';
 
 const propTypes = {
     /* Onyx Props */
@@ -50,12 +51,12 @@ const defaultProps = {
 class PasswordForm extends React.Component {
     constructor(props) {
         super(props);
-        this.submit = this.submit.bind(this);
-        this.validate = this.validate.bind(this);
+        this.validateAndSubmitForm = this.validateAndSubmitForm.bind(this);
         this.resetPassword = this.resetPassword.bind(this);
         this.clearSignInData = this.clearSignInData.bind(this);
 
         this.state = {
+            formError: false,
             password: '',
             twoFactorAuthCode: '',
         };
@@ -69,7 +70,7 @@ class PasswordForm extends React.Component {
     }
 
     componentDidUpdate(prevProps, prevState) {
-        if (!prevProps.isVisible && this.props.isVisible) {
+        if (this.inputPassword && !prevProps.isVisible && this.props.isVisible) {
             this.inputPassword.focus();
         }
         if (prevProps.isVisible && !this.props.isVisible && this.state.password) {
@@ -97,6 +98,7 @@ class PasswordForm extends React.Component {
         if (this.input2FA) {
             this.setState({twoFactorAuthCode: ''}, this.input2FA.clear);
         }
+        this.setState({formError: false});
         Session.resetPassword();
     }
 
@@ -104,88 +106,118 @@ class PasswordForm extends React.Component {
     * Clears local and Onyx sign in states
     */
     clearSignInData() {
+        this.setState({twoFactorAuthCode: '', formError: false});
         Session.clearSignInData();
     }
 
     /**
-     * @param {Object} values - form input values passed by the Form component
-     * @returns {Boolean}
+     * Check that all the form fields are valid, then trigger the submit callback
      */
-    validate(values) {
+    validateAndSubmitForm() {
+        const password = this.state.password.trim();
+        const twoFactorCode = this.state.twoFactorAuthCode.trim();
         const requiresTwoFactorAuth = this.props.account.requiresTwoFactorAuth;
-        const errors = {};
 
-        if (!values.password || !values.password.trim()) {
-            errors.password = this.props.translate('passwordForm.pleaseFillPassword');
-        } else if (!ValidationUtils.isValidPassword(values.password.trim())) {
-            errors.password = this.props.translate('passwordForm.error.incorrectPassword');
+        if (!password && requiresTwoFactorAuth && !twoFactorCode) {
+            this.setState({formError: 'passwordForm.pleaseFillOutAllFields'});
+            return;
         }
 
-        if (requiresTwoFactorAuth && !values.twoFactorAuthCode.trim()) {
-            errors.twoFactorAuthCode = this.props.translate('passwordForm.pleaseFillTwoFactorAuth');
-        } else if (requiresTwoFactorAuth && !ValidationUtils.isValidTwoFactorCode(values.twoFactorAuthCode.trim())) {
-            errors.twoFactorAuthCode = this.props.translate('passwordForm.error.incorrect2fa');
+        if (!password) {
+            this.setState({formError: 'passwordForm.pleaseFillPassword'});
+            return;
         }
 
-        return errors;
-    }
+        if (!ValidationUtils.isValidPassword(password)) {
+            this.setState({formError: 'passwordForm.error.incorrectPassword'});
+            return;
+        }
 
-    /**
-     * @param {Object} values - form input values passed by the Form component
-     */
-    submit(values) {
-        Session.signIn(values.password.trim(), values.twoFactorAuthCode.trim());
+        if (requiresTwoFactorAuth && !twoFactorCode) {
+            this.setState({formError: 'passwordForm.pleaseFillTwoFactorAuth'});
+            return;
+        }
+
+        if (requiresTwoFactorAuth && !ValidationUtils.isValidTwoFactorCode(twoFactorCode)) {
+            this.setState({formError: 'passwordForm.error.incorrect2fa'});
+            return;
+        }
+
+        this.setState({
+            formError: null,
+        });
+
+        Session.signIn(password, twoFactorCode);
     }
 
     render() {
         return (
             <>
-                <Form
-                    formID="PasswordForm"
-                    submitButtonText={this.props.translate('common.signIn')}
-                    validate={this.validate}
-                    onSubmit={this.submit}
-                >
+                <View style={[styles.mv3]}>
+                    <TextInput
+                        ref={el => this.inputPassword = el}
+                        label={this.props.translate('common.password')}
+                        secureTextEntry
+                        autoCompleteType={ComponentUtils.PASSWORD_AUTOCOMPLETE_TYPE}
+                        textContentType="password"
+                        nativeID="password"
+                        name="password"
+                        value={this.state.password}
+                        onChangeText={text => this.setState({password: text})}
+                        onSubmitEditing={this.validateAndSubmitForm}
+                        blurOnSubmit={false}
+                    />
+                    <View style={[styles.changeExpensifyLoginLinkContainer]}>
+                        <TouchableOpacity
+                            style={[styles.mt2]}
+                            onPress={this.resetPassword}
+                        >
+                            <Text style={[styles.link]}>
+                                {this.props.translate('passwordForm.forgot')}
+                            </Text>
+                        </TouchableOpacity>
+                    </View>
+                </View>
+
+                {this.props.account.requiresTwoFactorAuth && (
                     <View style={[styles.mv3]}>
                         <TextInput
-                            inputID="password"
-                            ref={el => this.inputPassword = el}
-                            label={this.props.translate('common.password')}
-                            secureTextEntry
-                            autoCompleteType={ComponentUtils.PASSWORD_AUTOCOMPLETE_TYPE}
-                            textContentType="password"
-                            nativeID="password"
-                            name="password"
+                            ref={el => this.input2FA = el}
+                            label={this.props.translate('passwordForm.twoFactorCode')}
+                            value={this.state.twoFactorAuthCode}
+                            placeholder={this.props.translate('passwordForm.requiredWhen2FAEnabled')}
+                            placeholderTextColor={themeColors.placeholderText}
+                            onChangeText={text => this.setState({twoFactorAuthCode: text})}
+                            onSubmitEditing={this.validateAndSubmitForm}
+                            keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
                             blurOnSubmit={false}
+                            maxLength={CONST.TFA_CODE_LENGTH}
                         />
-                        <View style={[styles.changeExpensifyLoginLinkContainer]}>
-                            <TouchableOpacity
-                                style={[styles.mt2]}
-                                onPress={this.resetPassword}
-                            >
-                                <Text style={[styles.link]}>
-                                    {this.props.translate('passwordForm.forgot')}
-                                </Text>
-                            </TouchableOpacity>
-                        </View>
                     </View>
-
-                    {this.props.account.requiresTwoFactorAuth && (
-                        <View style={[styles.mv3]}>
-                            <TextInput
-                                inputID="twoFactorAuthCode"
-                                ref={el => this.input2FA = el}
-                                label={this.props.translate('passwordForm.twoFactorCode')}
-                                placeholder={this.props.translate('passwordForm.requiredWhen2FAEnabled')}
-                                placeholderTextColor={themeColors.placeholderText}
-                                keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
-                                blurOnSubmit={false}
-                                maxLength={CONST.TFA_CODE_LENGTH}
-                            />
-                        </View>
-                    )}
-                </Form>
-                <ChangeExpensifyLoginLink onPress={this.clearSignInData} />
+                )}
+
+                {!this.state.formError && this.props.account && !_.isEmpty(this.props.account.errors) && (
+                    <Text style={[styles.formError]}>
+                        {ErrorUtils.getLatestErrorMessage(this.props.account)}
+                    </Text>
+                )}
+
+                {this.state.formError && (
+                    <Text style={[styles.formError]}>
+                        {this.props.translate(this.state.formError)}
+                    </Text>
+                )}
+                <View>
+                    <Button
+                        isDisabled={this.props.network.isOffline}
+                        success
+                        style={[styles.mv3]}
+                        text={this.props.translate('common.signIn')}
+                        isLoading={this.props.account.isLoading}
+                        onPress={this.validateAndSubmitForm}
+                    />
+                    <ChangeExpensifyLoginLink onPress={this.clearSignInData} />
+                </View>
                 <OfflineIndicator containerStyles={[styles.mv5]} />
             </>
         );

From 320bb442d729bc26508deffa60324f055613c481 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Mon, 30 Jan 2023 17:17:12 -0700
Subject: [PATCH 13/23] rm componentDidUpdate logic

---
 src/pages/signin/PasswordForm.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index 4d656f883bbd..d984e6fd27b3 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -70,7 +70,7 @@ class PasswordForm extends React.Component {
     }
 
     componentDidUpdate(prevProps, prevState) {
-        if (this.inputPassword && !prevProps.isVisible && this.props.isVisible) {
+        if (!prevProps.isVisible && this.props.isVisible) {
             this.inputPassword.focus();
         }
         if (prevProps.isVisible && !this.props.isVisible && this.state.password) {

From 07f0c98b0a8c9d34b1131a2194b5810f5a969107 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Mon, 30 Jan 2023 17:19:00 -0700
Subject: [PATCH 14/23] add consts for errors

---
 src/pages/signin/PasswordForm.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index d984e6fd27b3..e6b8acc26cb3 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -151,6 +151,8 @@ class PasswordForm extends React.Component {
     }
 
     render() {
+        const hasPasswordError = !this.state.formError && this.props.account && !_.isEmpty(this.props.account.errors)
+        const hasTwoFactorCodeError = false;
         return (
             <>
                 <View style={[styles.mv3]}>
@@ -166,6 +168,7 @@ class PasswordForm extends React.Component {
                         onChangeText={text => this.setState({password: text})}
                         onSubmitEditing={this.validateAndSubmitForm}
                         blurOnSubmit={false}
+                        hasError={hasPasswordError}
                     />
                     <View style={[styles.changeExpensifyLoginLinkContainer]}>
                         <TouchableOpacity
@@ -192,6 +195,7 @@ class PasswordForm extends React.Component {
                             keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
                             blurOnSubmit={false}
                             maxLength={CONST.TFA_CODE_LENGTH}
+                            hasError={hasTwoFactorCodeError}
                         />
                     </View>
                 )}

From b4e40fff17b15d56ed5da7a3166cd2ec6013a4ee Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Mon, 30 Jan 2023 17:37:49 -0700
Subject: [PATCH 15/23] fix style

---
 src/pages/signin/PasswordForm.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index e6b8acc26cb3..e0f2af0c2c9c 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -151,8 +151,8 @@ class PasswordForm extends React.Component {
     }
 
     render() {
-        const hasPasswordError = !this.state.formError && this.props.account && !_.isEmpty(this.props.account.errors)
-        const hasTwoFactorCodeError = false;
+        const hasPasswordError = !this.state.formError && this.props.account && !_.isEmpty(this.props.account.errors);
+        const hasTwoFactorCodeError = true;
         return (
             <>
                 <View style={[styles.mv3]}>

From d530dcc534be199e97b3396c12f5fb9365e5e31f Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Tue, 31 Jan 2023 12:39:06 -0700
Subject: [PATCH 16/23] fix errors in login form

---
 src/pages/signin/LoginForm.js | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/pages/signin/LoginForm.js b/src/pages/signin/LoginForm.js
index 1e7f32f7cf0a..07867403d050 100755
--- a/src/pages/signin/LoginForm.js
+++ b/src/pages/signin/LoginForm.js
@@ -165,8 +165,8 @@ class LoginForm extends React.Component {
     }
 
     render() {
-        const formErrorTranslated = this.state.formError && this.props.translate(this.state.formError);
-        const error = formErrorTranslated || ErrorUtils.getLatestErrorMessage(this.props.account);
+        const formErrorText = this.state.formError ? this.props.translate(this.state.formError) : '';
+        const serverErrorText = ErrorUtils.getLatestErrorMessage(this.props.account);
         return (
             <>
                 <View accessibilityLabel="Login form" style={[styles.mt3]}>
@@ -183,7 +183,7 @@ class LoginForm extends React.Component {
                         autoCapitalize="none"
                         autoCorrect={false}
                         keyboardType={CONST.KEYBOARD_TYPE.EMAIL_ADDRESS}
-                        hasError={Boolean(error)}
+                        errorText={formErrorText}
                     />
                 </View>
                 {!_.isEmpty(this.props.account.success) && (
@@ -204,8 +204,8 @@ class LoginForm extends React.Component {
                                 buttonText={this.props.translate('common.continue')}
                                 isLoading={this.props.account.isLoading}
                                 onSubmit={this.validateAndSubmitForm}
-                                message={error}
-                                isAlertVisible={!_.isEmpty(error)}
+                                message={serverErrorText}
+                                isAlertVisible={!_.isEmpty(serverErrorText)}
                                 containerStyles={[styles.mh0]}
                             />
                         </View>

From 24e5cf01301e578d8a2bd5f47459208424278007 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Tue, 31 Jan 2023 12:59:23 -0700
Subject: [PATCH 17/23] align errors with form design

---
 src/pages/signin/PasswordForm.js | 35 ++++++++++----------------------
 1 file changed, 11 insertions(+), 24 deletions(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index e0f2af0c2c9c..c3d04d6c20fa 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -56,7 +56,7 @@ class PasswordForm extends React.Component {
         this.clearSignInData = this.clearSignInData.bind(this);
 
         this.state = {
-            formError: false,
+            formError: {},
             password: '',
             twoFactorAuthCode: '',
         };
@@ -98,7 +98,7 @@ class PasswordForm extends React.Component {
         if (this.input2FA) {
             this.setState({twoFactorAuthCode: ''}, this.input2FA.clear);
         }
-        this.setState({formError: false});
+        this.setState({formError: {}});
         Session.resetPassword();
     }
 
@@ -106,7 +106,7 @@ class PasswordForm extends React.Component {
     * Clears local and Onyx sign in states
     */
     clearSignInData() {
-        this.setState({twoFactorAuthCode: '', formError: false});
+        this.setState({twoFactorAuthCode: '', formError: {}});
         Session.clearSignInData();
     }
 
@@ -118,41 +118,34 @@ class PasswordForm extends React.Component {
         const twoFactorCode = this.state.twoFactorAuthCode.trim();
         const requiresTwoFactorAuth = this.props.account.requiresTwoFactorAuth;
 
-        if (!password && requiresTwoFactorAuth && !twoFactorCode) {
-            this.setState({formError: 'passwordForm.pleaseFillOutAllFields'});
-            return;
-        }
-
         if (!password) {
-            this.setState({formError: 'passwordForm.pleaseFillPassword'});
+            this.setState({formError: {password: 'passwordForm.pleaseFillPassword'}});
             return;
         }
 
         if (!ValidationUtils.isValidPassword(password)) {
-            this.setState({formError: 'passwordForm.error.incorrectPassword'});
+            this.setState({formError: {password: 'passwordForm.error.incorrectPassword'}});
             return;
         }
 
         if (requiresTwoFactorAuth && !twoFactorCode) {
-            this.setState({formError: 'passwordForm.pleaseFillTwoFactorAuth'});
+            this.setState({formError: {twoFactorCode: 'passwordForm.pleaseFillTwoFactorAuth'}});
             return;
         }
 
         if (requiresTwoFactorAuth && !ValidationUtils.isValidTwoFactorCode(twoFactorCode)) {
-            this.setState({formError: 'passwordForm.error.incorrect2fa'});
+            this.setState({formError: {twoFactorCode: 'passwordForm.error.incorrect2fa'}});
             return;
         }
 
         this.setState({
-            formError: null,
+            formError: {},
         });
 
         Session.signIn(password, twoFactorCode);
     }
 
     render() {
-        const hasPasswordError = !this.state.formError && this.props.account && !_.isEmpty(this.props.account.errors);
-        const hasTwoFactorCodeError = true;
         return (
             <>
                 <View style={[styles.mv3]}>
@@ -168,7 +161,7 @@ class PasswordForm extends React.Component {
                         onChangeText={text => this.setState({password: text})}
                         onSubmitEditing={this.validateAndSubmitForm}
                         blurOnSubmit={false}
-                        hasError={hasPasswordError}
+                        errorText={this.state.formError.password ? this.props.translate(this.state.formError.password) : ''}
                     />
                     <View style={[styles.changeExpensifyLoginLinkContainer]}>
                         <TouchableOpacity
@@ -195,22 +188,16 @@ class PasswordForm extends React.Component {
                             keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
                             blurOnSubmit={false}
                             maxLength={CONST.TFA_CODE_LENGTH}
-                            hasError={hasTwoFactorCodeError}
+                            errorText={this.state.formError.twoFactorCode ? this.props.translate(this.state.formError.twoFactorCode) : ''}
                         />
                     </View>
                 )}
 
-                {!this.state.formError && this.props.account && !_.isEmpty(this.props.account.errors) && (
+                {this.props.account && !_.isEmpty(this.props.account.errors) && (
                     <Text style={[styles.formError]}>
                         {ErrorUtils.getLatestErrorMessage(this.props.account)}
                     </Text>
                 )}
-
-                {this.state.formError && (
-                    <Text style={[styles.formError]}>
-                        {this.props.translate(this.state.formError)}
-                    </Text>
-                )}
                 <View>
                     <Button
                         isDisabled={this.props.network.isOffline}

From 9f1f5e70cfafb249f44f6876d031aefc4f895708 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Tue, 31 Jan 2023 13:34:22 -0700
Subject: [PATCH 18/23] create onTextInput

---
 src/pages/signin/PasswordForm.js | 28 +++++++++++++++++++++++-----
 1 file changed, 23 insertions(+), 5 deletions(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index c3d04d6c20fa..875629690ede 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -54,6 +54,7 @@ class PasswordForm extends React.Component {
         this.validateAndSubmitForm = this.validateAndSubmitForm.bind(this);
         this.resetPassword = this.resetPassword.bind(this);
         this.clearSignInData = this.clearSignInData.bind(this);
+        this.onTextInput = this.onTextInput.bind(this);
 
         this.state = {
             formError: {},
@@ -84,6 +85,23 @@ class PasswordForm extends React.Component {
         }
     }
 
+    /**
+     * Handle text input and clear formError upon text change
+     *
+     * @param {String} text
+     * @param {String} key
+     */
+    onTextInput(text, key) {
+        this.setState({
+            [key]: text,
+            formError: {[key]: ''},
+        });
+
+        if (this.props.account.errors) {
+            Session.clearAccountMessages();
+        }
+    }
+
     /**
      * Clear Password from the state
      */
@@ -129,12 +147,12 @@ class PasswordForm extends React.Component {
         }
 
         if (requiresTwoFactorAuth && !twoFactorCode) {
-            this.setState({formError: {twoFactorCode: 'passwordForm.pleaseFillTwoFactorAuth'}});
+            this.setState({formError: {twoFactorAuthCode: 'passwordForm.pleaseFillTwoFactorAuth'}});
             return;
         }
 
         if (requiresTwoFactorAuth && !ValidationUtils.isValidTwoFactorCode(twoFactorCode)) {
-            this.setState({formError: {twoFactorCode: 'passwordForm.error.incorrect2fa'}});
+            this.setState({formError: {twoFactorAuthCode: 'passwordForm.error.incorrect2fa'}});
             return;
         }
 
@@ -158,7 +176,7 @@ class PasswordForm extends React.Component {
                         nativeID="password"
                         name="password"
                         value={this.state.password}
-                        onChangeText={text => this.setState({password: text})}
+                        onChangeText={text => this.onTextInput(text, 'password')}
                         onSubmitEditing={this.validateAndSubmitForm}
                         blurOnSubmit={false}
                         errorText={this.state.formError.password ? this.props.translate(this.state.formError.password) : ''}
@@ -183,12 +201,12 @@ class PasswordForm extends React.Component {
                             value={this.state.twoFactorAuthCode}
                             placeholder={this.props.translate('passwordForm.requiredWhen2FAEnabled')}
                             placeholderTextColor={themeColors.placeholderText}
-                            onChangeText={text => this.setState({twoFactorAuthCode: text})}
+                            onChangeText={text => this.onTextInput(text, 'twoFactorAuthCode')}
                             onSubmitEditing={this.validateAndSubmitForm}
                             keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
                             blurOnSubmit={false}
                             maxLength={CONST.TFA_CODE_LENGTH}
-                            errorText={this.state.formError.twoFactorCode ? this.props.translate(this.state.formError.twoFactorCode) : ''}
+                            errorText={this.state.formError.twoFactorAuthCode ? this.props.translate(this.state.formError.twoFactorAuthCode) : ''}
                         />
                     </View>
                 )}

From 15903238fa0a657bff374ae3d85aedcc0c00804f Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Tue, 31 Jan 2023 14:02:48 -0700
Subject: [PATCH 19/23] use FormAlertWithSubmitButton

---
 src/pages/signin/PasswordForm.js | 28 +++++++++-------------------
 1 file changed, 9 insertions(+), 19 deletions(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index 875629690ede..5548498e388c 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -6,7 +6,6 @@ import PropTypes from 'prop-types';
 import {withOnyx} from 'react-native-onyx';
 import _ from 'underscore';
 import styles from '../../styles/styles';
-import Button from '../../components/Button';
 import Text from '../../components/Text';
 import themeColors from '../../styles/themes/default';
 import * as Session from '../../libs/actions/Session';
@@ -21,9 +20,8 @@ import * as ValidationUtils from '../../libs/ValidationUtils';
 import withToggleVisibilityView, {toggleVisibilityViewPropTypes} from '../../components/withToggleVisibilityView';
 import canFocusInputOnScreenFocus from '../../libs/canFocusInputOnScreenFocus';
 import * as ErrorUtils from '../../libs/ErrorUtils';
-import {withNetwork} from '../../components/OnyxProvider';
-import networkPropTypes from '../../components/networkPropTypes';
 import OfflineIndicator from '../../components/OfflineIndicator';
+import FormAlertWithSubmitButton from '../../components/FormAlertWithSubmitButton';
 
 const propTypes = {
     /* Onyx Props */
@@ -37,9 +35,6 @@ const propTypes = {
         isLoading: PropTypes.bool,
     }),
 
-    /** Information about the network */
-    network: networkPropTypes.isRequired,
-
     ...withLocalizePropTypes,
     ...toggleVisibilityViewPropTypes,
 };
@@ -164,6 +159,7 @@ class PasswordForm extends React.Component {
     }
 
     render() {
+        const serverError = ErrorUtils.getLatestErrorMessage(this.props.account);
         return (
             <>
                 <View style={[styles.mv3]}>
@@ -211,19 +207,14 @@ class PasswordForm extends React.Component {
                     </View>
                 )}
 
-                {this.props.account && !_.isEmpty(this.props.account.errors) && (
-                    <Text style={[styles.formError]}>
-                        {ErrorUtils.getLatestErrorMessage(this.props.account)}
-                    </Text>
-                )}
-                <View>
-                    <Button
-                        isDisabled={this.props.network.isOffline}
-                        success
-                        style={[styles.mv3]}
-                        text={this.props.translate('common.signIn')}
+                <View style={[styles.mt5]}>
+                    <FormAlertWithSubmitButton
+                        buttonText={this.props.translate('common.signIn')}
                         isLoading={this.props.account.isLoading}
-                        onPress={this.validateAndSubmitForm}
+                        onSubmit={this.validateAndSubmitForm}
+                        message={serverError}
+                        isAlertVisible={!_.isEmpty(serverError)}
+                        containerStyles={[styles.mh0]}
                     />
                     <ChangeExpensifyLoginLink onPress={this.clearSignInData} />
                 </View>
@@ -242,5 +233,4 @@ export default compose(
         account: {key: ONYXKEYS.ACCOUNT},
     }),
     withToggleVisibilityView,
-    withNetwork(),
 )(PasswordForm);

From 60b9d02cab9511b21da32ab2bc9e3ccc47f2be2e Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Tue, 31 Jan 2023 14:27:45 -0700
Subject: [PATCH 20/23] use button

---
 src/pages/signin/PasswordForm.js | 27 ++++++++++++++++++---------
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index 5548498e388c..91d8fe7c3ea4 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -6,6 +6,7 @@ import PropTypes from 'prop-types';
 import {withOnyx} from 'react-native-onyx';
 import _ from 'underscore';
 import styles from '../../styles/styles';
+import Button from '../../components/Button';
 import Text from '../../components/Text';
 import themeColors from '../../styles/themes/default';
 import * as Session from '../../libs/actions/Session';
@@ -20,8 +21,10 @@ import * as ValidationUtils from '../../libs/ValidationUtils';
 import withToggleVisibilityView, {toggleVisibilityViewPropTypes} from '../../components/withToggleVisibilityView';
 import canFocusInputOnScreenFocus from '../../libs/canFocusInputOnScreenFocus';
 import * as ErrorUtils from '../../libs/ErrorUtils';
+import {withNetwork} from '../../components/OnyxProvider';
+import networkPropTypes from '../../components/networkPropTypes';
 import OfflineIndicator from '../../components/OfflineIndicator';
-import FormAlertWithSubmitButton from '../../components/FormAlertWithSubmitButton';
+import FormHelpMessage from '../../components/FormHelpMessage';
 
 const propTypes = {
     /* Onyx Props */
@@ -35,6 +38,9 @@ const propTypes = {
         isLoading: PropTypes.bool,
     }),
 
+    /** Information about the network */
+    network: networkPropTypes.isRequired,
+
     ...withLocalizePropTypes,
     ...toggleVisibilityViewPropTypes,
 };
@@ -159,7 +165,6 @@ class PasswordForm extends React.Component {
     }
 
     render() {
-        const serverError = ErrorUtils.getLatestErrorMessage(this.props.account);
         return (
             <>
                 <View style={[styles.mv3]}>
@@ -207,14 +212,17 @@ class PasswordForm extends React.Component {
                     </View>
                 )}
 
-                <View style={[styles.mt5]}>
-                    <FormAlertWithSubmitButton
-                        buttonText={this.props.translate('common.signIn')}
+                {this.props.account && !_.isEmpty(this.props.account.errors) && (
+                    <FormHelpMessage message={ErrorUtils.getLatestErrorMessage(this.props.account)} />
+                )}
+                <View>
+                    <Button
+                        isDisabled={this.props.network.isOffline}
+                        success
+                        style={[styles.mv3]}
+                        text={this.props.translate('common.signIn')}
                         isLoading={this.props.account.isLoading}
-                        onSubmit={this.validateAndSubmitForm}
-                        message={serverError}
-                        isAlertVisible={!_.isEmpty(serverError)}
-                        containerStyles={[styles.mh0]}
+                        onPress={this.validateAndSubmitForm}
                     />
                     <ChangeExpensifyLoginLink onPress={this.clearSignInData} />
                 </View>
@@ -233,4 +241,5 @@ export default compose(
         account: {key: ONYXKEYS.ACCOUNT},
     }),
     withToggleVisibilityView,
+    withNetwork(),
 )(PasswordForm);

From f8363ff496b0e51a4a28932bff3d2f320379299b Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Thu, 2 Feb 2023 13:05:18 -0700
Subject: [PATCH 21/23] update validatecode form

---
 src/pages/signin/ValidateCodeForm.js | 51 +++++++++++++++++-----------
 1 file changed, 32 insertions(+), 19 deletions(-)

diff --git a/src/pages/signin/ValidateCodeForm.js b/src/pages/signin/ValidateCodeForm.js
index 9da432f51cd3..8bf889f7f37b 100755
--- a/src/pages/signin/ValidateCodeForm.js
+++ b/src/pages/signin/ValidateCodeForm.js
@@ -24,6 +24,7 @@ import {withNetwork} from '../../components/OnyxProvider';
 import networkPropTypes from '../../components/networkPropTypes';
 import OfflineIndicator from '../../components/OfflineIndicator';
 import * as User from '../../libs/actions/User';
+import FormHelpMessage from '../../components/FormHelpMessage';
 
 const propTypes = {
     /* Onyx Props */
@@ -61,9 +62,10 @@ class ValidateCodeForm extends React.Component {
         this.validateAndSubmitForm = this.validateAndSubmitForm.bind(this);
         this.resendValidateCode = this.resendValidateCode.bind(this);
         this.clearSignInData = this.clearSignInData.bind(this);
+        this.onTextInput = this.onTextInput.bind(this);
 
         this.state = {
-            formError: false,
+            formError: {},
             validateCode: '',
             twoFactorAuthCode: '',
         };
@@ -91,6 +93,23 @@ class ValidateCodeForm extends React.Component {
         }
     }
 
+    /**
+     * Handle text input and clear formError upon text change
+     *
+     * @param {String} text
+     * @param {String} key
+     */
+    onTextInput(text, key) {
+        this.setState({
+            [key]: text,
+            formError: {[key]: ''},
+        });
+
+        if (this.props.account.errors) {
+            Session.clearAccountMessages();
+        }
+    }
+
     /**
      * Clear Validate Code from the state
      */
@@ -105,7 +124,7 @@ class ValidateCodeForm extends React.Component {
         if (this.input2FA) {
             this.setState({twoFactorAuthCode: ''}, this.input2FA.clear);
         }
-        this.setState({formError: false});
+        this.setState({formError: {}});
         User.resendValidateCode(this.props.credentials.login, true);
     }
 
@@ -113,7 +132,7 @@ class ValidateCodeForm extends React.Component {
     * Clears local and Onyx sign in states
     */
     clearSignInData() {
-        this.setState({twoFactorAuthCode: '', formError: false});
+        this.setState({twoFactorAuthCode: '', formError: {}});
         Session.clearSignInData();
     }
 
@@ -122,22 +141,22 @@ class ValidateCodeForm extends React.Component {
      */
     validateAndSubmitForm() {
         if (!this.state.validateCode.trim()) {
-            this.setState({formError: 'validateCodeForm.error.pleaseFillMagicCode'});
+            this.setState({formError: {validateCode: 'validateCodeForm.error.pleaseFillMagicCode'}});
             return;
         }
 
         if (!ValidationUtils.isValidValidateCode(this.state.validateCode)) {
-            this.setState({formError: 'validateCodeForm.error.incorrectMagicCode'});
+            this.setState({formError: {validateCode: 'validateCodeForm.error.incorrectMagicCode'}});
             return;
         }
 
         if (this.props.account.requiresTwoFactorAuth && !this.state.twoFactorAuthCode.trim()) {
-            this.setState({formError: 'validateCodeForm.error.pleaseFillTwoFactorAuth'});
+            this.setState({formError: {twoFactorAuthCode: 'validateCodeForm.error.pleaseFillTwoFactorAuth'}});
             return;
         }
 
         this.setState({
-            formError: null,
+            formError: {},
         });
 
         Session.signIn('', this.state.validateCode, this.state.twoFactorAuthCode);
@@ -155,11 +174,12 @@ class ValidateCodeForm extends React.Component {
                             value={this.state.twoFactorAuthCode}
                             placeholder={this.props.translate('validateCodeForm.requiredWhen2FAEnabled')}
                             placeholderTextColor={themeColors.placeholderText}
-                            onChangeText={text => this.setState({twoFactorAuthCode: text})}
+                            onChangeText={text => this.onTextInput(text, 'twoFactorAuthCode')}
                             onSubmitEditing={this.validateAndSubmitForm}
                             keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
                             blurOnSubmit={false}
                             maxLength={CONST.TFA_CODE_LENGTH}
+                            errorText={this.state.formError.twoFactorAuthCode ? this.props.translate(this.state.formError.twoFactorAuthCode) : ''}
                         />
                     </View>
                 ) : (
@@ -170,9 +190,10 @@ class ValidateCodeForm extends React.Component {
                             nativeID="validateCode"
                             name="validateCode"
                             value={this.state.validateCode}
-                            onChangeText={text => this.setState({validateCode: text})}
+                            onChangeText={text => this.onTextInput(text, 'validateCode')}
                             onSubmitEditing={this.validateAndSubmitForm}
                             blurOnSubmit={false}
+                            errorText={this.state.formError.validateCode ? this.props.translate(this.state.formError.validateCode) : ''}
                         />
                         <View style={[styles.changeExpensifyLoginLinkContainer]}>
                             <TouchableOpacity
@@ -188,16 +209,8 @@ class ValidateCodeForm extends React.Component {
                     </View>
                 )}
 
-                {!this.state.formError && this.props.account && !_.isEmpty(this.props.account.errors) && (
-                    <Text style={[styles.formError]}>
-                        {ErrorUtils.getLatestErrorMessage(this.props.account)}
-                    </Text>
-                )}
-
-                {this.state.formError && (
-                    <Text style={[styles.formError]}>
-                        {this.props.translate(this.state.formError)}
-                    </Text>
+                {this.props.account && !_.isEmpty(this.props.account.errors) && (
+                    <FormHelpMessage message={ErrorUtils.getLatestErrorMessage(this.props.account)} />
                 )}
                 <View>
                     <Button

From c533248f29949952319fed1f90c3241ee35fa822 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Thu, 2 Feb 2023 13:09:44 -0700
Subject: [PATCH 22/23] add error for invalid 2fa

---
 src/pages/signin/ValidateCodeForm.js | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/pages/signin/ValidateCodeForm.js b/src/pages/signin/ValidateCodeForm.js
index 8bf889f7f37b..b3a6655dcfaf 100755
--- a/src/pages/signin/ValidateCodeForm.js
+++ b/src/pages/signin/ValidateCodeForm.js
@@ -140,6 +140,8 @@ class ValidateCodeForm extends React.Component {
      * Check that all the form fields are valid, then trigger the submit callback
      */
     validateAndSubmitForm() {
+        const requiresTwoFactorAuth = this.props.account.requiresTwoFactorAuth;
+
         if (!this.state.validateCode.trim()) {
             this.setState({formError: {validateCode: 'validateCodeForm.error.pleaseFillMagicCode'}});
             return;
@@ -150,11 +152,16 @@ class ValidateCodeForm extends React.Component {
             return;
         }
 
-        if (this.props.account.requiresTwoFactorAuth && !this.state.twoFactorAuthCode.trim()) {
+        if (requiresTwoFactorAuth && !this.state.twoFactorAuthCode.trim()) {
             this.setState({formError: {twoFactorAuthCode: 'validateCodeForm.error.pleaseFillTwoFactorAuth'}});
             return;
         }
 
+        if (requiresTwoFactorAuth && !ValidationUtils.isValidTwoFactorCode(this.state.twoFactorAuthCode)) {
+            this.setState({formError: {twoFactorAuthCode: 'passwordForm.error.incorrect2fa'}});
+            return;
+        }
+
         this.setState({
             formError: {},
         });

From b6da1f5fa034c6e67d9c4cebc2bb9acc2fafe140 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Thu, 2 Feb 2023 13:11:35 -0700
Subject: [PATCH 23/23] rm unecessary bindings

---
 src/pages/signin/PasswordForm.js     | 1 -
 src/pages/signin/ValidateCodeForm.js | 1 -
 2 files changed, 2 deletions(-)

diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index 6dab2b1273a0..7d8813313089 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -55,7 +55,6 @@ class PasswordForm extends React.Component {
         this.validateAndSubmitForm = this.validateAndSubmitForm.bind(this);
         this.resetPassword = this.resetPassword.bind(this);
         this.clearSignInData = this.clearSignInData.bind(this);
-        this.onTextInput = this.onTextInput.bind(this);
 
         this.state = {
             formError: {},
diff --git a/src/pages/signin/ValidateCodeForm.js b/src/pages/signin/ValidateCodeForm.js
index b3a6655dcfaf..6ae3f50e3d5f 100755
--- a/src/pages/signin/ValidateCodeForm.js
+++ b/src/pages/signin/ValidateCodeForm.js
@@ -62,7 +62,6 @@ class ValidateCodeForm extends React.Component {
         this.validateAndSubmitForm = this.validateAndSubmitForm.bind(this);
         this.resendValidateCode = this.resendValidateCode.bind(this);
         this.clearSignInData = this.clearSignInData.bind(this);
-        this.onTextInput = this.onTextInput.bind(this);
 
         this.state = {
             formError: {},
