From 90b3a9d7060063976eb5099bd7b8dbf87c396199 Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Thu, 25 Mar 2021 22:55:56 +0200
Subject: [PATCH 01/23] refactor: extract attachment prop types for reuse

---
 src/components/AttachmentPicker/index.js        | 11 +++--------
 src/components/AttachmentPicker/index.native.js |  6 +-----
 src/components/AttachmentPicker/propTypes.js    | 10 ++++++++++
 3 files changed, 14 insertions(+), 13 deletions(-)
 create mode 100644 src/components/AttachmentPicker/propTypes.js

diff --git a/src/components/AttachmentPicker/index.js b/src/components/AttachmentPicker/index.js
index 3f2b648f57ef..4c6986b308d3 100644
--- a/src/components/AttachmentPicker/index.js
+++ b/src/components/AttachmentPicker/index.js
@@ -1,17 +1,12 @@
 import React from 'react';
-import PropTypes from 'prop-types';
-
-const propTypes = {
-    children: PropTypes.func.isRequired,
-};
+import propTypes from './propTypes';
 
 /**
  * This component renders a function as a child and
  * returns a "show attachment picker" method that takes
  * a callback. This is the web/mWeb/desktop version since
- * on iOS Safari we must append a hidden input to the DOM
- * and listen to onChange event. When the show method is
- * called an attachment
+ * on a Browser we must append a hidden input to the DOM
+ * and listen to onChange event.
  *
  * @example
  * <AttachmentPicker>
diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index 379c8697e3e4..c66cee01f6fc 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -4,7 +4,7 @@
 import {Alert, Linking} from 'react-native';
 import RNImagePicker from 'react-native-image-picker';
 import RNDocumentPicker from 'react-native-document-picker';
-import PropTypes from 'prop-types';
+import propTypes from './propTypes';
 
 /**
  * See https://github.com/react-native-community/react-native-image-picker/blob/master/docs/Reference.md#options
@@ -94,10 +94,6 @@ function getDataForUpload(fileData) {
     };
 }
 
-const propTypes = {
-    children: PropTypes.func.isRequired,
-};
-
 /**
  * This component renders a function as a child and
  * returns a "show attachment picker" method that takes
diff --git a/src/components/AttachmentPicker/propTypes.js b/src/components/AttachmentPicker/propTypes.js
new file mode 100644
index 000000000000..06a050e48a59
--- /dev/null
+++ b/src/components/AttachmentPicker/propTypes.js
@@ -0,0 +1,10 @@
+import PropTypes from 'prop-types';
+
+const propTypes = {
+    /** renderProp with the following interface
+     * @example
+     * function ({ openPicker: ({ onPicked: (file): void }}): void }): ReactNode */
+    children: PropTypes.func.isRequired,
+};
+
+export default propTypes;

From 8bb9bf8792c28d7303ee4669980f906c71705efc Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Thu, 25 Mar 2021 23:06:47 +0200
Subject: [PATCH 02/23] docs: fix attachment native documentation

---
 src/components/AttachmentPicker/index.native.js | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index c66cee01f6fc..779f985da52d 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -97,10 +97,8 @@ function getDataForUpload(fileData) {
 /**
  * This component renders a function as a child and
  * returns a "show attachment picker" method that takes
- * a callback. This is the web/mWeb/desktop version since
- * on iOS Safari we must append a hidden input to the DOM
- * and listen to onChange event. When the show method is
- * called an attachment
+ * a callback. This is the ios/android implementation
+ * opening a modal with attachment options
  *
  * @example
  * <AttachmentPicker>

From 9aea3dd20ac7c2e940a102058e2322abe0065e97 Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Thu, 25 Mar 2021 23:11:00 +0200
Subject: [PATCH 03/23] docs: attachment move example usage the renderProp

---
 src/components/AttachmentPicker/index.js      | 15 ---------------
 .../AttachmentPicker/index.native.js          | 17 +----------------
 src/components/AttachmentPicker/propTypes.js  | 19 +++++++++++++++++--
 3 files changed, 18 insertions(+), 33 deletions(-)

diff --git a/src/components/AttachmentPicker/index.js b/src/components/AttachmentPicker/index.js
index 4c6986b308d3..b414266e53fb 100644
--- a/src/components/AttachmentPicker/index.js
+++ b/src/components/AttachmentPicker/index.js
@@ -7,21 +7,6 @@ import propTypes from './propTypes';
  * a callback. This is the web/mWeb/desktop version since
  * on a Browser we must append a hidden input to the DOM
  * and listen to onChange event.
- *
- * @example
- * <AttachmentPicker>
- * {({openPicker}) => (
- *     <Button
- *         onPress={() => {
- *             openPicker({
- *                 onPicked: (file) => {
- *                     // Display or upload File
- *                 },
- *             });
- *         }}
- *     />
- * )}
- * </AttachmentPicker>
  */
 class AttachmentPicker extends React.Component {
     render() {
diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index 779f985da52d..602df1d03bba 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -100,22 +100,7 @@ function getDataForUpload(fileData) {
  * a callback. This is the ios/android implementation
  * opening a modal with attachment options
  *
- * @example
- * <AttachmentPicker>
- * {({openPicker}) => (
- *     <Button
- *         onPress={() => {
- *             openPicker({
- *                 onPicked: (file) => {
- *                     // Display or upload File
- *                 },
- *             });
- *         }}
- *     />
- * )}
- * </AttachmentPicker>
- *
- * @returns {Function}
+ * @returns {React.FunctionComponent}
  */
 const AttachmentPicker = ({children}) => children({
     openPicker: ({onPicked}) => {
diff --git a/src/components/AttachmentPicker/propTypes.js b/src/components/AttachmentPicker/propTypes.js
index 06a050e48a59..a14643396f92 100644
--- a/src/components/AttachmentPicker/propTypes.js
+++ b/src/components/AttachmentPicker/propTypes.js
@@ -1,9 +1,24 @@
 import PropTypes from 'prop-types';
 
 const propTypes = {
-    /** renderProp with the following interface
+    /**
+     * A renderProp with the following interface
+     *
      * @example
-     * function ({ openPicker: ({ onPicked: (file): void }}): void }): ReactNode */
+     * <AttachmentPicker>
+     * {({openPicker}) => (
+     *     <Button
+     *         onPress={() => {
+     *             openPicker({
+     *                 onPicked: (file) => {
+     *                     // Display or upload File
+     *                 },
+     *             });
+     *         }}
+     *     />
+     * )}
+     * </AttachmentPicker>
+     * */
     children: PropTypes.func.isRequired,
 };
 

From 2f35a55f7e37df49424b21e47f40518bebe0b261 Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Thu, 25 Mar 2021 23:51:49 +0200
Subject: [PATCH 04/23] refactor: convert AttachmentPicker to class and basic
 rendering

---
 .../AttachmentPicker/index.native.js          | 91 ++++++++++++++++---
 1 file changed, 80 insertions(+), 11 deletions(-)

diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index 602df1d03bba..d8e230ec850d 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -1,10 +1,21 @@
 /**
  * The react native image/document pickers work for iOS/Android, but we want to wrap them both within AttachmentPicker
  */
-import {Alert, Linking} from 'react-native';
+import React, {Component} from 'react';
+import {Alert, Linking, View} from 'react-native';
 import RNImagePicker from 'react-native-image-picker';
 import RNDocumentPicker from 'react-native-document-picker';
-import propTypes from './propTypes';
+import basePropTypes from './propTypes';
+import styles from '../../styles/styles';
+import Popover from '../Popover';
+import MenuItem from '../MenuItem';
+import {Paperclip} from '../Icon/Expensicons';
+import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions';
+
+const propTypes = {
+    ...basePropTypes,
+    ...windowDimensionsPropTypes,
+};
 
 /**
  * See https://github.com/react-native-community/react-native-image-picker/blob/master/docs/Reference.md#options
@@ -99,17 +110,75 @@ function getDataForUpload(fileData) {
  * returns a "show attachment picker" method that takes
  * a callback. This is the ios/android implementation
  * opening a modal with attachment options
- *
- * @returns {React.FunctionComponent}
  */
-const AttachmentPicker = ({children}) => children({
-    openPicker: ({onPicked}) => {
-        show((response) => {
-            onPicked(getDataForUpload(response));
+class AttachmentPicker extends Component {
+    constructor(...args) {
+        super(...args);
+
+        this.state = {};
+        this.resetState();
+        this.handlePopoverClose = this.handlePopoverClose.bind(this);
+    }
+
+    handlePopoverClose() {
+        // Todo: call `onPicked` with result
+        this.state.onPicked(null);
+        this.setState({isVisible: false});
+    }
+
+    resetState() {
+        this.setState({isVisible: false, onPicked: () => {}});
+    }
+
+    renderMenuItems() {
+        const menuItemData = [
+            {
+                icon: Paperclip,
+                text: 'Take Photo',
+            },
+            {
+                icon: Paperclip,
+                text: 'Choose from Gallery',
+            },
+            {
+                icon: Paperclip,
+                text: 'Choose Document',
+            },
+        ];
+
+        return menuItemData.map(({icon, text}) => (
+            <MenuItem key={text} icon={icon} title={text} onPress={this.handlePopoverClose} />
+        ));
+    }
+
+    renderChildren() {
+        return this.props.children({
+            openPicker: ({onPicked}) => {
+                // Todo: perhaps we want some error handling here if onPicked is missing
+                // As this is not a prop it won't be caught by prop types
+                this.setState({isVisible: true, onPicked});
+            },
         });
-    },
-});
+    }
+
+    render() {
+        return (
+            <>
+                <Popover
+                    onClose={this.handlePopoverClose}
+                    isVisible={this.state.isVisible}
+                    anchorPosition={styles.createMenuPosition}
+                >
+                    <View style={this.props.isSmallScreenWidth ? {} : styles.createMenuContainer}>
+                        {this.renderMenuItems()}
+                    </View>
+                </Popover>
+                {this.renderChildren()}
+            </>
+        );
+    }
+}
 
 AttachmentPicker.propTypes = propTypes;
 AttachmentPicker.displayName = 'AttachmentPicker';
-export default AttachmentPicker;
+export default withWindowDimensions(AttachmentPicker);

From c68147732117679509d6e66da60531c2f0b25978 Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Fri, 26 Mar 2021 05:02:02 +0200
Subject: [PATCH 05/23] refactor: AttachmentPicker add image and document pick
 handling

---
 .../AttachmentPicker/index.native.js          | 157 ++++++++++--------
 src/pages/home/report/ReportActionCompose.js  |  17 +-
 2 files changed, 100 insertions(+), 74 deletions(-)

diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index d8e230ec850d..8042138f5775 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -22,10 +22,6 @@ const propTypes = {
  * for ImagePicker configuration options
  */
 const imagePickerOptions = {
-    title: 'Select an Attachment',
-    takePhotoButtonTitle: 'Take Photo',
-    chooseFromLibraryButtonTitle: 'Choose from Gallery',
-    customButtons: [{name: 'Document', title: 'Choose Document'}],
     storageOptions: {
         skipBackup: true,
     },
@@ -39,54 +35,63 @@ const documentPickerOptions = {
 };
 
 /**
- * Launch the DocumentPicker. Results are in same format as ImagePicker, so we can pass the repsonse to the
- * callback as is.
+ * Launch the DocumentPicker. Results are in the same format as ImagePicker
  *
- * @param {Object} callback
+ * @returns {Promise<DocumentPickerResponse>}
  */
-function showDocumentPicker(callback) {
-    RNDocumentPicker.pick(documentPickerOptions).then((results) => {
-        callback(results);
-    }).catch((error) => {
+function showDocumentPicker() {
+    return RNDocumentPicker.pick(documentPickerOptions).catch((error) => {
         if (!RNDocumentPicker.isCancel(error)) {
             throw error;
         }
     });
 }
 
+function showPermissionsAlert() {
+    Alert.alert(
+        'ExpensifyCash does not have access to your camera. To enable access, tap Settings and turn on Camera.',
+        '',
+        [
+            {
+                text: 'Cancel',
+                style: 'cancel',
+            },
+            {
+                text: 'Settings',
+                onPress: () => Linking.openURL('app-settings:'),
+            },
+        ],
+        {cancelable: false},
+    );
+}
+
+function showGeneralAlert(message) {
+    Alert.alert(
+        'An error is preventing us to use handle the attachment',
+        message, // Todo: maybe we don't want to show this as it's probably not human friendly
+    );
+}
+
 /**
- * Launch the AttachmentPicker. We display the ImagePicker first, as the document option is displayed as a
- * custom ImagePicker list item.
+ * Common select image picker handling
  *
- * @param {Object} callback
+ * @param {function} imagePickerFunc - RNImagePicker.launchCamera or RNImagePicker.launchImageLibrary
+ * @returns {Promise<ImagePickerResponse>}
  */
-function show(callback) {
-    RNImagePicker.showImagePicker(imagePickerOptions, (response) => {
-        if (response.error) {
-            if (response.error === 'Camera permissions not granted') {
-                Alert.alert(
-                    // eslint-disable-next-line max-len
-                    'ExpensifyCash does not have access to your camera. To enable access, tap Settings and turn on Camera.',
-                    '',
-                    [
-                        {
-                            text: 'Cancel',
-                            style: 'cancel',
-                        },
-                        {
-                            text: 'Settings',
-                            onPress: () => Linking.openURL('app-settings:'),
-                        },
-                    ],
-                    {cancelable: false},
-                );
+function selectImage(imagePickerFunc) {
+    return new Promise((resolve, reject) => {
+        imagePickerFunc(imagePickerOptions, (response) => {
+            if (response.error) {
+                switch (response.error) {
+                    case 'Camera permissions not granted': showPermissionsAlert(); break;
+                    default: showGeneralAlert(response.error); break;
+                }
+
+                reject(new Error(`Error during attachment selection: ${response.error}`));
             }
-            console.debug(`Error during attachment selection: ${response.error}`);
-        } else if (response.customButton) {
-            showDocumentPicker(callback);
-        } else if (!response.didCancel) {
-            callback(response);
-        }
+
+            resolve(response);
+        });
     });
 }
 
@@ -115,49 +120,56 @@ class AttachmentPicker extends Component {
     constructor(...args) {
         super(...args);
 
-        this.state = {};
-        this.resetState();
-        this.handlePopoverClose = this.handlePopoverClose.bind(this);
-    }
+        this.state = {isVisible: false, onPicked: () => {}, result: null};
 
-    handlePopoverClose() {
-        // Todo: call `onPicked` with result
-        this.state.onPicked(null);
-        this.setState({isVisible: false});
-    }
-
-    resetState() {
-        this.setState({isVisible: false, onPicked: () => {}});
-    }
-
-    renderMenuItems() {
-        const menuItemData = [
+        this.menuItemData = [
             {
                 icon: Paperclip,
                 text: 'Take Photo',
+                pickAttachment: () => selectImage(RNImagePicker.launchCamera),
             },
             {
                 icon: Paperclip,
                 text: 'Choose from Gallery',
+                pickAttachment: () => selectImage(RNImagePicker.launchImageLibrary),
             },
             {
                 icon: Paperclip,
                 text: 'Choose Document',
+                pickAttachment: showDocumentPicker,
             },
         ];
 
-        return menuItemData.map(({icon, text}) => (
-            <MenuItem key={text} icon={icon} title={text} onPress={this.handlePopoverClose} />
-        ));
+        this.setResult = this.setResult.bind(this);
+        this.close = this.close.bind(this);
+        this.onModalHide = this.onModalHide.bind(this);
+    }
+
+    onModalHide() {
+        if (this.state.result) {
+            this.state.onPicked(this.state.result);
+        }
+    }
+
+    setResult(result) {
+        if (result && !result.didCancel && !result.error) {
+            this.setState({result: getDataForUpload(result)});
+        }
+    }
+
+    open(onPicked) {
+        // Todo: perhaps we want some error handling here if onPicked is missing
+        // As this is not a prop it won't be caught by prop types
+        this.setState({isVisible: true, onPicked, result: null});
+    }
+
+    close() {
+        this.setState({isVisible: false});
     }
 
     renderChildren() {
         return this.props.children({
-            openPicker: ({onPicked}) => {
-                // Todo: perhaps we want some error handling here if onPicked is missing
-                // As this is not a prop it won't be caught by prop types
-                this.setState({isVisible: true, onPicked});
-            },
+            openPicker: ({onPicked}) => this.open(onPicked),
         });
     }
 
@@ -165,12 +177,25 @@ class AttachmentPicker extends Component {
         return (
             <>
                 <Popover
-                    onClose={this.handlePopoverClose}
+                    onClose={this.close}
                     isVisible={this.state.isVisible}
                     anchorPosition={styles.createMenuPosition}
+                    onModalHide={this.onModalHide}
                 >
                     <View style={this.props.isSmallScreenWidth ? {} : styles.createMenuContainer}>
-                        {this.renderMenuItems()}
+                        {
+                            this.menuItemData.map(({icon, text, pickAttachment}) => (
+                                <MenuItem
+                                    key={text}
+                                    icon={icon}
+                                    title={text}
+                                    onPress={() => pickAttachment()
+                                        .then(this.setResult)
+                                        .then(this.close)
+                                        .catch(console.error)}
+                                />
+                            ))
+                        }
                     </View>
                 </Popover>
                 {this.renderChildren()}
diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js
index 85eb443286c1..7da2438edcef 100644
--- a/src/pages/home/report/ReportActionCompose.js
+++ b/src/pages/home/report/ReportActionCompose.js
@@ -83,10 +83,11 @@ class ReportActionCompose extends React.Component {
      * @param {Boolean} shouldHighlight
      */
     setIsFocused(shouldHighlight) {
-        this.setState({isFocused: shouldHighlight});
-        if (shouldHighlight && this.textInput) {
+        if (shouldHighlight && this.textInput && !this.state.isFocused) {
             this.textInput.focus();
         }
+
+        this.setState({isFocused: shouldHighlight});
     }
 
     /**
@@ -99,7 +100,7 @@ class ReportActionCompose extends React.Component {
     }
 
     /**
-     * Updates the visiblity state of the menu
+     * Updates the visibility state of the menu
      *
      * @param {Boolean} isMenuVisible
      */
@@ -223,11 +224,11 @@ class ReportActionCompose extends React.Component {
                                                         icon: Paperclip,
                                                         text: 'Upload Photo',
                                                         onSelected: () => {
-                                                            setTimeout(() => {
-                                                                openPicker({
-                                                                    onPicked: file => displayFileInModal({file}),
-                                                                });
-                                                            }, 10);
+                                                            openPicker({
+                                                                onPicked: (file) => {
+                                                                    displayFileInModal({file});
+                                                                },
+                                                            });
                                                         },
                                                     },
                                                 ]}

From abbb994e6065b6520eb7e6851363cf04526e035e Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Fri, 26 Mar 2021 14:36:32 +0200
Subject: [PATCH 06/23] docs: add method documentation for AttachmentPicker

---
 .../AttachmentPicker/index.native.js          | 22 +++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index 8042138f5775..2bf76fae4b56 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -42,11 +42,15 @@ const documentPickerOptions = {
 function showDocumentPicker() {
     return RNDocumentPicker.pick(documentPickerOptions).catch((error) => {
         if (!RNDocumentPicker.isCancel(error)) {
+            // Todo: alert the user that interaction failed for some reason
             throw error;
         }
     });
 }
 
+/**
+ * Inform the users when they need to grant camera access and guide them to settings
+ */
 function showPermissionsAlert() {
     Alert.alert(
         'ExpensifyCash does not have access to your camera. To enable access, tap Settings and turn on Camera.',
@@ -65,6 +69,11 @@ function showPermissionsAlert() {
     );
 }
 
+/**
+ * A generic handling when we don't know the exact reason for an error
+ *
+ * @param {String} message
+ */
 function showGeneralAlert(message) {
     Alert.alert(
         'An error is preventing us to use handle the attachment',
@@ -145,12 +154,20 @@ class AttachmentPicker extends Component {
         this.onModalHide = this.onModalHide.bind(this);
     }
 
+    /**
+     * After the modal closes, if an attachment was selected delegate it to the `onPicked` callback
+     */
     onModalHide() {
         if (this.state.result) {
             this.state.onPicked(this.state.result);
         }
     }
 
+    /**
+     * Store the selected attachment mapped to an appropriate file interface
+     *
+     * @param {ImagePickerResponse|DocumentPickerResponse} result
+     */
     setResult(result) {
         if (result && !result.didCancel && !result.error) {
             this.setState({result: getDataForUpload(result)});
@@ -167,6 +184,11 @@ class AttachmentPicker extends Component {
         this.setState({isVisible: false});
     }
 
+    /**
+     * Call the `children` renderProp with the interface defined in propTypes
+     *
+     * @returns {React.ReactNode}
+     */
     renderChildren() {
         return this.props.children({
             openPicker: ({onPicked}) => this.open(onPicked),

From e006ee7e9938005694e4ad7fd328d68d92fb0119 Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Fri, 26 Mar 2021 17:21:50 +0200
Subject: [PATCH 07/23] refactor: AttachmentPicker add error handling for the
 document picker too

---
 .../AttachmentPicker/index.native.js          | 28 +++++++++----------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index 2bf76fae4b56..d8d0bd8fbc83 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -34,20 +34,6 @@ const documentPickerOptions = {
     type: [RNDocumentPicker.types.allFiles],
 };
 
-/**
- * Launch the DocumentPicker. Results are in the same format as ImagePicker
- *
- * @returns {Promise<DocumentPickerResponse>}
- */
-function showDocumentPicker() {
-    return RNDocumentPicker.pick(documentPickerOptions).catch((error) => {
-        if (!RNDocumentPicker.isCancel(error)) {
-            // Todo: alert the user that interaction failed for some reason
-            throw error;
-        }
-    });
-}
-
 /**
  * Inform the users when they need to grant camera access and guide them to settings
  */
@@ -81,6 +67,20 @@ function showGeneralAlert(message) {
     );
 }
 
+/**
+ * Launch the DocumentPicker. Results are in the same format as ImagePicker
+ *
+ * @returns {Promise<DocumentPickerResponse>}
+ */
+function showDocumentPicker() {
+    return RNDocumentPicker.pick(documentPickerOptions).catch((error) => {
+        if (!RNDocumentPicker.isCancel(error)) {
+            showGeneralAlert(error.message);
+            throw error;
+        }
+    });
+}
+
 /**
  * Common select image picker handling
  *

From 8af8deb656f96fa10a7a7ecd007e73ac419d54e6 Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Fri, 26 Mar 2021 17:45:33 +0200
Subject: [PATCH 08/23] refactor: rename func to showImagePicker to be inline
 with showDocumentPicker

---
 src/components/AttachmentPicker/index.native.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index d8d0bd8fbc83..19fd6c93d1de 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -82,12 +82,12 @@ function showDocumentPicker() {
 }
 
 /**
- * Common select image picker handling
+ * Common image picker handling
  *
  * @param {function} imagePickerFunc - RNImagePicker.launchCamera or RNImagePicker.launchImageLibrary
  * @returns {Promise<ImagePickerResponse>}
  */
-function selectImage(imagePickerFunc) {
+function showImagePicker(imagePickerFunc) {
     return new Promise((resolve, reject) => {
         imagePickerFunc(imagePickerOptions, (response) => {
             if (response.error) {
@@ -135,12 +135,12 @@ class AttachmentPicker extends Component {
             {
                 icon: Paperclip,
                 text: 'Take Photo',
-                pickAttachment: () => selectImage(RNImagePicker.launchCamera),
+                pickAttachment: () => showImagePicker(RNImagePicker.launchCamera),
             },
             {
                 icon: Paperclip,
                 text: 'Choose from Gallery',
-                pickAttachment: () => selectImage(RNImagePicker.launchImageLibrary),
+                pickAttachment: () => showImagePicker(RNImagePicker.launchImageLibrary),
             },
             {
                 icon: Paperclip,

From a3c043c2c4efde701c69cab69145394f11656441 Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Fri, 26 Mar 2021 17:50:26 +0200
Subject: [PATCH 09/23] refactor: AttachmentPicker.open should throw when the
 interface is not respected

Not calling the render prop with the correct interface is a developer mistake
that we can catch easily if we throw an error here
---
 src/components/AttachmentPicker/index.native.js | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index 19fd6c93d1de..9940b42ff9e2 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -175,8 +175,12 @@ class AttachmentPicker extends Component {
     }
 
     open(onPicked) {
-        // Todo: perhaps we want some error handling here if onPicked is missing
-        // As this is not a prop it won't be caught by prop types
+        if (typeof onPicked !== 'function') {
+            throw new Error(
+                `Invalid onPicked parameter. Check the children passed to: ${AttachmentPicker.displayName}`,
+            );
+        }
+
         this.setState({isVisible: true, onPicked, result: null});
     }
 

From d517cdf854f57022d310ac183b7c9d4c9f75fa7c Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Fri, 26 Mar 2021 21:23:42 +0200
Subject: [PATCH 10/23] Add new icons for the Attachment Picker Menu

---
 assets/images/camera.svg                        | 8 ++++++++
 assets/images/gallery.svg                       | 8 ++++++++
 src/components/AttachmentPicker/index.native.js | 6 +++---
 src/components/Icon/Expensicons.js              | 4 ++++
 4 files changed, 23 insertions(+), 3 deletions(-)
 create mode 100644 assets/images/camera.svg
 create mode 100644 assets/images/gallery.svg

diff --git a/assets/images/camera.svg b/assets/images/camera.svg
new file mode 100644
index 000000000000..65c5e9301417
--- /dev/null
+++ b/assets/images/camera.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve">
+<path d="M17.5,5H15l-1.1-1.1c0,0-0.1-0.1-0.4-0.1h-7C6.2,3.8,6.1,3.9,6,4L5,5H2.5C1.8,5,1.2,5.6,1.2,6.2v9.2c0,1.1,0.9,2,2,2h13.5
+	c1.1,0,2-0.9,2-2V6.2C18.8,5.6,18.2,5,17.5,5z M10,13.8c-1.4,0-2.5-1.1-2.5-2.5S8.6,8.8,10,8.8s2.5,1.1,2.5,2.5S11.4,13.8,10,13.8z"
+	/>
+</svg>
diff --git a/assets/images/gallery.svg b/assets/images/gallery.svg
new file mode 100644
index 000000000000..b79d2010c02f
--- /dev/null
+++ b/assets/images/gallery.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve">
+<path d="M18.1,3.8H1.9C1.5,3.8,1.2,4,1.2,4.4v12.5c0,0.4,0.2,0.6,0.6,0.6h16.2c0.4,0,0.6-0.2,0.6-0.6V4.4C18.8,4,18.5,3.8,18.1,3.8z
+	 M14.5,15H4.8c-0.5,0-0.8-0.5-0.6-0.9l2.5-5.4c0.2-0.5,0.9-0.5,1.1,0l2.2,4.9l1.9-2.5c0.2-0.4,0.8-0.4,1,0L15,14
+	C15.2,14.4,15,15,14.5,15z M15,8.8c-0.8,0-1.2-0.5-1.2-1.2s0.5-1.2,1.2-1.2s1.2,0.5,1.2,1.2S15.8,8.8,15,8.8z"/>
+</svg>
diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index 9940b42ff9e2..890b8b8f5707 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -9,7 +9,7 @@ import basePropTypes from './propTypes';
 import styles from '../../styles/styles';
 import Popover from '../Popover';
 import MenuItem from '../MenuItem';
-import {Paperclip} from '../Icon/Expensicons';
+import {Camera, Gallery, Paperclip} from '../Icon/Expensicons';
 import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions';
 
 const propTypes = {
@@ -133,12 +133,12 @@ class AttachmentPicker extends Component {
 
         this.menuItemData = [
             {
-                icon: Paperclip,
+                icon: Camera,
                 text: 'Take Photo',
                 pickAttachment: () => showImagePicker(RNImagePicker.launchCamera),
             },
             {
-                icon: Paperclip,
+                icon: Gallery,
                 text: 'Choose from Gallery',
                 pickAttachment: () => showImagePicker(RNImagePicker.launchImageLibrary),
             },
diff --git a/src/components/Icon/Expensicons.js b/src/components/Icon/Expensicons.js
index fbc964a95bcf..d40bcc347ed3 100644
--- a/src/components/Icon/Expensicons.js
+++ b/src/components/Icon/Expensicons.js
@@ -25,6 +25,8 @@ import Wallet from '../../../assets/images/wallet.svg';
 import Lock from '../../../assets/images/lock.svg';
 import ArrowRight from '../../../assets/images/arrow-right.svg';
 import Upload from '../../../assets/images/upload.svg';
+import Camera from '../../../assets/images/camera.svg';
+import Gallery from '../../../assets/images/gallery.svg';
 
 export {
     BackArrow,
@@ -54,4 +56,6 @@ export {
     Lock,
     ArrowRight,
     Upload,
+    Camera,
+    Gallery,
 };

From a6ac191d4ae39d77237f14de871fa4515f3373b1 Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Mon, 29 Mar 2021 13:35:40 +0300
Subject: [PATCH 11/23] refactor: ReportActionCompose add separate focus/blur
 methods

`setIsFocused` should be used only to set/unset the highlight styles as documented

The text input already calls setIsFocused in the event of focus/blur
Added separate methods to better control this behavior
This would also remove a direct call to `setState` inside `componentDidUpdate`
---
 src/pages/home/report/ReportActionCompose.js | 27 ++++++++++++++++----
 1 file changed, 22 insertions(+), 5 deletions(-)

diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js
index 7da2438edcef..c40b5630aec2 100644
--- a/src/pages/home/report/ReportActionCompose.js
+++ b/src/pages/home/report/ReportActionCompose.js
@@ -60,6 +60,8 @@ class ReportActionCompose extends React.Component {
         this.triggerSubmitShortcut = this.triggerSubmitShortcut.bind(this);
         this.submitForm = this.submitForm.bind(this);
         this.setIsFocused = this.setIsFocused.bind(this);
+        this.focus = this.focus.bind(this);
+        this.blur = this.blur.bind(this);
         this.comment = props.comment;
 
         this.state = {
@@ -73,7 +75,7 @@ class ReportActionCompose extends React.Component {
     componentDidUpdate(prevProps) {
         // When any modal goes from visible to hidden, bring focus to the compose field
         if (prevProps.modal.isVisible && !this.props.modal.isVisible) {
-            this.setIsFocused(true);
+            this.focus();
         }
     }
 
@@ -83,10 +85,6 @@ class ReportActionCompose extends React.Component {
      * @param {Boolean} shouldHighlight
      */
     setIsFocused(shouldHighlight) {
-        if (shouldHighlight && this.textInput && !this.state.isFocused) {
-            this.textInput.focus();
-        }
-
         this.setState({isFocused: shouldHighlight});
     }
 
@@ -108,6 +106,24 @@ class ReportActionCompose extends React.Component {
         this.setState({isMenuVisible});
     }
 
+    /**
+     * Focus the composer text input
+     */
+    focus() {
+        if (this.textInput) {
+            this.textInput.focus();
+        }
+    }
+
+    /**
+     * Un-focus from the composer text input
+     */
+    blur() {
+        if (this.textInput) {
+            this.textInput.blur();
+        }
+    }
+
     /**
      * Save our report comment in Onyx. We debounce this method in the constructor so that it's not called too often
      * to update Onyx and re-render this component.
@@ -199,6 +215,7 @@ class ReportActionCompose extends React.Component {
                             addAction(this.props.reportID, '', file);
                             this.setTextInputShouldClear(false);
                         }}
+                        onModalHide={this.focus}
                     >
                         {({displayFileInModal}) => (
                             <>

From 2f664999b36cbda97d6fe5b527d5032ec825f48f Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Mon, 29 Mar 2021 13:38:38 +0300
Subject: [PATCH 12/23] optimization: ReportActionCompose blur the input before
 opening the first modal

On iOS when the modal closes the keyboard will appear again as focus is restored
to the last element. This creates a choppy transition between modals
E.g. create menu -> select attachment
---
 src/pages/home/report/ReportActionCompose.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js
index c40b5630aec2..f19b4b2f7d93 100644
--- a/src/pages/home/report/ReportActionCompose.js
+++ b/src/pages/home/report/ReportActionCompose.js
@@ -226,6 +226,9 @@ class ReportActionCompose extends React.Component {
                                                 onPress={(e) => {
                                                     e.preventDefault();
                                                     this.setMenuVisibility(true);
+
+                                                    // Hide the keyboard during a modal to modal transition
+                                                    this.blur();
                                                 }}
                                                 style={styles.chatItemAttachButton}
                                                 underlayColor={themeColors.componentBG}

From 3b335fbf83af97c33e8a27da9d85b5a2d4c52176 Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Tue, 30 Mar 2021 14:04:57 +0300
Subject: [PATCH 13/23] reverted changes made to
 ReportActionCompose.componentDidUpdate

---
 src/pages/home/report/ReportActionCompose.js | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js
index f19b4b2f7d93..2fd101d91a18 100644
--- a/src/pages/home/report/ReportActionCompose.js
+++ b/src/pages/home/report/ReportActionCompose.js
@@ -215,7 +215,6 @@ class ReportActionCompose extends React.Component {
                             addAction(this.props.reportID, '', file);
                             this.setTextInputShouldClear(false);
                         }}
-                        onModalHide={this.focus}
                     >
                         {({displayFileInModal}) => (
                             <>
@@ -226,9 +225,6 @@ class ReportActionCompose extends React.Component {
                                                 onPress={(e) => {
                                                     e.preventDefault();
                                                     this.setMenuVisibility(true);
-
-                                                    // Hide the keyboard during a modal to modal transition
-                                                    this.blur();
                                                 }}
                                                 style={styles.chatItemAttachButton}
                                                 underlayColor={themeColors.componentBG}

From 9137edf1871abeed190e18ccbb7337aeade678e1 Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Tue, 30 Mar 2021 14:22:24 +0300
Subject: [PATCH 14/23] refactor: ReportActionCompose keep focus inside the
 input

Closing a modal (on native) will restore focus to the last element

Current modal handling that's is forcing focus after each modal closes
would break modal to modal transition when the field was not initially
focused
---
 src/pages/home/report/ReportActionCompose.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js
index 2fd101d91a18..5b88bf7ac676 100644
--- a/src/pages/home/report/ReportActionCompose.js
+++ b/src/pages/home/report/ReportActionCompose.js
@@ -225,6 +225,10 @@ class ReportActionCompose extends React.Component {
                                                 onPress={(e) => {
                                                     e.preventDefault();
                                                     this.setMenuVisibility(true);
+
+                                                    /* Keep last focus inside the input so that focus is restored
+                                                     on modal close. Otherwise breaks modal 2 modal transition */
+                                                    this.focus();
                                                 }}
                                                 style={styles.chatItemAttachButton}
                                                 underlayColor={themeColors.componentBG}

From 40b827a7917a56b54b591ebe168be2d7f1a6813c Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Tue, 30 Mar 2021 19:46:53 +0300
Subject: [PATCH 15/23] docs: AttachmentPicker method documentation

---
 .../AttachmentPicker/index.native.js          | 31 ++++++++++++-------
 1 file changed, 20 insertions(+), 11 deletions(-)

diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index 890b8b8f5707..d434dbd7e20e 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -151,16 +151,7 @@ class AttachmentPicker extends Component {
 
         this.setResult = this.setResult.bind(this);
         this.close = this.close.bind(this);
-        this.onModalHide = this.onModalHide.bind(this);
-    }
-
-    /**
-     * After the modal closes, if an attachment was selected delegate it to the `onPicked` callback
-     */
-    onModalHide() {
-        if (this.state.result) {
-            this.state.onPicked(this.state.result);
-        }
+        this.completeAttachmentSelection = this.completeAttachmentSelection.bind(this);
     }
 
     /**
@@ -174,6 +165,21 @@ class AttachmentPicker extends Component {
         }
     }
 
+    /**
+     * Triggers the `onPicked` callback with the selected attachment
+     */
+    completeAttachmentSelection() {
+        if (this.state.result) {
+            this.state.onPicked(this.state.result);
+        }
+    }
+
+    /**
+     * Opens the attachment modal
+     *
+     * @param {function} onPicked A callback that will be called with the selected attachment
+     * @throws {Error} when the `onPicked` parameter is invalid
+     */
     open(onPicked) {
         if (typeof onPicked !== 'function') {
             throw new Error(
@@ -184,6 +190,9 @@ class AttachmentPicker extends Component {
         this.setState({isVisible: true, onPicked, result: null});
     }
 
+    /**
+     * Closes the attachment modal
+     */
     close() {
         this.setState({isVisible: false});
     }
@@ -206,7 +215,7 @@ class AttachmentPicker extends Component {
                     onClose={this.close}
                     isVisible={this.state.isVisible}
                     anchorPosition={styles.createMenuPosition}
-                    onModalHide={this.onModalHide}
+                    onModalHide={this.completeAttachmentSelection}
                 >
                     <View style={this.props.isSmallScreenWidth ? {} : styles.createMenuContainer}>
                         {

From 728870168703862c431c98ca602cf93b4d1cc0ef Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Tue, 30 Mar 2021 19:47:44 +0300
Subject: [PATCH 16/23] refactor: AttachmentPicker update switch signature

---
 src/components/AttachmentPicker/index.native.js | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index d434dbd7e20e..5b228f18a0d7 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -92,8 +92,12 @@ function showImagePicker(imagePickerFunc) {
         imagePickerFunc(imagePickerOptions, (response) => {
             if (response.error) {
                 switch (response.error) {
-                    case 'Camera permissions not granted': showPermissionsAlert(); break;
-                    default: showGeneralAlert(response.error); break;
+                    case 'Camera permissions not granted':
+                        showPermissionsAlert();
+                        break;
+                    default:
+                        showGeneralAlert(response.error);
+                        break;
                 }
 
                 reject(new Error(`Error during attachment selection: ${response.error}`));

From 53ae2668d483a3b006fe2cbbe13ad68b0f3592a9 Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Tue, 30 Mar 2021 19:49:19 +0300
Subject: [PATCH 17/23] refactor: AttachmentPicker Update camera permission
 message

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

diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index 5b228f18a0d7..a90ac544dd68 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -39,7 +39,7 @@ const documentPickerOptions = {
  */
 function showPermissionsAlert() {
     Alert.alert(
-        'ExpensifyCash does not have access to your camera. To enable access, tap Settings and turn on Camera.',
+        'Expensify.cash does not have access to your camera. To enable access, tap Settings and turn on Camera.',
         '',
         [
             {

From c98dac683f3899b9dc909bf7cebe8f7a78f0b5d2 Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Tue, 30 Mar 2021 20:03:56 +0300
Subject: [PATCH 18/23] refactor: AttachmentPicker address state and usages

---
 .../AttachmentPicker/index.native.js          | 21 +++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index a90ac544dd68..db8eb5f55a0b 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -133,7 +133,11 @@ class AttachmentPicker extends Component {
     constructor(...args) {
         super(...args);
 
-        this.state = {isVisible: false, onPicked: () => {}, result: null};
+        this.state = {
+            isVisible: false,
+            result: null,
+            onPicked: () => {},
+        };
 
         this.menuItemData = [
             {
@@ -161,11 +165,12 @@ class AttachmentPicker extends Component {
     /**
      * Store the selected attachment mapped to an appropriate file interface
      *
-     * @param {ImagePickerResponse|DocumentPickerResponse} result
+     * @param {ImagePickerResponse|DocumentPickerResponse} attachment
      */
-    setResult(result) {
-        if (result && !result.didCancel && !result.error) {
-            this.setState({result: getDataForUpload(result)});
+    setResult(attachment) {
+        if (attachment && !attachment.didCancel && !attachment.error) {
+            const result = getDataForUpload(attachment);
+            this.setState({result});
         }
     }
 
@@ -191,7 +196,11 @@ class AttachmentPicker extends Component {
             );
         }
 
-        this.setState({isVisible: true, onPicked, result: null});
+        this.setState({
+            isVisible: true,
+            result: null,
+            onPicked,
+        });
     }
 
     /**

From c61c8b20f838e5fbd509a7f4c9137708ef748ea4 Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Tue, 30 Mar 2021 20:12:14 +0300
Subject: [PATCH 19/23] fix: AttachmentPicker Handle Android permissions case

Since React Native 0.60 to open App settings use:
`Linking.openSettings()` - works on both android and ios

https://stackoverflow.com/a/58026920/4834103
---
 src/components/AttachmentPicker/index.native.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index db8eb5f55a0b..5355b6693dbc 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -48,7 +48,7 @@ function showPermissionsAlert() {
             },
             {
                 text: 'Settings',
-                onPress: () => Linking.openURL('app-settings:'),
+                onPress: () => Linking.openSettings(),
             },
         ],
         {cancelable: false},
@@ -93,6 +93,7 @@ function showImagePicker(imagePickerFunc) {
             if (response.error) {
                 switch (response.error) {
                     case 'Camera permissions not granted':
+                    case 'Permissions weren\'t granted':
                         showPermissionsAlert();
                         break;
                     default:

From 4b4b6fa366222a491db3052cba0a7ca7c38a130d Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Wed, 31 Mar 2021 14:57:59 +0300
Subject: [PATCH 20/23] refactor: AttachmentPicker addressed error messages

---
 src/components/AttachmentPicker/index.native.js | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index 5355b6693dbc..a6d337c1d00d 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -39,8 +39,8 @@ const documentPickerOptions = {
  */
 function showPermissionsAlert() {
     Alert.alert(
-        'Expensify.cash does not have access to your camera. To enable access, tap Settings and turn on Camera.',
-        '',
+        'Expensify.cash does not have access to your camera.',
+        'Please enable the permission and try again.',
         [
             {
                 text: 'Cancel',
@@ -58,12 +58,11 @@ function showPermissionsAlert() {
 /**
  * A generic handling when we don't know the exact reason for an error
  *
- * @param {String} message
  */
-function showGeneralAlert(message) {
+function showGeneralAlert() {
     Alert.alert(
-        'An error is preventing us to use handle the attachment',
-        message, // Todo: maybe we don't want to show this as it's probably not human friendly
+        'Oops. An error occurred while selecting an attachment.',
+        'Please try again.',
     );
 }
 

From 982a7cd01f329e209271019cf4683f3cd59462be Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Wed, 31 Mar 2021 15:00:18 +0300
Subject: [PATCH 21/23] refactor: AttachmentPicker rename propTypes to
 AttachmentPickerPropTypes

---
 .../{propTypes.js => AttachmentPickerPropTypes.js}              | 0
 src/components/AttachmentPicker/index.js                        | 2 +-
 src/components/AttachmentPicker/index.native.js                 | 2 +-
 3 files changed, 2 insertions(+), 2 deletions(-)
 rename src/components/AttachmentPicker/{propTypes.js => AttachmentPickerPropTypes.js} (100%)

diff --git a/src/components/AttachmentPicker/propTypes.js b/src/components/AttachmentPicker/AttachmentPickerPropTypes.js
similarity index 100%
rename from src/components/AttachmentPicker/propTypes.js
rename to src/components/AttachmentPicker/AttachmentPickerPropTypes.js
diff --git a/src/components/AttachmentPicker/index.js b/src/components/AttachmentPicker/index.js
index b414266e53fb..425e04d9ca72 100644
--- a/src/components/AttachmentPicker/index.js
+++ b/src/components/AttachmentPicker/index.js
@@ -1,5 +1,5 @@
 import React from 'react';
-import propTypes from './propTypes';
+import propTypes from './AttachmentPickerPropTypes';
 
 /**
  * This component renders a function as a child and
diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index a6d337c1d00d..e5038b4d52e1 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -5,7 +5,7 @@ import React, {Component} from 'react';
 import {Alert, Linking, View} from 'react-native';
 import RNImagePicker from 'react-native-image-picker';
 import RNDocumentPicker from 'react-native-document-picker';
-import basePropTypes from './propTypes';
+import basePropTypes from './AttachmentPickerPropTypes';
 import styles from '../../styles/styles';
 import Popover from '../Popover';
 import MenuItem from '../MenuItem';

From 2396ac59308a0978a50118fb27907afce5619a6a Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Wed, 31 Mar 2021 19:01:37 +0300
Subject: [PATCH 22/23] refactor: AttachmentPicker better alert messages

---
 src/components/AttachmentPicker/index.native.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index e5038b4d52e1..e469d696e1e2 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -39,8 +39,8 @@ const documentPickerOptions = {
  */
 function showPermissionsAlert() {
     Alert.alert(
-        'Expensify.cash does not have access to your camera.',
-        'Please enable the permission and try again.',
+        'Camera Permission Required',
+        'Expensify.cash does not have access to your camera, please enable the permission and try again.',
         [
             {
                 text: 'Cancel',
@@ -61,8 +61,8 @@ function showPermissionsAlert() {
  */
 function showGeneralAlert() {
     Alert.alert(
-        'Oops. An error occurred while selecting an attachment.',
-        'Please try again.',
+        'Attachment Error',
+        'An error occurred while selecting an attachment, please try again',
     );
 }
 

From 08d8d7b7cdfa87fba4395a9fe71fc119d2f5381f Mon Sep 17 00:00:00 2001
From: Peter Velkov <kidroca@gmail.com>
Date: Fri, 2 Apr 2021 17:18:17 +0300
Subject: [PATCH 23/23] refactor: address review requested changes

---
 src/components/AttachmentPicker/index.native.js |  7 -------
 src/pages/home/report/ReportActionCompose.js    | 10 ----------
 2 files changed, 17 deletions(-)

diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js
index e469d696e1e2..085a4061ce29 100644
--- a/src/components/AttachmentPicker/index.native.js
+++ b/src/components/AttachmentPicker/index.native.js
@@ -187,15 +187,8 @@ class AttachmentPicker extends Component {
      * Opens the attachment modal
      *
      * @param {function} onPicked A callback that will be called with the selected attachment
-     * @throws {Error} when the `onPicked` parameter is invalid
      */
     open(onPicked) {
-        if (typeof onPicked !== 'function') {
-            throw new Error(
-                `Invalid onPicked parameter. Check the children passed to: ${AttachmentPicker.displayName}`,
-            );
-        }
-
         this.setState({
             isVisible: true,
             result: null,
diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js
index 5b88bf7ac676..42879452601e 100644
--- a/src/pages/home/report/ReportActionCompose.js
+++ b/src/pages/home/report/ReportActionCompose.js
@@ -61,7 +61,6 @@ class ReportActionCompose extends React.Component {
         this.submitForm = this.submitForm.bind(this);
         this.setIsFocused = this.setIsFocused.bind(this);
         this.focus = this.focus.bind(this);
-        this.blur = this.blur.bind(this);
         this.comment = props.comment;
 
         this.state = {
@@ -115,15 +114,6 @@ class ReportActionCompose extends React.Component {
         }
     }
 
-    /**
-     * Un-focus from the composer text input
-     */
-    blur() {
-        if (this.textInput) {
-            this.textInput.blur();
-        }
-    }
-
     /**
      * Save our report comment in Onyx. We debounce this method in the constructor so that it's not called too often
      * to update Onyx and re-render this component.
