From 170f5f3e3bde331c30c1245e15e9c7f6ae688383 Mon Sep 17 00:00:00 2001
From: Rajat Parashar <parasharrajat@users.noreply.github.com>
Date: Wed, 24 Mar 2021 02:25:43 +0530
Subject: [PATCH 1/5] fix: keyboard appears on split bill page

---
 src/components/withNavigationContext.js      | 68 ++++++++++++++++++++
 src/pages/home/report/ReportActionCompose.js |  7 +-
 2 files changed, 73 insertions(+), 2 deletions(-)
 create mode 100644 src/components/withNavigationContext.js

diff --git a/src/components/withNavigationContext.js b/src/components/withNavigationContext.js
new file mode 100644
index 000000000000..554bfd175cd1
--- /dev/null
+++ b/src/components/withNavigationContext.js
@@ -0,0 +1,68 @@
+import React, {Component} from 'react';
+import PropTypes from 'prop-types';
+import {NavigationContext} from '@react-navigation/native';
+import getComponentDisplayName from '../libs/getComponentDisplayName';
+
+const navigationContextPropTypes = {
+    // Whether this screen is focused
+    isScreenFocused: PropTypes.number.isRequired,
+};
+
+export default function (WrappedComponent) {
+    class withNavigationContext extends Component {
+        constructor(props) {
+            super(props);
+            this.state = {
+                isScreenFocused: false,
+            };
+        }
+
+        componentDidMount() {
+            this.unsubscribeFocus = this.context.addListener('focus', () => this.setState({
+                isScreenFocused: true,
+            }));
+            this.unsubscribeBlur = this.context.addListener('blur', () => this.setState({
+                isScreenFocused: false,
+            }));
+        }
+
+        componentDidUpdate() {
+            const valueToReturn = this.context.isFocused();
+            if (this.state.isScreenFocused !== valueToReturn) {
+                // If the value has changed since the last render, we need to update it.
+                // This could happen if we missed an update from the event listeners during re-render.
+                // React will process this update immediately, so the old subscription value won't be committed.
+                // It is still nice to avoid returning a mismatched value though, so let's override the return value.
+                // This is the same logic as in https://github.com/facebook/react/tree/master/packages/use-subscription
+
+                // eslint-disable-next-line react/no-did-update-set-state
+                this.setState({
+                    isScreenFocused: valueToReturn,
+                });
+            }
+        }
+
+        componentWillUnmount() {
+            this.unsubscribeFocus();
+            this.unsubscribeBlur();
+        }
+
+        render() {
+            return (
+                <WrappedComponent
+                    // eslint-disable-next-line react/jsx-props-no-spreading
+                    {...this.props}
+                    isScreenFocused={this.state.isScreenFocused}
+                />
+            );
+        }
+    }
+
+    withNavigationContext.contextType = NavigationContext;
+    withNavigationContext.displayName = `withNavigationContext(${getComponentDisplayName(WrappedComponent)})`;
+    return withNavigationContext;
+}
+
+export {
+    navigationContextPropTypes,
+};
diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js
index 7bc8c8761c02..445465932c00 100644
--- a/src/pages/home/report/ReportActionCompose.js
+++ b/src/pages/home/report/ReportActionCompose.js
@@ -19,6 +19,7 @@ import compose from '../../../libs/compose';
 import CreateMenu from '../../../components/CreateMenu';
 import CONST from '../../../CONST';
 import Navigation from '../../../libs/Navigation/Navigation';
+import withNavigationContext, {navigationContextPropTypes} from '../../../components/withNavigationContext';
 
 const propTypes = {
     // A method to call when the form is submitted
@@ -44,6 +45,7 @@ const propTypes = {
     }).isRequired,
 
     ...windowDimensionsPropTypes,
+    ...navigationContextPropTypes,
 };
 
 const defaultProps = {
@@ -78,8 +80,8 @@ class ReportActionCompose extends React.Component {
             this.comment = this.props.comment;
         }
 
-        // When any modal goes from visible to hidden, bring focus to the compose field
-        if (prevProps.modal.isVisible && !this.props.modal.isVisible) {
+        // When any modal goes from visible to hidden, bring focus to the compose field only when the screen is focused.
+        if (this.props.isScreenFocused && prevProps.modal.isVisible && !this.props.modal.isVisible) {
             this.setIsFocused(true);
         }
     }
@@ -319,4 +321,5 @@ export default compose(
         },
     }),
     withWindowDimensions,
+    withNavigationContext,
 )(ReportActionCompose);

From 6e40e390d991af411688de54d5187c2d47e8e2ec Mon Sep 17 00:00:00 2001
From: Rajat Parashar <parasharrajat@users.noreply.github.com>
Date: Wed, 24 Mar 2021 03:06:49 +0530
Subject: [PATCH 2/5]  typo fixed

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

diff --git a/src/components/withNavigationContext.js b/src/components/withNavigationContext.js
index 554bfd175cd1..e0587f2eab64 100644
--- a/src/components/withNavigationContext.js
+++ b/src/components/withNavigationContext.js
@@ -5,7 +5,7 @@ import getComponentDisplayName from '../libs/getComponentDisplayName';
 
 const navigationContextPropTypes = {
     // Whether this screen is focused
-    isScreenFocused: PropTypes.number.isRequired,
+    isScreenFocused: PropTypes.bool.isRequired,
 };
 
 export default function (WrappedComponent) {

From c4f64e56e764c799c736b19bf87e571cda5bda71 Mon Sep 17 00:00:00 2001
From: Rajat Parashar <parasharrajat@users.noreply.github.com>
Date: Fri, 26 Mar 2021 02:52:31 +0530
Subject: [PATCH 3/5] refactor

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

diff --git a/src/components/withNavigationContext.js b/src/components/withNavigationContext.js
index e0587f2eab64..37d372732638 100644
--- a/src/components/withNavigationContext.js
+++ b/src/components/withNavigationContext.js
@@ -27,8 +27,8 @@ export default function (WrappedComponent) {
         }
 
         componentDidUpdate() {
-            const valueToReturn = this.context.isFocused();
-            if (this.state.isScreenFocused !== valueToReturn) {
+            const isCurrentlyFocused = this.context.isFocused();
+            if (this.state.isScreenFocused !== isCurrentlyFocused) {
                 // If the value has changed since the last render, we need to update it.
                 // This could happen if we missed an update from the event listeners during re-render.
                 // React will process this update immediately, so the old subscription value won't be committed.
@@ -37,7 +37,7 @@ export default function (WrappedComponent) {
 
                 // eslint-disable-next-line react/no-did-update-set-state
                 this.setState({
-                    isScreenFocused: valueToReturn,
+                    isScreenFocused: isCurrentlyFocused,
                 });
             }
         }

From 1c343db65ae576417b07e65062f9fb8171b71c22 Mon Sep 17 00:00:00 2001
From: Rajat Parashar <parasharrajat@users.noreply.github.com>
Date: Tue, 13 Apr 2021 06:54:03 +0530
Subject: [PATCH 4/5] fix: Composer focus

---
 src/components/withNavigationContext.js      | 59 ++++----------------
 src/pages/home/report/ReportActionCompose.js | 23 +++++---
 2 files changed, 26 insertions(+), 56 deletions(-)

diff --git a/src/components/withNavigationContext.js b/src/components/withNavigationContext.js
index 37d372732638..15832709534d 100644
--- a/src/components/withNavigationContext.js
+++ b/src/components/withNavigationContext.js
@@ -1,6 +1,6 @@
 import React, {Component} from 'react';
 import PropTypes from 'prop-types';
-import {NavigationContext} from '@react-navigation/native';
+import {useIsFocused} from '@react-navigation/native';
 import getComponentDisplayName from '../libs/getComponentDisplayName';
 
 const navigationContextPropTypes = {
@@ -9,56 +9,17 @@ const navigationContextPropTypes = {
 };
 
 export default function (WrappedComponent) {
-    class withNavigationContext extends Component {
-        constructor(props) {
-            super(props);
-            this.state = {
-                isScreenFocused: false,
-            };
-        }
-
-        componentDidMount() {
-            this.unsubscribeFocus = this.context.addListener('focus', () => this.setState({
-                isScreenFocused: true,
-            }));
-            this.unsubscribeBlur = this.context.addListener('blur', () => this.setState({
-                isScreenFocused: false,
-            }));
-        }
-
-        componentDidUpdate() {
-            const isCurrentlyFocused = this.context.isFocused();
-            if (this.state.isScreenFocused !== isCurrentlyFocused) {
-                // If the value has changed since the last render, we need to update it.
-                // This could happen if we missed an update from the event listeners during re-render.
-                // React will process this update immediately, so the old subscription value won't be committed.
-                // It is still nice to avoid returning a mismatched value though, so let's override the return value.
-                // This is the same logic as in https://github.com/facebook/react/tree/master/packages/use-subscription
-
-                // eslint-disable-next-line react/no-did-update-set-state
-                this.setState({
-                    isScreenFocused: isCurrentlyFocused,
-                });
-            }
-        }
-
-        componentWillUnmount() {
-            this.unsubscribeFocus();
-            this.unsubscribeBlur();
-        }
-
-        render() {
-            return (
-                <WrappedComponent
-                    // eslint-disable-next-line react/jsx-props-no-spreading
-                    {...this.props}
-                    isScreenFocused={this.state.isScreenFocused}
-                />
-            );
-        }
+    function withNavigationContext(props) {
+        const isScreenFocused = useIsFocused();
+        return (
+            <WrappedComponent
+                // eslint-disable-next-line react/jsx-props-no-spreading
+                {...props}
+                isScreenFocused={isScreenFocused}
+            />
+        );
     }
 
-    withNavigationContext.contextType = NavigationContext;
     withNavigationContext.displayName = `withNavigationContext(${getComponentDisplayName(WrappedComponent)})`;
     return withNavigationContext;
 }
diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js
index 2d8037b8afdf..224f57eb9b75 100644
--- a/src/pages/home/report/ReportActionCompose.js
+++ b/src/pages/home/report/ReportActionCompose.js
@@ -1,6 +1,6 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import {View, TouchableOpacity} from 'react-native';
+import {View, TouchableOpacity, InteractionManager} from 'react-native';
 import _ from 'underscore';
 import lodashGet from 'lodash/get';
 import {withOnyx} from 'react-native-onyx';
@@ -19,6 +19,8 @@ import CreateMenu from '../../../components/CreateMenu';
 import withNavigationContext, {navigationContextPropTypes} from '../../../components/withNavigationContext';
 import withWindowDimensions from '../../../components/withWindowDimensions';
 import withDrawerState from '../../../components/withDrawerState';
+import getPlatform from '../../../libs/getPlatform';
+import canUseTouchScreen from '../../../libs/canUseTouchscreen';
 
 const propTypes = {
     // A method to call when the form is submitted
@@ -70,9 +72,10 @@ class ReportActionCompose extends React.Component {
         this.setIsFocused = this.setIsFocused.bind(this);
         this.focus = this.focus.bind(this);
         this.comment = props.comment;
+        this.autoFocusEnabled = getPlatform() === 'desktop' || (getPlatform() === 'web' && !canUseTouchScreen());
 
         this.state = {
-            isFocused: true,
+            isFocused: this.autoFocusEnabled,
             textInputShouldClear: false,
             isCommentEmpty: props.comment.length === 0,
             isMenuVisible: false,
@@ -80,10 +83,14 @@ class ReportActionCompose extends React.Component {
     }
 
     componentDidUpdate(prevProps) {
-        // When any modal goes from visible to hidden or when the report ID changes,
-        // bring focus to the compose field only when the screen is focused.
-        if (this.props.isScreenFocused && prevProps.modal.isVisible && !this.props.modal.isVisible) {
+        // We want to manually set the focus only when.
+        //  1) Auto focus is enabled(we are on desktop/Desktop Web).
+        //  1) Screen is focsued.
+        //  2) When any modal goes from visible to hidden &
+        if (this.autoFocusEnabled && this.props.isScreenFocused
+            && prevProps.modal.isVisible && !this.props.modal.isVisible) {
             this.setIsFocused(true);
+            this.focus();
         }
     }
 
@@ -119,7 +126,9 @@ class ReportActionCompose extends React.Component {
      */
     focus() {
         if (this.textInput) {
-            this.textInput.focus();
+            InteractionManager.runAfterInteractions(() => {
+                this.textInput.focus();
+            });
         }
     }
 
@@ -267,7 +276,7 @@ class ReportActionCompose extends React.Component {
                                     )}
                                 </AttachmentPicker>
                                 <TextInputFocusable
-                                    autoFocus
+                                    autoFocus={this.autoFocusEnabled}
                                     multiline
                                     ref={el => this.textInput = el}
                                     textAlignVertical="top"

From 80411e8b0dfbdc9165eb5ce5eab3cee01132db21 Mon Sep 17 00:00:00 2001
From: Rajat Parashar <parasharrajat@users.noreply.github.com>
Date: Wed, 14 Apr 2021 03:24:51 +0530
Subject: [PATCH 5/5] refactor the Composer focus logic.

---
 package-lock.json                             |  5 ++++
 package.json                                  |  1 +
 src/components/withNavigationContext.js       | 29 -------------------
 src/libs/canFocusInputOnScreenFocus/index.js  |  3 ++
 .../index.native.js                           |  1 +
 src/pages/home/report/ReportActionCompose.js  | 27 ++++++++---------
 6 files changed, 24 insertions(+), 42 deletions(-)
 delete mode 100644 src/components/withNavigationContext.js
 create mode 100644 src/libs/canFocusInputOnScreenFocus/index.js
 create mode 100644 src/libs/canFocusInputOnScreenFocus/index.native.js

diff --git a/package-lock.json b/package-lock.json
index cce7ff37ad99..a9ab6ea15ca6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3925,6 +3925,11 @@
       "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-1.9.11.tgz",
       "integrity": "sha512-E7whvvMIl9Ln1sxgug7OAEVWQ69n82iV0d2OWWp5VV52+RW3azKh1IFm4rxdW5/oByMfl7FFL0eHNelGgY4BMQ=="
     },
+    "@react-navigation/compat": {
+      "version": "5.3.15",
+      "resolved": "https://registry.npmjs.org/@react-navigation/compat/-/compat-5.3.15.tgz",
+      "integrity": "sha512-Gh5O6Ng3z0/qSBhuuWuPSi24+RrkgNy5hzvEke8qjS+kbrPfSFLD6aq9B2Fdfndp/dAyfxgTi6aULUsnv847Hw=="
+    },
     "@react-navigation/core": {
       "version": "5.15.2",
       "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-5.15.2.tgz",
diff --git a/package.json b/package.json
index 0597552eee1b..31d5578e3767 100644
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
     "@react-native-firebase/app": "^8.4.5",
     "@react-native-firebase/crashlytics": "^8.4.9",
     "@react-native-picker/picker": "^1.9.11",
+    "@react-navigation/compat": "^5.3.15",
     "@react-navigation/drawer": "5.12.5",
     "@react-navigation/native": "5.9.2",
     "@react-navigation/stack": "5.14.2",
diff --git a/src/components/withNavigationContext.js b/src/components/withNavigationContext.js
deleted file mode 100644
index 15832709534d..000000000000
--- a/src/components/withNavigationContext.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import React, {Component} from 'react';
-import PropTypes from 'prop-types';
-import {useIsFocused} from '@react-navigation/native';
-import getComponentDisplayName from '../libs/getComponentDisplayName';
-
-const navigationContextPropTypes = {
-    // Whether this screen is focused
-    isScreenFocused: PropTypes.bool.isRequired,
-};
-
-export default function (WrappedComponent) {
-    function withNavigationContext(props) {
-        const isScreenFocused = useIsFocused();
-        return (
-            <WrappedComponent
-                // eslint-disable-next-line react/jsx-props-no-spreading
-                {...props}
-                isScreenFocused={isScreenFocused}
-            />
-        );
-    }
-
-    withNavigationContext.displayName = `withNavigationContext(${getComponentDisplayName(WrappedComponent)})`;
-    return withNavigationContext;
-}
-
-export {
-    navigationContextPropTypes,
-};
diff --git a/src/libs/canFocusInputOnScreenFocus/index.js b/src/libs/canFocusInputOnScreenFocus/index.js
new file mode 100644
index 000000000000..47ed8354fb47
--- /dev/null
+++ b/src/libs/canFocusInputOnScreenFocus/index.js
@@ -0,0 +1,3 @@
+import canUseTouchScreen from '../canUseTouchscreen';
+
+export default () => !canUseTouchScreen();
diff --git a/src/libs/canFocusInputOnScreenFocus/index.native.js b/src/libs/canFocusInputOnScreenFocus/index.native.js
new file mode 100644
index 000000000000..eae5767cffbc
--- /dev/null
+++ b/src/libs/canFocusInputOnScreenFocus/index.native.js
@@ -0,0 +1 @@
+export default () => false;
diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js
index 224f57eb9b75..7122a7d97e11 100644
--- a/src/pages/home/report/ReportActionCompose.js
+++ b/src/pages/home/report/ReportActionCompose.js
@@ -1,6 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import {View, TouchableOpacity, InteractionManager} from 'react-native';
+import {withNavigationFocus} from '@react-navigation/compat';
 import _ from 'underscore';
 import lodashGet from 'lodash/get';
 import {withOnyx} from 'react-native-onyx';
@@ -16,11 +17,9 @@ import ReportTypingIndicator from './ReportTypingIndicator';
 import AttachmentModal from '../../../components/AttachmentModal';
 import compose from '../../../libs/compose';
 import CreateMenu from '../../../components/CreateMenu';
-import withNavigationContext, {navigationContextPropTypes} from '../../../components/withNavigationContext';
 import withWindowDimensions from '../../../components/withWindowDimensions';
 import withDrawerState from '../../../components/withDrawerState';
-import getPlatform from '../../../libs/getPlatform';
-import canUseTouchScreen from '../../../libs/canUseTouchscreen';
+import canFocusInputOnScreenFocus from '../../../libs/canFocusInputOnScreenFocus';
 
 const propTypes = {
     // A method to call when the form is submitted
@@ -51,7 +50,8 @@ const propTypes = {
     /* Is the window width narrow, like on a mobile device */
     isSmallScreenWidth: PropTypes.bool.isRequired,
 
-    ...navigationContextPropTypes,
+    // Is composer screen focused
+    isFocused: PropTypes.bool.isRequired,
 
 };
 
@@ -72,10 +72,10 @@ class ReportActionCompose extends React.Component {
         this.setIsFocused = this.setIsFocused.bind(this);
         this.focus = this.focus.bind(this);
         this.comment = props.comment;
-        this.autoFocusEnabled = getPlatform() === 'desktop' || (getPlatform() === 'web' && !canUseTouchScreen());
+        this.shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus();
 
         this.state = {
-            isFocused: this.autoFocusEnabled,
+            isFocused: this.shouldFocusInputOnScreenFocus,
             textInputShouldClear: false,
             isCommentEmpty: props.comment.length === 0,
             isMenuVisible: false,
@@ -83,11 +83,10 @@ class ReportActionCompose extends React.Component {
     }
 
     componentDidUpdate(prevProps) {
-        // We want to manually set the focus only when.
-        //  1) Auto focus is enabled(we are on desktop/Desktop Web).
-        //  1) Screen is focsued.
-        //  2) When any modal goes from visible to hidden &
-        if (this.autoFocusEnabled && this.props.isScreenFocused
+        // We want to focus or refocus the input when a modal has been closed and the underlying screen is focused.
+        // We avoid doing this on native platforms since the software keyboard popping
+        // open creates a jarring and broken UX.
+        if (this.shouldFocusInputOnScreenFocus && this.props.isFocused
             && prevProps.modal.isVisible && !this.props.modal.isVisible) {
             this.setIsFocused(true);
             this.focus();
@@ -126,6 +125,8 @@ class ReportActionCompose extends React.Component {
      */
     focus() {
         if (this.textInput) {
+            // There could be other animations running while we trigger manual focus.
+            // This prevents focus from making those animations janky.
             InteractionManager.runAfterInteractions(() => {
                 this.textInput.focus();
             });
@@ -276,7 +277,7 @@ class ReportActionCompose extends React.Component {
                                     )}
                                 </AttachmentPicker>
                                 <TextInputFocusable
-                                    autoFocus={this.autoFocusEnabled}
+                                    autoFocus={this.shouldFocusInputOnScreenFocus}
                                     multiline
                                     ref={el => this.textInput = el}
                                     textAlignVertical="top"
@@ -334,6 +335,7 @@ ReportActionCompose.defaultProps = defaultProps;
 export default compose(
     withWindowDimensions,
     withDrawerState,
+    withNavigationFocus,
     withOnyx({
         comment: {
             key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`,
@@ -345,5 +347,4 @@ export default compose(
             key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
         },
     }),
-    withNavigationContext,
 )(ReportActionCompose);
