From 158020bee9bdd3c10a394edce2065823a3b15d37 Mon Sep 17 00:00:00 2001 From: Joe Gambino Date: Thu, 3 Sep 2020 18:43:42 -0700 Subject: [PATCH 01/15] get image picker working on web and mobile --- ios/Podfile.lock | 8 ++- package-lock.json | 5 ++ package.json | 1 + src/lib/ImagePicker/index.js | 70 ++++++++++++++++++++ src/lib/ImagePicker/index.native.js | 7 ++ src/page/home/report/ReportHistoryCompose.js | 53 ++++++++++++--- 6 files changed, 132 insertions(+), 12 deletions(-) create mode 100644 src/lib/ImagePicker/index.js create mode 100644 src/lib/ImagePicker/index.native.js diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 474661e449fc..9013b3ab5278 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -240,6 +240,8 @@ PODS: - react-native-config/App (= 1.3.3) - react-native-config/App (1.3.3): - React + - react-native-image-picker (2.3.3): + - React - react-native-netinfo (5.9.6): - React - react-native-safe-area-context (3.1.4): @@ -350,6 +352,7 @@ DEPENDENCIES: - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) - react-native-config (from `../node_modules/react-native-config`) + - react-native-image-picker (from `../node_modules/react-native-image-picker`) - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - react-native-webview (from `../node_modules/react-native-webview`) @@ -414,6 +417,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/jsinspector" react-native-config: :path: "../node_modules/react-native-config" + react-native-image-picker: + :path: "../node_modules/react-native-image-picker" react-native-netinfo: :path: "../node_modules/@react-native-community/netinfo" react-native-safe-area-context: @@ -473,6 +478,7 @@ SPEC CHECKSUMS: React-jsiexecutor: 8ca588cc921e70590820ce72b8789b02c67cce38 React-jsinspector: b14e62ebe7a66e9231e9581279909f2fc3db6606 react-native-config: 9a061347e0136fdb32d43a34d60999297d672361 + react-native-image-picker: a6c3d644751a388b0fc8b56822ff7cbd398a3008 react-native-netinfo: d2c312fa4b151214e1d5c8456ddb5f28ff24a576 react-native-safe-area-context: 0ed9288ed4409beabb0817b54efc047286fc84da react-native-webview: 797f50d16bb271c4270bc742040a64c79ec7147c @@ -492,4 +498,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 7f6bb9c130ea13236f8249f800f75d671c1833fa -COCOAPODS: 1.8.4 +COCOAPODS: 1.9.3 diff --git a/package-lock.json b/package-lock.json index 65752b3847cc..a2a1e1c81d1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14676,6 +14676,11 @@ "resolved": "https://registry.npmjs.org/react-native-config/-/react-native-config-1.3.3.tgz", "integrity": "sha512-uc84IFVLqu2dC7Zutg3OlK6RZYvJlGbIdi0v1ZxjUhU379Nz9IkZR1t8J1L5QSnGV8885mtwWDPJRdQyVNFsAw==" }, + "react-native-image-picker": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-2.3.3.tgz", + "integrity": "sha512-i/7JDnxKkUGSbFY2i7YqFNn3ncJm1YlcrPKXrXmJ/YUElz8tHkuwknuqBd9QCJivMfHX41cmq4XvdBDwIOtO+A==" + }, "react-native-keyboard-spacer": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/react-native-keyboard-spacer/-/react-native-keyboard-spacer-0.4.1.tgz", diff --git a/package.json b/package.json index a84e998580a0..ac2e8151486f 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "react-dom": "^16.13.1", "react-native": "0.63.2", "react-native-config": "^1.3.3", + "react-native-image-picker": "^2.3.3", "react-native-keyboard-spacer": "^0.4.1", "react-native-render-html": "^4.2.3", "react-native-safe-area-context": "^3.1.4", diff --git a/src/lib/ImagePicker/index.js b/src/lib/ImagePicker/index.js new file mode 100644 index 000000000000..155e9edfa380 --- /dev/null +++ b/src/lib/ImagePicker/index.js @@ -0,0 +1,70 @@ +/** + * Trick for getting file selector cancel using `body.onfocus` event found at + * http://trishulgoel.com/handle-cancel-click-on-file-input/ + */ + +const {body} = document; +const input = document.createElement('input'); +input.type = 'file'; + +const ImagePicker = { + showImagePicker(callback) { + const {files} = input; + if (files) { + Array.prototype.forEach.call(files, this.removeUri); + } + + this.showImagePickerWrapper() + .then((result) => { + callback(result); + }) + .catch((result) => { + callback(result); + }); + }, + + showImagePickerWrapper() { + const self = this; + const promise = new Promise((resolve, reject) => { + function onfocus() { + body.removeEventListener('focus', onfocus, true); + + setTimeout(() => { + const {files} = input; + if (!files.length) { + // eslint-disable-next-line prefer-promise-reject-errors + return reject({ + didCancel: true + }); + } + + self.addUri(files[0]); + resolve({ + uri: files[0].uri + }); + }, 500); + } + + body.addEventListener('focus', onfocus, true); + }); + + input.click(); + + return promise; + }, + + addUri(file) { + const newFile = file; + newFile.uri = URL.createObjectURL(file); + + return newFile; + }, + + removeUri(file) { + URL.revokeObjectURL(file.uri); + + delete file.uri; + }, +}; + +export default ImagePicker; diff --git a/src/lib/ImagePicker/index.native.js b/src/lib/ImagePicker/index.native.js new file mode 100644 index 000000000000..7af139c8f70f --- /dev/null +++ b/src/lib/ImagePicker/index.native.js @@ -0,0 +1,7 @@ +/** + * The react native document picker already works for iOS/Android, so just export the imported document picker + */ +import RNImagePicker from 'react-native-image-picker'; + +const ImagePicker = RNImagePicker; +export default ImagePicker; diff --git a/src/page/home/report/ReportHistoryCompose.js b/src/page/home/report/ReportHistoryCompose.js index b85183bacb0e..e0a338e75cfc 100644 --- a/src/page/home/report/ReportHistoryCompose.js +++ b/src/page/home/report/ReportHistoryCompose.js @@ -5,8 +5,7 @@ import styles, {colors} from '../../../style/StyleSheet'; import TextInputFocusable from '../../../components/TextInputFocusable'; import sendIcon from '../../../../assets/images/icon-send.png'; import paperClipIcon from '../../../../assets/images/icon-paper-clip.png'; -import TouchableOpacityNewTab from '../../../components/TouchableOpacityNewTab'; -import CONFIG from '../../../CONFIG'; +import ImagePicker from '../../../lib/ImagePicker'; const propTypes = { // A method to call when the form is submitted @@ -24,6 +23,7 @@ class ReportHistoryCompose extends React.Component { this.submitForm = this.submitForm.bind(this); this.triggerSubmitShortcut = this.triggerSubmitShortcut.bind(this); this.submitForm = this.submitForm.bind(this); + this.onAttachmentButtonTapped = this.onAttachmentButtonTapped.bind(this); this.state = { comment: '', @@ -31,13 +31,34 @@ class ReportHistoryCompose extends React.Component { } /** - * Update the value of the comment input in the state + * Handle the attachment icon being tapped * - * @param {string} newComment + * @param {SyntheticEvent} [e] */ - updateComment(newComment) { - this.setState({ - comment: newComment, + onAttachmentButtonTapped(e) { + if (e) { + e.preventDefault(); + } + + ImagePicker.showImagePicker((response) => { + console.log('Response = ', response); + + if (response.didCancel) { + console.log('User cancelled image picker'); + } else if (response.error) { + console.log('ImagePicker Error: ', response.error); + } else if (response.customButton) { + console.log('User tapped custom button: ', response.customButton); + } else { + const source = {uri: response.uri}; + console.log('Source: ', source); + + // You can also display the image using data: + // const source = { uri: 'data:image/jpeg;base64,' + response.data }; + // this.setState({ + // avatarSource: source, + // }); + } }); } @@ -78,13 +99,23 @@ class ReportHistoryCompose extends React.Component { }); } + /** + * Update the value of the comment input in the state + * + * @param {string} newComment + */ + updateComment(newComment) { + this.setState({ + comment: newComment, + }); + } + render() { - const href = `${CONFIG.PUSHER.AUTH_URL}/report?reportID=${this.props.reportID}&shouldScrollToLastUnread=true`; return ( - @@ -93,7 +124,7 @@ class ReportHistoryCompose extends React.Component { resizeMode="contain" source={paperClipIcon} /> - + Date: Wed, 9 Sep 2020 17:08:07 -0700 Subject: [PATCH 02/15] add permissions on ios/android, add temp message for uploading attachments --- android/app/src/main/AndroidManifest.xml | 2 ++ ios/ReactNativeChat/Info.plist | 24 ++++++++++++-------- src/lib/ImagePicker/index.js | 7 ++---- src/lib/actions/Report.js | 10 +++++--- src/page/home/report/ReportHistoryCompose.js | 18 +++++++++++---- 5 files changed, 39 insertions(+), 22 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 5f8a5ca5adbb..a13263627b25 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -3,6 +3,8 @@ + + CFBundleVersion 32 ITSAppUsesNonExemptEncryption - + LSRequiresIPhoneOS - + NSAppTransportSecurity NSAllowsArbitraryLoads - + NSExceptionDomains localhost NSExceptionAllowsInsecureHTTPLoads - + NSIncludesSubdomains - + www.expensify.com.dev NSExceptionAllowsInsecureHTTPLoads - + NSIncludesSubdomains - + NSLocationWhenInUseUsageDescription - + UIAppFonts GTAmericaExp-Bold.otf @@ -70,7 +70,13 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + NSPhotoLibraryUsageDescription + Your photos are used to create chat attachments. + NSCameraUsageDescription + Your camera is used to create chat attachments. + NSPhotoLibraryAddUsageDescription + Your camera roll is used to store chat attachments. UIViewControllerBasedStatusBarAppearance - + diff --git a/src/lib/ImagePicker/index.js b/src/lib/ImagePicker/index.js index 155e9edfa380..b1e24a38f4d6 100644 --- a/src/lib/ImagePicker/index.js +++ b/src/lib/ImagePicker/index.js @@ -8,7 +8,7 @@ const input = document.createElement('input'); input.type = 'file'; const ImagePicker = { - showImagePicker(callback) { + showImagePicker(options, callback) { const {files} = input; if (files) { Array.prototype.forEach.call(files, this.removeUri); @@ -38,10 +38,7 @@ const ImagePicker = { }); } - self.addUri(files[0]); - resolve({ - uri: files[0].uri - }); + resolve(self.addUri(files[0])); }, 500); } diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js index 810af249d53b..f6af732d92a7 100644 --- a/src/lib/actions/Report.js +++ b/src/lib/actions/Report.js @@ -368,14 +368,16 @@ function fetchChatReport(participants) { * * @param {string} reportID * @param {string} reportComment + * @param {Object} file * @returns {Promise} */ -function addHistoryItem(reportID, reportComment) { +function addHistoryItem(reportID, reportComment, file) { const historyKey = `${IONKEYS.REPORT_HISTORY}_${reportID}`; // Convert the comment from MD into HTML because that's how it is stored in the database const parser = new ExpensiMark(); const htmlComment = parser.replace(reportComment); + const isAttachment = _.isEmpty(reportComment) && file !== undefined; return Ion.multiGet([historyKey, IONKEYS.SESSION, IONKEYS.PERSONAL_DETAILS]) .then((values) => { @@ -410,10 +412,11 @@ function addHistoryItem(reportID, reportComment) { message: [ { type: 'COMMENT', - html: htmlComment, + html: isAttachment ? 'Uploading Attachment...' : htmlComment, // Remove HTML from text when applying optimistic offline comment - text: htmlComment.replace(/<[^>]*>?/gm, ''), + text: isAttachment ? 'Uploading Attachment...' + : htmlComment.replace(/<[^>]*>?/gm, ''), } ], isFirstItem: false, @@ -424,6 +427,7 @@ function addHistoryItem(reportID, reportComment) { .then(() => queueRequest('Report_AddComment', { reportID, reportComment: htmlComment, + file })); } diff --git a/src/page/home/report/ReportHistoryCompose.js b/src/page/home/report/ReportHistoryCompose.js index e0a338e75cfc..7db315929388 100644 --- a/src/page/home/report/ReportHistoryCompose.js +++ b/src/page/home/report/ReportHistoryCompose.js @@ -6,6 +6,7 @@ import TextInputFocusable from '../../../components/TextInputFocusable'; import sendIcon from '../../../../assets/images/icon-send.png'; import paperClipIcon from '../../../../assets/images/icon-paper-clip.png'; import ImagePicker from '../../../lib/ImagePicker'; +import {addHistoryItem} from '../../../lib/actions/Report'; const propTypes = { // A method to call when the form is submitted @@ -40,18 +41,25 @@ class ReportHistoryCompose extends React.Component { e.preventDefault(); } - ImagePicker.showImagePicker((response) => { + const reportID = this.props.reportID; + const options = { + storageOptions: { + skipBackup: true, + path: 'images', + cameraRoll: true, + }, + }; + ImagePicker.showImagePicker(options, (response) => { console.log('Response = ', response); if (response.didCancel) { console.log('User cancelled image picker'); } else if (response.error) { console.log('ImagePicker Error: ', response.error); - } else if (response.customButton) { - console.log('User tapped custom button: ', response.customButton); } else { - const source = {uri: response.uri}; - console.log('Source: ', source); + console.log('Response: ', response); + response.uri = response.uri.replace('file://', ''); + addHistoryItem(reportID, '', response); // You can also display the image using data: // const source = { uri: 'data:image/jpeg;base64,' + response.data }; From 59ca7d258e229e8ef86e4befd1fc960a4881f172 Mon Sep 17 00:00:00 2001 From: Joe Gambino Date: Thu, 10 Sep 2020 12:52:55 -0700 Subject: [PATCH 03/15] clean up web picker --- src/lib/ImagePicker/index.js | 83 +++++++++------------ src/page/home/report/ReportActionCompose.js | 1 + 2 files changed, 35 insertions(+), 49 deletions(-) diff --git a/src/lib/ImagePicker/index.js b/src/lib/ImagePicker/index.js index b1e24a38f4d6..5613f48f1842 100644 --- a/src/lib/ImagePicker/index.js +++ b/src/lib/ImagePicker/index.js @@ -1,67 +1,52 @@ -/** - * Trick for getting file selector cancel using `body.onfocus` event found at - * http://trishulgoel.com/handle-cancel-click-on-file-input/ - */ +// Implementation adapted from https://github.com/QuantumBA/foqum-react-native-document-picker/blob/master/web/index.js + +import _ from 'underscore'; const {body} = document; const input = document.createElement('input'); input.type = 'file'; +/** + * Sets the URI value on the file + * + * @param {object} file + * @returns {object} + */ +function addUri(file) { + const newFile = file; + newFile.uri = URL.createObjectURL(file); + + return newFile; +} + const ImagePicker = { showImagePicker(options, callback) { const {files} = input; + + // Clear previously saved files if (files) { - Array.prototype.forEach.call(files, this.removeUri); + input.value = ''; } - this.showImagePickerWrapper() - .then((result) => { - callback(result); - }) - .catch((result) => { - callback(result); - }); - }, - - showImagePickerWrapper() { - const self = this; - const promise = new Promise((resolve, reject) => { - function onfocus() { - body.removeEventListener('focus', onfocus, true); - - setTimeout(() => { - const {files} = input; - if (!files.length) { - // eslint-disable-next-line prefer-promise-reject-errors - return reject({ - didCancel: true - }); - } + function onfocus() { + body.removeEventListener('focus', onfocus, true); - resolve(self.addUri(files[0])); - }, 500); - } + _.delay(() => { + const inputFiles = input.files; + if (!inputFiles.length) { + callback({ + didCancel: true + }); + return; + } - body.addEventListener('focus', onfocus, true); - }); + callback(addUri(inputFiles[0])); + }); + } + body.addEventListener('focus', onfocus, true); input.click(); - - return promise; - }, - - addUri(file) { - const newFile = file; - newFile.uri = URL.createObjectURL(file); - - return newFile; - }, - - removeUri(file) { - URL.revokeObjectURL(file.uri); - - delete file.uri; - }, + } }; export default ImagePicker; diff --git a/src/page/home/report/ReportActionCompose.js b/src/page/home/report/ReportActionCompose.js index 0a71d2b5cc33..a5dbeab1dcd2 100644 --- a/src/page/home/report/ReportActionCompose.js +++ b/src/page/home/report/ReportActionCompose.js @@ -49,6 +49,7 @@ class ReportActionCompose extends React.Component { cameraRoll: true, }, }; + ImagePicker.showImagePicker(options, (response) => { console.log('Response = ', response); From d7d6bdcb9de98b9bd27afcc91055ffbb5a86ccfc Mon Sep 17 00:00:00 2001 From: Joe Gambino Date: Thu, 10 Sep 2020 17:40:06 -0700 Subject: [PATCH 04/15] add attachment placeholder --- src/lib/ImagePicker/index.js | 2 +- src/lib/actions/Report.js | 2 +- src/page/home/report/ReportActionItemFragment.js | 16 +++++++++++++++- src/page/home/report/ReportActionItemMessage.js | 1 + src/style/StyleSheet.js | 12 ++++++++++++ 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/lib/ImagePicker/index.js b/src/lib/ImagePicker/index.js index 5613f48f1842..71f449cfb315 100644 --- a/src/lib/ImagePicker/index.js +++ b/src/lib/ImagePicker/index.js @@ -41,7 +41,7 @@ const ImagePicker = { } callback(addUri(inputFiles[0])); - }); + }, 500); } body.addEventListener('focus', onfocus, true); diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js index a640162a5fe7..9a739715f876 100644 --- a/src/lib/actions/Report.js +++ b/src/lib/actions/Report.js @@ -410,7 +410,7 @@ function addAction(reportID, text, file) { } ], isFirstItem: false, - isAttachmentPlaceHolder: false, + isAttachment, } }); diff --git a/src/page/home/report/ReportActionItemFragment.js b/src/page/home/report/ReportActionItemFragment.js index 246bbc5898d0..d1b87cef3212 100644 --- a/src/page/home/report/ReportActionItemFragment.js +++ b/src/page/home/report/ReportActionItemFragment.js @@ -1,9 +1,10 @@ import React from 'react'; import HTML from 'react-native-render-html'; import {Linking} from 'react-native'; +import {View, ActivityIndicator} from 'react-native-web'; import Str from '../../../lib/Str'; import ReportActionFragmentPropTypes from './ReportActionFragmentPropTypes'; -import styles, {webViewStyles} from '../../../style/StyleSheet'; +import styles, {webViewStyles, colors} from '../../../style/StyleSheet'; import Text from '../../../components/Text'; import AnchorForCommentsOnly from '../../../components/AnchorForCommentsOnly'; import {getAuthToken} from '../../../lib/API'; @@ -57,6 +58,19 @@ class ReportActionItemFragment extends React.PureComponent { const {fragment} = this.props; switch (fragment.type) { case 'COMMENT': + // If this is an attachment placeholder, return the placeholder component + if (this.props.isAttachment && fragment.html === fragment.text) { + return ( + + + + ); + } + // Only render HTML if we have html in the fragment return fragment.html !== fragment.text ? ( diff --git a/src/page/home/report/ReportActionItemMessage.js b/src/page/home/report/ReportActionItemMessage.js index 7566e149750f..906596e880ff 100644 --- a/src/page/home/report/ReportActionItemMessage.js +++ b/src/page/home/report/ReportActionItemMessage.js @@ -15,6 +15,7 @@ const ReportActionItemMessage = ({action}) => ( ))} diff --git a/src/style/StyleSheet.js b/src/style/StyleSheet.js index e0ae6cba5f99..2341986e58d2 100644 --- a/src/style/StyleSheet.js +++ b/src/style/StyleSheet.js @@ -9,6 +9,7 @@ const colors = { black: '#000000', blue: '#2EAAE2', border: '#ECECEC', + borderLight: '#E0E0E0', green: '#2ECB70', heading: '#37444C', icon: '#C6C9CA', @@ -583,6 +584,17 @@ const styles = { width: 39, }, + chatItemAttachmentPlaceholder: { + backgroundColor: colors.border, + borderColor: colors.borderLight, + borderWidth: 1, + borderRadius: 8, + height: 150, + textAlign: 'center', + verticalAlign: 'middle', + width: 200, + }, + chatSwitcherInputClear: { alignSelf: 'center', }, From b0998a7576613000ae89a9eceb125cbe85dda6e7 Mon Sep 17 00:00:00 2001 From: Joe Gambino Date: Thu, 10 Sep 2020 19:22:06 -0700 Subject: [PATCH 05/15] get upload working on ios --- src/lib/actions/Report.js | 3 ++- src/page/home/report/ReportActionCompose.js | 20 +++++++++---------- .../home/report/ReportActionItemFragment.js | 3 +-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js index 9a739715f876..75763d050747 100644 --- a/src/lib/actions/Report.js +++ b/src/lib/actions/Report.js @@ -417,7 +417,8 @@ function addAction(reportID, text, file) { queueRequest('Report_AddComment', { reportID, reportComment: htmlComment, - file + // TODO: get working w/o base64image key + base64image: file }); } diff --git a/src/page/home/report/ReportActionCompose.js b/src/page/home/report/ReportActionCompose.js index a5dbeab1dcd2..d27025fa145b 100644 --- a/src/page/home/report/ReportActionCompose.js +++ b/src/page/home/report/ReportActionCompose.js @@ -47,26 +47,26 @@ class ReportActionCompose extends React.Component { skipBackup: true, path: 'images', cameraRoll: true, + waitUntilSaved: true }, }; ImagePicker.showImagePicker(options, (response) => { - console.log('Response = ', response); + //console.log('Response = ', response); if (response.didCancel) { console.log('User cancelled image picker'); } else if (response.error) { console.log('ImagePicker Error: ', response.error); } else { - console.log('Response: ', response); - response.uri = response.uri.replace('file://', ''); - addAction(reportID, '', response); - - // You can also display the image using data: - // const source = { uri: 'data:image/jpeg;base64,' + response.data }; - // this.setState({ - // avatarSource: source, - // }); + // TODO: make this work nicely w/ the web shim + addAction(reportID, '', { + // TODO: test if null name was causing empty response + name: 'uploaded_image', + type: response.type, + uri: response.uri.replace('file://', ''), + data: response.data + }); } }); } diff --git a/src/page/home/report/ReportActionItemFragment.js b/src/page/home/report/ReportActionItemFragment.js index d1b87cef3212..52f8725f2078 100644 --- a/src/page/home/report/ReportActionItemFragment.js +++ b/src/page/home/report/ReportActionItemFragment.js @@ -1,7 +1,6 @@ import React from 'react'; import HTML from 'react-native-render-html'; -import {Linking} from 'react-native'; -import {View, ActivityIndicator} from 'react-native-web'; +import {Linking, ActivityIndicator, View} from 'react-native'; import Str from '../../../lib/Str'; import ReportActionFragmentPropTypes from './ReportActionFragmentPropTypes'; import styles, {webViewStyles, colors} from '../../../style/StyleSheet'; From 41bff8bf51b0683f2c281e39b2b8992d62a118d4 Mon Sep 17 00:00:00 2001 From: Joe Gambino Date: Fri, 11 Sep 2020 14:57:34 -0700 Subject: [PATCH 06/15] get upload working on ios, android, and web --- src/lib/ImagePicker/index.js | 15 ++++++++++++++- src/lib/ImagePicker/index.native.js | 10 ++++++++++ src/lib/actions/Report.js | 3 +-- src/page/home/report/ReportActionCompose.js | 9 +-------- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/lib/ImagePicker/index.js b/src/lib/ImagePicker/index.js index 71f449cfb315..4cceb9df14c0 100644 --- a/src/lib/ImagePicker/index.js +++ b/src/lib/ImagePicker/index.js @@ -46,7 +46,20 @@ const ImagePicker = { body.addEventListener('focus', onfocus, true); input.click(); - } + }, + + /** + * + * The data returned from `showImagePicker` is different on web and mobile, so use this function to ensure the + * data we send to the xhr will be handled properly by the API. On web, we just want to send the file data returned + * from the input. + * + * @param {object} fileData + * @returns {object} + */ + getDataForUpload(fileData) { + return fileData; + }, }; export default ImagePicker; diff --git a/src/lib/ImagePicker/index.native.js b/src/lib/ImagePicker/index.native.js index 7af139c8f70f..e8fe1a5ad576 100644 --- a/src/lib/ImagePicker/index.native.js +++ b/src/lib/ImagePicker/index.native.js @@ -4,4 +4,14 @@ import RNImagePicker from 'react-native-image-picker'; const ImagePicker = RNImagePicker; + +/* + * The data returned from `showImagePicker` is different on web and mobile, so use this function to ensure the data we + * send to the xhr will be handled properly. + */ +ImagePicker.getDataForUpload = fileData => ({ + name: fileData.fileName || 'chat_attachment', + type: fileData.type, + uri: fileData.uri, +}); export default ImagePicker; diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js index 75763d050747..9a739715f876 100644 --- a/src/lib/actions/Report.js +++ b/src/lib/actions/Report.js @@ -417,8 +417,7 @@ function addAction(reportID, text, file) { queueRequest('Report_AddComment', { reportID, reportComment: htmlComment, - // TODO: get working w/o base64image key - base64image: file + file }); } diff --git a/src/page/home/report/ReportActionCompose.js b/src/page/home/report/ReportActionCompose.js index d27025fa145b..6708439c74fd 100644 --- a/src/page/home/report/ReportActionCompose.js +++ b/src/page/home/report/ReportActionCompose.js @@ -59,14 +59,7 @@ class ReportActionCompose extends React.Component { } else if (response.error) { console.log('ImagePicker Error: ', response.error); } else { - // TODO: make this work nicely w/ the web shim - addAction(reportID, '', { - // TODO: test if null name was causing empty response - name: 'uploaded_image', - type: response.type, - uri: response.uri.replace('file://', ''), - data: response.data - }); + addAction(reportID, '', ImagePicker.getDataForUpload(response)); } }); } From ab4cbba0ef73a0afd7b0e805b677aff518aa037b Mon Sep 17 00:00:00 2001 From: Joe Gambino Date: Fri, 11 Sep 2020 15:35:54 -0700 Subject: [PATCH 07/15] PR feedback and cleanup --- src/page/home/report/ReportActionCompose.js | 23 ++++++++----------- .../home/report/ReportActionItemFragment.js | 9 ++++++++ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/page/home/report/ReportActionCompose.js b/src/page/home/report/ReportActionCompose.js index 6708439c74fd..b93e6d83a4f0 100644 --- a/src/page/home/report/ReportActionCompose.js +++ b/src/page/home/report/ReportActionCompose.js @@ -37,30 +37,25 @@ class ReportActionCompose extends React.Component { * @param {SyntheticEvent} [e] */ onAttachmentButtonTapped(e) { - if (e) { - e.preventDefault(); - } + e.preventDefault(); - const reportID = this.props.reportID; const options = { storageOptions: { skipBackup: true, - path: 'images', - cameraRoll: true, - waitUntilSaved: true }, }; ImagePicker.showImagePicker(options, (response) => { - //console.log('Response = ', response); - if (response.didCancel) { - console.log('User cancelled image picker'); - } else if (response.error) { - console.log('ImagePicker Error: ', response.error); - } else { - addAction(reportID, '', ImagePicker.getDataForUpload(response)); + return; } + + if (response.error) { + console.error(`Error occurred picking image: ${response.error}`); + return; + } + + addAction(this.props.reportID, '', ImagePicker.getDataForUpload(response)); }); } diff --git a/src/page/home/report/ReportActionItemFragment.js b/src/page/home/report/ReportActionItemFragment.js index 52f8725f2078..6c9723ce2325 100644 --- a/src/page/home/report/ReportActionItemFragment.js +++ b/src/page/home/report/ReportActionItemFragment.js @@ -1,6 +1,7 @@ import React from 'react'; import HTML from 'react-native-render-html'; import {Linking, ActivityIndicator, View} from 'react-native'; +import PropTypes from 'prop-types'; import Str from '../../../lib/Str'; import ReportActionFragmentPropTypes from './ReportActionFragmentPropTypes'; import styles, {webViewStyles, colors} from '../../../style/StyleSheet'; @@ -11,6 +12,13 @@ import {getAuthToken} from '../../../lib/API'; const propTypes = { // The message fragment needing to be displayed fragment: ReportActionFragmentPropTypes.isRequired, + + // Is this fragment an attachment? + isAttachment: PropTypes.bool, +}; + +const defaultProps = { + isAttachment: false }; class ReportActionItemFragment extends React.PureComponent { @@ -114,6 +122,7 @@ class ReportActionItemFragment extends React.PureComponent { } ReportActionItemFragment.propTypes = propTypes; +ReportActionItemFragment.defaultProps = defaultProps; ReportActionItemFragment.displayName = 'ReportActionItemFragment'; export default ReportActionItemFragment; From dea2da3a6cb1b8526b867a5226902189f2cc9018 Mon Sep 17 00:00:00 2001 From: Joe Gambino Date: Fri, 11 Sep 2020 15:52:49 -0700 Subject: [PATCH 08/15] rename function --- src/page/home/report/ReportActionCompose.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/page/home/report/ReportActionCompose.js b/src/page/home/report/ReportActionCompose.js index d6d205c8d64a..d2b6353b6076 100644 --- a/src/page/home/report/ReportActionCompose.js +++ b/src/page/home/report/ReportActionCompose.js @@ -33,7 +33,7 @@ class ReportActionCompose extends React.Component { this.submitForm = this.submitForm.bind(this); this.triggerSubmitShortcut = this.triggerSubmitShortcut.bind(this); this.submitForm = this.submitForm.bind(this); - this.onAttachmentButtonTapped = this.onAttachmentButtonTapped.bind(this); + this.showAttachmentPicker = this.showAttachmentPicker.bind(this); } /** @@ -41,7 +41,7 @@ class ReportActionCompose extends React.Component { * * @param {SyntheticEvent} [e] */ - onAttachmentButtonTapped(e) { + showAttachmentPicker(e) { e.preventDefault(); const options = { @@ -113,7 +113,7 @@ class ReportActionCompose extends React.Component { From e981a72ed938ef15f9803421023d124caec35323 Mon Sep 17 00:00:00 2001 From: Joe Gambino Date: Fri, 11 Sep 2020 16:02:10 -0700 Subject: [PATCH 09/15] clarify delay usage --- src/lib/ImagePicker/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/ImagePicker/index.js b/src/lib/ImagePicker/index.js index 4cceb9df14c0..d9c39005bb7a 100644 --- a/src/lib/ImagePicker/index.js +++ b/src/lib/ImagePicker/index.js @@ -31,6 +31,7 @@ const ImagePicker = { function onfocus() { body.removeEventListener('focus', onfocus, true); + // Add a 500 millisecond delay here to ensure input.files is set before checking it _.delay(() => { const inputFiles = input.files; if (!inputFiles.length) { From f33829c2a5f3a27d781247083235839942c094ac Mon Sep 17 00:00:00 2001 From: Joe Gambino Date: Fri, 11 Sep 2020 16:42:50 -0700 Subject: [PATCH 10/15] fix error on native platforms --- src/components/AnchorForCommentsOnly/index.native.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/AnchorForCommentsOnly/index.native.js b/src/components/AnchorForCommentsOnly/index.native.js index 2cdd9956a648..d4ed3dfc944b 100644 --- a/src/components/AnchorForCommentsOnly/index.native.js +++ b/src/components/AnchorForCommentsOnly/index.native.js @@ -8,7 +8,7 @@ import {Linking, StyleSheet, Text} from 'react-native'; const propTypes = { // The URL to open - href: PropTypes.string.isRequired, + href: PropTypes.string, // What headers to send to the linked page (usually noopener and noreferrer) // This is unused in native, but is here for parity with web @@ -28,6 +28,7 @@ const propTypes = { }; const defaultProps = { + href: '', rel: null, target: null, children: null, From 89a5dbc8b0af72334406e5b37430991a961cc38c Mon Sep 17 00:00:00 2001 From: Joe Gambino Date: Mon, 14 Sep 2020 20:16:56 -0700 Subject: [PATCH 11/15] add patch to fix RN iOS upload issue --- ios/ReactNativeChat.xcodeproj/project.pbxproj | 60 +++--- package-lock.json | 198 ++++++++++++++++++ package.json | 2 + patches/react-native+0.63.2.patch | 13 ++ 4 files changed, 243 insertions(+), 30 deletions(-) create mode 100644 patches/react-native+0.63.2.patch diff --git a/ios/ReactNativeChat.xcodeproj/project.pbxproj b/ios/ReactNativeChat.xcodeproj/project.pbxproj index 5605613724a2..1243af0ae86d 100644 --- a/ios/ReactNativeChat.xcodeproj/project.pbxproj +++ b/ios/ReactNativeChat.xcodeproj/project.pbxproj @@ -8,17 +8,17 @@ /* Begin PBXBuildFile section */ 00E356F31AD99517003FC87E /* ReactNativeChatTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* ReactNativeChatTests.m */; }; + 097C76E5807C5DFC565DC655 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C4F81DB9C6DA6418525441E5 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; }; 11FA3546D54246CD8F819152 /* GTAmericaExp-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 46C9221F3EA3DD84BD134825 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 074C00650065A406EE912586 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; }; + 347C4A976B972BC7A903F97D /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6ABF95746519766B23B4E2D0 /* libPods-ReactNativeChat.a */; }; 4912B60FA66C4604A56AD575 /* GTAmericaExp-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */; }; 49F157C845CE4D56AA72B80E /* GTAmericaExp-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */; }; 7F7721B764D444D6861A9B39 /* GTAmericaExp-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; CC07911F22C2437DA6708BD2 /* GTAmericaExp-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */; }; - EB699F7A1A5F6AF391540E98 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D428799B67913B9C601A158 /* libPods-ReactNativeChat.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -36,24 +36,24 @@ 00E356EE1AD99517003FC87E /* ReactNativeChatTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactNativeChatTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* ReactNativeChatTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactNativeChatTests.m; sourceTree = ""; }; - 05C75E1764EA836C54E53E91 /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = ""; }; - 074C00650065A406EE912586 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07F961A680F5B00A75B9A /* Chat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Chat.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ReactNativeChat/AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ReactNativeChat/AppDelegate.m; sourceTree = ""; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ReactNativeChat/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeChat/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeChat/main.m; sourceTree = ""; }; - 45D4E4039B764281ECADF639 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = ""; }; - 4DAAA8DFB4A4EF50B076BE3D /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = ""; }; + 1CFBC073CF30A72CCF109A40 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = ""; }; 67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = ""; }; - 7D428799B67913B9C601A158 /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 6ABF95746519766B23B4E2D0 /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeChat/LaunchScreen.storyboard; sourceTree = ""; }; 8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Regular.otf"; path = "../assets/fonts/GTAmericaExp-Regular.otf"; sourceTree = ""; }; + 9B9FEA91E46D2B2609028E69 /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = ""; }; A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Thin.otf"; path = "../assets/fonts/GTAmericaExp-Thin.otf"; sourceTree = ""; }; A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Bold.otf"; path = "../assets/fonts/GTAmericaExp-Bold.otf"; sourceTree = ""; }; AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Medium.otf"; path = "../assets/fonts/GTAmericaExp-Medium.otf"; sourceTree = ""; }; - D232EFF53A6330C07E8BC0F8 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = ""; }; + BA6C613DE6755E40F7EDDC68 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = ""; }; + C4F81DB9C6DA6418525441E5 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + C6C1D4F5C262ACDAF1800CAD /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; /* End PBXFileReference section */ @@ -63,7 +63,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 46C9221F3EA3DD84BD134825 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */, + 097C76E5807C5DFC565DC655 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -71,7 +71,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - EB699F7A1A5F6AF391540E98 /* libPods-ReactNativeChat.a in Frameworks */, + 347C4A976B972BC7A903F97D /* libPods-ReactNativeChat.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -114,8 +114,8 @@ children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED2971642150620600B7C4FE /* JavaScriptCore.framework */, - 7D428799B67913B9C601A158 /* libPods-ReactNativeChat.a */, - 074C00650065A406EE912586 /* libPods-ReactNativeChat-ReactNativeChatTests.a */, + 6ABF95746519766B23B4E2D0 /* libPods-ReactNativeChat.a */, + C4F81DB9C6DA6418525441E5 /* libPods-ReactNativeChat-ReactNativeChatTests.a */, ); name = Frameworks; sourceTree = ""; @@ -167,10 +167,10 @@ EC29677F0A49C2946A495A33 /* Pods */ = { isa = PBXGroup; children = ( - 05C75E1764EA836C54E53E91 /* Pods-ReactNativeChat.debug.xcconfig */, - D232EFF53A6330C07E8BC0F8 /* Pods-ReactNativeChat.release.xcconfig */, - 4DAAA8DFB4A4EF50B076BE3D /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */, - 45D4E4039B764281ECADF639 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */, + 9B9FEA91E46D2B2609028E69 /* Pods-ReactNativeChat.debug.xcconfig */, + C6C1D4F5C262ACDAF1800CAD /* Pods-ReactNativeChat.release.xcconfig */, + BA6C613DE6755E40F7EDDC68 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */, + 1CFBC073CF30A72CCF109A40 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -182,11 +182,11 @@ isa = PBXNativeTarget; buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ReactNativeChatTests" */; buildPhases = ( - 19A04238C3D2F6F09678CFA0 /* [CP] Check Pods Manifest.lock */, + 67625BD6E56D7B445CE0B75D /* [CP] Check Pods Manifest.lock */, 00E356EA1AD99517003FC87E /* Sources */, 00E356EB1AD99517003FC87E /* Frameworks */, 00E356EC1AD99517003FC87E /* Resources */, - ABE62262A44D31B78B23818B /* [CP] Copy Pods Resources */, + 740ED6E57E4D77AE97454014 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -202,13 +202,13 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeChat" */; buildPhases = ( - 387B40C2520CE2773517F705 /* [CP] Check Pods Manifest.lock */, + 25778851D923FF95A2D6EE6D /* [CP] Check Pods Manifest.lock */, FD10A7F022414F080027D42C /* Start Packager */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - EC2DD7546E5E118A6D3073B2 /* [CP] Copy Pods Resources */, + 930B6ED2A0414681598DB7E1 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -297,7 +297,7 @@ shellPath = /bin/sh; shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; }; - 19A04238C3D2F6F09678CFA0 /* [CP] Check Pods Manifest.lock */ = { + 25778851D923FF95A2D6EE6D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -312,14 +312,14 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 387B40C2520CE2773517F705 /* [CP] Check Pods Manifest.lock */ = { + 67625BD6E56D7B445CE0B75D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -334,14 +334,14 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - ABE62262A44D31B78B23818B /* [CP] Copy Pods Resources */ = { + 740ED6E57E4D77AE97454014 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -359,7 +359,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; - EC2DD7546E5E118A6D3073B2 /* [CP] Copy Pods Resources */ = { + 930B6ED2A0414681598DB7E1 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -429,7 +429,7 @@ /* Begin XCBuildConfiguration section */ 00E356F61AD99517003FC87E /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4DAAA8DFB4A4EF50B076BE3D /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */; + baseConfigurationReference = BA6C613DE6755E40F7EDDC68 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -453,7 +453,7 @@ }; 00E356F71AD99517003FC87E /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 45D4E4039B764281ECADF639 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */; + baseConfigurationReference = 1CFBC073CF30A72CCF109A40 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -474,7 +474,7 @@ }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 05C75E1764EA836C54E53E91 /* Pods-ReactNativeChat.debug.xcconfig */; + baseConfigurationReference = 9B9FEA91E46D2B2609028E69 /* Pods-ReactNativeChat.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -502,7 +502,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D232EFF53A6330C07E8BC0F8 /* Pods-ReactNativeChat.release.xcconfig */; + baseConfigurationReference = C6C1D4F5C262ACDAF1800CAD /* Pods-ReactNativeChat.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; diff --git a/package-lock.json b/package-lock.json index 01290e2646b9..dd2b52634a47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2372,6 +2372,11 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" + }, "abab": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.4.tgz", @@ -8228,6 +8233,104 @@ "path-exists": "^4.0.0" } }, + "find-yarn-workspace-root": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz", + "integrity": "sha512-dVtfb0WuQG+8Ag2uWkbG79hOUzEsRrhBzgfn86g2sJPkzmcpGdghbNTfUKGTxymFrY/tLIodDzLoW9nOJ4FY8Q==", + "requires": { + "fs-extra": "^4.0.3", + "micromatch": "^3.1.4" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, "findup-sync": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", @@ -10815,6 +10918,14 @@ "graceful-fs": "^4.1.9" } }, + "klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "requires": { + "graceful-fs": "^4.1.11" + } + }, "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -13634,6 +13745,93 @@ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" }, + "patch-package": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.2.2.tgz", + "integrity": "sha512-YqScVYkVcClUY0v8fF0kWOjDYopzIM8e3bj/RU1DPeEF14+dCGm6UeOYm4jvCyxqIEQ5/eJzmbWfDWnUleFNMg==", + "requires": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^2.4.2", + "cross-spawn": "^6.0.5", + "find-yarn-workspace-root": "^1.2.1", + "fs-extra": "^7.0.1", + "is-ci": "^2.0.0", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.0", + "rimraf": "^2.6.3", + "semver": "^5.6.0", + "slash": "^2.0.0", + "tmp": "^0.0.33" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "path-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", diff --git a/package.json b/package.json index a96c22ee2a8c..86c7de422a71 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "android-build": "fastlane android build", "test": "jest", "lint": "eslint .", + "postinstall": "patch-package", "postversion": "react-native-version", "print-version": "echo $npm_package_version" }, @@ -37,6 +38,7 @@ "lodash.orderby": "^4.6.0", "moment": "^2.27.0", "moment-timezone": "^0.5.31", + "patch-package": "^6.2.2", "prop-types": "^15.7.2", "pusher-js": "^7.0.0", "react": "^16.13.1", diff --git a/patches/react-native+0.63.2.patch b/patches/react-native+0.63.2.patch new file mode 100644 index 000000000000..bc5e41d5a9d5 --- /dev/null +++ b/patches/react-native+0.63.2.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/react-native/Libraries/Image/RCTImageLoader.mm b/node_modules/react-native/Libraries/Image/RCTImageLoader.mm +index 3571647..ffb64b1 100644 +--- a/node_modules/react-native/Libraries/Image/RCTImageLoader.mm ++++ b/node_modules/react-native/Libraries/Image/RCTImageLoader.mm +@@ -1040,7 +1040,7 @@ - (BOOL)canHandleRequest:(NSURLRequest *)request + + - (id)sendRequest:(NSURLRequest *)request withDelegate:(id)delegate + { +- __block RCTImageLoaderCancellationBlock requestToken; ++ __block RCTImageLoaderCancellationBlock requestToken = ^{}; + requestToken = [self loadImageWithURLRequest:request callback:^(NSError *error, UIImage *image) { + if (error) { + [delegate URLRequest:requestToken didCompleteWithError:error]; From 7850295342df0c6f45e5284e7c1bb4819cde1166 Mon Sep 17 00:00:00 2001 From: Joe Gambino Date: Mon, 14 Sep 2020 20:41:35 -0700 Subject: [PATCH 12/15] add link to options --- src/page/home/report/ReportActionCompose.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/page/home/report/ReportActionCompose.js b/src/page/home/report/ReportActionCompose.js index ef8019034550..3b7dd04db303 100644 --- a/src/page/home/report/ReportActionCompose.js +++ b/src/page/home/report/ReportActionCompose.js @@ -111,6 +111,10 @@ class ReportActionCompose extends React.Component { showAttachmentPicker(e) { e.preventDefault(); + /** + * See https://github.com/react-native-community/react-native-image-picker/blob/master/docs/Reference.md#options + * for option definitions + */ const options = { storageOptions: { skipBackup: true, From 20e6fb824fd434864e06db9795428b611f949ea3 Mon Sep 17 00:00:00 2001 From: Joe Gambino Date: Tue, 15 Sep 2020 13:37:33 -0700 Subject: [PATCH 13/15] fix android 10 permission issues --- android/app/src/main/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6151d9a68fa5..68c269fe1bbe 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -12,6 +12,7 @@ android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" + android:requestLegacyExternalStorage="true" android:theme="@style/AppTheme"> Date: Thu, 17 Sep 2020 19:23:56 -0700 Subject: [PATCH 14/15] simplify web image picker --- src/lib/ImagePicker/index.js | 49 ++++++------------------------------ 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/src/lib/ImagePicker/index.js b/src/lib/ImagePicker/index.js index d9c39005bb7a..2129bd12face 100644 --- a/src/lib/ImagePicker/index.js +++ b/src/lib/ImagePicker/index.js @@ -1,51 +1,16 @@ // Implementation adapted from https://github.com/QuantumBA/foqum-react-native-document-picker/blob/master/web/index.js -import _ from 'underscore'; - -const {body} = document; -const input = document.createElement('input'); -input.type = 'file'; - -/** - * Sets the URI value on the file - * - * @param {object} file - * @returns {object} - */ -function addUri(file) { - const newFile = file; - newFile.uri = URL.createObjectURL(file); - - return newFile; -} - const ImagePicker = { showImagePicker(options, callback) { - const {files} = input; - - // Clear previously saved files - if (files) { - input.value = ''; - } - - function onfocus() { - body.removeEventListener('focus', onfocus, true); - - // Add a 500 millisecond delay here to ensure input.files is set before checking it - _.delay(() => { - const inputFiles = input.files; - if (!inputFiles.length) { - callback({ - didCancel: true - }); - return; - } + const input = document.createElement('input'); + input.type = 'file'; + input.onchange = function () { + const file = input.files[0]; + file.uri = URL.createObjectURL(file); - callback(addUri(inputFiles[0])); - }, 500); - } + callback(file); + }; - body.addEventListener('focus', onfocus, true); input.click(); }, From d2c8077d734a0f791ac1843afd86da7538b33cb5 Mon Sep 17 00:00:00 2001 From: Joe Gambino Date: Thu, 17 Sep 2020 19:34:30 -0700 Subject: [PATCH 15/15] update jsdoc --- src/page/home/report/ReportActionCompose.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/page/home/report/ReportActionCompose.js b/src/page/home/report/ReportActionCompose.js index 3b7dd04db303..57aa78032667 100644 --- a/src/page/home/report/ReportActionCompose.js +++ b/src/page/home/report/ReportActionCompose.js @@ -106,7 +106,7 @@ class ReportActionCompose extends React.Component { /** * Handle the attachment icon being tapped * - * @param {SyntheticEvent} [e] + * @param {SyntheticEvent} e */ showAttachmentPicker(e) { e.preventDefault();