From 981c6f0671981753998d2fb8341594b47f2c7778 Mon Sep 17 00:00:00 2001
From: Dmytro Klymenko <dmytro.klymenko@protonmail.com>
Date: Sun, 18 Jul 2021 13:36:04 +0300
Subject: [PATCH 01/79] #3770 - persist selected locale

---
 src/libs/actions/SignInRedirect.js | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/libs/actions/SignInRedirect.js b/src/libs/actions/SignInRedirect.js
index 9e7b787afbe1..2284f504847b 100644
--- a/src/libs/actions/SignInRedirect.js
+++ b/src/libs/actions/SignInRedirect.js
@@ -19,6 +19,12 @@ Onyx.connect({
     },
 });
 
+let currentPreferredLocale;
+Onyx.connect({
+    key: ONYXKEYS.PREFERRED_LOCALE,
+    callback: val => currentPreferredLocale = val,
+});
+
 /**
  * Clears the Onyx store and redirects to the sign in page.
  * Normally this method would live in Session.js, but that would cause a circular dependency with Network.js.
@@ -36,12 +42,16 @@ function redirectToSignIn(errorMessage) {
     }
 
     const activeClients = currentActiveClients;
+    const preferredLocale = currentPreferredLocale;
 
     // We must set the authToken to null so we can navigate to "signin" it's not possible to navigate to the route as
     // it only exists when the authToken is null.
     Onyx.set(ONYXKEYS.SESSION, {authToken: null})
         .then(() => {
             Onyx.clear().then(() => {
+                if (preferredLocale) {
+                    Onyx.set(ONYXKEYS.PREFERRED_LOCALE, preferredLocale);
+                }
                 if (errorMessage) {
                     Onyx.set(ONYXKEYS.SESSION, {error: errorMessage});
                 }

From 800fd63dde93871960a519dac0a52868e6773b01 Mon Sep 17 00:00:00 2001
From: Dmytro Klymenko <dmytro.klymenko@protonmail.com>
Date: Sun, 18 Jul 2021 14:23:04 +0300
Subject: [PATCH 02/79] #3770 move LocalePicker to a separate component

---
 src/components/LocalePicker/index.js  | 63 +++++++++++++++++++++++++++
 src/pages/settings/PreferencesPage.js | 34 ++-------------
 2 files changed, 66 insertions(+), 31 deletions(-)
 create mode 100644 src/components/LocalePicker/index.js

diff --git a/src/components/LocalePicker/index.js b/src/components/LocalePicker/index.js
new file mode 100644
index 000000000000..f3bda11b06fc
--- /dev/null
+++ b/src/components/LocalePicker/index.js
@@ -0,0 +1,63 @@
+import React from 'react';
+import {View} from 'react-native';
+import {withOnyx} from 'react-native-onyx';
+import PropTypes from 'prop-types';
+import styles from '../../styles/styles';
+import Picker from '../Picker';
+import compose from '../../libs/compose';
+import {setLocale} from '../../libs/actions/App';
+import withLocalize, {withLocalizePropTypes} from '../withLocalize';
+import ONYXKEYS from '../../ONYXKEYS';
+import CONST from '../../CONST';
+
+const propTypes = {
+    /** Indicates which locale the user currently has selected */
+    preferredLocale: PropTypes.string,
+
+    ...withLocalizePropTypes,
+};
+
+const defaultProps = {
+    preferredLocale: CONST.DEFAULT_LOCALE,
+};
+
+const LocalePicker = ({preferredLocale, translate}) => {
+    const localesToLanguages = {
+        default: {
+            value: 'en',
+            label: translate('preferencesPage.languages.english'),
+        },
+        es: {
+            value: 'es',
+            label: translate('preferencesPage.languages.spanish'),
+        },
+    };
+
+    return (
+        <View style={[styles.mb2]}>
+            <Picker
+                onChange={(locale) => {
+                    if (locale !== preferredLocale) {
+                        setLocale(locale);
+                    }
+                }}
+                items={Object.values(localesToLanguages)}
+                value={preferredLocale}
+            />
+        </View>
+    );
+};
+
+
+LocalePicker.defaultProps = defaultProps;
+LocalePicker.propTypes = propTypes;
+LocalePicker.displayName = 'LocalePicker';
+
+export default compose(
+    withLocalize,
+    withOnyx({
+        preferredLocale: {
+            key: ONYXKEYS.PREFERRED_LOCALE,
+        },
+    }),
+)(LocalePicker);
diff --git a/src/pages/settings/PreferencesPage.js b/src/pages/settings/PreferencesPage.js
index 3fd50a75a334..b1538f9c5b28 100755
--- a/src/pages/settings/PreferencesPage.js
+++ b/src/pages/settings/PreferencesPage.js
@@ -4,6 +4,7 @@ import {withOnyx} from 'react-native-onyx';
 import PropTypes from 'prop-types';
 
 import HeaderWithCloseButton from '../../components/HeaderWithCloseButton';
+import LocalePicker from '../../components/LocalePicker';
 import Navigation from '../../libs/Navigation/Navigation';
 import ROUTES from '../../ROUTES';
 import ONYXKEYS from '../../ONYXKEYS';
@@ -12,7 +13,6 @@ import Text from '../../components/Text';
 import NameValuePair from '../../libs/actions/NameValuePair';
 import CONST from '../../CONST';
 import {setExpensifyNewsStatus} from '../../libs/actions/User';
-import {setLocale} from '../../libs/actions/App';
 import ScreenWrapper from '../../components/ScreenWrapper';
 import Switch from '../../components/Switch';
 import Picker from '../../components/Picker';
@@ -29,20 +29,16 @@ const propTypes = {
         expensifyNewsStatus: PropTypes.bool,
     }),
 
-    /** Indicates which locale the user currently has selected */
-    preferredLocale: PropTypes.string,
-
     ...withLocalizePropTypes,
 };
 
 const defaultProps = {
     priorityMode: CONST.PRIORITY_MODE.DEFAULT,
     user: {},
-    preferredLocale: CONST.DEFAULT_LOCALE,
 };
 
 const PreferencesPage = ({
-    priorityMode, user, translate, preferredLocale,
+    priorityMode, user, translate,
 }) => {
     const priorityModes = {
         default: {
@@ -57,17 +53,6 @@ const PreferencesPage = ({
         },
     };
 
-    const localesToLanguages = {
-        default: {
-            value: 'en',
-            label: translate('preferencesPage.languages.english'),
-        },
-        es: {
-            value: 'es',
-            label: translate('preferencesPage.languages.spanish'),
-        },
-    };
-
     return (
         <ScreenWrapper>
             <HeaderWithCloseButton
@@ -112,17 +97,7 @@ const PreferencesPage = ({
                     <Text style={[styles.formLabel]} numberOfLines={1}>
                         {translate('preferencesPage.language')}
                     </Text>
-                    <View style={[styles.mb2]}>
-                        <Picker
-                            onChange={(locale) => {
-                                if (locale !== preferredLocale) {
-                                    setLocale(locale);
-                                }
-                            }}
-                            items={Object.values(localesToLanguages)}
-                            value={preferredLocale}
-                        />
-                    </View>
+                    <LocalePicker />
                 </View>
             </View>
         </ScreenWrapper>
@@ -142,8 +117,5 @@ export default compose(
         user: {
             key: ONYXKEYS.USER,
         },
-        preferredLocale: {
-            key: ONYXKEYS.PREFERRED_LOCALE,
-        },
     }),
 )(PreferencesPage);

From b2d03e134c882c01955d4528798bc6fa49103cbb Mon Sep 17 00:00:00 2001
From: Dmytro Klymenko <dmytro.klymenko@protonmail.com>
Date: Sun, 18 Jul 2021 14:46:20 +0300
Subject: [PATCH 03/79] #3770 add betas permission check + move picker label to
 new component

---
 src/CONST.js                          |  1 +
 src/components/LocalePicker/index.js  | 41 +++++++++++++++++++--------
 src/libs/Permissions.js               |  9 ++++++
 src/pages/settings/PreferencesPage.js |  3 --
 4 files changed, 39 insertions(+), 15 deletions(-)

diff --git a/src/CONST.js b/src/CONST.js
index 511ba361a309..5858ca5859f6 100755
--- a/src/CONST.js
+++ b/src/CONST.js
@@ -83,6 +83,7 @@ const CONST = {
         PAY_WITH_EXPENSIFY: 'payWithExpensify',
         FREE_PLAN: 'freePlan',
         DEFAULT_ROOMS: 'defaultRooms',
+        INTERNATIONALIZATION: 'internationalization',
     },
     BUTTON_STATES: {
         DEFAULT: 'default',
diff --git a/src/components/LocalePicker/index.js b/src/components/LocalePicker/index.js
index f3bda11b06fc..e90c70bd9acf 100644
--- a/src/components/LocalePicker/index.js
+++ b/src/components/LocalePicker/index.js
@@ -4,16 +4,21 @@ import {withOnyx} from 'react-native-onyx';
 import PropTypes from 'prop-types';
 import styles from '../../styles/styles';
 import Picker from '../Picker';
+import Text from '../Text';
 import compose from '../../libs/compose';
 import {setLocale} from '../../libs/actions/App';
 import withLocalize, {withLocalizePropTypes} from '../withLocalize';
 import ONYXKEYS from '../../ONYXKEYS';
 import CONST from '../../CONST';
+import Permissions from '../../libs/Permissions';
 
 const propTypes = {
     /** Indicates which locale the user currently has selected */
     preferredLocale: PropTypes.string,
 
+    /** Beta features list */
+    betas: PropTypes.arrayOf(PropTypes.string).isRequired,
+
     ...withLocalizePropTypes,
 };
 
@@ -21,7 +26,11 @@ const defaultProps = {
     preferredLocale: CONST.DEFAULT_LOCALE,
 };
 
-const LocalePicker = ({preferredLocale, translate}) => {
+const LocalePicker = ({preferredLocale, translate, betas}) => {
+    if (!Permissions.canUseInternationalization(betas)) {
+        return null;
+    }
+
     const localesToLanguages = {
         default: {
             value: 'en',
@@ -34,17 +43,22 @@ const LocalePicker = ({preferredLocale, translate}) => {
     };
 
     return (
-        <View style={[styles.mb2]}>
-            <Picker
-                onChange={(locale) => {
-                    if (locale !== preferredLocale) {
-                        setLocale(locale);
-                    }
-                }}
-                items={Object.values(localesToLanguages)}
-                value={preferredLocale}
-            />
-        </View>
+        <>
+            <Text style={[styles.formLabel]} numberOfLines={1}>
+                {translate('preferencesPage.language')}
+            </Text>
+            <View style={[styles.mb2]}>
+                <Picker
+                    onChange={(locale) => {
+                        if (locale !== preferredLocale) {
+                            setLocale(locale);
+                        }
+                    }}
+                    items={Object.values(localesToLanguages)}
+                    value={preferredLocale}
+                />
+            </View>
+        </>
     );
 };
 
@@ -59,5 +73,8 @@ export default compose(
         preferredLocale: {
             key: ONYXKEYS.PREFERRED_LOCALE,
         },
+        betas: {
+            key: ONYXKEYS.BETAS,
+        },
     }),
 )(LocalePicker);
diff --git a/src/libs/Permissions.js b/src/libs/Permissions.js
index 7063273b245c..1ca31ce88712 100644
--- a/src/libs/Permissions.js
+++ b/src/libs/Permissions.js
@@ -51,10 +51,19 @@ function canUseDefaultRooms(betas) {
     return _.contains(betas, CONST.BETAS.DEFAULT_ROOMS) || canUseAllBetas(betas);
 }
 
+/**
+ * @param {Array<String>} betas
+ * @returns {Boolean}
+ */
+function canUseInternationalization(betas) {
+    return _.contains(betas, CONST.BETAS.INTERNATIONALIZATION) || canUseAllBetas(betas);
+}
+
 export default {
     canUseChronos,
     canUseIOU,
     canUsePayWithExpensify,
     canUseFreePlan,
     canUseDefaultRooms,
+    canUseInternationalization,
 };
diff --git a/src/pages/settings/PreferencesPage.js b/src/pages/settings/PreferencesPage.js
index b1538f9c5b28..39163cdc4215 100755
--- a/src/pages/settings/PreferencesPage.js
+++ b/src/pages/settings/PreferencesPage.js
@@ -94,9 +94,6 @@ const PreferencesPage = ({
                     <Text style={[styles.textLabel, styles.colorMuted, styles.mb6]}>
                         {priorityModes[priorityMode].description}
                     </Text>
-                    <Text style={[styles.formLabel]} numberOfLines={1}>
-                        {translate('preferencesPage.language')}
-                    </Text>
                     <LocalePicker />
                 </View>
             </View>

From 86a95500faca895028dc2233923737a2191c0180 Mon Sep 17 00:00:00 2001
From: Dmytro Klymenko <dmytro.klymenko@protonmail.com>
Date: Sun, 18 Jul 2021 14:57:05 +0300
Subject: [PATCH 04/79] #3770 add LocalePicker to SignInPageLayout

---
 src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js | 2 ++
 src/pages/signin/SignInPageLayout/SignInPageLayoutWide.js   | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js b/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js
index 23b169f713b9..34f6bd3c0af9 100755
--- a/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js
+++ b/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js
@@ -7,6 +7,7 @@ import ExpensifyCashLogo from '../../../components/ExpensifyCashLogo';
 import Text from '../../../components/Text';
 import TermsAndLicenses from '../TermsAndLicenses';
 import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
+import LocalePicker from '../../../components/LocalePicker';
 import compose from '../../../libs/compose';
 import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
 
@@ -53,6 +54,7 @@ const SignInPageLayoutNarrow = props => (
             <View style={[styles.mt3]}>
                 <TermsAndLicenses />
             </View>
+            <LocalePicker />
         </View>
     </ScrollView>
 );
diff --git a/src/pages/signin/SignInPageLayout/SignInPageLayoutWide.js b/src/pages/signin/SignInPageLayout/SignInPageLayoutWide.js
index ff878c4e8c4a..a8396282a09a 100755
--- a/src/pages/signin/SignInPageLayout/SignInPageLayoutWide.js
+++ b/src/pages/signin/SignInPageLayout/SignInPageLayoutWide.js
@@ -7,6 +7,7 @@ import Text from '../../../components/Text';
 import welcomeScreenshot from '../../../../assets/images/welcome-screenshot.png';
 import variables from '../../../styles/variables';
 import TermsAndLicenses from '../TermsAndLicenses';
+import LocalePicker from '../../../components/LocalePicker';
 import CONST from '../../../CONST';
 import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
 import TextLink from '../../../components/TextLink';
@@ -48,6 +49,7 @@ const SignInPageLayoutWide = props => (
                     <View style={[styles.mv5]}>
                         <TermsAndLicenses />
                     </View>
+                    <LocalePicker />
                 </View>
             </View>
             <View style={[

From acd7df518ba1fb0ec389cd207790cd15c13def1f Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Tue, 20 Jul 2021 10:40:11 -0600
Subject: [PATCH 05/79] fix phone style

---
 src/pages/DetailsPage.js | 21 +++++----------------
 1 file changed, 5 insertions(+), 16 deletions(-)

diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js
index c36129e5d319..91af858f86ee 100755
--- a/src/pages/DetailsPage.js
+++ b/src/pages/DetailsPage.js
@@ -92,22 +92,11 @@ const DetailsPage = ({
                                 imageStyles={[styles.avatarLarge]}
                                 source={details.avatar}
                             />
-                            {details.displayName && isSMSLogin
-                                ? (
-                                    <CommunicationsLink
-                                        style={[styles.mt1, styles.mb6]}
-                                        type={CONST.LOGIN_TYPE.PHONE}
-                                        value={getPhoneNumber(details)}
-                                    >
-                                        <Text style={[styles.displayName]} numberOfLines={1}>
-                                            {toLocalPhone(details.displayName)}
-                                        </Text>
-                                    </CommunicationsLink>
-                                ) : (
-                                    <Text style={[styles.displayName]} numberOfLines={1}>
-                                        {details.displayName || null}
-                                    </Text>
-                                )}
+                            {details.displayName && (
+                                <Text style={[styles.displayName, styles.mb6]} numberOfLines={1}>
+                                    {isSMSLogin ? toLocalPhone(details.displayName) : details.displayName || null}
+                                </Text>
+                            )}
                             {details.login ? (
                                 <View style={[styles.mb6, styles.detailsPageSectionContainer]}>
                                     <Text style={[styles.formLabel, styles.mb2]} numberOfLines={1}>

From 0be9de358a0162ef2d29e67dce9c9161d732e414 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Tue, 20 Jul 2021 10:47:48 -0600
Subject: [PATCH 06/79] simplify conditional

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

diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js
index 91af858f86ee..9a4966651a67 100755
--- a/src/pages/DetailsPage.js
+++ b/src/pages/DetailsPage.js
@@ -94,7 +94,7 @@ const DetailsPage = ({
                             />
                             {details.displayName && (
                                 <Text style={[styles.displayName, styles.mb6]} numberOfLines={1}>
-                                    {isSMSLogin ? toLocalPhone(details.displayName) : details.displayName || null}
+                                    {isSMSLogin ? toLocalPhone(details.displayName) : details.displayName}
                                 </Text>
                             )}
                             {details.login ? (

From fce7b22ccc4af341243cd68e2ee811b62c50b748 Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Tue, 20 Jul 2021 07:36:23 -1000
Subject: [PATCH 07/79] Fix localize displayname

---
 .../InvertedFlatList/BaseInvertedFlatList.js  |   5 +-
 src/components/withLocalize.js                | 103 +++++++++++-------
 .../home/report/ReportActionContextMenu.js    |   4 +-
 .../home/report/ReportActionItemSingle.js     |   2 +
 src/pages/home/report/ReportView.js           |   1 +
 5 files changed, 70 insertions(+), 45 deletions(-)

diff --git a/src/components/InvertedFlatList/BaseInvertedFlatList.js b/src/components/InvertedFlatList/BaseInvertedFlatList.js
index 4e3c91ee0704..36bcd6c2e88f 100644
--- a/src/components/InvertedFlatList/BaseInvertedFlatList.js
+++ b/src/components/InvertedFlatList/BaseInvertedFlatList.js
@@ -150,10 +150,7 @@ class BaseInvertedFlatList extends Component {
                 // Web requires that items be measured or else crazy things happen when scrolling.
                 getItemLayout={this.props.shouldMeasureItems ? this.getItemLayout : undefined}
                 bounces={false}
-
-                // We keep this property very low so that chat switching remains fast
-                maxToRenderPerBatch={1}
-                windowSize={15}
+                windowSize={5}
                 removeClippedSubviews={this.props.shouldRemoveClippedSubviews}
             />
         );
diff --git a/src/components/withLocalize.js b/src/components/withLocalize.js
index 28b9409796d3..69bc1667da35 100755
--- a/src/components/withLocalize.js
+++ b/src/components/withLocalize.js
@@ -2,7 +2,6 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import {withOnyx} from 'react-native-onyx';
 import getComponentDisplayName from '../libs/getComponentDisplayName';
-import compose from '../libs/compose';
 import ONYXKEYS from '../ONYXKEYS';
 import {translate} from '../libs/translate';
 import DateUtils from '../libs/DateUtils';
@@ -30,35 +29,65 @@ const withLocalizePropTypes = {
     fromLocalPhone: PropTypes.func.isRequired,
 };
 
-function withLocalizeHOC(WrappedComponent) {
-    const WithLocalize = (props) => {
-        const translations = {
-            translate: (phrase, variables) => translate(props.preferredLocale, phrase, variables),
-            numberFormat: (number, options) => numberFormat(props.preferredLocale, number, options),
-            timestampToRelative: timestamp => DateUtils.timestampToRelative(props.preferredLocale, timestamp),
-            timestampToDateTime: (timestamp, includeTimezone) => DateUtils.timestampToDateTime(
-                props.preferredLocale,
+const withLocalizeHOC = (WrappedComponent) => {
+    class WithLocalize extends React.Component {
+        constructor(props) {
+            super(props);
+
+            this.translate = this.translate.bind(this);
+            this.numberFormat = this.numberFormat.bind(this);
+            this.timestampToRelative = this.timestampToRelative.bind(this);
+            this.timestampToDateTime = this.timestampToDateTime.bind(this);
+            this.fromLocalPhone = this.fromLocalPhone.bind(this);
+            this.toLocalPhone = this.toLocalPhone.bind(this);
+            this.preferredLocale = 'en';
+        }
+
+        translate(phrase, variables) {
+            return translate(this.preferredLocale, phrase, variables);
+        }
+
+        numberFormat(number, options) {
+            return numberFormat(this.preferredLocale, number, options);
+        }
+
+        timestampToRelative(timestamp) {
+            return DateUtils.timestampToRelative(this.preferredLocale, timestamp);
+        }
+
+        timestampToDateTime(timestamp, includeTimezone) {
+            return DateUtils.timestampToDateTime(
+                this.preferredLocale,
                 timestamp,
                 includeTimezone,
-            ),
-            toLocalPhone: number => toLocalPhone(props.preferredLocale, number),
-            fromLocalPhone: number => fromLocalPhone(props.preferredLocale, number),
-        };
-        return (
-            <WrappedComponent
-                // eslint-disable-next-line react/jsx-props-no-spreading
-                {...props}
-                ref={props.forwardedRef}
-                translate={translations.translate}
-                numberFormat={translations.numberFormat}
-                timestampToRelative={translations.timestampToRelative}
-                timestampToDateTime={translations.timestampToDateTime}
-                toLocalPhone={translations.toLocalPhone}
-                fromLocalPhone={translations.fromLocalPhone}
-            />
-        );
-    };
-    WithLocalize.displayName = `WithLocalize(${getComponentDisplayName(WrappedComponent)})`;
+            );
+        }
+
+        toLocalPhone(number) {
+            return toLocalPhone(this.preferredLocale, number);
+        }
+
+        fromLocalPhone(number) {
+            return fromLocalPhone(this.preferredLocale, number);
+        }
+
+        render() {
+            return (
+                <WrappedComponent
+                    // eslint-disable-next-line react/jsx-props-no-spreading
+                    {...this.props}
+                    ref={this.props.forwardedRef}
+                    translate={this.translate}
+                    numberFormat={this.numberFormat}
+                    timestampToRelative={this.timestampToRelative}
+                    timestampToDateTime={this.timestampToDateTime}
+                    toLocalPhone={this.toLocalPhone}
+                    fromLocalPhone={this.fromLocalPhone}
+                />
+            );
+        }
+    }
+
     WithLocalize.propTypes = {
         preferredLocale: PropTypes.string,
         forwardedRef: PropTypes.oneOfType([
@@ -70,19 +99,17 @@ function withLocalizeHOC(WrappedComponent) {
         preferredLocale: CONST.DEFAULT_LOCALE,
         forwardedRef: undefined,
     };
-    return React.forwardRef((props, ref) => (
+
+    const withForwardedRef = React.forwardRef((props, ref) => (
         // eslint-disable-next-line react/jsx-props-no-spreading
         <WithLocalize {...props} forwardedRef={ref} />
     ));
-}
-export default compose(
-    withOnyx({
-        preferredLocale: {
-            key: ONYXKEYS.PREFERRED_LOCALE,
-        },
-    }),
-    withLocalizeHOC,
-);
+
+    withForwardedRef.displayName = `WithLocalize(${getComponentDisplayName(WrappedComponent)})`;
+    return withForwardedRef;
+};
+
+export default withLocalizeHOC;
 
 export {
     withLocalizePropTypes,
diff --git a/src/pages/home/report/ReportActionContextMenu.js b/src/pages/home/report/ReportActionContextMenu.js
index ca8fcc35b04f..7386c9cd9f62 100755
--- a/src/pages/home/report/ReportActionContextMenu.js
+++ b/src/pages/home/report/ReportActionContextMenu.js
@@ -197,6 +197,4 @@ class ReportActionContextMenu extends React.Component {
 ReportActionContextMenu.propTypes = propTypes;
 ReportActionContextMenu.defaultProps = defaultProps;
 
-export default compose(
-    withLocalize,
-)(ReportActionContextMenu);
+export default withLocalize(ReportActionContextMenu);
diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js
index 1cf5bf99fce8..84befd28f4c8 100644
--- a/src/pages/home/report/ReportActionItemSingle.js
+++ b/src/pages/home/report/ReportActionItemSingle.js
@@ -95,6 +95,8 @@ const ReportActionItemSingle = ({
 
 ReportActionItemSingle.propTypes = propTypes;
 ReportActionItemSingle.defaultProps = defaultProps;
+ReportActionItemSingle.displayName = 'ReportActionItemSingle';
+
 export default compose(
     withLocalize,
     withOnyx({
diff --git a/src/pages/home/report/ReportView.js b/src/pages/home/report/ReportView.js
index 8481be201ed8..1eb4ea57589a 100644
--- a/src/pages/home/report/ReportView.js
+++ b/src/pages/home/report/ReportView.js
@@ -47,6 +47,7 @@ const ReportView = ({reportID, session}) => (
 
 ReportView.propTypes = propTypes;
 ReportView.defaultProps = defaultProps;
+ReportView.displayName = 'ReportView';
 
 export default withOnyx({
     session: {

From b00c34b91a1f36077a6104ee56fcf7119cc8561d Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Tue, 20 Jul 2021 07:40:30 -1000
Subject: [PATCH 08/79] add Onyx back in

---
 src/components/withLocalize.js | 53 +++++++++++++++++++---------------
 1 file changed, 30 insertions(+), 23 deletions(-)

diff --git a/src/components/withLocalize.js b/src/components/withLocalize.js
index 69bc1667da35..b0abd481162f 100755
--- a/src/components/withLocalize.js
+++ b/src/components/withLocalize.js
@@ -29,7 +29,20 @@ const withLocalizePropTypes = {
     fromLocalPhone: PropTypes.func.isRequired,
 };
 
-const withLocalizeHOC = (WrappedComponent) => {
+export default (WrappedComponent) => {
+    const propTypes = {
+        preferredLocale: PropTypes.string,
+        forwardedRef: PropTypes.oneOfType([
+            PropTypes.func,
+            PropTypes.shape({current: PropTypes.instanceOf(React.Component)}),
+        ]),
+    };
+
+    const defaultProps = {
+        preferredLocale: CONST.DEFAULT_LOCALE,
+        forwardedRef: undefined,
+    };
+
     class WithLocalize extends React.Component {
         constructor(props) {
             super(props);
@@ -40,35 +53,34 @@ const withLocalizeHOC = (WrappedComponent) => {
             this.timestampToDateTime = this.timestampToDateTime.bind(this);
             this.fromLocalPhone = this.fromLocalPhone.bind(this);
             this.toLocalPhone = this.toLocalPhone.bind(this);
-            this.preferredLocale = 'en';
         }
 
         translate(phrase, variables) {
-            return translate(this.preferredLocale, phrase, variables);
+            return translate(this.props.preferredLocale, phrase, variables);
         }
 
         numberFormat(number, options) {
-            return numberFormat(this.preferredLocale, number, options);
+            return numberFormat(this.props.preferredLocale, number, options);
         }
 
         timestampToRelative(timestamp) {
-            return DateUtils.timestampToRelative(this.preferredLocale, timestamp);
+            return DateUtils.timestampToRelative(this.props.preferredLocale, timestamp);
         }
 
         timestampToDateTime(timestamp, includeTimezone) {
             return DateUtils.timestampToDateTime(
-                this.preferredLocale,
+                this.props.preferredLocale,
                 timestamp,
                 includeTimezone,
             );
         }
 
         toLocalPhone(number) {
-            return toLocalPhone(this.preferredLocale, number);
+            return toLocalPhone(this.props.preferredLocale, number);
         }
 
         fromLocalPhone(number) {
-            return fromLocalPhone(this.preferredLocale, number);
+            return fromLocalPhone(this.props.preferredLocale, number);
         }
 
         render() {
@@ -88,28 +100,23 @@ const withLocalizeHOC = (WrappedComponent) => {
         }
     }
 
-    WithLocalize.propTypes = {
-        preferredLocale: PropTypes.string,
-        forwardedRef: PropTypes.oneOfType([
-            PropTypes.func,
-            PropTypes.shape({current: PropTypes.instanceOf(React.Component)}),
-        ]),
-    };
-    WithLocalize.defaultProps = {
-        preferredLocale: CONST.DEFAULT_LOCALE,
-        forwardedRef: undefined,
-    };
+    WithLocalize.propTypes = propTypes;
+
+    WithLocalize.defaultProps = defaultProps;
 
     const withForwardedRef = React.forwardRef((props, ref) => (
         // eslint-disable-next-line react/jsx-props-no-spreading
         <WithLocalize {...props} forwardedRef={ref} />
     ));
 
-    withForwardedRef.displayName = `WithLocalize(${getComponentDisplayName(WrappedComponent)})`;
-    return withForwardedRef;
-};
+    withForwardedRef.displayName = `withLocalize(${getComponentDisplayName(WrappedComponent)})`;
 
-export default withLocalizeHOC;
+    return withOnyx({
+        preferredLocale: {
+            key: ONYXKEYS.PREFERRED_LOCALE,
+        },
+    })(withForwardedRef);
+};
 
 export {
     withLocalizePropTypes,

From 82bb5dd55e30e8132d089fb21abb0d30c5df0e3f Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Tue, 20 Jul 2021 07:47:40 -1000
Subject: [PATCH 09/79] remove bad change

---
 src/components/InvertedFlatList/BaseInvertedFlatList.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/components/InvertedFlatList/BaseInvertedFlatList.js b/src/components/InvertedFlatList/BaseInvertedFlatList.js
index 36bcd6c2e88f..4e3c91ee0704 100644
--- a/src/components/InvertedFlatList/BaseInvertedFlatList.js
+++ b/src/components/InvertedFlatList/BaseInvertedFlatList.js
@@ -150,7 +150,10 @@ class BaseInvertedFlatList extends Component {
                 // Web requires that items be measured or else crazy things happen when scrolling.
                 getItemLayout={this.props.shouldMeasureItems ? this.getItemLayout : undefined}
                 bounces={false}
-                windowSize={5}
+
+                // We keep this property very low so that chat switching remains fast
+                maxToRenderPerBatch={1}
+                windowSize={15}
                 removeClippedSubviews={this.props.shouldRemoveClippedSubviews}
             />
         );

From b15b302b41927fd2b1e7bfc8e0c7af14e27e048e Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Tue, 20 Jul 2021 08:00:37 -1000
Subject: [PATCH 10/79] add docs

---
 src/components/withLocalize.js | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/src/components/withLocalize.js b/src/components/withLocalize.js
index b0abd481162f..5b2714be8c6c 100755
--- a/src/components/withLocalize.js
+++ b/src/components/withLocalize.js
@@ -55,18 +55,37 @@ export default (WrappedComponent) => {
             this.toLocalPhone = this.toLocalPhone.bind(this);
         }
 
+        /**
+         * @param {String} phrase
+         * @param {Object} [variables]
+         * @returns {String}
+         */
         translate(phrase, variables) {
             return translate(this.props.preferredLocale, phrase, variables);
         }
 
+        /**
+         * @param {Number} number
+         * @param {Intl.NumberFormatOptions} options
+         * @returns {String}
+         */
         numberFormat(number, options) {
             return numberFormat(this.props.preferredLocale, number, options);
         }
 
+        /**
+         * @param {Number} timestamp
+         * @returns {String}
+         */
         timestampToRelative(timestamp) {
             return DateUtils.timestampToRelative(this.props.preferredLocale, timestamp);
         }
 
+        /**
+         * @param {Number} timestamp
+         * @param {Boolean} [includeTimezone]
+         * @returns {String}
+         */
         timestampToDateTime(timestamp, includeTimezone) {
             return DateUtils.timestampToDateTime(
                 this.props.preferredLocale,
@@ -75,10 +94,18 @@ export default (WrappedComponent) => {
             );
         }
 
+        /**
+         * @param {Number} number
+         * @returns {String}
+         */
         toLocalPhone(number) {
             return toLocalPhone(this.props.preferredLocale, number);
         }
 
+        /**
+         * @param {Number} number
+         * @returns {String}
+         */
         fromLocalPhone(number) {
             return fromLocalPhone(this.props.preferredLocale, number);
         }
@@ -101,7 +128,6 @@ export default (WrappedComponent) => {
     }
 
     WithLocalize.propTypes = propTypes;
-
     WithLocalize.defaultProps = defaultProps;
 
     const withForwardedRef = React.forwardRef((props, ref) => (

From 4fc6270e2095f05fa703909ffa6ad2aaa8d4c493 Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Tue, 20 Jul 2021 08:03:59 -1000
Subject: [PATCH 11/79] fix style

---
 src/pages/home/report/ReportActionContextMenu.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/pages/home/report/ReportActionContextMenu.js b/src/pages/home/report/ReportActionContextMenu.js
index 7386c9cd9f62..f7e0b7f63d71 100755
--- a/src/pages/home/report/ReportActionContextMenu.js
+++ b/src/pages/home/report/ReportActionContextMenu.js
@@ -14,7 +14,6 @@ import {
 import ContextMenuItem from '../../../components/ContextMenuItem';
 import ReportActionPropTypes from './ReportActionPropTypes';
 import Clipboard from '../../../libs/Clipboard';
-import compose from '../../../libs/compose';
 import {isReportMessageAttachment, canEditReportAction, canDeleteReportAction} from '../../../libs/reportUtils';
 import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
 import ReportActionComposeFocusManager from '../../../libs/ReportActionComposeFocusManager';

From 79931f7ea412c44d02bafd66dc9a9b827a0dbb6f Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Tue, 20 Jul 2021 08:12:26 -1000
Subject: [PATCH 12/79] add missing docs to propTypes

---
 src/components/withLocalize.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/components/withLocalize.js b/src/components/withLocalize.js
index 5b2714be8c6c..1bf30d50e5db 100755
--- a/src/components/withLocalize.js
+++ b/src/components/withLocalize.js
@@ -31,7 +31,10 @@ const withLocalizePropTypes = {
 
 export default (WrappedComponent) => {
     const propTypes = {
+        /** The user's preferred locale e.g. en or es */
         preferredLocale: PropTypes.string,
+
+        /** Passed ref from whatever component is wrapped in the HOC */
         forwardedRef: PropTypes.oneOfType([
             PropTypes.func,
             PropTypes.shape({current: PropTypes.instanceOf(React.Component)}),

From 67e0a87183f96c005ed7af7f05bcd0d13f549ddb Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Tue, 20 Jul 2021 08:13:15 -1000
Subject: [PATCH 13/79] fix docs

---
 src/components/withLocalize.js | 2 +-
 src/libs/LocalePhoneNumber.js  | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/components/withLocalize.js b/src/components/withLocalize.js
index 1bf30d50e5db..daa35a12c41d 100755
--- a/src/components/withLocalize.js
+++ b/src/components/withLocalize.js
@@ -31,7 +31,7 @@ const withLocalizePropTypes = {
 
 export default (WrappedComponent) => {
     const propTypes = {
-        /** The user's preferred locale e.g. en or es */
+        /** The user's preferred locale e.g. 'en', 'es-ES' */
         preferredLocale: PropTypes.string,
 
         /** Passed ref from whatever component is wrapped in the HOC */
diff --git a/src/libs/LocalePhoneNumber.js b/src/libs/LocalePhoneNumber.js
index 4d51e711d705..3d47e7c6a5a9 100644
--- a/src/libs/LocalePhoneNumber.js
+++ b/src/libs/LocalePhoneNumber.js
@@ -10,7 +10,7 @@ import translations from '../languages/translations';
  *
  * @param {String} locale eg 'en', 'es-ES'
  * @param {String} number
- * @returns {string}
+ * @returns {String}
  */
 function toLocalPhone(locale, number) {
     const numString = lodashTrim(number);
@@ -31,7 +31,7 @@ function toLocalPhone(locale, number) {
  *
  * @param {String} locale eg 'en', 'es-ES'
  * @param {String} number
- * @returns {string}
+ * @returns {String}
  */
 function fromLocalPhone(locale, number) {
     const numString = lodashTrim(number);

From e5974e6cae4faa17d8ac544f2d25dab1aff34b04 Mon Sep 17 00:00:00 2001
From: Carlos Martins <cmartins@expensify.com>
Date: Tue, 20 Jul 2021 12:57:31 -0600
Subject: [PATCH 14/79] fix style

---
 src/components/CommunicationsLink.js | 13 ++++++-------
 src/pages/DetailsPage.js             |  2 +-
 src/styles/styles.js                 |  6 ++----
 3 files changed, 9 insertions(+), 12 deletions(-)

diff --git a/src/components/CommunicationsLink.js b/src/components/CommunicationsLink.js
index c60ced905fc8..bbaa924ebc08 100644
--- a/src/components/CommunicationsLink.js
+++ b/src/components/CommunicationsLink.js
@@ -44,16 +44,15 @@ const CommunicationsLink = props => (
                 >
                     {props.children}
                 </Pressable>
-            )
-            : props.children}
-        {!props.isSmallScreenWidth
-            && (
+            ) : (
                 <View style={[
-                    styles.pAbsolute,
+                    styles.flexRow,
                     styles.alignItemsCenter,
-                    styles.justifyContentCenter,
-                    styles.communicationsLinkIcon]}
+                    styles.w100,
+                    styles.communicationsLinkHeight,
+                ]}
                 >
+                    {props.children}
                     <ContextMenuItem
                         icon={ClipboardIcon}
                         text={props.translate('contextMenuItem.copyToClipboard')}
diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js
index 9a4966651a67..ad99904118c9 100755
--- a/src/pages/DetailsPage.js
+++ b/src/pages/DetailsPage.js
@@ -98,7 +98,7 @@ const DetailsPage = ({
                                 </Text>
                             )}
                             {details.login ? (
-                                <View style={[styles.mb6, styles.detailsPageSectionContainer]}>
+                                <View style={[styles.mb6, styles.detailsPageSectionContainer, styles.w100]}>
                                     <Text style={[styles.formLabel, styles.mb2]} numberOfLines={1}>
                                         {translate(isSMSLogin
                                             ? 'common.phoneNumber'
diff --git a/src/styles/styles.js b/src/styles/styles.js
index 854cef4d0049..40cf62fd509d 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -1793,10 +1793,8 @@ const styles = {
         ...whiteSpace.noWrap,
     },
 
-    communicationsLinkIcon: {
-        right: -36,
-        top: 0,
-        bottom: 0,
+    communicationsLinkHeight: {
+        height: 20,
     },
 };
 

From 84c6065bd6b38d76e4187d3cdd208c0eb2966276 Mon Sep 17 00:00:00 2001
From: Rushat Gabhane <rushatgabhane@gmail.com>
Date: Wed, 21 Jul 2021 08:52:31 +0300
Subject: [PATCH 15/79] escape key dismiss modal instead of go back

---
 src/components/ScreenWrapper.js | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/src/components/ScreenWrapper.js b/src/components/ScreenWrapper.js
index 094b8b597fdd..f3ac8a258d06 100644
--- a/src/components/ScreenWrapper.js
+++ b/src/components/ScreenWrapper.js
@@ -8,6 +8,7 @@ import styles, {getSafeAreaPadding} from '../styles/styles';
 import HeaderGap from './HeaderGap';
 import KeyboardShortcut from '../libs/KeyboardShortcut';
 import onScreenTransitionEnd from '../libs/onScreenTransitionEnd';
+import Navigation from '../libs/Navigation/Navigation';
 
 const propTypes = {
     /** Array of additional styles to add */
@@ -32,9 +33,6 @@ const propTypes = {
     navigation: PropTypes.shape({
         // Method to attach listener to Navigation state.
         addListener: PropTypes.func.isRequired,
-
-        // Returns to the previous navigation state e.g. if this is inside a Modal we will dismiss it
-        goBack: PropTypes.func,
     }),
 };
 
@@ -45,7 +43,6 @@ const defaultProps = {
     onTransitionEnd: () => {},
     navigation: {
         addListener: () => {},
-        goBack: () => {},
     },
 };
 
@@ -59,7 +56,7 @@ class ScreenWrapper extends React.Component {
 
     componentDidMount() {
         this.unsubscribeEscapeKey = KeyboardShortcut.subscribe('Escape', () => {
-            this.props.navigation.goBack();
+            Navigation.dismissModal();
         }, [], true);
 
         this.unsubscribeTransitionEnd = onScreenTransitionEnd(this.props.navigation, () => {

From 19f6f4f0b5838ff1befd3150fb38a78405f4290c Mon Sep 17 00:00:00 2001
From: Rushat Gabhane <rushatgabhane@gmail.com>
Date: Wed, 21 Jul 2021 09:22:14 +0300
Subject: [PATCH 16/79] pop keyboard event

---
 src/libs/KeyboardShortcut/index.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/KeyboardShortcut/index.js b/src/libs/KeyboardShortcut/index.js
index 3cc302801e99..00d39ceaf109 100644
--- a/src/libs/KeyboardShortcut/index.js
+++ b/src/libs/KeyboardShortcut/index.js
@@ -126,7 +126,7 @@ const KeyboardShortcut = {
      */
     unsubscribe(key) {
         const keyCode = this.getKeyCode(key);
-        delete events[keyCode];
+        events[keyCode].pop();
     },
 };
 

From 8823c72026959fdd5a7057f6a49aa27997dc9cd4 Mon Sep 17 00:00:00 2001
From: Rushat Gabhane <rushatgabhane@gmail.com>
Date: Wed, 21 Jul 2021 09:36:07 +0300
Subject: [PATCH 17/79] loop eventCallbacks in reverese, and return early

---
 src/libs/KeyboardShortcut/index.js | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/libs/KeyboardShortcut/index.js b/src/libs/KeyboardShortcut/index.js
index 00d39ceaf109..5e0dc2c6ec7c 100644
--- a/src/libs/KeyboardShortcut/index.js
+++ b/src/libs/KeyboardShortcut/index.js
@@ -12,9 +12,10 @@ function bindHandlerToKeyupEvent(event) {
     }
 
     const eventCallbacks = events[event.keyCode];
+    const reversedEventCallbacks = [...eventCallbacks].reverse();
 
     // Loop over all the callbacks
-    eventCallbacks.forEach((callback) => {
+    _.every(reversedEventCallbacks, (callback) => {
         const pressedModifiers = _.all(callback.modifiers, (modifier) => {
             if (modifier === 'shift' && !event.shiftKey) {
                 return false;
@@ -50,7 +51,7 @@ function bindHandlerToKeyupEvent(event) {
             return false;
         });
         if (!pressedModifiers || pressedExtraModifiers) {
-            return;
+            return true;
         }
 
         // If configured to do so, prevent input text control to trigger this event
@@ -59,13 +60,16 @@ function bindHandlerToKeyupEvent(event) {
             || event.target.nodeName === 'TEXTAREA'
             || event.target.contentEditable === 'true'
         )) {
-            return;
+            return true;
         }
 
         if (_.isFunction(callback.callback)) {
             callback.callback(event);
         }
         event.preventDefault();
+
+        // Short circuit the loop because the event is triggered
+        return false;
     });
 }
 

From 39b389a71d666172ac46671d2cb90ed0838cae1a Mon Sep 17 00:00:00 2001
From: Rushat Gabhane <rushatgabhane@gmail.com>
Date: Wed, 21 Jul 2021 09:54:13 +0300
Subject: [PATCH 18/79] make unsubscribe private, refactor

---
 src/libs/KeyboardShortcut/index.js | 91 ++++++++++++++++--------------
 1 file changed, 48 insertions(+), 43 deletions(-)

diff --git a/src/libs/KeyboardShortcut/index.js b/src/libs/KeyboardShortcut/index.js
index 5e0dc2c6ec7c..1f82b4cab687 100644
--- a/src/libs/KeyboardShortcut/index.js
+++ b/src/libs/KeyboardShortcut/index.js
@@ -5,6 +5,7 @@ const events = {};
 /**
  * Checks if an event for that key is configured and if so, runs it.
  * @param {Event} event
+ * @private
  */
 function bindHandlerToKeyupEvent(event) {
     if (events[event.keyCode] === undefined) {
@@ -77,6 +78,52 @@ function bindHandlerToKeyupEvent(event) {
 document.removeEventListener('keydown', bindHandlerToKeyupEvent, {capture: true});
 document.addEventListener('keydown', bindHandlerToKeyupEvent, {capture: true});
 
+/**
+ * Returns keyCode for a given key
+ * @param {String} key The key to watch, i.e. 'K' or 'Escape'
+ * @returns {Number} The key's keyCode, i.e. 75 or 27
+ * @private
+ */
+function getKeyCode(key) {
+    // For keys that have longer names we must catch and return the correct key key.charCodeAt(0) would return the
+    // key code for 'E' (the letter at index 0 in the string) not 'Escape'
+    switch (key) {
+        case 'Enter':
+            return 13;
+        case 'Escape':
+            return 27;
+        default:
+            return key.charCodeAt(0);
+    }
+}
+
+/**
+ * Unsubscribes to a keyboard event.
+ * @param {Number} key The key to stop watching
+ * @private
+ */
+function unsubscribe(key) {
+    const keyCode = getKeyCode(key);
+    events[keyCode].pop();
+}
+
+/**
+ * Subscribes to a keyboard event.
+ * @param {String} key The key to watch, i.e. 'K' or 'Escape'
+ * @param {Function} callback The callback to call
+ * @param {String|Array} modifiers Can either be shift or control
+ * @param {Boolean} captureOnInputs Should we capture the event on inputs too?
+ * @returns {Function} clean up method
+ */
+function subscribe(key, callback, modifiers = 'shift', captureOnInputs = false) {
+    const keyCode = getKeyCode(key);
+    if (events[keyCode] === undefined) {
+        events[keyCode] = [];
+    }
+    events[keyCode].push({callback, modifiers: _.isArray(modifiers) ? modifiers : [modifiers], captureOnInputs});
+    return () => unsubscribe(key);
+}
+
 /**
  * Module storing the different keyboard shortcut
  *
@@ -89,49 +136,7 @@ document.addEventListener('keydown', bindHandlerToKeyupEvent, {capture: true});
  * The "subClass" is used by pages to bind /unbind with no worries
  */
 const KeyboardShortcut = {
-    /**
-     * Returns keyCode for a given key
-     * @param {String} key The key to watch, i.e. 'K' or 'Escape'
-     * @returns {Number} The key's keyCode, i.e. 75 or 27
-     */
-    getKeyCode(key) {
-        // For keys that have longer names we must catch and return the correct key key.charCodeAt(0) would return the
-        // key code for 'E' (the letter at index 0 in the string) not 'Escape'
-        switch (key) {
-            case 'Enter':
-                return 13;
-            case 'Escape':
-                return 27;
-            default:
-                return key.charCodeAt(0);
-        }
-    },
-
-    /**
-     * Subscribes to a keyboard event.
-     * @param {String} key The key to watch, i.e. 'K' or 'Escape'
-     * @param {Function} callback The callback to call
-     * @param {String|Array} modifiers Can either be shift or control
-     * @param {Boolean} captureOnInputs Should we capture the event on inputs too?
-     * @returns {Function} clean up method
-     */
-    subscribe(key, callback, modifiers = 'shift', captureOnInputs = false) {
-        const keyCode = this.getKeyCode(key);
-        if (events[keyCode] === undefined) {
-            events[keyCode] = [];
-        }
-        events[keyCode].push({callback, modifiers: _.isArray(modifiers) ? modifiers : [modifiers], captureOnInputs});
-        return () => this.unsubscribe(key);
-    },
-
-    /**
-     * Unsubscribes to a keyboard event.
-     * @param {Number} key The key to stop watching
-     */
-    unsubscribe(key) {
-        const keyCode = this.getKeyCode(key);
-        events[keyCode].pop();
-    },
+    subscribe,
 };
 
 export default KeyboardShortcut;

From 4e0c12f3780f44b1668b9346a4ac3e68d6169b3a Mon Sep 17 00:00:00 2001
From: Rushat Gabhane <rushatgabhane@gmail.com>
Date: Wed, 21 Jul 2021 09:54:57 +0300
Subject: [PATCH 19/79] refactor AuthScreen KeyboardShortcut

---
 .../Navigation/AppNavigator/AuthScreens.js    | 36 ++++++++++---------
 1 file changed, 20 insertions(+), 16 deletions(-)

diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js
index ff370c7ef42e..9ed32a56da1e 100644
--- a/src/libs/Navigation/AppNavigator/AuthScreens.js
+++ b/src/libs/Navigation/AppNavigator/AuthScreens.js
@@ -174,24 +174,23 @@ class AuthScreens extends React.Component {
 
         Timing.end(CONST.TIMING.HOMEPAGE_INITIAL_RENDER);
 
+        let searchShortcutModifiers = ['control'];
+        let groupShortcutModifiers = ['control', 'shift'];
+
+        if (getOperatingSystem() === CONST.OS.MAC_OS) {
+            searchShortcutModifiers = ['meta'];
+            groupShortcutModifiers = ['meta', 'shift'];
+        }
+
         // Listen for the key K being pressed so that focus can be given to
         // the chat switcher, or new group chat
         // based on the key modifiers pressed and the operating system
-        if (getOperatingSystem() === CONST.OS.MAC_OS) {
-            KeyboardShortcut.subscribe('K', () => {
-                Navigation.navigate(ROUTES.SEARCH);
-            }, ['meta'], true);
-            KeyboardShortcut.subscribe('K', () => {
-                Navigation.navigate(ROUTES.NEW_GROUP);
-            }, ['meta', 'shift'], true);
-        } else {
-            KeyboardShortcut.subscribe('K', () => {
-                Navigation.navigate(ROUTES.SEARCH);
-            }, ['control'], true);
-            KeyboardShortcut.subscribe('K', () => {
-                Navigation.navigate(ROUTES.NEW_GROUP);
-            }, ['control', 'shift'], true);
-        }
+        this.unsubscribeSearchShortcut = KeyboardShortcut.subscribe('K', () => {
+            Navigation.navigate(ROUTES.SEARCH);
+        }, searchShortcutModifiers, true);
+        this.unsubscribeGroupShortcut = KeyboardShortcut.subscribe('K', () => {
+            Navigation.navigate(ROUTES.NEW_GROUP);
+        }, groupShortcutModifiers, true);
     }
 
     shouldComponentUpdate(nextProps) {
@@ -211,7 +210,12 @@ class AuthScreens extends React.Component {
     }
 
     componentWillUnmount() {
-        KeyboardShortcut.unsubscribe('K');
+        if (this.unsubscribeSearchShortcut) {
+            this.unsubscribeSearchShortcut();
+        }
+        if (this.unsubscribeGroupShortcut) {
+            this.unsubscribeGroupShortcut();
+        }
         NetworkConnection.stopListeningForReconnect();
         clearInterval(this.interval);
         this.interval = null;

From f6bd991a8a31a0dbc476ab246baad7c9bcc7d02e Mon Sep 17 00:00:00 2001
From: Dmytro Klymenko <dmytro.klymenko@protonmail.com>
Date: Wed, 21 Jul 2021 11:47:40 +0300
Subject: [PATCH 20/79] #3770 create small picker for locale selector on sign
 in page

---
 src/components/LocalePicker/index.js          | 33 ++++++-----
 src/components/Picker/PickerPropTypes.js      |  4 ++
 src/components/Picker/index.js                | 36 +++++++-----
 src/pages/settings/PreferencesPage.js         |  4 +-
 .../SignInPageLayoutNarrow.js                 |  2 -
 .../SignInPageLayout/SignInPageLayoutWide.js  |  2 -
 .../TermsWithLicenses/index.android.js        |  7 +++
 .../TermsWithLicenses/index.ios.js            |  7 +++
 .../TermsWithLicenses/index.js                |  8 ++-
 src/styles/styles.js                          | 56 +++++++++++++++++++
 10 files changed, 124 insertions(+), 35 deletions(-)

diff --git a/src/components/LocalePicker/index.js b/src/components/LocalePicker/index.js
index e90c70bd9acf..89c9c111cbb8 100644
--- a/src/components/LocalePicker/index.js
+++ b/src/components/LocalePicker/index.js
@@ -1,5 +1,4 @@
 import React from 'react';
-import {View} from 'react-native';
 import {withOnyx} from 'react-native-onyx';
 import PropTypes from 'prop-types';
 import styles from '../../styles/styles';
@@ -16,6 +15,9 @@ const propTypes = {
     /** Indicates which locale the user currently has selected */
     preferredLocale: PropTypes.string,
 
+    /** Indicates size of a picker component and whether to render the label or not */
+    size: PropTypes.oneOf(['normal', 'small']),
+
     /** Beta features list */
     betas: PropTypes.arrayOf(PropTypes.string).isRequired,
 
@@ -24,9 +26,12 @@ const propTypes = {
 
 const defaultProps = {
     preferredLocale: CONST.DEFAULT_LOCALE,
+    size: 'normal',
 };
 
-const LocalePicker = ({preferredLocale, translate, betas}) => {
+const LocalePicker = ({
+    preferredLocale, translate, betas, size,
+}) => {
     if (!Permissions.canUseInternationalization(betas)) {
         return null;
     }
@@ -44,25 +49,25 @@ const LocalePicker = ({preferredLocale, translate, betas}) => {
 
     return (
         <>
+            {size === 'normal' && (
             <Text style={[styles.formLabel]} numberOfLines={1}>
                 {translate('preferencesPage.language')}
             </Text>
-            <View style={[styles.mb2]}>
-                <Picker
-                    onChange={(locale) => {
-                        if (locale !== preferredLocale) {
-                            setLocale(locale);
-                        }
-                    }}
-                    items={Object.values(localesToLanguages)}
-                    value={preferredLocale}
-                />
-            </View>
+            )}
+            <Picker
+                onChange={(locale) => {
+                    if (locale !== preferredLocale) {
+                        setLocale(locale);
+                    }
+                }}
+                items={Object.values(localesToLanguages)}
+                size={size}
+                value={preferredLocale}
+            />
         </>
     );
 };
 
-
 LocalePicker.defaultProps = defaultProps;
 LocalePicker.propTypes = propTypes;
 LocalePicker.displayName = 'LocalePicker';
diff --git a/src/components/Picker/PickerPropTypes.js b/src/components/Picker/PickerPropTypes.js
index 633966a2bef4..631194544414 100644
--- a/src/components/Picker/PickerPropTypes.js
+++ b/src/components/Picker/PickerPropTypes.js
@@ -36,6 +36,9 @@ const propTypes = {
 
     /** An icon to display with the picker */
     icon: PropTypes.func,
+
+    /** Size of a picker component */
+    size: PropTypes.oneOf(['normal', 'small']),
 };
 const defaultProps = {
     useDisabledStyles: false,
@@ -43,6 +46,7 @@ const defaultProps = {
     placeholder: {},
     value: null,
     icon: () => <Icon src={DownArrow} />,
+    size: 'normal',
 };
 
 export {
diff --git a/src/components/Picker/index.js b/src/components/Picker/index.js
index 427d38184ef3..dd0370c69999 100644
--- a/src/components/Picker/index.js
+++ b/src/components/Picker/index.js
@@ -13,19 +13,29 @@ const Picker = ({
     value,
     icon,
     disabled,
-}) => (
-    <RNPickerSelect
-        onValueChange={onChange}
-        items={items}
-        style={useDisabledStyles ? pickerDisabledStyles : styles.picker}
-        useNativeAndroidPickerStyle={false}
-        placeholder={placeholder}
-        value={value}
-        Icon={icon}
-        disabled={disabled}
-        fixAndroidTouchableBug
-    />
-);
+    size,
+}) => {
+    let pickerStyles;
+    if (size === 'small') {
+        pickerStyles = styles.pickerSmall;
+    } else {
+        pickerStyles = useDisabledStyles ? pickerDisabledStyles : styles.picker;
+    }
+
+    return (
+        <RNPickerSelect
+            onValueChange={onChange}
+            items={items}
+            style={pickerStyles}
+            useNativeAndroidPickerStyle={false}
+            placeholder={placeholder}
+            value={value}
+            Icon={icon}
+            disabled={disabled}
+            fixAndroidTouchableBug
+        />
+    );
+};
 
 Picker.propTypes = pickerPropTypes.propTypes;
 Picker.defaultProps = pickerPropTypes.defaultProps;
diff --git a/src/pages/settings/PreferencesPage.js b/src/pages/settings/PreferencesPage.js
index 39163cdc4215..03e8c23c017c 100755
--- a/src/pages/settings/PreferencesPage.js
+++ b/src/pages/settings/PreferencesPage.js
@@ -94,7 +94,9 @@ const PreferencesPage = ({
                     <Text style={[styles.textLabel, styles.colorMuted, styles.mb6]}>
                         {priorityModes[priorityMode].description}
                     </Text>
-                    <LocalePicker />
+                    <View style={[styles.mb2]}>
+                        <LocalePicker />
+                    </View>
                 </View>
             </View>
         </ScreenWrapper>
diff --git a/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js b/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js
index 34f6bd3c0af9..23b169f713b9 100755
--- a/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js
+++ b/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js
@@ -7,7 +7,6 @@ import ExpensifyCashLogo from '../../../components/ExpensifyCashLogo';
 import Text from '../../../components/Text';
 import TermsAndLicenses from '../TermsAndLicenses';
 import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
-import LocalePicker from '../../../components/LocalePicker';
 import compose from '../../../libs/compose';
 import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
 
@@ -54,7 +53,6 @@ const SignInPageLayoutNarrow = props => (
             <View style={[styles.mt3]}>
                 <TermsAndLicenses />
             </View>
-            <LocalePicker />
         </View>
     </ScrollView>
 );
diff --git a/src/pages/signin/SignInPageLayout/SignInPageLayoutWide.js b/src/pages/signin/SignInPageLayout/SignInPageLayoutWide.js
index a8396282a09a..ff878c4e8c4a 100755
--- a/src/pages/signin/SignInPageLayout/SignInPageLayoutWide.js
+++ b/src/pages/signin/SignInPageLayout/SignInPageLayoutWide.js
@@ -7,7 +7,6 @@ import Text from '../../../components/Text';
 import welcomeScreenshot from '../../../../assets/images/welcome-screenshot.png';
 import variables from '../../../styles/variables';
 import TermsAndLicenses from '../TermsAndLicenses';
-import LocalePicker from '../../../components/LocalePicker';
 import CONST from '../../../CONST';
 import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
 import TextLink from '../../../components/TextLink';
@@ -49,7 +48,6 @@ const SignInPageLayoutWide = props => (
                     <View style={[styles.mv5]}>
                         <TermsAndLicenses />
                     </View>
-                    <LocalePicker />
                 </View>
             </View>
             <View style={[
diff --git a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.android.js b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.android.js
index fa0d4cbed3c6..617ace48ab9c 100644
--- a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.android.js
+++ b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.android.js
@@ -7,6 +7,7 @@ import withLocalize, {
     withLocalizePropTypes,
 } from '../../../../components/withLocalize';
 import LogoWordmark from '../../../../../assets/images/expensify-wordmark.svg';
+import LocalePicker from '../../../../components/LocalePicker';
 
 const TermsWithLicenses = ({translate}) => (
     <View>
@@ -61,6 +62,12 @@ const TermsWithLicenses = ({translate}) => (
             </TextLink>
             <Text style={[styles.textAlignCenter, styles.loginTermsText]}>.</Text>
         </View>
+        <View style={[
+            styles.mt4, styles.alignItemsCenter, styles.mb2, styles.flexRow, styles.justifyContentBetween]}
+        >
+            <LogoWordmark height={30} width={80} />
+            <LocalePicker size="small" />
+        </View>
     </View>
 );
 
diff --git a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js
index 720c89434bf3..7f8e6dd64218 100644
--- a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js
+++ b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js
@@ -7,6 +7,7 @@ import withLocalize, {
     withLocalizePropTypes,
 } from '../../../../components/withLocalize';
 import LogoWordmark from '../../../../../assets/images/expensify-wordmark.svg';
+import LocalePicker from '../../../../components/LocalePicker';
 
 const TermsWithLicenses = ({translate}) => (
     <View>
@@ -67,6 +68,12 @@ const TermsWithLicenses = ({translate}) => (
                 <Text style={[styles.textAlignCenter, styles.loginTermsText]}>.</Text>
             </View>
         </View>
+        <View style={[
+            styles.mt4, styles.alignItemsCenter, styles.mb2, styles.flexRow, styles.justifyContentBetween]}
+        >
+            <LogoWordmark height={30} width={80} />
+            <LocalePicker size="small" />
+        </View>
     </View>
 );
 
diff --git a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.js b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.js
index 175bda985afc..6b332da1ec3c 100755
--- a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.js
+++ b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.js
@@ -7,12 +7,10 @@ import withLocalize, {
     withLocalizePropTypes,
 } from '../../../../components/withLocalize';
 import LogoWordmark from '../../../../../assets/images/expensify-wordmark.svg';
+import LocalePicker from '../../../../components/LocalePicker';
 
 const TermsWithLicenses = ({translate}) => (
     <View>
-        <View style={[styles.mt1, styles.alignItemsCenter, styles.mb3]}>
-            <LogoWordmark height={30} width={80} />
-        </View>
         <Text style={[styles.textAlignCenter, styles.loginTermsText]}>
             {translate('termsOfUse.phrase1')}
             {' '}
@@ -45,6 +43,10 @@ const TermsWithLicenses = ({translate}) => (
             </TextLink>
             .
         </Text>
+        <View style={[styles.mt4, styles.alignItemsCenter, styles.mb2, styles.flexRow, styles.justifyContentBetween]}>
+            <LogoWordmark height={30} width={80} />
+            <LocalePicker size="small" />
+        </View>
     </View>
 );
 
diff --git a/src/styles/styles.js b/src/styles/styles.js
index 854cef4d0049..9d533bd3c0ca 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -333,6 +333,62 @@ const styles = {
         },
     },
 
+    pickerSmall: {
+        inputIOS: {
+            fontFamily: fontFamily.GTA,
+            fontSize: variables.fontSizeSmall,
+            paddingLeft: 10,
+            paddingRight: 26,
+            paddingTop: 6,
+            paddingBottom: 6,
+            borderRadius: variables.componentBorderRadius,
+            borderWidth: 1,
+            borderColor: themeColors.border,
+            color: themeColors.text,
+            height: variables.componentSizeSmall,
+            opacity: 1,
+            backgroundColor: 'transparent',
+        },
+        inputWeb: {
+            fontFamily: fontFamily.GTA,
+            fontSize: variables.fontSizeSmall,
+            paddingLeft: 10,
+            paddingRight: 26,
+            paddingTop: 6,
+            paddingBottom: 6,
+            borderWidth: 1,
+            borderRadius: variables.componentBorderRadius,
+            borderColor: themeColors.border,
+            color: themeColors.text,
+            appearance: 'none',
+            height: variables.componentSizeSmall,
+            opacity: 1,
+            cursor: 'pointer',
+            backgroundColor: 'transparent',
+        },
+        inputAndroid: {
+            fontFamily: fontFamily.GTA,
+            fontSize: variables.fontSizeSmall,
+            paddingLeft: 10,
+            paddingRight: 26,
+            paddingTop: 6,
+            paddingBottom: 6,
+            borderWidth: 1,
+            borderRadius: variables.componentBorderRadius,
+            borderColor: themeColors.border,
+            color: themeColors.text,
+            height: variables.componentSizeSmall,
+            opacity: 1,
+        },
+        iconContainer: {
+            top: 8,
+            right: 13,
+            width: variables.iconSizeExtraSmall,
+            height: variables.iconSizeExtraSmall,
+            pointerEvents: 'none',
+        },
+    },
+
     badge: {
         backgroundColor: themeColors.badgeDefaultBG,
         borderRadius: 14,

From b465606ef6a9be5ecdf7955e195c3dd5af5b7787 Mon Sep 17 00:00:00 2001
From: Dmytro Klymenko <dmytro.klymenko@protonmail.com>
Date: Wed, 21 Jul 2021 14:34:32 +0300
Subject: [PATCH 21/79] #3770 fix arrow on native

---
 assets/images/down-small.svg             |  3 +++
 src/components/Icon/Expensicons.js       |  2 ++
 src/components/LocalePicker/index.js     |  3 ++-
 src/components/Picker/PickerPropTypes.js | 21 ++++++++++++++++++---
 src/components/Picker/index.js           |  2 +-
 src/styles/styles.js                     | 18 ++++++++----------
 6 files changed, 34 insertions(+), 15 deletions(-)
 create mode 100644 assets/images/down-small.svg

diff --git a/assets/images/down-small.svg b/assets/images/down-small.svg
new file mode 100644
index 000000000000..d0de7b1a4425
--- /dev/null
+++ b/assets/images/down-small.svg
@@ -0,0 +1,3 @@
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5.99998 9.30791C5.69998 9.30791 5.39998 9.23291 5.17498 9.00791L1.27498 4.95791C1.12498 4.80791 1.12498 4.58291 1.27498 4.43291L1.79998 3.90791C1.94998 3.75791 2.17498 3.75791 2.32498 3.90791L5.99998 7.65791L9.74998 3.90791C9.89998 3.75791 10.125 3.75791 10.275 3.90791L10.8 4.43291C10.95 4.58291 10.95 4.80791 10.8 4.95791L6.82498 8.93291C6.59998 9.15791 6.29998 9.30791 5.99998 9.30791Z" fill="#C6C9CA"/>
+</svg>
diff --git a/src/components/Icon/Expensicons.js b/src/components/Icon/Expensicons.js
index 42c8dc079b0f..84946a053e7b 100644
--- a/src/components/Icon/Expensicons.js
+++ b/src/components/Icon/Expensicons.js
@@ -13,6 +13,7 @@ import Clipboard from '../../../assets/images/clipboard.svg';
 import Close from '../../../assets/images/close.svg';
 import CreditCard from '../../../assets/images/creditcard.svg';
 import DownArrow from '../../../assets/images/down.svg';
+import DownArrowSmall from '../../../assets/images/down-small.svg';
 import Download from '../../../assets/images/download.svg';
 import Emoji from '../../../assets/images/emoji.svg';
 import Exclamation from '../../../assets/images/exclamation.svg';
@@ -68,6 +69,7 @@ export {
     Close,
     CreditCard,
     DownArrow,
+    DownArrowSmall,
     Download,
     Emoji,
     Exclamation,
diff --git a/src/components/LocalePicker/index.js b/src/components/LocalePicker/index.js
index 89c9c111cbb8..604f5a0cd136 100644
--- a/src/components/LocalePicker/index.js
+++ b/src/components/LocalePicker/index.js
@@ -19,7 +19,7 @@ const propTypes = {
     size: PropTypes.oneOf(['normal', 'small']),
 
     /** Beta features list */
-    betas: PropTypes.arrayOf(PropTypes.string).isRequired,
+    betas: PropTypes.arrayOf(PropTypes.string),
 
     ...withLocalizePropTypes,
 };
@@ -27,6 +27,7 @@ const propTypes = {
 const defaultProps = {
     preferredLocale: CONST.DEFAULT_LOCALE,
     size: 'normal',
+    betas: [],
 };
 
 const LocalePicker = ({
diff --git a/src/components/Picker/PickerPropTypes.js b/src/components/Picker/PickerPropTypes.js
index 631194544414..4cb8f063b682 100644
--- a/src/components/Picker/PickerPropTypes.js
+++ b/src/components/Picker/PickerPropTypes.js
@@ -1,7 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import Icon from '../Icon';
-import {DownArrow} from '../Icon/Expensicons';
+import {DownArrow, DownArrowSmall} from '../Icon/Expensicons';
 
 const propTypes = {
     /** A callback method that is called when the value changes and it received the selected value as an argument */
@@ -45,8 +45,23 @@ const defaultProps = {
     disabled: false,
     placeholder: {},
     value: null,
-    icon: () => <Icon src={DownArrow} />,
-    size: 'normal',
+    icon: size => (
+        <>
+            {size === 'small'
+                ? (
+                    <Icon
+                        width={12}
+                        height={12}
+                        src={DownArrowSmall}
+                    />
+                )
+                : (
+                    <Icon
+                        src={DownArrow}
+                    />
+                )}
+        </>
+    ),
 };
 
 export {
diff --git a/src/components/Picker/index.js b/src/components/Picker/index.js
index dd0370c69999..b963c81601b8 100644
--- a/src/components/Picker/index.js
+++ b/src/components/Picker/index.js
@@ -30,7 +30,7 @@ const Picker = ({
             useNativeAndroidPickerStyle={false}
             placeholder={placeholder}
             value={value}
-            Icon={icon}
+            Icon={() => icon(size)}
             disabled={disabled}
             fixAndroidTouchableBug
         />
diff --git a/src/styles/styles.js b/src/styles/styles.js
index 9d533bd3c0ca..acf4e8f3d83d 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -337,8 +337,8 @@ const styles = {
         inputIOS: {
             fontFamily: fontFamily.GTA,
             fontSize: variables.fontSizeSmall,
-            paddingLeft: 10,
-            paddingRight: 26,
+            paddingLeft: 9,
+            paddingRight: 25,
             paddingTop: 6,
             paddingBottom: 6,
             borderRadius: variables.componentBorderRadius,
@@ -352,8 +352,8 @@ const styles = {
         inputWeb: {
             fontFamily: fontFamily.GTA,
             fontSize: variables.fontSizeSmall,
-            paddingLeft: 10,
-            paddingRight: 26,
+            paddingLeft: 9,
+            paddingRight: 25,
             paddingTop: 6,
             paddingBottom: 6,
             borderWidth: 1,
@@ -369,8 +369,8 @@ const styles = {
         inputAndroid: {
             fontFamily: fontFamily.GTA,
             fontSize: variables.fontSizeSmall,
-            paddingLeft: 10,
-            paddingRight: 26,
+            paddingLeft: 9,
+            paddingRight: 25,
             paddingTop: 6,
             paddingBottom: 6,
             borderWidth: 1,
@@ -381,10 +381,8 @@ const styles = {
             opacity: 1,
         },
         iconContainer: {
-            top: 8,
-            right: 13,
-            width: variables.iconSizeExtraSmall,
-            height: variables.iconSizeExtraSmall,
+            top: 7,
+            right: 9,
             pointerEvents: 'none',
         },
     },

From c59ac05a1a7cf9669b281e972f7369e15803ef51 Mon Sep 17 00:00:00 2001
From: Dmytro Klymenko <dmytro.klymenko@protonmail.com>
Date: Wed, 21 Jul 2021 14:55:38 +0300
Subject: [PATCH 22/79] #3770 remove duplicate logo watermark on native

---
 .../signin/TermsAndLicenses/TermsWithLicenses/index.android.js | 3 ---
 .../signin/TermsAndLicenses/TermsWithLicenses/index.ios.js     | 3 ---
 2 files changed, 6 deletions(-)

diff --git a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.android.js b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.android.js
index 617ace48ab9c..c7e34d7143de 100644
--- a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.android.js
+++ b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.android.js
@@ -11,9 +11,6 @@ import LocalePicker from '../../../../components/LocalePicker';
 
 const TermsWithLicenses = ({translate}) => (
     <View>
-        <View style={[styles.mt1, styles.alignItemsCenter, styles.mb3]}>
-            <LogoWordmark height={30} width={80} />
-        </View>
         <View
             style={[
                 styles.dFlex,
diff --git a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js
index 7f8e6dd64218..3f7abd8d12d6 100644
--- a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js
+++ b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js
@@ -11,9 +11,6 @@ import LocalePicker from '../../../../components/LocalePicker';
 
 const TermsWithLicenses = ({translate}) => (
     <View>
-        <View style={[styles.mt1, styles.alignItemsCenter, styles.mb3]}>
-            <LogoWordmark height={30} width={80} />
-        </View>
         <View
             style={[
                 styles.dFlex,

From 4032b034cad00aa0843fb7ef7018bc4135312185 Mon Sep 17 00:00:00 2001
From: Dmytro Klymenko <dmytro.klymenko@protonmail.com>
Date: Wed, 21 Jul 2021 18:42:21 +0300
Subject: [PATCH 23/79] #3770 make legal text aligned to the left

---
 .../signin/TermsAndLicenses/TermsWithLicenses/index.android.js  | 1 -
 .../signin/TermsAndLicenses/TermsWithLicenses/index.ios.js      | 1 -
 src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.js    | 2 +-
 3 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.android.js b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.android.js
index c7e34d7143de..4ef064130e71 100644
--- a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.android.js
+++ b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.android.js
@@ -18,7 +18,6 @@ const TermsWithLicenses = ({translate}) => (
                 styles.flexWrap,
                 styles.textAlignCenter,
                 styles.alignItemsCenter,
-                styles.justifyContentCenter,
             ]}
         >
             <Text style={[styles.textAlignCenter, styles.loginTermsText]}>
diff --git a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js
index 3f7abd8d12d6..4851ec32cb30 100644
--- a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js
+++ b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js
@@ -18,7 +18,6 @@ const TermsWithLicenses = ({translate}) => (
                 styles.flexWrap,
                 styles.textAlignCenter,
                 styles.alignItemsCenter,
-                styles.justifyContentCenter,
             ]}
         >
             <View style={[styles.dFlex, styles.flexRow, styles.alignItemsCenter]}>
diff --git a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.js b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.js
index 6b332da1ec3c..a022e771f31b 100755
--- a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.js
+++ b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.js
@@ -11,7 +11,7 @@ import LocalePicker from '../../../../components/LocalePicker';
 
 const TermsWithLicenses = ({translate}) => (
     <View>
-        <Text style={[styles.textAlignCenter, styles.loginTermsText]}>
+        <Text style={[styles.loginTermsText]}>
             {translate('termsOfUse.phrase1')}
             {' '}
             <TextLink

From 9c05cbb64727bfbafc78cf09dcc57507c0eed4b6 Mon Sep 17 00:00:00 2001
From: Dmytro Klymenko <dmytro.klymenko@protonmail.com>
Date: Wed, 21 Jul 2021 18:59:13 +0300
Subject: [PATCH 24/79] #3770 fix left align for legal text on ios

---
 .../signin/TermsAndLicenses/TermsWithLicenses/index.ios.js      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js
index 4851ec32cb30..f884c94d7d8e 100644
--- a/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js
+++ b/src/pages/signin/TermsAndLicenses/TermsWithLicenses/index.ios.js
@@ -17,7 +17,7 @@ const TermsWithLicenses = ({translate}) => (
                 styles.flexColumn,
                 styles.flexWrap,
                 styles.textAlignCenter,
-                styles.alignItemsCenter,
+                styles.justifyContentCenter,
             ]}
         >
             <View style={[styles.dFlex, styles.flexRow, styles.alignItemsCenter]}>

From ed94eb9ac15993bd87d84826fcf9b864f15c87a9 Mon Sep 17 00:00:00 2001
From: Dmytro Klymenko <dmytro.klymenko@protonmail.com>
Date: Wed, 21 Jul 2021 19:39:36 +0300
Subject: [PATCH 25/79] #3770 reuse existing down arrow asset

---
 assets/images/down-small.svg             | 3 ---
 src/components/Icon/Expensicons.js       | 2 --
 src/components/Picker/PickerPropTypes.js | 4 ++--
 3 files changed, 2 insertions(+), 7 deletions(-)
 delete mode 100644 assets/images/down-small.svg

diff --git a/assets/images/down-small.svg b/assets/images/down-small.svg
deleted file mode 100644
index d0de7b1a4425..000000000000
--- a/assets/images/down-small.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M5.99998 9.30791C5.69998 9.30791 5.39998 9.23291 5.17498 9.00791L1.27498 4.95791C1.12498 4.80791 1.12498 4.58291 1.27498 4.43291L1.79998 3.90791C1.94998 3.75791 2.17498 3.75791 2.32498 3.90791L5.99998 7.65791L9.74998 3.90791C9.89998 3.75791 10.125 3.75791 10.275 3.90791L10.8 4.43291C10.95 4.58291 10.95 4.80791 10.8 4.95791L6.82498 8.93291C6.59998 9.15791 6.29998 9.30791 5.99998 9.30791Z" fill="#C6C9CA"/>
-</svg>
diff --git a/src/components/Icon/Expensicons.js b/src/components/Icon/Expensicons.js
index 84946a053e7b..42c8dc079b0f 100644
--- a/src/components/Icon/Expensicons.js
+++ b/src/components/Icon/Expensicons.js
@@ -13,7 +13,6 @@ import Clipboard from '../../../assets/images/clipboard.svg';
 import Close from '../../../assets/images/close.svg';
 import CreditCard from '../../../assets/images/creditcard.svg';
 import DownArrow from '../../../assets/images/down.svg';
-import DownArrowSmall from '../../../assets/images/down-small.svg';
 import Download from '../../../assets/images/download.svg';
 import Emoji from '../../../assets/images/emoji.svg';
 import Exclamation from '../../../assets/images/exclamation.svg';
@@ -69,7 +68,6 @@ export {
     Close,
     CreditCard,
     DownArrow,
-    DownArrowSmall,
     Download,
     Emoji,
     Exclamation,
diff --git a/src/components/Picker/PickerPropTypes.js b/src/components/Picker/PickerPropTypes.js
index 4cb8f063b682..e7140b0bef5a 100644
--- a/src/components/Picker/PickerPropTypes.js
+++ b/src/components/Picker/PickerPropTypes.js
@@ -1,7 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import Icon from '../Icon';
-import {DownArrow, DownArrowSmall} from '../Icon/Expensicons';
+import {DownArrow} from '../Icon/Expensicons';
 
 const propTypes = {
     /** A callback method that is called when the value changes and it received the selected value as an argument */
@@ -52,7 +52,7 @@ const defaultProps = {
                     <Icon
                         width={12}
                         height={12}
-                        src={DownArrowSmall}
+                        src={DownArrow}
                     />
                 )
                 : (

From 62e3786d4085218bc6a92dd866864ab157b89a75 Mon Sep 17 00:00:00 2001
From: Rafael <rdjuric@pm.me>
Date: Wed, 21 Jul 2021 23:21:39 -0300
Subject: [PATCH 26/79] avoid keyboard, moves error message

---
 src/pages/signin/LoginForm.js                 | 18 +++----
 src/pages/signin/PasswordForm.js              | 18 +++++--
 src/pages/signin/ResendValidationForm.js      | 11 ++--
 src/pages/signin/SignInPage.js                | 15 +-----
 .../SignInPageLayoutNarrow.js                 | 53 ++++++++++---------
 5 files changed, 55 insertions(+), 60 deletions(-)

diff --git a/src/pages/signin/LoginForm.js b/src/pages/signin/LoginForm.js
index 547c43760fe9..d6f7d8463830 100755
--- a/src/pages/signin/LoginForm.js
+++ b/src/pages/signin/LoginForm.js
@@ -88,15 +88,6 @@ class LoginForm extends React.Component {
                         autoFocus={canFocusInputOnScreenFocus()}
                     />
                 </View>
-                <View style={[styles.mt5]}>
-                    <Button
-                        success
-                        text={this.props.translate('common.continue')}
-                        isLoading={this.props.account.loading}
-                        onPress={this.validateAndSubmitForm}
-                    />
-                </View>
-
                 {this.state.formError && (
                     <Text style={[styles.formError]}>
                         {this.state.formError}
@@ -113,6 +104,15 @@ class LoginForm extends React.Component {
                         {this.props.account.success}
                     </Text>
                 )}
+                <View style={[styles.mt5]}>
+                    <Button
+                        success
+                        text={this.props.translate('common.continue')}
+                        isLoading={this.props.account.loading}
+                        onPress={this.validateAndSubmitForm}
+                    />
+                </View>
+
             </>
         );
     }
diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js
index 9fdc16c599f8..fc2cf2e804e6 100755
--- a/src/pages/signin/PasswordForm.js
+++ b/src/pages/signin/PasswordForm.js
@@ -4,6 +4,7 @@ import {
 } from 'react-native';
 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';
@@ -110,6 +111,18 @@ class PasswordForm extends React.Component {
                         />
                     </View>
                 )}
+
+                {this.props.account && !_.isEmpty(this.props.account.error) && (
+                    <Text style={[styles.formError]}>
+                        {this.props.account.error}
+                    </Text>
+                )}
+
+                {this.state.formError && (
+                    <Text style={[styles.formError]}>
+                        {this.state.formError}
+                    </Text>
+                )}
                 <View>
                     <Button
                         success
@@ -120,11 +133,6 @@ class PasswordForm extends React.Component {
                     />
                     <ChangeExpensifyLoginLink />
                 </View>
-                {this.state.formError && (
-                    <Text style={[styles.formError]}>
-                        {this.state.formError}
-                    </Text>
-                )}
             </>
         );
     }
diff --git a/src/pages/signin/ResendValidationForm.js b/src/pages/signin/ResendValidationForm.js
index f95d7cea43f8..cbae3f489a83 100755
--- a/src/pages/signin/ResendValidationForm.js
+++ b/src/pages/signin/ResendValidationForm.js
@@ -80,6 +80,11 @@ class ResendValidationForm extends React.Component {
                         {this.props.translate('resendValidationForm.weSentYouMagicSignInLink')}
                     </Text>
                 </View>
+                {!_.isEmpty(this.state.formSuccess) && (
+                    <Text style={[styles.formSuccess]}>
+                        {this.state.formSuccess}
+                    </Text>
+                )}
                 <View style={[styles.mt4]}>
                     <Button
                         success
@@ -90,12 +95,6 @@ class ResendValidationForm extends React.Component {
                     />
                     <ChangeExpensifyLoginLink />
                 </View>
-
-                {!_.isEmpty(this.state.formSuccess) && (
-                    <Text style={[styles.formSuccess]}>
-                        {this.state.formSuccess}
-                    </Text>
-                )}
             </>
         );
     }
diff --git a/src/pages/signin/SignInPage.js b/src/pages/signin/SignInPage.js
index bab1ccdd3564..dd12cdf992bb 100644
--- a/src/pages/signin/SignInPage.js
+++ b/src/pages/signin/SignInPage.js
@@ -1,11 +1,9 @@
 import React, {Component} from 'react';
 import {
-    SafeAreaView, View,
+    SafeAreaView,
 } from 'react-native';
 import PropTypes from 'prop-types';
 import {withOnyx} from 'react-native-onyx';
-import _ from 'underscore';
-import Text from '../../components/Text';
 import ONYXKEYS from '../../ONYXKEYS';
 import styles from '../../styles/styles';
 import updateUnread from '../../libs/UnreadIndicatorUpdater/updateUnread/index';
@@ -94,17 +92,6 @@ class SignInPage extends Component {
                         {showPasswordForm && <PasswordForm />}
 
                         {showResendValidationLinkForm && <ResendValidationForm />}
-
-                        {/* Because of the custom layout of the login form, session errors are displayed differently */}
-                        {!showLoginForm && (
-                            <View>
-                                {this.props.account && !_.isEmpty(this.props.account.error) && (
-                                    <Text style={[styles.formError]}>
-                                        {this.props.account.error}
-                                    </Text>
-                                )}
-                            </View>
-                        )}
                     </SignInPageLayout>
                 </SafeAreaView>
             </>
diff --git a/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js b/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js
index 23b169f713b9..d4a72dd5ffeb 100755
--- a/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js
+++ b/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js
@@ -1,5 +1,5 @@
 import React from 'react';
-import {ScrollView, View} from 'react-native';
+import {ScrollView, View, KeyboardAvoidingView} from 'react-native';
 import PropTypes from 'prop-types';
 import styles from '../../../styles/styles';
 import variables from '../../../styles/variables';
@@ -20,39 +20,40 @@ const propTypes = {
 
 const SignInPageLayoutNarrow = props => (
     <ScrollView
-        keyboardShouldPersistTaps="handled"
-        contentContainerStyle={[
+        showsVerticalScrollIndicator={false}
+        style={[
             styles.flexGrow1,
-            styles.pb5,
             styles.mh5,
             styles.signInPageNarrowContentContainer,
             styles.alignSelfCenter,
         ]}
     >
-        <View style={[styles.flexGrow1]}>
-            <View
-                style={[
-                    styles.signInPageInnerNative,
-                    styles.flex1,
-                    styles.dFlex,
-                    styles.flexColumn,
-                    props.windowHeight > props.windowWidth ? styles.mt40Percentage : null,
-                ]}
-            >
-                <View style={[styles.signInPageLogoNative, styles.mb2]}>
-                    <ExpensifyCashLogo
-                        width={variables.componentSizeLarge}
-                        height={variables.componentSizeLarge}
-                    />
+        <KeyboardAvoidingView behavior="position">
+            <View style={[styles.flexGrow1]}>
+                <View
+                    style={[
+                        styles.signInPageInnerNative,
+                        styles.flex1,
+                        styles.dFlex,
+                        styles.flexColumn,
+                        props.windowHeight > props.windowWidth ? styles.mt40Percentage : null,
+                    ]}
+                >
+                    <View style={[styles.signInPageLogoNative, styles.mb2]}>
+                        <ExpensifyCashLogo
+                            width={variables.componentSizeLarge}
+                            height={variables.componentSizeLarge}
+                        />
+                    </View>
+                    <Text style={[styles.mv5, styles.textLabel, styles.h3]}>
+                        {props.translate('welcomeText.phrase1')}
+                    </Text>
+                    {props.children}
                 </View>
-                <Text style={[styles.mv5, styles.textLabel, styles.h3]}>
-                    {props.translate('welcomeText.phrase1')}
-                </Text>
-                {props.children}
-            </View>
-            <View style={[styles.mt3]}>
-                <TermsAndLicenses />
             </View>
+        </KeyboardAvoidingView>
+        <View style={[styles.mt3, styles.mb5, styles.alignSelfCenter]}>
+            <TermsAndLicenses />
         </View>
     </ScrollView>
 );

From 7974ea0c36d6ea6e9ccf3209eb9056ea66379006 Mon Sep 17 00:00:00 2001
From: Rafael <rdjuric@pm.me>
Date: Wed, 21 Jul 2021 23:32:29 -0300
Subject: [PATCH 27/79] removes mistake commit

---
 src/components/TextInputAutoWidth.js           | 18 ++++++++----------
 .../SignInPageLayout/SignInPageLayoutNarrow.js |  1 +
 src/styles/styles.js                           |  3 ++-
 3 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/src/components/TextInputAutoWidth.js b/src/components/TextInputAutoWidth.js
index 18a04bb9e335..9dbc83537cd6 100644
--- a/src/components/TextInputAutoWidth.js
+++ b/src/components/TextInputAutoWidth.js
@@ -42,15 +42,13 @@ class TextInputAutoWidth extends React.Component {
     render() {
         const propsWithoutStyles = _.omit(this.props, ['inputStyle', 'textStyle']);
         return (
-            <>
-                <View>
-                    <TextInputFocusable
-                        style={[this.props.inputStyle, getAutoGrowTextInputStyle(this.state.textInputWidth)]}
-                        ref={this.props.forwardedRef}
-                        /* eslint-disable-next-line react/jsx-props-no-spreading */
-                        {...propsWithoutStyles}
-                    />
-                </View>
+            <View>
+                <TextInputFocusable
+                    style={[this.props.inputStyle, getAutoGrowTextInputStyle(this.state.textInputWidth)]}
+                    ref={this.props.forwardedRef}
+                    /* eslint-disable-next-line react/jsx-props-no-spreading */
+                    {...propsWithoutStyles}
+                />
                 {/*
                 Text input component doesn't support auto grow by default.
                 We're using a hidden text input to achieve that.
@@ -63,7 +61,7 @@ class TextInputAutoWidth extends React.Component {
                 >
                     {this.props.value || this.props.placeholder}
                 </Text>
-            </>
+            </View>
         );
     }
 }
diff --git a/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js b/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js
index d4a72dd5ffeb..475b0a30d613 100755
--- a/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js
+++ b/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js
@@ -20,6 +20,7 @@ const propTypes = {
 
 const SignInPageLayoutNarrow = props => (
     <ScrollView
+        keyboardShouldPersistTaps="handled"
         showsVerticalScrollIndicator={false}
         style={[
             styles.flexGrow1,
diff --git a/src/styles/styles.js b/src/styles/styles.js
index 8a33e4e24941..e11f4ee0c920 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -1641,10 +1641,11 @@ const styles = {
     },
 
     hiddenElementOutsideOfWindow: {
-        position: 'absolute',
+        position: 'fixed',
         top: 0,
         left: 0,
         opacity: 0,
+        transform: 'translateX(-100%)',
     },
 
     growlNotificationWrapper: {

From 37b911ae3ff981d9c7e4e58fe7e9f09d39c69e05 Mon Sep 17 00:00:00 2001
From: Dmytro Klymenko <dmytro.klymenko@protonmail.com>
Date: Thu, 22 Jul 2021 21:45:41 +0300
Subject: [PATCH 28/79] #3770 - use constant for small icon size

---
 src/components/Picker/PickerPropTypes.js | 5 +++--
 src/styles/styles.js                     | 4 ++++
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/components/Picker/PickerPropTypes.js b/src/components/Picker/PickerPropTypes.js
index e7140b0bef5a..3a6077b5886c 100644
--- a/src/components/Picker/PickerPropTypes.js
+++ b/src/components/Picker/PickerPropTypes.js
@@ -1,6 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import Icon from '../Icon';
+import styles from '../../styles/styles';
 import {DownArrow} from '../Icon/Expensicons';
 
 const propTypes = {
@@ -50,8 +51,8 @@ const defaultProps = {
             {size === 'small'
                 ? (
                     <Icon
-                        width={12}
-                        height={12}
+                        width={styles.pickerSmall.icon.width}
+                        height={styles.pickerSmall.icon.height}
                         src={DownArrow}
                     />
                 )
diff --git a/src/styles/styles.js b/src/styles/styles.js
index acf4e8f3d83d..98b866114455 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -385,6 +385,10 @@ const styles = {
             right: 9,
             pointerEvents: 'none',
         },
+        icon: {
+            width: variables.iconSizeExtraSmall,
+            height: variables.iconSizeExtraSmall,
+        },
     },
 
     badge: {

From 35029bbab5e33bb57d80b9e1e37f69b545656aa0 Mon Sep 17 00:00:00 2001
From: Dmytro Klymenko <dmytro.klymenko@protonmail.com>
Date: Thu, 22 Jul 2021 21:50:39 +0300
Subject: [PATCH 29/79] #3770 move LocalePicker/index.js to LocalePicker.js

---
 .../{LocalePicker/index.js => LocalePicker.js} | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)
 rename src/components/{LocalePicker/index.js => LocalePicker.js} (83%)

diff --git a/src/components/LocalePicker/index.js b/src/components/LocalePicker.js
similarity index 83%
rename from src/components/LocalePicker/index.js
rename to src/components/LocalePicker.js
index 604f5a0cd136..a634ac6ba7e1 100644
--- a/src/components/LocalePicker/index.js
+++ b/src/components/LocalePicker.js
@@ -1,15 +1,15 @@
 import React from 'react';
 import {withOnyx} from 'react-native-onyx';
 import PropTypes from 'prop-types';
-import styles from '../../styles/styles';
-import Picker from '../Picker';
-import Text from '../Text';
-import compose from '../../libs/compose';
-import {setLocale} from '../../libs/actions/App';
-import withLocalize, {withLocalizePropTypes} from '../withLocalize';
-import ONYXKEYS from '../../ONYXKEYS';
-import CONST from '../../CONST';
-import Permissions from '../../libs/Permissions';
+import styles from '../styles/styles';
+import Picker from './Picker';
+import Text from './Text';
+import compose from '../libs/compose';
+import {setLocale} from '../libs/actions/App';
+import withLocalize, {withLocalizePropTypes} from './withLocalize';
+import ONYXKEYS from '../ONYXKEYS';
+import CONST from '../CONST';
+import Permissions from '../libs/Permissions';
 
 const propTypes = {
     /** Indicates which locale the user currently has selected */

From b74d826f96fee4cc65714429b9f5818c9a053d5f Mon Sep 17 00:00:00 2001
From: Dmytro Klymenko <dmytro.klymenko@protonmail.com>
Date: Thu, 22 Jul 2021 22:04:48 +0300
Subject: [PATCH 30/79] #3770 do not translate language names

---
 src/components/LocalePicker.js | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/src/components/LocalePicker.js b/src/components/LocalePicker.js
index a634ac6ba7e1..88a25971d7a3 100644
--- a/src/components/LocalePicker.js
+++ b/src/components/LocalePicker.js
@@ -10,6 +10,7 @@ import withLocalize, {withLocalizePropTypes} from './withLocalize';
 import ONYXKEYS from '../ONYXKEYS';
 import CONST from '../CONST';
 import Permissions from '../libs/Permissions';
+import {translate} from '../libs/translate';
 
 const propTypes = {
     /** Indicates which locale the user currently has selected */
@@ -30,24 +31,25 @@ const defaultProps = {
     betas: [],
 };
 
+const localesToLanguages = {
+    default: {
+        value: 'en',
+        label: translate('en', 'preferencesPage.languages.english'),
+    },
+    es: {
+        value: 'es',
+        label: translate('es', 'preferencesPage.languages.spanish'),
+    },
+};
+
 const LocalePicker = ({
+    // eslint-disable-next-line no-shadow
     preferredLocale, translate, betas, size,
 }) => {
     if (!Permissions.canUseInternationalization(betas)) {
         return null;
     }
 
-    const localesToLanguages = {
-        default: {
-            value: 'en',
-            label: translate('preferencesPage.languages.english'),
-        },
-        es: {
-            value: 'es',
-            label: translate('preferencesPage.languages.spanish'),
-        },
-    };
-
     return (
         <>
             {size === 'normal' && (

From be9b7480ab03b4b82fbcc56afdc4b468707e9f55 Mon Sep 17 00:00:00 2001
From: Rajat Parashar <parasharrajat@users.noreply.github.com>
Date: Fri, 23 Jul 2021 02:06:30 +0530
Subject: [PATCH 31/79] fix: Enable default context menu messages being edited

---
 src/components/PressableWithSecondaryInteraction/index.js     | 4 +++-
 .../pressableWithSecondaryInteractionPropTypes.js             | 4 ++++
 src/pages/home/report/ReportActionItem.js                     | 1 +
 3 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/components/PressableWithSecondaryInteraction/index.js b/src/components/PressableWithSecondaryInteraction/index.js
index cc9645d98180..bf6c981f4d9a 100644
--- a/src/components/PressableWithSecondaryInteraction/index.js
+++ b/src/components/PressableWithSecondaryInteraction/index.js
@@ -30,7 +30,9 @@ class PressableWithSecondaryInteraction extends Component {
      */
     executeSecondaryInteractionOnContextMenu(e) {
         const selection = window.getSelection().toString();
-        e.preventDefault();
+        if (this.props.preventDefaultContentMenu) {
+            e.preventDefault();
+        }
         this.props.onSecondaryInteraction(e, selection);
     }
 
diff --git a/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js b/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js
index 372eabd424cd..beb257922b0e 100644
--- a/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js
+++ b/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js
@@ -15,12 +15,16 @@ const propTypes = {
 
     /** The ref to the search input (may be null on small screen widths) */
     forwardedRef: PropTypes.func,
+
+    /** Prevent the default ContextMenu on web/Desktop */
+    preventDefaultContentMenu: PropTypes.bool,
 };
 
 const defaultProps = {
     forwardedRef: () => {},
     onPressIn: () => {},
     onPressOut: () => {},
+    preventDefaultContentMenu: true,
 };
 
 export {propTypes, defaultProps};
diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index 2561f83b111c..64b3a9fa4929 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -273,6 +273,7 @@ class ReportActionItem extends Component {
                     onPressIn={() => this.props.isSmallScreenWidth && canUseTouchScreen() && ControlSelection.block()}
                     onPressOut={() => ControlSelection.unblock()}
                     onSecondaryInteraction={this.showPopover}
+                    preventDefaultContentMenu={!this.props.draftMessage}
                 >
                     <Hoverable resetsOnClickOutside={false}>
                         {hovered => (

From 5c15b0af5978a938a9250170a91f484655bc3f37 Mon Sep 17 00:00:00 2001
From: Aldo Canepa <aldo@expensify.com>
Date: Thu, 22 Jul 2021 17:42:17 -0700
Subject: [PATCH 32/79] Fix link to AsyncStorage documentation

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 104687db9a5e..c3ee08137fc0 100644
--- a/README.md
+++ b/README.md
@@ -204,7 +204,7 @@ This application is built with the following principles.
     1. UI inputs push data to the server (React component -> Action -> XHR to server).
     1. Go to 1
 1. **Offline first**
-    - All data that is brought into the app and is necessary to display the app when offline should be stored on disk in persistent storage (eg. localStorage on browser platforms). [AsyncStorage](https://react-native-community.github.io/async-storage/) is a cross-platform abstraction layer that is used to access persistent storage.
+    - All data that is brought into the app and is necessary to display the app when offline should be stored on disk in persistent storage (eg. localStorage on browser platforms). [AsyncStorage](https://reactnative.dev/docs/asyncstorage) is a cross-platform abstraction layer that is used to access persistent storage.
     - All data that is displayed, comes from persistent storage.
 1. **UI Binds to data on disk**
     - Onyx is a Pub/Sub library to connect the application to the data stored on disk.

From 3f08a49e2846bff9f68cab030c119e02bfe6a5f6 Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Thu, 22 Jul 2021 10:53:28 -1000
Subject: [PATCH 33/79] Add flipper performance plugin

Use hermes

use number format for iOS too

undo changes

undo some changes

undo flipper stuff

undo other changes

undo report.js changes
---
 ios/ExpensifyCash.xcodeproj/project.pbxproj   |  6 ++
 ios/Podfile                                   | 12 ++--
 ios/Podfile.lock                              | 59 +++++++++++++------
 .../{index.android.js => index.native.js}     |  0
 4 files changed, 54 insertions(+), 23 deletions(-)
 rename src/libs/numberFormat/{index.android.js => index.native.js} (100%)

diff --git a/ios/ExpensifyCash.xcodeproj/project.pbxproj b/ios/ExpensifyCash.xcodeproj/project.pbxproj
index eb03211ebd49..18e0eafb0d1f 100644
--- a/ios/ExpensifyCash.xcodeproj/project.pbxproj
+++ b/ios/ExpensifyCash.xcodeproj/project.pbxproj
@@ -527,12 +527,14 @@
 			);
 			inputPaths = (
 				"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash-frameworks.sh",
+				"${PODS_ROOT}/hermes-engine/destroot/Library/Frameworks/iphoneos/hermes.framework",
 				"${PODS_XCFRAMEWORKS_BUILD_DIR}/Onfido/Onfido.framework/Onfido",
 				"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL/OpenSSL.framework/OpenSSL",
 				"${PODS_XCFRAMEWORKS_BUILD_DIR}/LinkKit/LinkKit.framework/LinkKit",
 			);
 			name = "[CP] Embed Pods Frameworks";
 			outputPaths = (
+				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Onfido.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LinkKit.framework",
@@ -712,12 +714,14 @@
 			);
 			inputPaths = (
 				"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests-frameworks.sh",
+				"${PODS_ROOT}/hermes-engine/destroot/Library/Frameworks/iphoneos/hermes.framework",
 				"${PODS_XCFRAMEWORKS_BUILD_DIR}/Onfido/Onfido.framework/Onfido",
 				"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL/OpenSSL.framework/OpenSSL",
 				"${PODS_XCFRAMEWORKS_BUILD_DIR}/LinkKit/LinkKit.framework/LinkKit",
 			);
 			name = "[CP] Embed Pods Frameworks";
 			outputPaths = (
+				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Onfido.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LinkKit.framework",
@@ -926,6 +930,7 @@
 				COPY_PHASE_STRIP = NO;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				ENABLE_TESTABILITY = YES;
+				"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 i386";
 				GCC_C_LANGUAGE_STANDARD = gnu99;
 				GCC_DYNAMIC_NO_PIC = NO;
 				GCC_NO_COMMON_BLOCKS = YES;
@@ -986,6 +991,7 @@
 				COPY_PHASE_STRIP = YES;
 				ENABLE_NS_ASSERTIONS = NO;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 i386";
 				GCC_C_LANGUAGE_STANDARD = gnu99;
 				GCC_NO_COMMON_BLOCKS = YES;
 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
diff --git a/ios/Podfile b/ios/Podfile
index 78bd8b4ce645..f0fff9d30d96 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -14,7 +14,10 @@ target 'ExpensifyCash' do
   pod 'Permission-LocationWhenInUse', :path => "#{permissions_path}/LocationWhenInUse"
 
   config = use_native_modules!
-  use_react_native!(:path => config["reactNativePath"])
+  use_react_native!(
+    :path => config["reactNativePath"],
+    :hermes_enabled => true
+  )
 
   target 'ExpensifyCashTests' do
     inherit! :complete
@@ -24,10 +27,11 @@ target 'ExpensifyCash' do
   # Enables Flipper.
   #
   # Note that if you have use_frameworks! enabled, Flipper will not work and
-  # you should disable these next few lines.
-  use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1')
+  # you should disable the next line.
+  use_flipper!()
+
   post_install do |installer|
-    flipper_post_install(installer)
+    react_native_post_install(installer)
 
     installer.pods_project.build_configurations.each do |config|
       config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 3f4e4773ee98..3be3a461ff59 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -177,6 +177,7 @@ PODS:
     - GoogleUtilities/Logger
   - GoogleUtilities/UserDefaults (7.5.0):
     - GoogleUtilities/Logger
+  - hermes-engine (0.7.2)
   - libevent (2.1.12)
   - nanopb (2.30908.0):
     - nanopb/decode (= 2.30908.0)
@@ -194,7 +195,7 @@ PODS:
     - RNPermissions
   - Permission-LocationWhenInUse (3.0.1):
     - RNPermissions
-  - Plaid (2.1.2)
+  - Plaid (2.1.3)
   - PromisesObjC (2.0.0)
   - Protobuf (3.17.0)
   - RCT-Folly (2020.01.13.00):
@@ -206,6 +207,11 @@ PODS:
     - boost-for-react-native
     - DoubleConversion
     - glog
+  - RCT-Folly/Futures (2020.01.13.00):
+    - boost-for-react-native
+    - DoubleConversion
+    - glog
+    - libevent
   - RCTRequired (0.64.1)
   - RCTTypeSafety (0.64.1):
     - FBLazyVector (= 0.64.1)
@@ -263,6 +269,16 @@ PODS:
     - React-jsinspector (= 0.64.1)
     - React-perflogger (= 0.64.1)
     - Yoga
+  - React-Core/Hermes (0.64.1):
+    - glog
+    - hermes-engine
+    - RCT-Folly (= 2020.01.13.00)
+    - RCT-Folly/Futures
+    - React-cxxreact (= 0.64.1)
+    - React-jsi (= 0.64.1)
+    - React-jsiexecutor (= 0.64.1)
+    - React-perflogger (= 0.64.1)
+    - Yoga
   - React-Core/RCTActionSheetHeaders (0.64.1):
     - glog
     - RCT-Folly (= 2020.01.13.00)
@@ -573,26 +589,28 @@ DEPENDENCIES:
   - EXHaptics (from `../node_modules/expo-haptics/ios`)
   - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
   - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
-  - Flipper (= 0.75.1)
+  - Flipper (~> 0.75.1)
   - Flipper-DoubleConversion (= 1.1.7)
-  - Flipper-Folly (= 2.5.3)
+  - Flipper-Folly (~> 2.5.3)
   - Flipper-Glog (= 0.3.6)
   - Flipper-PeerTalk (~> 0.0.4)
-  - Flipper-RSocket (= 1.3.1)
-  - FlipperKit (= 0.75.1)
-  - FlipperKit/Core (= 0.75.1)
-  - FlipperKit/CppBridge (= 0.75.1)
-  - FlipperKit/FBCxxFollyDynamicConvert (= 0.75.1)
-  - FlipperKit/FBDefines (= 0.75.1)
-  - FlipperKit/FKPortForwarding (= 0.75.1)
-  - FlipperKit/FlipperKitHighlightOverlay (= 0.75.1)
-  - FlipperKit/FlipperKitLayoutPlugin (= 0.75.1)
-  - FlipperKit/FlipperKitLayoutTextSearchable (= 0.75.1)
-  - FlipperKit/FlipperKitNetworkPlugin (= 0.75.1)
-  - FlipperKit/FlipperKitReactPlugin (= 0.75.1)
-  - FlipperKit/FlipperKitUserDefaultsPlugin (= 0.75.1)
-  - FlipperKit/SKIOSNetworkPlugin (= 0.75.1)
+  - Flipper-RSocket (~> 1.3)
+  - FlipperKit (~> 0.75.1)
+  - FlipperKit/Core (~> 0.75.1)
+  - FlipperKit/CppBridge (~> 0.75.1)
+  - FlipperKit/FBCxxFollyDynamicConvert (~> 0.75.1)
+  - FlipperKit/FBDefines (~> 0.75.1)
+  - FlipperKit/FKPortForwarding (~> 0.75.1)
+  - FlipperKit/FlipperKitHighlightOverlay (~> 0.75.1)
+  - FlipperKit/FlipperKitLayoutPlugin (~> 0.75.1)
+  - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.75.1)
+  - FlipperKit/FlipperKitNetworkPlugin (~> 0.75.1)
+  - FlipperKit/FlipperKitReactPlugin (~> 0.75.1)
+  - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.75.1)
+  - FlipperKit/SKIOSNetworkPlugin (~> 0.75.1)
   - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
+  - hermes-engine (~> 0.7.2)
+  - libevent (~> 2.1.12)
   - "onfido-react-native-sdk (from `../node_modules/@onfido/react-native-sdk`)"
   - Permission-LocationAccuracy (from `../node_modules/react-native-permissions/ios/LocationAccuracy`)
   - Permission-LocationAlways (from `../node_modules/react-native-permissions/ios/LocationAlways`)
@@ -605,6 +623,7 @@ DEPENDENCIES:
   - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
   - React-Core (from `../node_modules/react-native/`)
   - React-Core/DevSupport (from `../node_modules/react-native/`)
+  - React-Core/Hermes (from `../node_modules/react-native/`)
   - React-Core/RCTWebSocket (from `../node_modules/react-native/`)
   - React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
   - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
@@ -687,6 +706,7 @@ SPEC REPOS:
     - GoogleAppMeasurement
     - GoogleDataTransport
     - GoogleUtilities
+    - hermes-engine
     - libevent
     - nanopb
     - Onfido
@@ -868,6 +888,7 @@ SPEC CHECKSUMS:
   GoogleAppMeasurement: 6b6a08fd9c71f4dbc89e0e812acca81d797aa342
   GoogleDataTransport: 85fd18ff3019bb85d3f2c551d04c481dedf71fc9
   GoogleUtilities: eea970f4a389963963bffe8d8fabe43540678b9c
+  hermes-engine: 7d97ba46a1e29bacf3e3c61ecb2804a5ddd02d4f
   libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
   nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
   Onfido: 116a268e4cb8b767c15285e8071c2e8304673cdf
@@ -876,7 +897,7 @@ SPEC CHECKSUMS:
   Permission-LocationAccuracy: e8adff9ede1b23b43b7054a4500113d515fc87a8
   Permission-LocationAlways: 7f7f373d086af7a81b2f4f20d65d29266ca2043b
   Permission-LocationWhenInUse: 3ae82a9feb5da4e94e386dba17c7dd3531af9feb
-  Plaid: c02276ccc630a726a9ed790bf923d29839ff4017
+  Plaid: f55c6acdc249245c6778a4045757eb4e839cca61
   PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58
   Protobuf: 7327d4444215b5f18e560a97f879ff5503c4581c
   RCT-Folly: ec7a233ccc97cc556cf7237f0db1ff65b986f27c
@@ -943,6 +964,6 @@ SPEC CHECKSUMS:
   Yoga: a7de31c64fe738607e7a3803e3f591a4b1df7393
   YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
 
-PODFILE CHECKSUM: 2b0558e25f01204512de38c2f656a63b5d86d19c
+PODFILE CHECKSUM: 2092930e33e7aa4d68cdf783d6174465bd3ce380
 
 COCOAPODS: 1.10.1
diff --git a/src/libs/numberFormat/index.android.js b/src/libs/numberFormat/index.native.js
similarity index 100%
rename from src/libs/numberFormat/index.android.js
rename to src/libs/numberFormat/index.native.js

From 63454a8ec3e6c61ec424329c35d14b0e725ec4bb Mon Sep 17 00:00:00 2001
From: Rajat Parashar <parasharrajat@users.noreply.github.com>
Date: Fri, 23 Jul 2021 06:58:53 +0530
Subject: [PATCH 34/79] fix: bash escaping in gh action

---
 .github/workflows/deployBlocker.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/deployBlocker.yml b/.github/workflows/deployBlocker.yml
index fcbcdf9c5aa6..cce29a3112c1 100644
--- a/.github/workflows/deployBlocker.yml
+++ b/.github/workflows/deployBlocker.yml
@@ -24,7 +24,7 @@ jobs:
         run: |
           echo "DEPLOY_BLOCKER_URL=${{ github.event.issue.html_url }}" >> $GITHUB_ENV
           echo "DEPLOY_BLOCKER_NUMBER=${{ github.event.issue.number }}" >> $GITHUB_ENV
-          echo "DEPLOY_BLOCKER_TITLE=${{ github.event.issue.title }}" >> $GITHUB_ENV
+          echo "DEPLOY_BLOCKER_TITLE=$(sed -e "s/'/'\\\\''/g; s/\`/\\\\\`/g; 1s/^/'/; \$s/\$/'/" <<< ${{ github.event.issue.title }})" >> $GITHUB_ENV
 
       - name: Get URL, title, & number of new deploy blocker (pull request)
         if: ${{ github.event_name == 'pull_request' }}

From 59280bb672b9658657ecb72773468bb5e5073dd3 Mon Sep 17 00:00:00 2001
From: Aman Ansari <amanansari.a2@gmail.com>
Date: Fri, 23 Jul 2021 17:51:57 +0530
Subject: [PATCH 35/79] use trim() on required places

---
 src/pages/SearchPage.js                        | 2 +-
 src/pages/home/report/EmojiPickerMenu/index.js | 2 +-
 src/pages/settings/Profile/ProfilePage.js      | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js
index 564e0181679c..7244c9460a34 100755
--- a/src/pages/SearchPage.js
+++ b/src/pages/SearchPage.js
@@ -128,7 +128,7 @@ class SearchPage extends Component {
         } = getSearchOptions(
             this.props.reports,
             this.props.personalDetails,
-            this.state.searchValue,
+            this.state.searchValue.trim(),
             this.props.betas,
         );
         this.setState({
diff --git a/src/pages/home/report/EmojiPickerMenu/index.js b/src/pages/home/report/EmojiPickerMenu/index.js
index 044abc87a2cc..9ac5b2b2a253 100755
--- a/src/pages/home/report/EmojiPickerMenu/index.js
+++ b/src/pages/home/report/EmojiPickerMenu/index.js
@@ -274,7 +274,7 @@ class EmojiPickerMenu extends Component {
      * @param {String} searchTerm
      */
     filterEmojis(searchTerm) {
-        const normalizedSearchTerm = searchTerm.toLowerCase();
+        const normalizedSearchTerm = searchTerm.toLowerCase().trim();
         if (normalizedSearchTerm === '') {
             // There are no headers when searching, so we need to re-make them sticky when there is no search term
             this.setState({
diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js
index df883255c03a..c8d7e5ba0b09 100755
--- a/src/pages/settings/Profile/ProfilePage.js
+++ b/src/pages/settings/Profile/ProfilePage.js
@@ -193,8 +193,8 @@ class ProfilePage extends Component {
                 && this.props.myPersonalDetails.pronouns === this.state.selfSelectedPronouns);
 
         // Disables button if none of the form values have changed
-        const isButtonDisabled = (this.props.myPersonalDetails.firstName === this.state.firstName)
-            && (this.props.myPersonalDetails.lastName === this.state.lastName)
+        const isButtonDisabled = (this.props.myPersonalDetails.firstName === this.state.firstName.trim())
+            && (this.props.myPersonalDetails.lastName === this.state.lastName.trim())
             && (this.props.myPersonalDetails.timezone.selected === this.state.selectedTimezone)
             && (this.props.myPersonalDetails.timezone.automatic === this.state.isAutomaticTimezone)
             && arePronounsUnchanged;

From 0b75a8b6d04879a654efe28500df0f01149e93a7 Mon Sep 17 00:00:00 2001
From: chiragsalian <chirag@expensify.com>
Date: Fri, 23 Jul 2021 16:09:17 -0700
Subject: [PATCH 36/79] Beta race fixes

---
 src/Expensify.js                              | 16 +++++++++++++---
 src/libs/Permissions.js                       |  3 +--
 src/libs/actions/Session.js                   | 10 ++++++++++
 src/pages/ValidateLogin2FANewWorkspacePage.js | 16 ++++++++++++++--
 src/pages/ValidateLoginNewWorkspacePage.js    | 15 +++++++++++++--
 src/pages/workspace/PublicWorkspaceNewView.js |  5 ++---
 6 files changed, 53 insertions(+), 12 deletions(-)

diff --git a/src/Expensify.js b/src/Expensify.js
index 421658fb55ee..4fe81c89f5c4 100644
--- a/src/Expensify.js
+++ b/src/Expensify.js
@@ -20,6 +20,7 @@ import GrowlNotification from './components/GrowlNotification';
 import {growlRef} from './libs/Growl';
 import Navigation from './libs/Navigation/Navigation';
 import ROUTES from './ROUTES';
+import {setRedirectToWorkspaceNewAfterSignIn} from './libs/actions/Session';
 
 // Initialize the store when the app loads for the first time
 Onyx.init({
@@ -68,6 +69,9 @@ const propTypes = {
 
     /** Whether the initial data needed to render the app is ready */
     initialReportDataLoaded: PropTypes.bool,
+
+    /** List of betas */
+    betas: PropTypes.arrayOf(PropTypes.string),
 };
 
 const defaultProps = {
@@ -78,6 +82,7 @@ const defaultProps = {
     },
     updateAvailable: false,
     initialReportDataLoaded: false,
+    betas: null,
 };
 
 class Expensify extends PureComponent {
@@ -121,9 +126,11 @@ class Expensify extends PureComponent {
         const previousAuthToken = lodashGet(prevProps, 'session.authToken', null);
         if (this.getAuthToken() && !previousAuthToken) {
             BootSplash.show({fade: true});
-            if (lodashGet(this.props, 'session.redirectToWorkspaceNewAfterSignIn', false)) {
-                Navigation.navigate(ROUTES.WORKSPACE_NEW);
-            }
+        }
+
+        if (this.getAuthToken() && this.props.betas && this.props.session.redirectToWorkspaceNewAfterSignIn) {
+            setRedirectToWorkspaceNewAfterSignIn(false);
+            Navigation.navigate(ROUTES.WORKSPACE_NEW);
         }
 
         if (this.getAuthToken() && this.props.initialReportDataLoaded) {
@@ -180,6 +187,9 @@ export default withOnyx({
     session: {
         key: ONYXKEYS.SESSION,
     },
+    betas: {
+        key: ONYXKEYS.BETAS,
+    },
     updateAvailable: {
         key: ONYXKEYS.UPDATE_AVAILABLE,
         initWithStoredValues: false,
diff --git a/src/libs/Permissions.js b/src/libs/Permissions.js
index 7063273b245c..15a70cf2e6f8 100644
--- a/src/libs/Permissions.js
+++ b/src/libs/Permissions.js
@@ -1,5 +1,4 @@
 import _ from 'underscore';
-import {isDevelopment} from './Environment/Environment';
 import CONST from '../CONST';
 
 /**
@@ -8,7 +7,7 @@ import CONST from '../CONST';
  * @returns {Boolean}
  */
 function canUseAllBetas(betas) {
-    return isDevelopment() || _.contains(betas, CONST.BETAS.ALL);
+    return _.contains(betas, CONST.BETAS.ALL);
 }
 
 /**
diff --git a/src/libs/actions/Session.js b/src/libs/actions/Session.js
index ebd314644b07..452cd1e06921 100644
--- a/src/libs/actions/Session.js
+++ b/src/libs/actions/Session.js
@@ -299,6 +299,15 @@ function continueSessionFromECom(accountID, validateCode, twoFactorAuthCode) {
     });
 }
 
+/**
+ * Sets the redirectToWorkspaceNewAfterSignIn flag in the session variable
+ *
+ * @param {Boolean} shouldRedirect
+ */
+function setRedirectToWorkspaceNewAfterSignIn(shouldRedirect) {
+    Onyx.merge(ONYXKEYS.SESSION, {redirectToWorkspaceNewAfterSignIn: shouldRedirect});
+}
+
 export {
     continueSessionFromECom,
     fetchAccountDetails,
@@ -309,4 +318,5 @@ export {
     resendValidationLink,
     resetPassword,
     restartSignin,
+    setRedirectToWorkspaceNewAfterSignIn,
 };
diff --git a/src/pages/ValidateLogin2FANewWorkspacePage.js b/src/pages/ValidateLogin2FANewWorkspacePage.js
index bbe5b804a200..702d4be73242 100644
--- a/src/pages/ValidateLogin2FANewWorkspacePage.js
+++ b/src/pages/ValidateLogin2FANewWorkspacePage.js
@@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
 import {withOnyx} from 'react-native-onyx';
 import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
 import validateLinkPropTypes from './validateLinkPropTypes';
-import {continueSessionFromECom} from '../libs/actions/Session';
+import {continueSessionFromECom, setRedirectToWorkspaceNewAfterSignIn} from '../libs/actions/Session';
 import styles from '../styles/styles';
 import ExpensifyCashLogo from '../components/ExpensifyCashLogo';
 import variables from '../styles/variables';
@@ -30,6 +30,9 @@ const propTypes = {
     /** The accountID and validateCode are passed via the URL */
     route: validateLinkPropTypes,
 
+    /** List of betas */
+    betas: PropTypes.arrayOf(PropTypes.string),
+
     ...withLocalizePropTypes,
 };
 
@@ -38,6 +41,7 @@ const defaultProps = {
         params: {},
     },
     session: {},
+    betas: null,
 };
 class ValidateLogin2FANewWorkspacePage extends Component {
     constructor(props) {
@@ -62,7 +66,12 @@ class ValidateLogin2FANewWorkspacePage extends Component {
             // by calling dismissModal(), the /v/... route is removed from history so the user will get taken to `/`
             // if they cancel out of the new workspace modal.
             Navigation.dismissModal();
-            Navigation.navigate(ROUTES.WORKSPACE_NEW);
+
+            if (this.props.betas) {
+                Navigation.navigate(ROUTES.WORKSPACE_NEW);
+            } else {
+                setRedirectToWorkspaceNewAfterSignIn(true);
+            }
         }
     }
 
@@ -144,5 +153,8 @@ export default compose(
         session: {
             key: ONYXKEYS.SESSION,
         },
+        betas: {
+            key: ONYXKEYS.BETAS,
+        },
     }),
 )(ValidateLogin2FANewWorkspacePage);
diff --git a/src/pages/ValidateLoginNewWorkspacePage.js b/src/pages/ValidateLoginNewWorkspacePage.js
index 5e672f1a4d9b..2cdaabc74253 100644
--- a/src/pages/ValidateLoginNewWorkspacePage.js
+++ b/src/pages/ValidateLoginNewWorkspacePage.js
@@ -7,7 +7,7 @@ import compose from '../libs/compose';
 import ONYXKEYS from '../ONYXKEYS';
 import Navigation from '../libs/Navigation/Navigation';
 import ROUTES from '../ROUTES';
-import {continueSessionFromECom} from '../libs/actions/Session';
+import {continueSessionFromECom, setRedirectToWorkspaceNewAfterSignIn} from '../libs/actions/Session';
 
 const propTypes = {
     /* Onyx Props */
@@ -20,6 +20,9 @@ const propTypes = {
 
     /** The accountID and validateCode are passed via the URL */
     route: validateLinkPropTypes,
+
+    /** List of betas */
+    betas: PropTypes.arrayOf(PropTypes.string),
 };
 
 const defaultProps = {
@@ -27,6 +30,7 @@ const defaultProps = {
         params: {},
     },
     session: {},
+    betas: null,
 };
 class ValidateLoginNewWorkspacePage extends Component {
     componentDidMount() {
@@ -39,7 +43,11 @@ class ValidateLoginNewWorkspacePage extends Component {
             // by calling dismissModal(), the /v/... route is removed from history so the user will get taken to `/`
             // if they cancel out of the new workspace modal.
             Navigation.dismissModal();
-            Navigation.navigate(ROUTES.WORKSPACE_NEW);
+            if (this.props.betas) {
+                Navigation.navigate(ROUTES.WORKSPACE_NEW);
+            } else {
+                setRedirectToWorkspaceNewAfterSignIn(true);
+            }
             return;
         }
 
@@ -63,5 +71,8 @@ export default compose(
         session: {
             key: ONYXKEYS.SESSION,
         },
+        betas: {
+            key: ONYXKEYS.BETAS,
+        },
     }),
 )(ValidateLoginNewWorkspacePage);
diff --git a/src/pages/workspace/PublicWorkspaceNewView.js b/src/pages/workspace/PublicWorkspaceNewView.js
index fa0ac88b4420..3d4a62f581a7 100644
--- a/src/pages/workspace/PublicWorkspaceNewView.js
+++ b/src/pages/workspace/PublicWorkspaceNewView.js
@@ -1,8 +1,7 @@
 import React from 'react';
-import Onyx from 'react-native-onyx';
 import PropTypes from 'prop-types';
-import ONYXKEYS from '../../ONYXKEYS';
 import SCREENS from '../../SCREENS';
+import {setRedirectToWorkspaceNewAfterSignIn} from '../../libs/actions/Session';
 
 const propTypes = {
     /** react-navigation navigation object available to screen components */
@@ -14,7 +13,7 @@ const propTypes = {
 
 class PublicWorkspaceNewView extends React.PureComponent {
     componentDidMount() {
-        Onyx.merge(ONYXKEYS.SESSION, {redirectToWorkspaceNewAfterSignIn: true});
+        setRedirectToWorkspaceNewAfterSignIn(true);
         this.props.navigation.replace(SCREENS.HOME);
     }
 

From 9318e5d7f12d8e392de16bb17bba78636ec101e3 Mon Sep 17 00:00:00 2001
From: chiragsalian <chirag@expensify.com>
Date: Fri, 23 Jul 2021 16:11:10 -0700
Subject: [PATCH 37/79] revert test changes.

---
 src/libs/Permissions.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/libs/Permissions.js b/src/libs/Permissions.js
index 15a70cf2e6f8..7063273b245c 100644
--- a/src/libs/Permissions.js
+++ b/src/libs/Permissions.js
@@ -1,4 +1,5 @@
 import _ from 'underscore';
+import {isDevelopment} from './Environment/Environment';
 import CONST from '../CONST';
 
 /**
@@ -7,7 +8,7 @@ import CONST from '../CONST';
  * @returns {Boolean}
  */
 function canUseAllBetas(betas) {
-    return _.contains(betas, CONST.BETAS.ALL);
+    return isDevelopment() || _.contains(betas, CONST.BETAS.ALL);
 }
 
 /**

From f33b3575e1e986765569fe01a9cf6a54901ac557 Mon Sep 17 00:00:00 2001
From: Rafael <rdjuric@pm.me>
Date: Fri, 23 Jul 2021 21:12:38 -0300
Subject: [PATCH 38/79] adds VS16 unicode to emoji items

---
 src/pages/home/report/EmojiPickerMenu/index.js | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/pages/home/report/EmojiPickerMenu/index.js b/src/pages/home/report/EmojiPickerMenu/index.js
index 044abc87a2cc..49a001868fbd 100755
--- a/src/pages/home/report/EmojiPickerMenu/index.js
+++ b/src/pages/home/report/EmojiPickerMenu/index.js
@@ -298,7 +298,9 @@ class EmojiPickerMenu extends Component {
     /**
      * Given an emoji item object, render a component based on its type.
      * Items with the code "SPACER" return nothing and are used to fill rows up to 8
-     * so that the sticky headers function properly
+     * so that the sticky headers function properly.
+     * We add '\uFE0F' to our unicode to force the correct emoji presentation (VS16) 
+     * on emojis that also have a text style presentation (VS15).
      *
      * @param {Object} item
      * @param {Number} index
@@ -313,7 +315,7 @@ class EmojiPickerMenu extends Component {
         if (header) {
             return (
                 <Text style={styles.emojiHeaderStyle}>
-                    {code}
+                    {code+'\uFE0F'}
                 </Text>
             );
         }
@@ -322,7 +324,7 @@ class EmojiPickerMenu extends Component {
             <EmojiPickerMenuItem
                 onPress={this.props.onEmojiSelected}
                 onHover={() => this.setState({highlightedIndex: index})}
-                emoji={code}
+                emoji={code+'\uFE0F'}
                 isHighlighted={index === this.state.highlightedIndex}
                 emojiSize={this.emojiSize}
             />

From d4dc90319c980a55849fa0ca4a5dc275bfdb82b9 Mon Sep 17 00:00:00 2001
From: Rafael <rdjuric@pm.me>
Date: Sat, 24 Jul 2021 03:31:42 -0300
Subject: [PATCH 39/79] adds fix on native, lint fixes

---
 src/pages/home/report/EmojiPickerMenu/index.js        | 6 +++---
 src/pages/home/report/EmojiPickerMenu/index.native.js | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/pages/home/report/EmojiPickerMenu/index.js b/src/pages/home/report/EmojiPickerMenu/index.js
index 49a001868fbd..c55beca29883 100755
--- a/src/pages/home/report/EmojiPickerMenu/index.js
+++ b/src/pages/home/report/EmojiPickerMenu/index.js
@@ -299,7 +299,7 @@ class EmojiPickerMenu extends Component {
      * Given an emoji item object, render a component based on its type.
      * Items with the code "SPACER" return nothing and are used to fill rows up to 8
      * so that the sticky headers function properly.
-     * We add '\uFE0F' to our unicode to force the correct emoji presentation (VS16) 
+     * We add '\uFE0F' to our unicode to force the correct emoji presentation (VS16)
      * on emojis that also have a text style presentation (VS15).
      *
      * @param {Object} item
@@ -315,7 +315,7 @@ class EmojiPickerMenu extends Component {
         if (header) {
             return (
                 <Text style={styles.emojiHeaderStyle}>
-                    {code+'\uFE0F'}
+                    {`${code}\uFE0F`}
                 </Text>
             );
         }
@@ -324,7 +324,7 @@ class EmojiPickerMenu extends Component {
             <EmojiPickerMenuItem
                 onPress={this.props.onEmojiSelected}
                 onHover={() => this.setState({highlightedIndex: index})}
-                emoji={code+'\uFE0F'}
+                emoji={`${code}\uFE0F`}
                 isHighlighted={index === this.state.highlightedIndex}
                 emojiSize={this.emojiSize}
             />
diff --git a/src/pages/home/report/EmojiPickerMenu/index.native.js b/src/pages/home/report/EmojiPickerMenu/index.native.js
index b1a4d1302506..6708330dffca 100644
--- a/src/pages/home/report/EmojiPickerMenu/index.native.js
+++ b/src/pages/home/report/EmojiPickerMenu/index.native.js
@@ -58,7 +58,7 @@ class EmojiPickerMenu extends Component {
         if (item.header) {
             return (
                 <Text style={styles.emojiHeaderStyle}>
-                    {item.code}
+                    {`${item.code}\uFE0F`}
                 </Text>
             );
         }
@@ -66,7 +66,7 @@ class EmojiPickerMenu extends Component {
         return (
             <EmojiPickerMenuItem
                 onPress={this.props.onEmojiSelected}
-                emoji={item.code}
+                emoji={`${item.code}\uFE0F`}
                 emojiSize={this.emojiSize}
             />
         );

From 6f83e44313689fbdafc443b80711f1d745a4be8b Mon Sep 17 00:00:00 2001
From: Alex Beaman <alexbeaman@expensify.com>
Date: Mon, 26 Jul 2021 12:38:14 +0200
Subject: [PATCH 40/79] Add Daily label to issue templates

---
 .github/ISSUE_TEMPLATE/Performance.md | 2 +-
 .github/ISSUE_TEMPLATE/Standard.md    | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/ISSUE_TEMPLATE/Performance.md b/.github/ISSUE_TEMPLATE/Performance.md
index df1881aaf5d2..49871a616bc3 100644
--- a/.github/ISSUE_TEMPLATE/Performance.md
+++ b/.github/ISSUE_TEMPLATE/Performance.md
@@ -2,7 +2,7 @@
 name: Report a performance issue
 about: Use to report an issue or propose a solution related to app performance
 title: "[Performance] "
-labels: Engineering
+labels: Engineering, Daily
 ---
 
 If you haven’t already, check out our [contributing guidelines](https://github.com/Expensify/ReactNativeChat/blob/main/CONTRIBUTING.md) for onboarding and email contributors@expensify.com to request to join our Slack channel!
diff --git a/.github/ISSUE_TEMPLATE/Standard.md b/.github/ISSUE_TEMPLATE/Standard.md
index f0c8561d2a8a..514d25f8b29f 100644
--- a/.github/ISSUE_TEMPLATE/Standard.md
+++ b/.github/ISSUE_TEMPLATE/Standard.md
@@ -1,7 +1,7 @@
 ---
 name: Standard issue template
 about: A standard template to follow when creating a new issue in this repository
-labels: AutoAssignerTriage
+labels: AutoAssignerTriage, Daily
 ---
 
 If you haven’t already, check out our [contributing guidelines](https://github.com/Expensify/ReactNativeChat/blob/main/CONTRIBUTING.md) for onboarding and email contributors@expensify.com to request to join our Slack channel!

From f0f5a7b9eab000141517f94c3672bdb3bbf0b83b Mon Sep 17 00:00:00 2001
From: Jules Rosser <jules@expensify.com>
Date: Mon, 26 Jul 2021 12:32:10 +0100
Subject: [PATCH 41/79] fetch local currency on each IOU request, to ensure we
 update when entering new countries

---
 src/pages/iou/IOUModal.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/pages/iou/IOUModal.js b/src/pages/iou/IOUModal.js
index 284148db3ea3..1e1291186eb4 100755
--- a/src/pages/iou/IOUModal.js
+++ b/src/pages/iou/IOUModal.js
@@ -20,6 +20,7 @@ import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndica
 import ScreenWrapper from '../../components/ScreenWrapper';
 import CONST from '../../CONST';
 import KeyboardAvoidingView from '../../components/KeyboardAvoidingView';
+import * as PersonalDetails from '../../libs/actions/PersonalDetails';
 
 /**
  * IOU modal for requesting money and splitting bills.
@@ -129,6 +130,7 @@ class IOUModal extends Component {
     }
 
     componentDidMount() {
+        PersonalDetails.fetchLocalCurrency();
         setIOUSelectedCurrency(this.props.myPersonalDetails.localCurrencyCode);
     }
 

From a3ad8efe71b82dbc9cff7aa36c123748106f563b Mon Sep 17 00:00:00 2001
From: Jules Rosser <jules@expensify.com>
Date: Mon, 26 Jul 2021 13:17:44 +0100
Subject: [PATCH 42/79] don't display loading indicator during currency
 retrieval, as it can block progress when offline

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

diff --git a/src/pages/iou/IOUModal.js b/src/pages/iou/IOUModal.js
index 1e1291186eb4..660ca27b0422 100755
--- a/src/pages/iou/IOUModal.js
+++ b/src/pages/iou/IOUModal.js
@@ -289,9 +289,9 @@ class IOUModal extends Component {
                         </View>
                         <View style={[styles.pRelative, styles.flex1]}>
                             <FullScreenLoadingIndicator
-                                visible={!didScreenTransitionEnd || this.props.iou.isRetrievingCurrency}
+                                visible={!didScreenTransitionEnd}
                             />
-                            {didScreenTransitionEnd && !this.props.iou.isRetrievingCurrency && (
+                            {didScreenTransitionEnd && (
                                 <>
                                     {currentStep === Steps.IOUAmount && (
                                         <IOUAmountPage

From 94b8c726b1836b5cd9fa9e44397612873a5a1421 Mon Sep 17 00:00:00 2001
From: Amal Nazeem <amal@expensify.com>
Date: Mon, 26 Jul 2021 12:23:15 -0400
Subject: [PATCH 43/79] Fix Keyboard input for workspace invite page on mobile
 keyboards

---
 src/pages/workspace/WorkspaceInvitePage.js | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/pages/workspace/WorkspaceInvitePage.js b/src/pages/workspace/WorkspaceInvitePage.js
index 577df15669c9..cd107c43c84a 100644
--- a/src/pages/workspace/WorkspaceInvitePage.js
+++ b/src/pages/workspace/WorkspaceInvitePage.js
@@ -15,7 +15,6 @@ import compose from '../../libs/compose';
 import ONYXKEYS from '../../ONYXKEYS';
 import {invite} from '../../libs/actions/Policy';
 import TextLink from '../../components/TextLink';
-import getEmailKeyboardType from '../../libs/getEmailKeyboardType';
 import themeColors from '../../styles/themes/default';
 import Growl from '../../libs/Growl';
 import FixedFooter from '../../components/FixedFooter';
@@ -107,7 +106,6 @@ class WorkspaceInvitePage extends React.Component {
                                 autoCapitalize="none"
                                 style={[styles.textInput]}
                                 value={this.state.userLogins}
-                                keyboardType={getEmailKeyboardType()}
                                 onChangeText={text => this.setState({userLogins: text})}
                             />
                         </View>

From b3005dce8def1b84063d273f9a876322183960c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Horus=20Lugo=20L=C3=B3pez?= <horus@expensify.com>
Date: Mon, 26 Jul 2021 18:40:18 +0200
Subject: [PATCH 44/79] Remove bintray repository to speed up first builds

---
 android/build.gradle | 2 --
 1 file changed, 2 deletions(-)

diff --git a/android/build.gradle b/android/build.gradle
index 66a9c1a64a72..0f9aece0cfa0 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -28,8 +28,6 @@ buildscript {
 
 allprojects {
     repositories {
-        maven { url "https://dl.bintray.com/onfido/maven" }
-
         mavenLocal()
         maven {
             // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm

From 9be3668030986ed605462be576ff688229aa2d0c Mon Sep 17 00:00:00 2001
From: Hamza El Aoutar <elaoutarhamza@gmail.com>
Date: Sat, 24 Jul 2021 15:32:40 +0100
Subject: [PATCH 45/79] Allow messages scrolling within edit box.

---
 src/components/TextInputFocusable/index.js | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/src/components/TextInputFocusable/index.js b/src/components/TextInputFocusable/index.js
index 1ee3da703a2b..70fe18d64004 100755
--- a/src/components/TextInputFocusable/index.js
+++ b/src/components/TextInputFocusable/index.js
@@ -123,6 +123,7 @@ class TextInputFocusable extends React.Component {
         this.dragNDropListener = this.dragNDropListener.bind(this);
         this.handlePaste = this.handlePaste.bind(this);
         this.handlePastedHTML = this.handlePastedHTML.bind(this);
+        this.handleWheel = this.handleWheel.bind(this);
     }
 
     componentDidMount() {
@@ -146,6 +147,7 @@ class TextInputFocusable extends React.Component {
             document.addEventListener('dragleave', this.dragNDropListener);
             document.addEventListener('drop', this.dragNDropListener);
             this.textInput.addEventListener('paste', this.handlePaste);
+            this.textInput.addEventListener('wheel', this.handleWheel);
         }
     }
 
@@ -173,6 +175,7 @@ class TextInputFocusable extends React.Component {
             document.removeEventListener('dragleave', this.dragNDropListener);
             document.removeEventListener('drop', this.dragNDropListener);
             this.textInput.removeEventListener('paste', this.handlePaste);
+            this.textInput.removeEventListener('wheel', this.handleWheel);
         }
     }
 
@@ -322,6 +325,18 @@ class TextInputFocusable extends React.Component {
         }
     }
 
+    /**
+     * Manually scrolls the text input, then prevents the event from being passed up to the parent.
+     * @param {Object} event native Event
+     */
+    handleWheel(event) {
+        if (event.target === document.activeElement) {
+            this.textInput.scrollTop += event.deltaY;
+            event.preventDefault();
+            event.stopPropagation();
+        }
+    }
+
     /**
      * Check the current scrollHeight of the textarea (minus any padding) and
      * divide by line height to get the total number of rows for the textarea.

From 0b6b40896f084d7b1a89f6363fb7bb2854a6ed73 Mon Sep 17 00:00:00 2001
From: chiragsalian <chirag@expensify.com>
Date: Mon, 26 Jul 2021 12:27:00 -0700
Subject: [PATCH 46/79] setting betas as empty array and using lodashGet

---
 src/Expensify.js                              |  5 +++--
 src/pages/ValidateLogin2FANewWorkspacePage.js |  9 +++++----
 src/pages/ValidateLoginNewWorkspacePage.js    | 10 ++++++----
 3 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/src/Expensify.js b/src/Expensify.js
index 3b6309ca2f03..c3886e197cf7 100644
--- a/src/Expensify.js
+++ b/src/Expensify.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
 import React, {PureComponent} from 'react';
 import {View, AppState} from 'react-native';
 import Onyx, {withOnyx} from 'react-native-onyx';
+import _ from 'underscore';
 
 import BootSplash from './libs/BootSplash';
 import listenToStorageEvents from './libs/listenToStorageEvents';
@@ -83,7 +84,7 @@ const defaultProps = {
     },
     updateAvailable: false,
     initialReportDataLoaded: false,
-    betas: null,
+    betas: [],
 };
 
 class Expensify extends PureComponent {
@@ -136,7 +137,7 @@ class Expensify extends PureComponent {
             BootSplash.show({fade: true});
         }
 
-        if (this.getAuthToken() && this.props.betas && this.props.session.redirectToWorkspaceNewAfterSignIn) {
+        if (this.getAuthToken() && !_.isEmpty(this.props.betas) && lodashGet(this.props, 'session.redirectToWorkspaceNewAfterSignIn', false)) {
             setRedirectToWorkspaceNewAfterSignIn(false);
             Navigation.navigate(ROUTES.WORKSPACE_NEW);
         }
diff --git a/src/pages/ValidateLogin2FANewWorkspacePage.js b/src/pages/ValidateLogin2FANewWorkspacePage.js
index 702d4be73242..6e9c07d44b9b 100644
--- a/src/pages/ValidateLogin2FANewWorkspacePage.js
+++ b/src/pages/ValidateLogin2FANewWorkspacePage.js
@@ -3,6 +3,7 @@ import {TextInput, View} from 'react-native';
 import lodashGet from 'lodash/get';
 import PropTypes from 'prop-types';
 import {withOnyx} from 'react-native-onyx';
+import _ from 'underscore';
 import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
 import validateLinkPropTypes from './validateLinkPropTypes';
 import {continueSessionFromECom, setRedirectToWorkspaceNewAfterSignIn} from '../libs/actions/Session';
@@ -41,7 +42,7 @@ const defaultProps = {
         params: {},
     },
     session: {},
-    betas: null,
+    betas: [],
 };
 class ValidateLogin2FANewWorkspacePage extends Component {
     constructor(props) {
@@ -67,10 +68,10 @@ class ValidateLogin2FANewWorkspacePage extends Component {
             // if they cancel out of the new workspace modal.
             Navigation.dismissModal();
 
-            if (this.props.betas) {
-                Navigation.navigate(ROUTES.WORKSPACE_NEW);
-            } else {
+            if (_.isEmpty(this.props.betas)) {
                 setRedirectToWorkspaceNewAfterSignIn(true);
+            } else {
+                Navigation.navigate(ROUTES.WORKSPACE_NEW);
             }
         }
     }
diff --git a/src/pages/ValidateLoginNewWorkspacePage.js b/src/pages/ValidateLoginNewWorkspacePage.js
index 2cdaabc74253..864d742dd8b9 100644
--- a/src/pages/ValidateLoginNewWorkspacePage.js
+++ b/src/pages/ValidateLoginNewWorkspacePage.js
@@ -2,6 +2,7 @@ import {Component} from 'react';
 import PropTypes from 'prop-types';
 import {withOnyx} from 'react-native-onyx';
 import lodashGet from 'lodash/get';
+import _ from 'underscore';
 import validateLinkPropTypes from './validateLinkPropTypes';
 import compose from '../libs/compose';
 import ONYXKEYS from '../ONYXKEYS';
@@ -9,6 +10,7 @@ import Navigation from '../libs/Navigation/Navigation';
 import ROUTES from '../ROUTES';
 import {continueSessionFromECom, setRedirectToWorkspaceNewAfterSignIn} from '../libs/actions/Session';
 
+
 const propTypes = {
     /* Onyx Props */
 
@@ -30,7 +32,7 @@ const defaultProps = {
         params: {},
     },
     session: {},
-    betas: null,
+    betas: [],
 };
 class ValidateLoginNewWorkspacePage extends Component {
     componentDidMount() {
@@ -43,10 +45,10 @@ class ValidateLoginNewWorkspacePage extends Component {
             // by calling dismissModal(), the /v/... route is removed from history so the user will get taken to `/`
             // if they cancel out of the new workspace modal.
             Navigation.dismissModal();
-            if (this.props.betas) {
-                Navigation.navigate(ROUTES.WORKSPACE_NEW);
-            } else {
+            if (_.isEmpty(this.props.betas)) {
                 setRedirectToWorkspaceNewAfterSignIn(true);
+            } else {
+                Navigation.navigate(ROUTES.WORKSPACE_NEW);
             }
             return;
         }

From ce7b7e719d4c46fb191d51e8fccedb47ee2cd0de Mon Sep 17 00:00:00 2001
From: chiragsalian <chirag@expensify.com>
Date: Mon, 26 Jul 2021 12:31:10 -0700
Subject: [PATCH 47/79] warning cleanup

---
 src/Expensify.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/Expensify.js b/src/Expensify.js
index c3886e197cf7..87db377bffad 100644
--- a/src/Expensify.js
+++ b/src/Expensify.js
@@ -137,7 +137,9 @@ class Expensify extends PureComponent {
             BootSplash.show({fade: true});
         }
 
-        if (this.getAuthToken() && !_.isEmpty(this.props.betas) && lodashGet(this.props, 'session.redirectToWorkspaceNewAfterSignIn', false)) {
+        if (this.getAuthToken()
+            && !_.isEmpty(this.props.betas)
+            && lodashGet(this.props, 'session.redirectToWorkspaceNewAfterSignIn', false)) {
             setRedirectToWorkspaceNewAfterSignIn(false);
             Navigation.navigate(ROUTES.WORKSPACE_NEW);
         }

From 70d7da6d7ab9fe99b60b6ceafbbbfd8c45015af1 Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Mon, 26 Jul 2021 10:42:30 -1000
Subject: [PATCH 48/79] Defer non critical requests and report view to speed up
 android start time

---
 src/CONST.js                                  |  1 +
 src/Expensify.js                              |  1 +
 src/ONYXKEYS.js                               |  3 ++
 src/components/OptionsList.js                 |  8 ++++
 .../Navigation/AppNavigator/AuthScreens.js    | 22 ++++++----
 src/libs/actions/App.js                       | 24 +++++++++++
 src/libs/actions/Timing.js                    | 13 +++---
 src/pages/home/report/ReportView.js           | 43 ++++++++++++-------
 src/pages/home/sidebar/SidebarLinks.js        |  4 ++
 src/pages/home/sidebar/SidebarScreen.js       |  4 ++
 10 files changed, 94 insertions(+), 29 deletions(-)

diff --git a/src/CONST.js b/src/CONST.js
index 1b0a49e36d18..dadb6b95fc21 100755
--- a/src/CONST.js
+++ b/src/CONST.js
@@ -193,6 +193,7 @@ const CONST = {
         HOMEPAGE_INITIAL_RENDER: 'homepage_initial_render',
         HOMEPAGE_REPORTS_LOADED: 'homepage_reports_loaded',
         SWITCH_REPORT: 'switch_report',
+        SIDEBAR_LOADED: 'sidebar_loaded',
         COLD: 'cold',
         REPORT_ACTION_ITEM_LAYOUT_DEBOUNCE_TIME: 1500,
     },
diff --git a/src/Expensify.js b/src/Expensify.js
index d933be7ccc1d..0c942a9a3973 100644
--- a/src/Expensify.js
+++ b/src/Expensify.js
@@ -35,6 +35,7 @@ Onyx.init({
         [ONYXKEYS.IOU]: {
             loading: false, error: false, creatingIOUTransaction: false, isRetrievingCurrency: false,
         },
+        [ONYXKEYS.IS_SIDEBAR_LOADED]: false,
     },
     registerStorageEventListener: (onStorageEvent) => {
         listenToStorageEvents(onStorageEvent);
diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js
index ded87710b8e1..71b269290712 100755
--- a/src/ONYXKEYS.js
+++ b/src/ONYXKEYS.js
@@ -16,6 +16,9 @@ export default {
     // Boolean flag set whenever we are waiting for the reconnection callbacks to finish.
     IS_LOADING_AFTER_RECONNECT: 'isLoadingAfterReconnect',
 
+    // Boolean flag set whenever the sidebar has loaded
+    IS_SIDEBAR_LOADED: 'isSidebarLoaded',
+
     NETWORK_REQUEST_QUEUE: 'networkRequestQueue',
 
     // What the active route is for our navigator. Global route that determines what views to display.
diff --git a/src/components/OptionsList.js b/src/components/OptionsList.js
index dbb6e968da42..efd01ea6a5f1 100644
--- a/src/components/OptionsList.js
+++ b/src/components/OptionsList.js
@@ -78,6 +78,9 @@ const propTypes = {
 
     /** Whether to disable the interactivity of the list's option row(s) */
     disableRowInteractivity: PropTypes.bool,
+
+    /** Callback to execute when the SectionList lays out */
+    onLayout: PropTypes.func,
 };
 
 const defaultProps = {
@@ -99,6 +102,7 @@ const defaultProps = {
     showTitleTooltip: false,
     optionMode: undefined,
     disableRowInteractivity: false,
+    onLayout: undefined,
 };
 
 class OptionsList extends Component {
@@ -228,6 +232,10 @@ class OptionsList extends Component {
                     renderItem={this.renderItem}
                     renderSectionHeader={this.renderSectionHeader}
                     extraData={this.props.focusedIndex}
+                    initialNumToRender={5}
+                    maxToRenderPerBatch={5}
+                    windowSize={5}
+                    onLayout={this.props.onLayout}
                 />
             </View>
         );
diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js
index 5ba802e792bf..099960f2c44e 100644
--- a/src/libs/Navigation/AppNavigator/AuthScreens.js
+++ b/src/libs/Navigation/AppNavigator/AuthScreens.js
@@ -25,6 +25,7 @@ import {fetchCountryCodeByRequestIP} from '../../actions/GeoLocation';
 import KeyboardShortcut from '../../KeyboardShortcut';
 import Navigation from '../Navigation';
 import * as User from '../../actions/User';
+import * as App from '../../actions/App';
 import {setModalVisibility} from '../../actions/Modal';
 import NameValuePair from '../../actions/NameValuePair';
 import {getPolicySummaries, getPolicyList} from '../../actions/Policy';
@@ -151,18 +152,21 @@ class AuthScreens extends React.Component {
         });
 
         // Fetch some data we need on initialization
-        NameValuePair.get(CONST.NVP.PRIORITY_MODE, ONYXKEYS.NVP_PRIORITY_MODE, 'default');
-        NameValuePair.get(CONST.NVP.PREFERRED_LOCALE, ONYXKEYS.NVP_PREFERRED_LOCALE, 'en');
-        PersonalDetails.fetchPersonalDetails();
-        User.getUserDetails();
         User.getBetas();
-        User.getDomainInfo();
-        PersonalDetails.fetchLocalCurrency();
         fetchAllReports(true, true);
-        fetchCountryCodeByRequestIP();
-        UnreadIndicatorUpdater.listenForReportChanges();
+        PersonalDetails.fetchPersonalDetails();
+        NameValuePair.get(CONST.NVP.PRIORITY_MODE, ONYXKEYS.NVP_PRIORITY_MODE, 'default');
 
-        loadPoliciesBehindBeta(this.props.betas);
+        // Defer less critical API requests until after the sidebar loads
+        App.onSidebarLoaded(() => {
+            NameValuePair.get(CONST.NVP.PREFERRED_LOCALE, ONYXKEYS.NVP_PREFERRED_LOCALE, 'en');
+            User.getUserDetails();
+            User.getDomainInfo();
+            PersonalDetails.fetchLocalCurrency();
+            fetchCountryCodeByRequestIP();
+            UnreadIndicatorUpdater.listenForReportChanges();
+            loadPoliciesBehindBeta(this.props.betas);
+        });
 
         // Refresh the personal details, timezone and betas every 30 minutes
         // There is no pusher event that sends updated personal details data yet
diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js
index 9635948348cd..ea8afb71e9d3 100644
--- a/src/libs/actions/App.js
+++ b/src/libs/actions/App.js
@@ -2,6 +2,15 @@ import Onyx from 'react-native-onyx';
 import ONYXKEYS from '../../ONYXKEYS';
 import * as API from '../API';
 
+let isSidebarLoaded;
+let sidebarLoadedCallback;
+
+Onyx.connect({
+    key: ONYXKEYS.IS_SIDEBAR_LOADED,
+    callback: val => isSidebarLoaded = val,
+    initWithStoredValues: false,
+});
+
 /**
  * @param {String} url
  */
@@ -17,7 +26,22 @@ function setLocale(locale) {
     Onyx.merge(ONYXKEYS.NVP_PREFERRED_LOCALE, locale);
 }
 
+function onSidebarLoaded(callback) {
+    sidebarLoadedCallback = callback;
+}
+
+function setSidebarLoaded() {
+    if (isSidebarLoaded) {
+        return;
+    }
+
+    sidebarLoadedCallback();
+    Onyx.set(ONYXKEYS.IS_SIDEBAR_LOADED, true);
+}
+
 export {
     setCurrentURL,
     setLocale,
+    setSidebarLoaded,
+    onSidebarLoaded,
 };
diff --git a/src/libs/actions/Timing.js b/src/libs/actions/Timing.js
index 4d1eae31a36f..e995eedd481c 100644
--- a/src/libs/actions/Timing.js
+++ b/src/libs/actions/Timing.js
@@ -27,11 +27,14 @@ function end(eventName, secondaryName = '') {
 
         console.debug(`Timing:${grafanaEventName}`, eventTime);
 
-        Graphite_Timer({
-            name: grafanaEventName,
-            value: eventTime,
-            platform: `${getPlatform()}`,
-        });
+        // eslint-disable-next-line no-undef
+        if (!__DEV__) {
+            Graphite_Timer({
+                name: grafanaEventName,
+                value: eventTime,
+                platform: `${getPlatform()}`,
+            });
+        }
 
         delete timestampData[eventName];
     }
diff --git a/src/pages/home/report/ReportView.js b/src/pages/home/report/ReportView.js
index 8481be201ed8..a4049f1b3f1c 100644
--- a/src/pages/home/report/ReportView.js
+++ b/src/pages/home/report/ReportView.js
@@ -21,29 +21,39 @@ const propTypes = {
     session: PropTypes.shape({
         shouldShowComposeInput: PropTypes.bool,
     }),
+
+    /** Used to defer rendering the report view until after the sidebar has loaded */
+    isSidebarLoaded: PropTypes.bool,
 };
 
 const defaultProps = {
     session: {
         shouldShowComposeInput: true,
     },
+    isSidebarLoaded: false,
 };
 
-const ReportView = ({reportID, session}) => (
-    <View nativeID={CONST.REPORT.DROP_NATIVE_ID} key={reportID} style={[styles.flex1, styles.justifyContentEnd]}>
-        <ReportActionsView reportID={reportID} />
-
-        {session.shouldShowComposeInput && (
-            <SwipeableView onSwipeDown={() => Keyboard.dismiss()}>
-                <ReportActionCompose
-                    onSubmit={text => addAction(reportID, text)}
-                    reportID={reportID}
-                />
-            </SwipeableView>
-        )}
-        <KeyboardSpacer />
-    </View>
-);
+const ReportView = ({reportID, session, isSidebarLoaded}) => {
+    if (!isSidebarLoaded) {
+        return null;
+    }
+
+    return (
+        <View nativeID={CONST.REPORT.DROP_NATIVE_ID} key={reportID} style={[styles.flex1, styles.justifyContentEnd]}>
+            <ReportActionsView reportID={reportID} />
+
+            {session.shouldShowComposeInput && (
+                <SwipeableView onSwipeDown={() => Keyboard.dismiss()}>
+                    <ReportActionCompose
+                        onSubmit={text => addAction(reportID, text)}
+                        reportID={reportID}
+                    />
+                </SwipeableView>
+            )}
+            <KeyboardSpacer />
+        </View>
+    );
+};
 
 ReportView.propTypes = propTypes;
 ReportView.defaultProps = defaultProps;
@@ -52,4 +62,7 @@ export default withOnyx({
     session: {
         key: ONYXKEYS.SESSION,
     },
+    isSidebarLoaded: {
+        key: ONYXKEYS.IS_SIDEBAR_LOADED,
+    },
 })(ReportView);
diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js
index 48a9c30b842e..fa8d8b9b26f3 100644
--- a/src/pages/home/sidebar/SidebarLinks.js
+++ b/src/pages/home/sidebar/SidebarLinks.js
@@ -20,6 +20,7 @@ import CONST from '../../../CONST';
 import {participantPropTypes} from './optionPropTypes';
 import themeColors from '../../../styles/themes/default';
 import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
+import Timing from '../../../libs/actions/Timing';
 
 
 const propTypes = {
@@ -179,6 +180,9 @@ class SidebarLinks extends React.Component {
                     showTitleTooltip
                     disableFocusOptions={this.props.isSmallScreenWidth}
                     optionMode={this.props.priorityMode === CONST.PRIORITY_MODE.GSD ? 'compact' : 'default'}
+                    onLayout={() => {
+                        Timing.end(CONST.TIMING.SIDEBAR_LOADED);
+                    }}
                 />
                 <KeyboardSpacer />
             </View>
diff --git a/src/pages/home/sidebar/SidebarScreen.js b/src/pages/home/sidebar/SidebarScreen.js
index fcfd60c59950..a3df1741dce7 100755
--- a/src/pages/home/sidebar/SidebarScreen.js
+++ b/src/pages/home/sidebar/SidebarScreen.js
@@ -47,6 +47,10 @@ class SidebarScreen extends Component {
         };
     }
 
+    componentDidMount() {
+        Timing.start(CONST.TIMING.SIDEBAR_LOADED);
+    }
+
     /**
      * Method called when a Create Menu item is selected.
      */

From f72f60ee7eac08e7b94cf69850134d64d59dd155 Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Mon, 26 Jul 2021 10:57:48 -1000
Subject: [PATCH 49/79] add Firebase lib

---
 src/libs/Firebase/index.js        |  6 ++++++
 src/libs/Firebase/index.native.js | 34 +++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)
 create mode 100644 src/libs/Firebase/index.js
 create mode 100644 src/libs/Firebase/index.native.js

diff --git a/src/libs/Firebase/index.js b/src/libs/Firebase/index.js
new file mode 100644
index 000000000000..a8c7a34f869f
--- /dev/null
+++ b/src/libs/Firebase/index.js
@@ -0,0 +1,6 @@
+/** Web does not use Firebase for performance tracing */
+
+export default {
+    startTrace() {},
+    stopTrace() {},
+};
diff --git a/src/libs/Firebase/index.native.js b/src/libs/Firebase/index.native.js
new file mode 100644
index 000000000000..6754074931b4
--- /dev/null
+++ b/src/libs/Firebase/index.native.js
@@ -0,0 +1,34 @@
+import perf from '@react-native-firebase/perf';
+
+const traceMap = {};
+
+/**
+ * @param {String} customEventName
+ */
+function startTrace(customEventName) {
+    if (traceMap[customEventName]) {
+        console.debug('[Firebase] Trace with this name has already started');
+        return;
+    }
+
+    perf().startTrace(customEventName)
+        .then(trace => traceMap[customEventName] = trace);
+}
+
+/**
+ * @param {String} customEventName
+ */
+function stopTrace(customEventName) {
+    const trace = traceMap[customEventName];
+    if (!trace) {
+        console.debug('[Firebase] Could not find trace');
+    }
+
+    trace.stop();
+    delete traceMap[customEventName];
+}
+
+export default {
+    startTrace,
+    stopTrace,
+};

From 65abdc1c92b982389743b1e8b41a5b53584777ea Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Mon, 26 Jul 2021 11:13:56 -1000
Subject: [PATCH 50/79] Setup firebase trace

---
 src/libs/Firebase/index.native.js       | 16 ++++++++++++++--
 src/libs/actions/App.js                 |  3 +++
 src/libs/actions/Timing.js              | 17 ++++++++++-------
 src/pages/home/sidebar/SidebarLinks.js  |  7 ++-----
 src/pages/home/sidebar/SidebarScreen.js |  3 ++-
 5 files changed, 31 insertions(+), 15 deletions(-)

diff --git a/src/libs/Firebase/index.native.js b/src/libs/Firebase/index.native.js
index 6754074931b4..f779df0d47a8 100644
--- a/src/libs/Firebase/index.native.js
+++ b/src/libs/Firebase/index.native.js
@@ -1,4 +1,5 @@
 import perf from '@react-native-firebase/perf';
+import {isDevelopment} from '../Environment/Environment';
 
 const traceMap = {};
 
@@ -6,19 +7,30 @@ const traceMap = {};
  * @param {String} customEventName
  */
 function startTrace(customEventName) {
+    if (isDevelopment()) {
+        return;
+    }
+
     if (traceMap[customEventName]) {
         console.debug('[Firebase] Trace with this name has already started');
         return;
     }
 
-    perf().startTrace(customEventName)
-        .then(trace => traceMap[customEventName] = trace);
+    perf()
+        .then((trace) => {
+            traceMap[customEventName] = trace;
+            trace.startTrace(customEventName);
+        });
 }
 
 /**
  * @param {String} customEventName
  */
 function stopTrace(customEventName) {
+    if (isDevelopment()) {
+        return;
+    }
+
     const trace = traceMap[customEventName];
     if (!trace) {
         console.debug('[Firebase] Could not find trace');
diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js
index ea8afb71e9d3..eb53924b9bc9 100644
--- a/src/libs/actions/App.js
+++ b/src/libs/actions/App.js
@@ -1,6 +1,8 @@
 import Onyx from 'react-native-onyx';
 import ONYXKEYS from '../../ONYXKEYS';
 import * as API from '../API';
+import Firebase from '../Firebase';
+import CONST from '../../CONST';
 
 let isSidebarLoaded;
 let sidebarLoadedCallback;
@@ -37,6 +39,7 @@ function setSidebarLoaded() {
 
     sidebarLoadedCallback();
     Onyx.set(ONYXKEYS.IS_SIDEBAR_LOADED, true);
+    Firebase.stopTrace(CONST.TIMING.SIDEBAR_LOADED);
 }
 
 export {
diff --git a/src/libs/actions/Timing.js b/src/libs/actions/Timing.js
index e995eedd481c..a0cbae149360 100644
--- a/src/libs/actions/Timing.js
+++ b/src/libs/actions/Timing.js
@@ -1,5 +1,6 @@
 import getPlatform from '../getPlatform';
 import {Graphite_Timer} from '../API';
+import {isDevelopment} from '../Environment/Environment';
 
 let timestampData = {};
 
@@ -26,17 +27,19 @@ function end(eventName, secondaryName = '') {
             : `expensify.cash.${eventName}`;
 
         console.debug(`Timing:${grafanaEventName}`, eventTime);
+        delete timestampData[eventName];
 
         // eslint-disable-next-line no-undef
-        if (!__DEV__) {
-            Graphite_Timer({
-                name: grafanaEventName,
-                value: eventTime,
-                platform: `${getPlatform()}`,
-            });
+        if (isDevelopment()) {
+            // Don't create traces on dev as this will mess up the accuracy of data in release builds of the app
+            return;
         }
 
-        delete timestampData[eventName];
+        Graphite_Timer({
+            name: grafanaEventName,
+            value: eventTime,
+            platform: `${getPlatform()}`,
+        });
     }
 }
 
diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js
index fa8d8b9b26f3..d846b67e9e29 100644
--- a/src/pages/home/sidebar/SidebarLinks.js
+++ b/src/pages/home/sidebar/SidebarLinks.js
@@ -20,8 +20,7 @@ import CONST from '../../../CONST';
 import {participantPropTypes} from './optionPropTypes';
 import themeColors from '../../../styles/themes/default';
 import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
-import Timing from '../../../libs/actions/Timing';
-
+import * as App from '../../../libs/actions/App';
 
 const propTypes = {
     /** Toggles the navigation menu open and closed */
@@ -180,9 +179,7 @@ class SidebarLinks extends React.Component {
                     showTitleTooltip
                     disableFocusOptions={this.props.isSmallScreenWidth}
                     optionMode={this.props.priorityMode === CONST.PRIORITY_MODE.GSD ? 'compact' : 'default'}
-                    onLayout={() => {
-                        Timing.end(CONST.TIMING.SIDEBAR_LOADED);
-                    }}
+                    onLayout={App.setSidebarLoaded}
                 />
                 <KeyboardSpacer />
             </View>
diff --git a/src/pages/home/sidebar/SidebarScreen.js b/src/pages/home/sidebar/SidebarScreen.js
index a3df1741dce7..b4f9dabeb2ed 100755
--- a/src/pages/home/sidebar/SidebarScreen.js
+++ b/src/pages/home/sidebar/SidebarScreen.js
@@ -23,6 +23,7 @@ import {
 } from '../../../components/Icon/Expensicons';
 import Permissions from '../../../libs/Permissions';
 import ONYXKEYS from '../../../ONYXKEYS';
+import Firebase from '../../../libs/Firebase';
 
 const propTypes = {
     /** Beta features list */
@@ -48,7 +49,7 @@ class SidebarScreen extends Component {
     }
 
     componentDidMount() {
-        Timing.start(CONST.TIMING.SIDEBAR_LOADED);
+        Firebase.startTrace(CONST.TIMING.SIDEBAR_LOADED);
     }
 
     /**

From cdb4c88c5be8a723998d4dff731168ac4d5473f7 Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Mon, 26 Jul 2021 11:45:09 -1000
Subject: [PATCH 51/79] fix firebase trace

---
 src/libs/Firebase/index.native.js | 7 ++-----
 src/libs/actions/App.js           | 5 ++++-
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/libs/Firebase/index.native.js b/src/libs/Firebase/index.native.js
index f779df0d47a8..f4db4ca77961 100644
--- a/src/libs/Firebase/index.native.js
+++ b/src/libs/Firebase/index.native.js
@@ -16,11 +16,8 @@ function startTrace(customEventName) {
         return;
     }
 
-    perf()
-        .then((trace) => {
-            traceMap[customEventName] = trace;
-            trace.startTrace(customEventName);
-        });
+    perf().startTrace(customEventName)
+        .then(trace => traceMap[customEventName] = trace);
 }
 
 /**
diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js
index eb53924b9bc9..cb20fa05872c 100644
--- a/src/libs/actions/App.js
+++ b/src/libs/actions/App.js
@@ -37,7 +37,10 @@ function setSidebarLoaded() {
         return;
     }
 
-    sidebarLoadedCallback();
+    if (sidebarLoadedCallback) {
+        sidebarLoadedCallback();
+    }
+
     Onyx.set(ONYXKEYS.IS_SIDEBAR_LOADED, true);
     Firebase.stopTrace(CONST.TIMING.SIDEBAR_LOADED);
 }

From 9df5a0b936fe3bc25c80bccc33f872e201b652f5 Mon Sep 17 00:00:00 2001
From: Yuwen Memon <yuwen@expensify.com>
Date: Mon, 26 Jul 2021 15:44:05 -0700
Subject: [PATCH 52/79] Make sure we get state/status from the right variable
 name when we get chat report name

---
 src/libs/actions/Report.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js
index 94347c7608cd..09e9875d19e4 100644
--- a/src/libs/actions/Report.js
+++ b/src/libs/actions/Report.js
@@ -140,8 +140,8 @@ function getChatReportName(fullReport, chatType) {
     if (isDefaultRoom({chatType})) {
         return `#${fullReport.reportName}${(isArchivedRoom({
             chatType,
-            stateNum: fullReport.stateNum,
-            statusNum: fullReport.reportStatus,
+            stateNum: fullReport.state,
+            statusNum: fullReport.status,
         })
             ? ` (${translateLocal('common.deleted')})`
             : '')}`;

From cebfaf59bf4103c9d9cb638c6fddd4449280c302 Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Mon, 26 Jul 2021 13:36:37 -1000
Subject: [PATCH 53/79] Defer more calls

---
 src/components/OptionsList.js                   |  5 +++++
 src/libs/Navigation/AppNavigator/AuthScreens.js |  8 ++++----
 src/pages/home/sidebar/SidebarLinks.js          | 13 +++++++++++++
 3 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/src/components/OptionsList.js b/src/components/OptionsList.js
index efd01ea6a5f1..bdcd33cf95df 100644
--- a/src/components/OptionsList.js
+++ b/src/components/OptionsList.js
@@ -81,6 +81,9 @@ const propTypes = {
 
     /** Callback to execute when the SectionList lays out */
     onLayout: PropTypes.func,
+
+    /** Peformance optimization used when rows have a fixed height */
+    getItemLayout: PropTypes.func,
 };
 
 const defaultProps = {
@@ -103,6 +106,7 @@ const defaultProps = {
     optionMode: undefined,
     disableRowInteractivity: false,
     onLayout: undefined,
+    getItemLayout: undefined,
 };
 
 class OptionsList extends Component {
@@ -235,6 +239,7 @@ class OptionsList extends Component {
                     initialNumToRender={5}
                     maxToRenderPerBatch={5}
                     windowSize={5}
+                    getItemLayout={this.props.getItemLayout}
                     onLayout={this.props.onLayout}
                 />
             </View>
diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js
index 099960f2c44e..76a9e241f5b0 100644
--- a/src/libs/Navigation/AppNavigator/AuthScreens.js
+++ b/src/libs/Navigation/AppNavigator/AuthScreens.js
@@ -151,14 +151,14 @@ class AuthScreens extends React.Component {
             User.subscribeToUserEvents();
         });
 
-        // Fetch some data we need on initialization
-        User.getBetas();
+        // Fetch main data we need on initialization for the LHN
         fetchAllReports(true, true);
-        PersonalDetails.fetchPersonalDetails();
-        NameValuePair.get(CONST.NVP.PRIORITY_MODE, ONYXKEYS.NVP_PRIORITY_MODE, 'default');
 
         // Defer less critical API requests until after the sidebar loads
         App.onSidebarLoaded(() => {
+            User.getBetas();
+            PersonalDetails.fetchPersonalDetails();
+            NameValuePair.get(CONST.NVP.PRIORITY_MODE, ONYXKEYS.NVP_PRIORITY_MODE, 'default');
             NameValuePair.get(CONST.NVP.PREFERRED_LOCALE, ONYXKEYS.NVP_PREFERRED_LOCALE, 'en');
             User.getUserDetails();
             User.getDomainInfo();
diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js
index d846b67e9e29..d99f142b1cc4 100644
--- a/src/pages/home/sidebar/SidebarLinks.js
+++ b/src/pages/home/sidebar/SidebarLinks.js
@@ -94,6 +94,18 @@ const defaultProps = {
 };
 
 class SidebarLinks extends React.Component {
+    constructor(props) {
+        super(props);
+        this.getItemLayout = this.getItemLayout.bind(this);
+    }
+
+    getItemLayout(data, index) {
+        const length = this.props.priorityMode === CONST.PRIORITY_MODE.GSD ? 52 : 64;
+        return (
+            {length, offset: length * index, index}
+        );
+    }
+
     showSearchPage() {
         Navigation.navigate(ROUTES.SEARCH);
     }
@@ -180,6 +192,7 @@ class SidebarLinks extends React.Component {
                     disableFocusOptions={this.props.isSmallScreenWidth}
                     optionMode={this.props.priorityMode === CONST.PRIORITY_MODE.GSD ? 'compact' : 'default'}
                     onLayout={App.setSidebarLoaded}
+                    getItemLayout={this.getItemLayout}
                 />
                 <KeyboardSpacer />
             </View>

From 4a539afac93d96c7dfb01a2715e9c0c614256820 Mon Sep 17 00:00:00 2001
From: Rafael <rdjuric@pm.me>
Date: Mon, 26 Jul 2021 20:42:41 -0300
Subject: [PATCH 54/79] changes to participantLocalTime

---
 src/pages/home/report/ParticipantLocalTime.js | 9 +++++++--
 src/pages/home/report/ReportActionCompose.js  | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/src/pages/home/report/ParticipantLocalTime.js b/src/pages/home/report/ParticipantLocalTime.js
index 595ccb40ae29..cfd902da46e4 100644
--- a/src/pages/home/report/ParticipantLocalTime.js
+++ b/src/pages/home/report/ParticipantLocalTime.js
@@ -42,9 +42,14 @@ class ParticipantLocalTime extends React.Component {
 
     getParticipantLocalTime() {
         const reportRecipientTimezone = lodashGet(this.props.participant, 'timezone', {});
-        return moment().tz(reportRecipientTimezone.selected).format('LT');
-    }
+        const reportRecipientDay = moment().tz(reportRecipientTimezone.selected).format('dddd');
+        const currentUserDay = moment().tz(this.props.currentUserTimezone.selected).format('dddd');
 
+        if (reportRecipientDay !== currentUserDay) {
+            return `${moment().tz(reportRecipientTimezone.selected).format('LT')} ${reportRecipientDay}`;
+        }
+        return `${moment().tz(reportRecipientTimezone.selected).format('LT')}`;
+    }
 
     render() {
         // Moment.format does not return AM or PM values immediately.
diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js
index d0bd5fef9e77..f67ea162dafd 100755
--- a/src/pages/home/report/ReportActionCompose.js
+++ b/src/pages/home/report/ReportActionCompose.js
@@ -452,7 +452,7 @@ class ReportActionCompose extends React.Component {
             ]}
             >
                 {shouldShowReportRecipientLocalTime
-                    && <ParticipantLocalTime participant={reportRecipient} />}
+                    && <ParticipantLocalTime participant={reportRecipient} currentUserTimezone={currentUserTimezone} />}
                 <View style={[
                     (this.state.isFocused || this.state.isDraggingOver)
                         ? styles.chatItemComposeBoxFocusedColor

From 1bda2b74c31f89df6292032f0dd0893c9f4b2a07 Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Mon, 26 Jul 2021 14:26:48 -1000
Subject: [PATCH 55/79] hmm

---
 src/libs/Firebase/index.native.js             |  8 ++++
 .../Navigation/AppNavigator/AuthScreens.js    |  6 +--
 src/pages/home/ReportScreen.js                | 13 +++++-
 src/pages/home/report/ReportView.js           | 43 +++++++------------
 src/pages/home/sidebar/SidebarLinks.js        | 11 +++--
 5 files changed, 45 insertions(+), 36 deletions(-)

diff --git a/src/libs/Firebase/index.native.js b/src/libs/Firebase/index.native.js
index f4db4ca77961..abe34e68c302 100644
--- a/src/libs/Firebase/index.native.js
+++ b/src/libs/Firebase/index.native.js
@@ -3,10 +3,15 @@ import {isDevelopment} from '../Environment/Environment';
 
 const traceMap = {};
 
+let start;
+let end;
+
 /**
  * @param {String} customEventName
  */
 function startTrace(customEventName) {
+    start = global.performance.now();
+
     if (isDevelopment()) {
         return;
     }
@@ -24,6 +29,9 @@ function startTrace(customEventName) {
  * @param {String} customEventName
  */
 function stopTrace(customEventName) {
+    end = global.performance.now();
+    console.debug('sidebar_loaded in ', end - start, ' ms');
+
     if (isDevelopment()) {
         return;
     }
diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js
index 76a9e241f5b0..9b6c0fbb5716 100644
--- a/src/libs/Navigation/AppNavigator/AuthScreens.js
+++ b/src/libs/Navigation/AppNavigator/AuthScreens.js
@@ -152,13 +152,13 @@ class AuthScreens extends React.Component {
         });
 
         // Fetch main data we need on initialization for the LHN
+        User.getBetas();
         fetchAllReports(true, true);
+        PersonalDetails.fetchPersonalDetails();
+        NameValuePair.get(CONST.NVP.PRIORITY_MODE, ONYXKEYS.NVP_PRIORITY_MODE, 'default');
 
         // Defer less critical API requests until after the sidebar loads
         App.onSidebarLoaded(() => {
-            User.getBetas();
-            PersonalDetails.fetchPersonalDetails();
-            NameValuePair.get(CONST.NVP.PRIORITY_MODE, ONYXKEYS.NVP_PRIORITY_MODE, 'default');
             NameValuePair.get(CONST.NVP.PREFERRED_LOCALE, ONYXKEYS.NVP_PREFERRED_LOCALE, 'en');
             User.getUserDetails();
             User.getDomainInfo();
diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js
index ae87bc4b6921..ad66c0e839a4 100644
--- a/src/pages/home/ReportScreen.js
+++ b/src/pages/home/ReportScreen.js
@@ -1,4 +1,5 @@
 import React from 'react';
+import {withOnyx} from 'react-native-onyx';
 import PropTypes from 'prop-types';
 import styles from '../../styles/styles';
 import ReportView from './report/ReportView';
@@ -8,6 +9,7 @@ import Navigation from '../../libs/Navigation/Navigation';
 import ROUTES from '../../ROUTES';
 import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator';
 import {updateCurrentlyViewedReportID} from '../../libs/actions/Report';
+import ONYXKEYS from '../../ONYXKEYS';
 
 const propTypes = {
     /** Navigation route context info provided by react navigation */
@@ -84,6 +86,11 @@ class ReportScreen extends React.Component {
     }
 
     render() {
+        return null;
+        if (!this.props.isSidebarLoaded) {
+            return null;
+        }
+
         return (
             <ScreenWrapper style={[styles.appContent, styles.flex1]}>
                 <HeaderView
@@ -100,4 +107,8 @@ class ReportScreen extends React.Component {
 }
 
 ReportScreen.propTypes = propTypes;
-export default ReportScreen;
+export default withOnyx({
+    isSidebarLoaded: {
+        key: ONYXKEYS.IS_SIDEBAR_LOADED,
+    },
+})(ReportScreen);
diff --git a/src/pages/home/report/ReportView.js b/src/pages/home/report/ReportView.js
index a4049f1b3f1c..8481be201ed8 100644
--- a/src/pages/home/report/ReportView.js
+++ b/src/pages/home/report/ReportView.js
@@ -21,39 +21,29 @@ const propTypes = {
     session: PropTypes.shape({
         shouldShowComposeInput: PropTypes.bool,
     }),
-
-    /** Used to defer rendering the report view until after the sidebar has loaded */
-    isSidebarLoaded: PropTypes.bool,
 };
 
 const defaultProps = {
     session: {
         shouldShowComposeInput: true,
     },
-    isSidebarLoaded: false,
 };
 
-const ReportView = ({reportID, session, isSidebarLoaded}) => {
-    if (!isSidebarLoaded) {
-        return null;
-    }
-
-    return (
-        <View nativeID={CONST.REPORT.DROP_NATIVE_ID} key={reportID} style={[styles.flex1, styles.justifyContentEnd]}>
-            <ReportActionsView reportID={reportID} />
-
-            {session.shouldShowComposeInput && (
-                <SwipeableView onSwipeDown={() => Keyboard.dismiss()}>
-                    <ReportActionCompose
-                        onSubmit={text => addAction(reportID, text)}
-                        reportID={reportID}
-                    />
-                </SwipeableView>
-            )}
-            <KeyboardSpacer />
-        </View>
-    );
-};
+const ReportView = ({reportID, session}) => (
+    <View nativeID={CONST.REPORT.DROP_NATIVE_ID} key={reportID} style={[styles.flex1, styles.justifyContentEnd]}>
+        <ReportActionsView reportID={reportID} />
+
+        {session.shouldShowComposeInput && (
+            <SwipeableView onSwipeDown={() => Keyboard.dismiss()}>
+                <ReportActionCompose
+                    onSubmit={text => addAction(reportID, text)}
+                    reportID={reportID}
+                />
+            </SwipeableView>
+        )}
+        <KeyboardSpacer />
+    </View>
+);
 
 ReportView.propTypes = propTypes;
 ReportView.defaultProps = defaultProps;
@@ -62,7 +52,4 @@ export default withOnyx({
     session: {
         key: ONYXKEYS.SESSION,
     },
-    isSidebarLoaded: {
-        key: ONYXKEYS.IS_SIDEBAR_LOADED,
-    },
 })(ReportView);
diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js
index d99f142b1cc4..52af728d02ed 100644
--- a/src/pages/home/sidebar/SidebarLinks.js
+++ b/src/pages/home/sidebar/SidebarLinks.js
@@ -97,6 +97,12 @@ class SidebarLinks extends React.Component {
     constructor(props) {
         super(props);
         this.getItemLayout = this.getItemLayout.bind(this);
+        this.onSelectRow = this.onSelectRow.bind(this);
+    }
+
+    onSelectRow(option) {
+        Navigation.navigate(ROUTES.getReportRoute(option.reportID));
+        this.props.onLinkClick();
     }
 
     getItemLayout(data, index) {
@@ -182,10 +188,7 @@ class SidebarLinks extends React.Component {
                     focusedIndex={_.findIndex(recentReports, (
                         option => option.reportID === activeReportID
                     ))}
-                    onSelectRow={(option) => {
-                        Navigation.navigate(ROUTES.getReportRoute(option.reportID));
-                        this.props.onLinkClick();
-                    }}
+                    onSelectRow={this.onSelectRow}
                     optionBackgroundColor={themeColors.sidebar}
                     hideSectionHeaders
                     showTitleTooltip

From 2a2b7b7d836ffe085538303244082d5abfb1847a Mon Sep 17 00:00:00 2001
From: Hamza El Aoutar <elaoutarhamza@gmail.com>
Date: Tue, 27 Jul 2021 01:33:03 +0100
Subject: [PATCH 56/79] Fix preferences regression

---
 src/pages/settings/AboutPage.js | 2 +-
 src/styles/styles.js            | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/pages/settings/AboutPage.js b/src/pages/settings/AboutPage.js
index f0e398209c3a..03706a6d0cdf 100644
--- a/src/pages/settings/AboutPage.js
+++ b/src/pages/settings/AboutPage.js
@@ -88,7 +88,7 @@ const AboutPage = ({translate, session}) => {
             >
                 <View style={[styles.flex1]}>
                     <View style={styles.pageWrapper}>
-                        <View style={[styles.settingsPageBody, styles.mb6]}>
+                        <View style={[styles.settingsPageBody, styles.mb6, styles.alignItemsCenter]}>
                             <Logo height={80} width={80} />
                             <Text
                                 style={[
diff --git a/src/styles/styles.js b/src/styles/styles.js
index 1810fc0d5abf..4d4b6aacd523 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -1405,7 +1405,6 @@ const styles = {
     settingsPageBody: {
         width: '100%',
         justifyContent: 'space-around',
-        alignItems: 'center',
     },
 
     settingsPageColumn: {

From feba29a1a1e31e9e40b34abe6a63eed4302ac185 Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Mon, 26 Jul 2021 15:08:36 -1000
Subject: [PATCH 57/79] optimize sidebar links

---
 src/pages/home/ReportScreen.js         | 10 +++++++++-
 src/pages/home/sidebar/SidebarLinks.js |  4 ++++
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js
index ad66c0e839a4..dfe7cea28c1b 100644
--- a/src/pages/home/ReportScreen.js
+++ b/src/pages/home/ReportScreen.js
@@ -20,6 +20,13 @@ const propTypes = {
             reportID: PropTypes.string,
         }).isRequired,
     }).isRequired,
+
+    /** Tells us if the sidebar has rendered */
+    isSidebarLoaded: PropTypes.bool,
+};
+
+const defaultProps = {
+    isSidebarLoaded: false,
 };
 
 class ReportScreen extends React.Component {
@@ -86,7 +93,6 @@ class ReportScreen extends React.Component {
     }
 
     render() {
-        return null;
         if (!this.props.isSidebarLoaded) {
             return null;
         }
@@ -107,6 +113,8 @@ class ReportScreen extends React.Component {
 }
 
 ReportScreen.propTypes = propTypes;
+ReportScreen.defaultProps = defaultProps;
+
 export default withOnyx({
     isSidebarLoaded: {
         key: ONYXKEYS.IS_SIDEBAR_LOADED,
diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js
index 52af728d02ed..e0de12b3d3e3 100644
--- a/src/pages/home/sidebar/SidebarLinks.js
+++ b/src/pages/home/sidebar/SidebarLinks.js
@@ -100,6 +100,10 @@ class SidebarLinks extends React.Component {
         this.onSelectRow = this.onSelectRow.bind(this);
     }
 
+    shouldComponentUpdate(prevProps) {
+        return _.isEqual(prevProps, this.props);
+    }
+
     onSelectRow(option) {
         Navigation.navigate(ROUTES.getReportRoute(option.reportID));
         this.props.onLinkClick();

From 44847b7db596983feffb67647c9f935439770bab Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Mon, 26 Jul 2021 15:25:49 -1000
Subject: [PATCH 58/79] remove some logs

---
 src/libs/Firebase/index.native.js   |  8 --------
 src/libs/NetworkConnection.js       |  4 ----
 src/libs/Pusher/pusher.js           |  1 -
 src/libs/PusherConnectionManager.js | 10 ----------
 4 files changed, 23 deletions(-)

diff --git a/src/libs/Firebase/index.native.js b/src/libs/Firebase/index.native.js
index abe34e68c302..f4db4ca77961 100644
--- a/src/libs/Firebase/index.native.js
+++ b/src/libs/Firebase/index.native.js
@@ -3,15 +3,10 @@ import {isDevelopment} from '../Environment/Environment';
 
 const traceMap = {};
 
-let start;
-let end;
-
 /**
  * @param {String} customEventName
  */
 function startTrace(customEventName) {
-    start = global.performance.now();
-
     if (isDevelopment()) {
         return;
     }
@@ -29,9 +24,6 @@ function startTrace(customEventName) {
  * @param {String} customEventName
  */
 function stopTrace(customEventName) {
-    end = global.performance.now();
-    console.debug('sidebar_loaded in ', end - start, ' ms');
-
     if (isDevelopment()) {
         return;
     }
diff --git a/src/libs/NetworkConnection.js b/src/libs/NetworkConnection.js
index cb104b18d868..6fcc4b767248 100644
--- a/src/libs/NetworkConnection.js
+++ b/src/libs/NetworkConnection.js
@@ -49,8 +49,6 @@ function setOfflineStatus(isCurrentlyOffline) {
  * `disconnected` event which takes about 10-15 seconds to emit.
  */
 function listenForReconnect() {
-    logInfo('[NetworkConnection] listenForReconnect called', true);
-
     unsubscribeFromAppState = AppStateMonitor.addBecameActiveListener(() => {
         triggerReconnectionCallbacks('app became active');
     });
@@ -58,7 +56,6 @@ function listenForReconnect() {
     // Subscribe to the state change event via NetInfo so we can update
     // whether a user has internet connectivity or not.
     unsubscribeFromNetInfo = NetInfo.addEventListener((state) => {
-        logInfo(`[NetworkConnection] NetInfo isConnected: ${state && state.isConnected}`);
         setOfflineStatus(!state.isConnected);
     });
 }
@@ -67,7 +64,6 @@ function listenForReconnect() {
  * Tear down the event listeners when we are finished with them.
  */
 function stopListeningForReconnect() {
-    logInfo('[NetworkConnection] stopListeningForReconnect called', true);
     if (unsubscribeFromNetInfo) {
         unsubscribeFromNetInfo();
         unsubscribeFromNetInfo = undefined;
diff --git a/src/libs/Pusher/pusher.js b/src/libs/Pusher/pusher.js
index e64644c1441d..1afbd7db49e3 100644
--- a/src/libs/Pusher/pusher.js
+++ b/src/libs/Pusher/pusher.js
@@ -193,7 +193,6 @@ function subscribe(
             most likely has been called before Pusher.init()`);
         }
 
-        console.debug('[Pusher] Attempting to subscribe to channel', true, {channelName, eventName});
         let channel = getChannel(channelName);
 
         if (!channel || !channel.subscribed) {
diff --git a/src/libs/PusherConnectionManager.js b/src/libs/PusherConnectionManager.js
index 164efb40d7b3..74d74359590b 100644
--- a/src/libs/PusherConnectionManager.js
+++ b/src/libs/PusherConnectionManager.js
@@ -7,7 +7,6 @@ import Log from './Log';
 // reconnect each time when we only need to reconnect once. This way, if an authToken is expired and we try to
 // subscribe to a bunch of channels at once we will only reauthenticate and force reconnect Pusher once.
 const reauthenticate = _.throttle(() => {
-    Log.info('[Pusher] Re-authenticating and then reconnecting', true);
     API.reauthenticate('Push_Authenticate')
         .then(() => Pusher.reconnect())
         .catch(() => {
@@ -27,8 +26,6 @@ function init() {
      */
     Pusher.registerCustomAuthorizer(channel => ({
         authorize: (socketID, callback) => {
-            Log.info('[PusherConnectionManager] Attempting to authorize Pusher', true);
-
             API.Push_Authenticate({
                 socket_id: socketID,
                 channel_name: channel.name,
@@ -44,7 +41,6 @@ function init() {
                         return;
                     }
 
-                    Log.info('[PusherConnectionManager] Pusher authenticated successfully', true);
                     callback(null, data);
                 })
                 .catch((error) => {
@@ -66,12 +62,6 @@ function init() {
                 Log.info('[PusherConnectionManager] error event', true, {error: data});
                 reauthenticate();
                 break;
-            case 'connected':
-                Log.info('[PusherConnectionManager] connected event', true);
-                break;
-            case 'disconnected':
-                Log.info('[PusherConnectionManager] disconnected event', true);
-                break;
             default:
                 break;
         }

From c3e6c4d2461f7e30f09f60bdd2fba621e285a1ec Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Mon, 26 Jul 2021 15:49:04 -1000
Subject: [PATCH 59/79] Use viewability to determine that the sidebar has
 rendered items on screen

---
 src/Expensify.js              |  9 ++++++++-
 src/components/OptionsList.js | 12 +++++++++++-
 2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/src/Expensify.js b/src/Expensify.js
index 0c942a9a3973..b6b4a9bd20f5 100644
--- a/src/Expensify.js
+++ b/src/Expensify.js
@@ -70,6 +70,9 @@ const propTypes = {
 
     /** Whether the initial data needed to render the app is ready */
     initialReportDataLoaded: PropTypes.bool,
+
+    /** Tells us if the sidebar has rendered */
+    isSidebarLoaded: PropTypes.bool,
 };
 
 const defaultProps = {
@@ -80,6 +83,7 @@ const defaultProps = {
     },
     updateAvailable: false,
     initialReportDataLoaded: false,
+    isSidebarLoaded: false,
 };
 
 class Expensify extends PureComponent {
@@ -135,7 +139,7 @@ class Expensify extends PureComponent {
             }
         }
 
-        if (this.getAuthToken() && this.props.initialReportDataLoaded) {
+        if (this.getAuthToken() && this.props.initialReportDataLoaded && this.props.isSidebarLoaded) {
             BootSplash.getVisibilityStatus()
                 .then((value) => {
                     if (value !== 'visible') {
@@ -196,4 +200,7 @@ export default withOnyx({
     initialReportDataLoaded: {
         key: ONYXKEYS.INITIAL_REPORT_DATA_LOADED,
     },
+    isSidebarLoaded: {
+        key: ONYXKEYS.IS_SIDEBAR_LOADED,
+    },
 })(Expensify);
diff --git a/src/components/OptionsList.js b/src/components/OptionsList.js
index bdcd33cf95df..38d8d2c86099 100644
--- a/src/components/OptionsList.js
+++ b/src/components/OptionsList.js
@@ -117,6 +117,8 @@ class OptionsList extends Component {
         this.renderSectionHeader = this.renderSectionHeader.bind(this);
         this.extractKey = this.extractKey.bind(this);
         this.onScrollToIndexFailed = this.onScrollToIndexFailed.bind(this);
+        this.viewabilityConfig = {viewAreaCoveragePercentThreshold: 95};
+        this.didLayout = false;
     }
 
     shouldComponentUpdate(nextProps) {
@@ -240,7 +242,15 @@ class OptionsList extends Component {
                     maxToRenderPerBatch={5}
                     windowSize={5}
                     getItemLayout={this.props.getItemLayout}
-                    onLayout={this.props.onLayout}
+                    viewabilityConfig={this.viewabilityConfig}
+                    onViewableItemsChanged={() => {
+                        if (this.didLayout) {
+                            return;
+                        }
+
+                        this.didLayout = true;
+                        this.props.onLayout();
+                    }}
                 />
             </View>
         );

From bf5fdc84849a93acec722ddffbd262fc26be862b Mon Sep 17 00:00:00 2001
From: OSBotify <runner@Mac-1627351717481.local>
Date: Tue, 27 Jul 2021 02:10:37 +0000
Subject: [PATCH 60/79] Update version to 1.0.80-1

---
 android/app/build.gradle          | 4 ++--
 ios/ExpensifyCash/Info.plist      | 2 +-
 ios/ExpensifyCashTests/Info.plist | 2 +-
 package-lock.json                 | 2 +-
 package.json                      | 2 +-
 5 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/android/app/build.gradle b/android/app/build.gradle
index f2c0450fd3cf..2d7aa0b46534 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -150,8 +150,8 @@ android {
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
         multiDexEnabled rootProject.ext.multiDexEnabled
-        versionCode 1001008000
-        versionName "1.0.80-0"
+        versionCode 1001008001
+        versionName "1.0.80-1"
     }
     splits {
         abi {
diff --git a/ios/ExpensifyCash/Info.plist b/ios/ExpensifyCash/Info.plist
index b533a5b493a0..c24b4495add5 100644
--- a/ios/ExpensifyCash/Info.plist
+++ b/ios/ExpensifyCash/Info.plist
@@ -30,7 +30,7 @@
 		</dict>
 	</array>
 	<key>CFBundleVersion</key>
-	<string>1.0.80.0</string>
+	<string>1.0.80.1</string>
 	<key>ITSAppUsesNonExemptEncryption</key>
 	<false/>
 	<key>LSApplicationQueriesSchemes</key>
diff --git a/ios/ExpensifyCashTests/Info.plist b/ios/ExpensifyCashTests/Info.plist
index d4a2205b9a43..237fd0793fc7 100644
--- a/ios/ExpensifyCashTests/Info.plist
+++ b/ios/ExpensifyCashTests/Info.plist
@@ -19,6 +19,6 @@
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>
-	<string>1.0.80.0</string>
+	<string>1.0.80.1</string>
 </dict>
 </plist>
diff --git a/package-lock.json b/package-lock.json
index 593979c5c69a..d093e4575523 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "expensify.cash",
-  "version": "1.0.80-0",
+  "version": "1.0.80-1",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/package.json b/package.json
index 690fd5f82d84..166318fe73bf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "expensify.cash",
-  "version": "1.0.80-0",
+  "version": "1.0.80-1",
   "author": "Expensify, Inc.",
   "homepage": "https://new.expensify.com",
   "description": "Expensify.cash is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",

From 85d0f73f12f6d4f01ad7eeac8daf10fcea4c7f4f Mon Sep 17 00:00:00 2001
From: OSBotify <runner@Mac-1627353635315.local>
Date: Tue, 27 Jul 2021 02:42:48 +0000
Subject: [PATCH 61/79] Update version to 1.0.80-2

---
 android/app/build.gradle          | 4 ++--
 ios/ExpensifyCash/Info.plist      | 2 +-
 ios/ExpensifyCashTests/Info.plist | 2 +-
 package-lock.json                 | 2 +-
 package.json                      | 2 +-
 5 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/android/app/build.gradle b/android/app/build.gradle
index 2d7aa0b46534..a3a6cd83df46 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -150,8 +150,8 @@ android {
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
         multiDexEnabled rootProject.ext.multiDexEnabled
-        versionCode 1001008001
-        versionName "1.0.80-1"
+        versionCode 1001008002
+        versionName "1.0.80-2"
     }
     splits {
         abi {
diff --git a/ios/ExpensifyCash/Info.plist b/ios/ExpensifyCash/Info.plist
index c24b4495add5..c69435b11314 100644
--- a/ios/ExpensifyCash/Info.plist
+++ b/ios/ExpensifyCash/Info.plist
@@ -30,7 +30,7 @@
 		</dict>
 	</array>
 	<key>CFBundleVersion</key>
-	<string>1.0.80.1</string>
+	<string>1.0.80.2</string>
 	<key>ITSAppUsesNonExemptEncryption</key>
 	<false/>
 	<key>LSApplicationQueriesSchemes</key>
diff --git a/ios/ExpensifyCashTests/Info.plist b/ios/ExpensifyCashTests/Info.plist
index 237fd0793fc7..ced27dd4ac02 100644
--- a/ios/ExpensifyCashTests/Info.plist
+++ b/ios/ExpensifyCashTests/Info.plist
@@ -19,6 +19,6 @@
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>
-	<string>1.0.80.1</string>
+	<string>1.0.80.2</string>
 </dict>
 </plist>
diff --git a/package-lock.json b/package-lock.json
index d093e4575523..b955b2077ef8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "expensify.cash",
-  "version": "1.0.80-1",
+  "version": "1.0.80-2",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/package.json b/package.json
index 166318fe73bf..868ff2d6e21b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "expensify.cash",
-  "version": "1.0.80-1",
+  "version": "1.0.80-2",
   "author": "Expensify, Inc.",
   "homepage": "https://new.expensify.com",
   "description": "Expensify.cash is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",

From 79d8bd205be0879d3914fe926eaced3629caa4a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Horus=20Lugo=20L=C3=B3pez?= <horus@expensify.com>
Date: Tue, 27 Jul 2021 10:20:01 +0200
Subject: [PATCH 62/79] Replace NEW! with the total count of unread messages

---
 src/libs/UnreadIndicatorUpdater/updateUnread/index.website.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/UnreadIndicatorUpdater/updateUnread/index.website.js b/src/libs/UnreadIndicatorUpdater/updateUnread/index.website.js
index aca9134a16ab..75f6d3f31d08 100644
--- a/src/libs/UnreadIndicatorUpdater/updateUnread/index.website.js
+++ b/src/libs/UnreadIndicatorUpdater/updateUnread/index.website.js
@@ -10,7 +10,7 @@ import CONFIG from '../../../CONFIG';
  */
 function updateUnread(totalCount) {
     const hasUnread = totalCount !== 0;
-    document.title = hasUnread ? `(NEW!) ${CONFIG.SITE_TITLE}` : CONFIG.SITE_TITLE;
+    document.title = hasUnread ? `(${totalCount}) ${CONFIG.SITE_TITLE}` : CONFIG.SITE_TITLE;
     document.getElementById('favicon').href = hasUnread ? CONFIG.FAVICON.UNREAD : CONFIG.FAVICON.DEFAULT;
 }
 

From 62bf8f58910423f9938c161f06bd658d0411e6f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Horus=20Lugo=20L=C3=B3pez?= <horus@expensify.com>
Date: Tue, 27 Jul 2021 10:56:26 +0200
Subject: [PATCH 63/79] Wait for the next tick to ensure that the unread
 indicator is set to 0 in the SignInPage component

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

diff --git a/src/pages/signin/SignInPage.js b/src/pages/signin/SignInPage.js
index dd12cdf992bb..7af84f558677 100644
--- a/src/pages/signin/SignInPage.js
+++ b/src/pages/signin/SignInPage.js
@@ -53,7 +53,8 @@ const defaultProps = {
 class SignInPage extends Component {
     componentDidMount() {
         // Always reset the unread counter to zero on this page
-        updateUnread(0);
+        // NOTE: We need to wait for the next tick to ensure that the unread indicator is updated
+        setTimeout(() => updateUnread(0), 0);
     }
 
     render() {

From 46976454c2b4b36b5141140de8db9bb7efdb3cfe Mon Sep 17 00:00:00 2001
From: Clem Dal Palu <clem@expensify.com>
Date: Tue, 27 Jul 2021 11:28:04 +0200
Subject: [PATCH 64/79] Do not use a company address for a personal requestor

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

diff --git a/src/pages/ReimbursementAccount/IdentityForm.js b/src/pages/ReimbursementAccount/IdentityForm.js
index ce0a0383f330..3122d5e6532b 100644
--- a/src/pages/ReimbursementAccount/IdentityForm.js
+++ b/src/pages/ReimbursementAccount/IdentityForm.js
@@ -96,7 +96,7 @@ const IdentityForm = ({
                 onChangeText={val => onFieldChange('ssnLast4', val)}
             />
             <TextInputWithLabel
-                label={translate('common.companyAddressNoPO')}
+                label={translate('common.addressNoPO')}
                 containerStyles={[styles.mt4]}
                 value={street}
                 onChangeText={val => onFieldChange('street', val)}

From 354d1a55b2191dbcc5000d4db40630bf6fc1d5db Mon Sep 17 00:00:00 2001
From: chiragsalian <chirag@expensify.com>
Date: Tue, 27 Jul 2021 09:21:36 -0700
Subject: [PATCH 65/79] style fix

---
 src/pages/ValidateLoginNewWorkspacePage.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/pages/ValidateLoginNewWorkspacePage.js b/src/pages/ValidateLoginNewWorkspacePage.js
index 864d742dd8b9..5a1b8ab1f1c8 100644
--- a/src/pages/ValidateLoginNewWorkspacePage.js
+++ b/src/pages/ValidateLoginNewWorkspacePage.js
@@ -10,7 +10,6 @@ import Navigation from '../libs/Navigation/Navigation';
 import ROUTES from '../ROUTES';
 import {continueSessionFromECom, setRedirectToWorkspaceNewAfterSignIn} from '../libs/actions/Session';
 
-
 const propTypes = {
     /* Onyx Props */
 

From ea3c03ce14013eae9946adce013f8bb4ee6c4e0e Mon Sep 17 00:00:00 2001
From: Amal Nazeem <amal@expensify.com>
Date: Tue, 27 Jul 2021 16:01:23 -0400
Subject: [PATCH 66/79] Add beta check to Workspaces page

---
 src/pages/workspace/WorkspaceCardPage.js | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/src/pages/workspace/WorkspaceCardPage.js b/src/pages/workspace/WorkspaceCardPage.js
index 98f18de11873..b77f05efce9f 100644
--- a/src/pages/workspace/WorkspaceCardPage.js
+++ b/src/pages/workspace/WorkspaceCardPage.js
@@ -20,12 +20,16 @@ import themeDefault from '../../styles/themes/default';
 import ROUTES from '../../ROUTES';
 import CONFIG from '../../CONFIG';
 import CONST from '../../CONST';
+import Permissions from '../../libs/Permissions';
 import HeroCardWebImage from '../../../assets/images/cascading-cards-web.svg';
 import HeroCardMobileImage from '../../../assets/images/cascading-cards-mobile.svg';
 
 const propTypes = {
     /* Onyx Props */
 
+    /** Beta features list */
+    betas: PropTypes.arrayOf(PropTypes.string).isRequired,
+
     /** The details about the user that is signed in */
     user: PropTypes.shape({
         /** Whether or not the user has public domain */
@@ -65,6 +69,7 @@ const publicLink = CONFIG.EXPENSIFY.URL_EXPENSIFY_COM + CONST.ADD_SECONDARY_LOGI
 const manageCardLink = CONFIG.EXPENSIFY.URL_EXPENSIFY_COM + CONST.MANAGE_CARDS_URL;
 
 const WorkspaceCardPage = ({
+    betas,
     user,
     translate,
     isSmallScreenWidth,
@@ -92,6 +97,11 @@ const WorkspaceCardPage = ({
         }
     };
 
+    if (!Permissions.canUseFreePlan(betas)) {
+        console.debug('Not showing workspace card page because user is not on free plan beta');
+        return <Navigation.DismissModal />;
+    }
+
     return (
         <ScreenWrapper style={[styles.defaultModalContainer]}>
             <HeaderWithCloseButton
@@ -193,5 +203,8 @@ export default compose(
         reimbursementAccount: {
             key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
         },
+        betas: {
+            key: ONYXKEYS.BETAS,
+        },
     }),
 )(WorkspaceCardPage);

From 934f4df5bb9ebfff408ff8f0c123f3f3d5ea0edb Mon Sep 17 00:00:00 2001
From: Brandon Stites <bstites+1@expensify.com>
Date: Tue, 27 Jul 2021 16:24:16 -0600
Subject: [PATCH 67/79] Don't get deleted/unissued cards

---
 src/libs/actions/PaymentMethods.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/libs/actions/PaymentMethods.js b/src/libs/actions/PaymentMethods.js
index f6474b581cd9..5f995433e465 100644
--- a/src/libs/actions/PaymentMethods.js
+++ b/src/libs/actions/PaymentMethods.js
@@ -13,6 +13,8 @@ function getPaymentMethods() {
     return API.Get({
         returnValueList: 'bankAccountList, cardList, userWallet, nameValuePairs',
         name: 'paypalMeAddress',
+        includeDeleted: false,
+        includeNotIssued: false,
     })
         .then((response) => {
             Onyx.multiSet({

From 72da49aed541970be6a7294adc958aecf5f1614d Mon Sep 17 00:00:00 2001
From: Brandon Stites <bstites+1@expensify.com>
Date: Tue, 27 Jul 2021 16:31:05 -0600
Subject: [PATCH 68/79] Add a ternary to ensure that in case of accounts not
 having the numbers, we can still render the item

---
 src/pages/settings/Payments/PaymentMethodList.js | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/pages/settings/Payments/PaymentMethodList.js b/src/pages/settings/Payments/PaymentMethodList.js
index c76ac9024f4e..25c276d35cd4 100644
--- a/src/pages/settings/Payments/PaymentMethodList.js
+++ b/src/pages/settings/Payments/PaymentMethodList.js
@@ -83,12 +83,15 @@ class PaymentMethodList extends Component {
         _.each(this.props.bankAccountList, (bankAccount) => {
             // Add all bank accounts besides the wallet
             if (bankAccount.type !== CONST.BANK_ACCOUNT_TYPES.WALLET) {
+                const formattedBankAccountNumber = bankAccount.accountNumber
+                    ? `${this.props.translate('paymentMethodList.accountLastFour')} ${bankAccount.accountNumber.slice(-4)}`
+                    : null;
                 combinedPaymentMethods.push({
                     type: MENU_ITEM,
                     title: bankAccount.addressName,
 
                     // eslint-disable-next-line
-                    description: `${this.props.translate('paymentMethodList.accountLastFour')} ${bankAccount.accountNumber.slice(-4)}`,
+                    description: formattedBankAccountNumber,
                     icon: Bank,
                     onPress: e => this.props.onPress(e, bankAccount.bankAccountID),
                     key: `bankAccount-${bankAccount.bankAccountID}`,
@@ -99,12 +102,15 @@ class PaymentMethodList extends Component {
         _.each(this.props.cardList, (card) => {
             // Add all cards besides the "cash" card
             if (card.cardName !== CONST.CARD_TYPES.DEFAULT_CASH) {
+                const formattedCardNumber = card.cardNumber
+                    ? `${this.props.translate('paymentMethodList.cardLastFour')} ${card.cardNumber.slice(-4)}`
+                    : null;
                 combinedPaymentMethods.push({
                     type: MENU_ITEM,
                     title: card.cardName,
 
                     // eslint-disable-next-line
-                    description: `${this.props.translate('paymentMethodList.cardLastFour')} ${card.cardNumber.slice(-4)}`,
+                    description: formattedCardNumber,
                     icon: CreditCard,
                     onPress: e => this.props.onPress(e, card.cardID),
                     key: `card-${card.cardID}`,

From 63ed060bf540ecd4a25f989552fefe0dbaada7ce Mon Sep 17 00:00:00 2001
From: Amal Nazeem <amal@expensify.com>
Date: Tue, 27 Jul 2021 19:29:15 -0400
Subject: [PATCH 69/79] Add beta check to people page as well

---
 src/pages/workspace/WorkspacePeoplePage.js | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/pages/workspace/WorkspacePeoplePage.js b/src/pages/workspace/WorkspacePeoplePage.js
index abbcd2c81d35..bf2325860730 100644
--- a/src/pages/workspace/WorkspacePeoplePage.js
+++ b/src/pages/workspace/WorkspacePeoplePage.js
@@ -21,6 +21,7 @@ import Text from '../../components/Text';
 import ROUTES from '../../ROUTES';
 import ConfirmModal from '../../components/ConfirmModal';
 import personalDetailsPropType from '../personalDetailsPropType';
+import Permissions from '../../libs/Permissions';
 import withWindowDimensions, {windowDimensionsPropTypes} from '../../components/withWindowDimensions';
 import OptionRow from '../home/sidebar/OptionRow';
 
@@ -29,6 +30,9 @@ const propTypes = {
 
     ...windowDimensionsPropTypes,
 
+    /** List of betas */
+    betas: PropTypes.arrayOf(PropTypes.string),
+
     /** The personal details of the person who is logged in */
     personalDetails: personalDetailsPropType.isRequired,
 
@@ -204,6 +208,10 @@ class WorkspacePeoplePage extends React.Component {
     }
 
     render() {
+        if (!Permissions.canUseFreePlan(this.props.betas)) {
+            console.debug('Not showing workspace people page because user is not on free plan beta');
+            return <Navigation.DismissModal />;
+        }
         const policyEmployeeList = lodashGet(this.props, 'policy.employeeList', []);
         const data = _.chain(policyEmployeeList)
             .map(email => this.props.personalDetails[email])
@@ -285,5 +293,8 @@ export default compose(
         session: {
             key: ONYXKEYS.SESSION,
         },
+        betas : {
+            key: ONYXKEYS.BETAS,
+        },
     }),
 )(WorkspacePeoplePage);

From 42a8a6039c25e62b023f7ace88ba73d1ea73fe4a Mon Sep 17 00:00:00 2001
From: Rushat Gabhane <rushatgabhane@gmail.com>
Date: Wed, 28 Jul 2021 02:32:33 +0300
Subject: [PATCH 70/79] autoFocus

---
 src/components/IOUConfirmationList.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/components/IOUConfirmationList.js b/src/components/IOUConfirmationList.js
index 8b46974f39ad..7b121d6c366c 100755
--- a/src/components/IOUConfirmationList.js
+++ b/src/components/IOUConfirmationList.js
@@ -275,6 +275,7 @@ class IOUConfirmationList extends Component {
                             onChangeText={this.props.onUpdateComment}
                             placeholder={this.props.translate('common.optional')}
                             placeholderTextColor={themeColors.placeholderText}
+                            autoFocus
                         />
                     </View>
                 </ScrollView>

From 03132080b0613a8d8cabc955635cdfa05d055896 Mon Sep 17 00:00:00 2001
From: Amal Nazeem <amal@expensify.com>
Date: Tue, 27 Jul 2021 20:02:56 -0400
Subject: [PATCH 71/79] style fixes

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

diff --git a/src/pages/workspace/WorkspacePeoplePage.js b/src/pages/workspace/WorkspacePeoplePage.js
index bf2325860730..a396454083fe 100644
--- a/src/pages/workspace/WorkspacePeoplePage.js
+++ b/src/pages/workspace/WorkspacePeoplePage.js
@@ -31,7 +31,7 @@ const propTypes = {
     ...windowDimensionsPropTypes,
 
     /** List of betas */
-    betas: PropTypes.arrayOf(PropTypes.string),
+    betas: PropTypes.arrayOf(PropTypes.string).isRequired,
 
     /** The personal details of the person who is logged in */
     personalDetails: personalDetailsPropType.isRequired,
@@ -293,7 +293,7 @@ export default compose(
         session: {
             key: ONYXKEYS.SESSION,
         },
-        betas : {
+        betas: {
             key: ONYXKEYS.BETAS,
         },
     }),

From ae9cc84bf12eb3b3f8879a1afa9920e4ade0136e Mon Sep 17 00:00:00 2001
From: Brandon Stites <bstites+1@expensify.com>
Date: Tue, 27 Jul 2021 18:46:07 -0600
Subject: [PATCH 72/79] Style

---
 src/pages/settings/Payments/PaymentMethodList.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/pages/settings/Payments/PaymentMethodList.js b/src/pages/settings/Payments/PaymentMethodList.js
index 25c276d35cd4..7e62429e1f38 100644
--- a/src/pages/settings/Payments/PaymentMethodList.js
+++ b/src/pages/settings/Payments/PaymentMethodList.js
@@ -84,7 +84,9 @@ class PaymentMethodList extends Component {
             // Add all bank accounts besides the wallet
             if (bankAccount.type !== CONST.BANK_ACCOUNT_TYPES.WALLET) {
                 const formattedBankAccountNumber = bankAccount.accountNumber
-                    ? `${this.props.translate('paymentMethodList.accountLastFour')} ${bankAccount.accountNumber.slice(-4)}`
+                    ? `${this.props.translate('paymentMethodList.accountLastFour')} ${
+                        bankAccount.accountNumber.slice(-4)
+                    }`
                     : null;
                 combinedPaymentMethods.push({
                     type: MENU_ITEM,

From a058756f88d1f6de73a15ec3706a3a2f93984690 Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Tue, 27 Jul 2021 14:52:46 -1000
Subject: [PATCH 73/79] undo logs

---
 src/libs/Firebase/index.native.js   | 11 +++++++++--
 src/libs/NetworkConnection.js       |  4 ++++
 src/libs/PusherConnectionManager.js | 10 ++++++++++
 3 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/src/libs/Firebase/index.native.js b/src/libs/Firebase/index.native.js
index f4db4ca77961..b86e091c7178 100644
--- a/src/libs/Firebase/index.native.js
+++ b/src/libs/Firebase/index.native.js
@@ -1,18 +1,21 @@
 import perf from '@react-native-firebase/perf';
 import {isDevelopment} from '../Environment/Environment';
+import Log from '../Log';
 
 const traceMap = {};
 
+let start, stop;
+
 /**
  * @param {String} customEventName
  */
 function startTrace(customEventName) {
+    start = global.performance.now();
     if (isDevelopment()) {
         return;
     }
 
     if (traceMap[customEventName]) {
-        console.debug('[Firebase] Trace with this name has already started');
         return;
     }
 
@@ -24,16 +27,20 @@ function startTrace(customEventName) {
  * @param {String} customEventName
  */
 function stopTrace(customEventName) {
+    stop = global.performance.now();
+
     if (isDevelopment()) {
         return;
     }
 
     const trace = traceMap[customEventName];
     if (!trace) {
-        console.debug('[Firebase] Could not find trace');
+        return;
     }
 
     trace.stop();
+
+    Log.info(`@marcaaron sidebar_loaded took: ${stop - start} ms`, true);
     delete traceMap[customEventName];
 }
 
diff --git a/src/libs/NetworkConnection.js b/src/libs/NetworkConnection.js
index 6fcc4b767248..cb104b18d868 100644
--- a/src/libs/NetworkConnection.js
+++ b/src/libs/NetworkConnection.js
@@ -49,6 +49,8 @@ function setOfflineStatus(isCurrentlyOffline) {
  * `disconnected` event which takes about 10-15 seconds to emit.
  */
 function listenForReconnect() {
+    logInfo('[NetworkConnection] listenForReconnect called', true);
+
     unsubscribeFromAppState = AppStateMonitor.addBecameActiveListener(() => {
         triggerReconnectionCallbacks('app became active');
     });
@@ -56,6 +58,7 @@ function listenForReconnect() {
     // Subscribe to the state change event via NetInfo so we can update
     // whether a user has internet connectivity or not.
     unsubscribeFromNetInfo = NetInfo.addEventListener((state) => {
+        logInfo(`[NetworkConnection] NetInfo isConnected: ${state && state.isConnected}`);
         setOfflineStatus(!state.isConnected);
     });
 }
@@ -64,6 +67,7 @@ function listenForReconnect() {
  * Tear down the event listeners when we are finished with them.
  */
 function stopListeningForReconnect() {
+    logInfo('[NetworkConnection] stopListeningForReconnect called', true);
     if (unsubscribeFromNetInfo) {
         unsubscribeFromNetInfo();
         unsubscribeFromNetInfo = undefined;
diff --git a/src/libs/PusherConnectionManager.js b/src/libs/PusherConnectionManager.js
index 74d74359590b..164efb40d7b3 100644
--- a/src/libs/PusherConnectionManager.js
+++ b/src/libs/PusherConnectionManager.js
@@ -7,6 +7,7 @@ import Log from './Log';
 // reconnect each time when we only need to reconnect once. This way, if an authToken is expired and we try to
 // subscribe to a bunch of channels at once we will only reauthenticate and force reconnect Pusher once.
 const reauthenticate = _.throttle(() => {
+    Log.info('[Pusher] Re-authenticating and then reconnecting', true);
     API.reauthenticate('Push_Authenticate')
         .then(() => Pusher.reconnect())
         .catch(() => {
@@ -26,6 +27,8 @@ function init() {
      */
     Pusher.registerCustomAuthorizer(channel => ({
         authorize: (socketID, callback) => {
+            Log.info('[PusherConnectionManager] Attempting to authorize Pusher', true);
+
             API.Push_Authenticate({
                 socket_id: socketID,
                 channel_name: channel.name,
@@ -41,6 +44,7 @@ function init() {
                         return;
                     }
 
+                    Log.info('[PusherConnectionManager] Pusher authenticated successfully', true);
                     callback(null, data);
                 })
                 .catch((error) => {
@@ -62,6 +66,12 @@ function init() {
                 Log.info('[PusherConnectionManager] error event', true, {error: data});
                 reauthenticate();
                 break;
+            case 'connected':
+                Log.info('[PusherConnectionManager] connected event', true);
+                break;
+            case 'disconnected':
+                Log.info('[PusherConnectionManager] disconnected event', true);
+                break;
             default:
                 break;
         }

From 2fad2584b55ac4799c6f84de148205d8043f84bf Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Tue, 27 Jul 2021 14:53:38 -1000
Subject: [PATCH 74/79] fix logs

---
 src/libs/Pusher/pusher.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/libs/Pusher/pusher.js b/src/libs/Pusher/pusher.js
index 1afbd7db49e3..e64644c1441d 100644
--- a/src/libs/Pusher/pusher.js
+++ b/src/libs/Pusher/pusher.js
@@ -193,6 +193,7 @@ function subscribe(
             most likely has been called before Pusher.init()`);
         }
 
+        console.debug('[Pusher] Attempting to subscribe to channel', true, {channelName, eventName});
         let channel = getChannel(channelName);
 
         if (!channel || !channel.subscribed) {

From f4c28b9ec5b35711020b566ea44a2b7c9f230c45 Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Tue, 27 Jul 2021 16:47:01 -1000
Subject: [PATCH 75/79] Add log line to uncomment

---
 src/components/OptionsList.js                 |  5 ----
 src/libs/Firebase/index.native.js             | 20 ++++++++-----
 .../Navigation/AppNavigator/AuthScreens.js    | 23 +++++++--------
 src/libs/actions/App.js                       | 10 -------
 src/pages/home/sidebar/SidebarLinks.js        | 28 +++----------------
 5 files changed, 27 insertions(+), 59 deletions(-)

diff --git a/src/components/OptionsList.js b/src/components/OptionsList.js
index 38d8d2c86099..cfb25b7e5e19 100644
--- a/src/components/OptionsList.js
+++ b/src/components/OptionsList.js
@@ -81,9 +81,6 @@ const propTypes = {
 
     /** Callback to execute when the SectionList lays out */
     onLayout: PropTypes.func,
-
-    /** Peformance optimization used when rows have a fixed height */
-    getItemLayout: PropTypes.func,
 };
 
 const defaultProps = {
@@ -106,7 +103,6 @@ const defaultProps = {
     optionMode: undefined,
     disableRowInteractivity: false,
     onLayout: undefined,
-    getItemLayout: undefined,
 };
 
 class OptionsList extends Component {
@@ -241,7 +237,6 @@ class OptionsList extends Component {
                     initialNumToRender={5}
                     maxToRenderPerBatch={5}
                     windowSize={5}
-                    getItemLayout={this.props.getItemLayout}
                     viewabilityConfig={this.viewabilityConfig}
                     onViewableItemsChanged={() => {
                         if (this.didLayout) {
diff --git a/src/libs/Firebase/index.native.js b/src/libs/Firebase/index.native.js
index b86e091c7178..af627091a0ae 100644
--- a/src/libs/Firebase/index.native.js
+++ b/src/libs/Firebase/index.native.js
@@ -1,16 +1,15 @@
+/* eslint-disable no-unused-vars */
 import perf from '@react-native-firebase/perf';
 import {isDevelopment} from '../Environment/Environment';
 import Log from '../Log';
 
 const traceMap = {};
 
-let start, stop;
-
 /**
  * @param {String} customEventName
  */
 function startTrace(customEventName) {
-    start = global.performance.now();
+    const start = global.performance.now();
     if (isDevelopment()) {
         return;
     }
@@ -20,27 +19,34 @@ function startTrace(customEventName) {
     }
 
     perf().startTrace(customEventName)
-        .then(trace => traceMap[customEventName] = trace);
+        .then((trace) => {
+            traceMap[customEventName] = {
+                trace,
+                start,
+            };
+        });
 }
 
 /**
  * @param {String} customEventName
  */
 function stopTrace(customEventName) {
-    stop = global.performance.now();
+    const stop = global.performance.now();
 
     if (isDevelopment()) {
         return;
     }
 
-    const trace = traceMap[customEventName];
+    const {trace, start} = traceMap[customEventName];
     if (!trace) {
         return;
     }
 
     trace.stop();
 
-    Log.info(`@marcaaron sidebar_loaded took: ${stop - start} ms`, true);
+    // Uncomment to inspect logs on release builds
+    // Log.info(`sidebar_loaded: ${stop - start} ms`, true);
+
     delete traceMap[customEventName];
 }
 
diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js
index 9b6c0fbb5716..92b9a31c2e02 100644
--- a/src/libs/Navigation/AppNavigator/AuthScreens.js
+++ b/src/libs/Navigation/AppNavigator/AuthScreens.js
@@ -151,22 +151,19 @@ class AuthScreens extends React.Component {
             User.subscribeToUserEvents();
         });
 
-        // Fetch main data we need on initialization for the LHN
+        // Fetch some data we need on initialization
+        NameValuePair.get(CONST.NVP.PRIORITY_MODE, ONYXKEYS.NVP_PRIORITY_MODE, 'default');
+        NameValuePair.get(CONST.NVP.PREFERRED_LOCALE, ONYXKEYS.NVP_PREFERRED_LOCALE, 'en');
+        PersonalDetails.fetchPersonalDetails();
+        User.getUserDetails();
         User.getBetas();
+        User.getDomainInfo();
+        PersonalDetails.fetchLocalCurrency();
         fetchAllReports(true, true);
-        PersonalDetails.fetchPersonalDetails();
-        NameValuePair.get(CONST.NVP.PRIORITY_MODE, ONYXKEYS.NVP_PRIORITY_MODE, 'default');
+        fetchCountryCodeByRequestIP();
+        UnreadIndicatorUpdater.listenForReportChanges();
 
-        // Defer less critical API requests until after the sidebar loads
-        App.onSidebarLoaded(() => {
-            NameValuePair.get(CONST.NVP.PREFERRED_LOCALE, ONYXKEYS.NVP_PREFERRED_LOCALE, 'en');
-            User.getUserDetails();
-            User.getDomainInfo();
-            PersonalDetails.fetchLocalCurrency();
-            fetchCountryCodeByRequestIP();
-            UnreadIndicatorUpdater.listenForReportChanges();
-            loadPoliciesBehindBeta(this.props.betas);
-        });
+        loadPoliciesBehindBeta(this.props.betas);
 
         // Refresh the personal details, timezone and betas every 30 minutes
         // There is no pusher event that sends updated personal details data yet
diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js
index cb20fa05872c..e3b339fdc7ab 100644
--- a/src/libs/actions/App.js
+++ b/src/libs/actions/App.js
@@ -5,7 +5,6 @@ import Firebase from '../Firebase';
 import CONST from '../../CONST';
 
 let isSidebarLoaded;
-let sidebarLoadedCallback;
 
 Onyx.connect({
     key: ONYXKEYS.IS_SIDEBAR_LOADED,
@@ -28,19 +27,11 @@ function setLocale(locale) {
     Onyx.merge(ONYXKEYS.NVP_PREFERRED_LOCALE, locale);
 }
 
-function onSidebarLoaded(callback) {
-    sidebarLoadedCallback = callback;
-}
-
 function setSidebarLoaded() {
     if (isSidebarLoaded) {
         return;
     }
 
-    if (sidebarLoadedCallback) {
-        sidebarLoadedCallback();
-    }
-
     Onyx.set(ONYXKEYS.IS_SIDEBAR_LOADED, true);
     Firebase.stopTrace(CONST.TIMING.SIDEBAR_LOADED);
 }
@@ -49,5 +40,4 @@ export {
     setCurrentURL,
     setLocale,
     setSidebarLoaded,
-    onSidebarLoaded,
 };
diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js
index e0de12b3d3e3..d846b67e9e29 100644
--- a/src/pages/home/sidebar/SidebarLinks.js
+++ b/src/pages/home/sidebar/SidebarLinks.js
@@ -94,28 +94,6 @@ const defaultProps = {
 };
 
 class SidebarLinks extends React.Component {
-    constructor(props) {
-        super(props);
-        this.getItemLayout = this.getItemLayout.bind(this);
-        this.onSelectRow = this.onSelectRow.bind(this);
-    }
-
-    shouldComponentUpdate(prevProps) {
-        return _.isEqual(prevProps, this.props);
-    }
-
-    onSelectRow(option) {
-        Navigation.navigate(ROUTES.getReportRoute(option.reportID));
-        this.props.onLinkClick();
-    }
-
-    getItemLayout(data, index) {
-        const length = this.props.priorityMode === CONST.PRIORITY_MODE.GSD ? 52 : 64;
-        return (
-            {length, offset: length * index, index}
-        );
-    }
-
     showSearchPage() {
         Navigation.navigate(ROUTES.SEARCH);
     }
@@ -192,14 +170,16 @@ class SidebarLinks extends React.Component {
                     focusedIndex={_.findIndex(recentReports, (
                         option => option.reportID === activeReportID
                     ))}
-                    onSelectRow={this.onSelectRow}
+                    onSelectRow={(option) => {
+                        Navigation.navigate(ROUTES.getReportRoute(option.reportID));
+                        this.props.onLinkClick();
+                    }}
                     optionBackgroundColor={themeColors.sidebar}
                     hideSectionHeaders
                     showTitleTooltip
                     disableFocusOptions={this.props.isSmallScreenWidth}
                     optionMode={this.props.priorityMode === CONST.PRIORITY_MODE.GSD ? 'compact' : 'default'}
                     onLayout={App.setSidebarLoaded}
-                    getItemLayout={this.getItemLayout}
                 />
                 <KeyboardSpacer />
             </View>

From 8a623c6e3804e7652b08ec8428b8bb0b5cf776e8 Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Tue, 27 Jul 2021 16:49:25 -1000
Subject: [PATCH 76/79] remove unused import

---
 src/libs/Navigation/AppNavigator/AuthScreens.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js
index 92b9a31c2e02..5ba802e792bf 100644
--- a/src/libs/Navigation/AppNavigator/AuthScreens.js
+++ b/src/libs/Navigation/AppNavigator/AuthScreens.js
@@ -25,7 +25,6 @@ import {fetchCountryCodeByRequestIP} from '../../actions/GeoLocation';
 import KeyboardShortcut from '../../KeyboardShortcut';
 import Navigation from '../Navigation';
 import * as User from '../../actions/User';
-import * as App from '../../actions/App';
 import {setModalVisibility} from '../../actions/Modal';
 import NameValuePair from '../../actions/NameValuePair';
 import {getPolicySummaries, getPolicyList} from '../../actions/Policy';

From c367c0618c074157a460a0f9119659fc28d4654c Mon Sep 17 00:00:00 2001
From: Matt Allen <mallen@expensify.com>
Date: Tue, 27 Jul 2021 20:28:29 -0700
Subject: [PATCH 77/79] Update CONTRIBUTING.md

In line 37, updating Bonus amount for reporting an issue to $250 per https://expensify.slack.com/archives/C01SKUP7QR0/p1627355560292500?thread_ts=1626903884.206900&cid=C01SKUP7QR0
---
 CONTRIBUTING.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 947cfba1851d..c55a3f68ca0c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -34,7 +34,7 @@ This is the most common scenario for contributors. The Expensify team posts new
 
 #### Proposing a job that Expensify hasn’t posted
 
-In this scenario, it’s possible that you found a bug or enhancement that we haven’t posted to the [Upwork job list](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2) or [Github repository](https://github.com/Expensify/Expensify.cash/issues?q=is%3Aissue). This is an opportunity to propose a job, and (optionally) a solution. If it's a valid job proposal, we will compensate you for the solution and give an additional bonus of $150 for proactively proposing the job. In this case, please take the following steps:
+In this scenario, it’s possible that you found a bug or enhancement that we haven’t posted to the [Upwork job list](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2) or [Github repository](https://github.com/Expensify/Expensify.cash/issues?q=is%3Aissue). This is an opportunity to propose a job, and (optionally) a solution. If it's a valid job proposal, we will compensate you for the solution and give an additional bonus of $250 for proactively proposing the job. In this case, please take the following steps:
 
     1. Check to ensure an issue does not already exist in the New Expensify Issue list or Upwork job list. Please use your best judgement to search for similar titles and issue descriptions.
     2. If your bug or enhancement matches an existing issue, please feel free to comment on that GitHub issue with your findings if you think it’ll help solve a problem.

From c687d63142df2eafa862257a54c5e3e270ce40ff Mon Sep 17 00:00:00 2001
From: Marc Glasser <marc.aaron.glasser@gmail.com>
Date: Tue, 27 Jul 2021 19:07:22 -1000
Subject: [PATCH 78/79] add mock for rn firebase

---
 __mocks__/@react-native-firebase/perf.js | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 __mocks__/@react-native-firebase/perf.js

diff --git a/__mocks__/@react-native-firebase/perf.js b/__mocks__/@react-native-firebase/perf.js
new file mode 100644
index 000000000000..2d1ec238274a
--- /dev/null
+++ b/__mocks__/@react-native-firebase/perf.js
@@ -0,0 +1 @@
+export default () => {};

From 40e81147ab28a74a066b303ef8660a508a29a2bf Mon Sep 17 00:00:00 2001
From: OSBotify <runner@Mac-1627504721394.local>
Date: Wed, 28 Jul 2021 20:43:25 +0000
Subject: [PATCH 79/79] Update version to 1.0.80-3

---
 android/app/build.gradle          | 4 ++--
 ios/ExpensifyCash/Info.plist      | 2 +-
 ios/ExpensifyCashTests/Info.plist | 2 +-
 package-lock.json                 | 2 +-
 package.json                      | 2 +-
 5 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/android/app/build.gradle b/android/app/build.gradle
index a3a6cd83df46..004a45bedeee 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -150,8 +150,8 @@ android {
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
         multiDexEnabled rootProject.ext.multiDexEnabled
-        versionCode 1001008002
-        versionName "1.0.80-2"
+        versionCode 1001008003
+        versionName "1.0.80-3"
     }
     splits {
         abi {
diff --git a/ios/ExpensifyCash/Info.plist b/ios/ExpensifyCash/Info.plist
index c69435b11314..8fa71fe6b76f 100644
--- a/ios/ExpensifyCash/Info.plist
+++ b/ios/ExpensifyCash/Info.plist
@@ -30,7 +30,7 @@
 		</dict>
 	</array>
 	<key>CFBundleVersion</key>
-	<string>1.0.80.2</string>
+	<string>1.0.80.3</string>
 	<key>ITSAppUsesNonExemptEncryption</key>
 	<false/>
 	<key>LSApplicationQueriesSchemes</key>
diff --git a/ios/ExpensifyCashTests/Info.plist b/ios/ExpensifyCashTests/Info.plist
index ced27dd4ac02..978608834649 100644
--- a/ios/ExpensifyCashTests/Info.plist
+++ b/ios/ExpensifyCashTests/Info.plist
@@ -19,6 +19,6 @@
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>
-	<string>1.0.80.2</string>
+	<string>1.0.80.3</string>
 </dict>
 </plist>
diff --git a/package-lock.json b/package-lock.json
index b955b2077ef8..fda49353ba70 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "expensify.cash",
-  "version": "1.0.80-2",
+  "version": "1.0.80-3",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/package.json b/package.json
index 868ff2d6e21b..b525b4fd9693 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "expensify.cash",
-  "version": "1.0.80-2",
+  "version": "1.0.80-3",
   "author": "Expensify, Inc.",
   "homepage": "https://new.expensify.com",
   "description": "Expensify.cash is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
