diff --git a/src/components/AvatarWithImagePicker.js b/src/components/AvatarWithImagePicker.js index 808d4cb39076..aa50d6db574b 100644 --- a/src/components/AvatarWithImagePicker.js +++ b/src/components/AvatarWithImagePicker.js @@ -22,6 +22,7 @@ import * as FileUtils from '../libs/fileDownload/FileUtils'; import getImageResolution from '../libs/fileDownload/getImageResolution'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; import DotIndicatorMessage from './DotIndicatorMessage'; +import * as Browser from '../libs/Browser'; const propTypes = { /** Avatar source to display */ @@ -229,42 +230,24 @@ class AvatarWithImagePicker extends React.Component { render() { const DefaultAvatar = this.props.DefaultAvatar; const additionalStyles = _.isArray(this.props.style) ? this.props.style : [this.props.style]; - const menuItems = [ - { - icon: Expensicons.Upload, - text: this.props.translate('avatarWithImagePicker.uploadPhoto'), - onSelected: () => {}, - }, - ]; - - // If current avatar isn't a default avatar, allow Remove Photo option - if (!this.props.isUsingDefaultAvatar) { - menuItems.push({ - icon: Expensicons.Trashcan, - text: this.props.translate('avatarWithImagePicker.removePhoto'), - onSelected: () => { - this.setError(null, {}); - this.props.onImageRemoved(); - }, - }); - } return ( - this.setState((prev) => ({isMenuVisible: !prev.isMenuVisible}))} - accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} - accessibilityLabel={this.props.translate('avatarWithImagePicker.editImage')} - disabled={this.state.isAvatarCropModalOpen} - > - - - + + + + this.setState((prev) => ({isMenuVisible: !prev.isMenuVisible}))} + accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} + accessibilityLabel={this.props.translate('avatarWithImagePicker.editImage')} + disabled={this.state.isAvatarCropModalOpen} + ref={this.anchorRef} + > {this.props.source ? ( )} - - - - - {({openPicker}) => ( - <> - - - - - - this.setState({isMenuVisible: false})} - onItemSelected={(item, index) => { - this.setState({isMenuVisible: false}); - // In order for the file picker to open dynamically, the click - // function must be called from within a event handler that was initiated - // by the user. - if (index === 0) { - openPicker({ - onPicked: this.showAvatarCropModal, - }); - } - }} - menuItems={menuItems} - anchorPosition={this.props.anchorPosition} - withoutOverlay - anchorRef={this.anchorRef} - anchorAlignment={this.props.anchorAlignment} + + - - )} - - - + + + + + + {({openPicker}) => { + const menuItems = [ + { + icon: Expensicons.Upload, + text: this.props.translate('avatarWithImagePicker.uploadPhoto'), + onSelected: () => { + if (Browser.isSafari()) { + return; + } + openPicker({ + onPicked: this.showAvatarCropModal, + }); + }, + }, + ]; + + // If current avatar isn't a default avatar, allow Remove Photo option + if (!this.props.isUsingDefaultAvatar) { + menuItems.push({ + icon: Expensicons.Trashcan, + text: this.props.translate('avatarWithImagePicker.removePhoto'), + onSelected: () => { + this.setError(null, {}); + this.props.onImageRemoved(); + }, + }); + } + return ( + this.setState({isMenuVisible: false})} + onItemSelected={(item, index) => { + this.setState({isMenuVisible: false}); + // In order for the file picker to open dynamically, the click + // function must be called from within a event handler that was initiated + // by the user on Safari. + if (index === 0 && Browser.isSafari()) { + openPicker({ + onPicked: this.showAvatarCropModal, + }); + } + }} + menuItems={menuItems} + anchorPosition={this.props.anchorPosition} + withoutOverlay + anchorRef={this.anchorRef} + anchorAlignment={this.props.anchorAlignment} + /> + ); + }} + + {this.state.validationError && ( { - document.removeEventListener('keydown', listener); + document.removeEventListener('keydown', listener, true); }; }, [closePopover]); diff --git a/src/libs/Browser/index.js b/src/libs/Browser/index.js index f579b551cb64..f37aa3d481bb 100644 --- a/src/libs/Browser/index.js +++ b/src/libs/Browser/index.js @@ -13,6 +13,10 @@ function isMobileChrome() { return false; } +function isSafari() { + return false; +} + function openRouteInDesktopApp() {} -export {getBrowser, isMobile, isMobileSafari, isMobileChrome, openRouteInDesktopApp}; +export {getBrowser, isMobile, isMobileSafari, isSafari, isMobileChrome, openRouteInDesktopApp}; diff --git a/src/libs/Browser/index.web.js b/src/libs/Browser/index.web.js index cb0b98d0a958..9e48bb25a105 100644 --- a/src/libs/Browser/index.web.js +++ b/src/libs/Browser/index.web.js @@ -61,6 +61,10 @@ function isMobileChrome() { return /Android/i.test(userAgent) && /chrome|chromium|crios/i.test(userAgent); } +function isSafari() { + return getBrowser() === 'safari' || isMobileSafari(); +} + /** * The session information needs to be passed to the Desktop app, and the only way to do that is by using query params. There is no other way to transfer the data. * @param {String} shortLivedAuthToken @@ -100,4 +104,4 @@ function openRouteInDesktopApp(shortLivedAuthToken = '', email = '') { } } -export {getBrowser, isMobile, isMobileSafari, isMobileChrome, openRouteInDesktopApp}; +export {getBrowser, isMobile, isMobileSafari, isSafari, isMobileChrome, openRouteInDesktopApp}; diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 0cf1853b3e31..48173754c3be 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -1005,16 +1005,6 @@ class ReportActionCompose extends React.Component { runOnJS(submit)(); }); - const menuItems = [ - ...this.getMoneyRequestOptions(reportParticipants), - ...this.getTaskOption(reportParticipants), - { - icon: Expensicons.Paperclip, - text: this.props.translate('reportActionCompose.addAttachment'), - onSelected: () => {}, - }, - ]; - return ( ( <> - {({openPicker}) => ( - <> - - {this.props.isComposerFullSize && ( - - { - e.preventDefault(); - this.setShouldShowSuggestionMenuToFalse(); - Report.setIsComposerFullSize(this.props.reportID, false); - }} - // Keep focus on the composer when Collapse button is clicked. - onMouseDown={(e) => e.preventDefault()} - style={styles.composerSizeButton} - disabled={isBlockedFromConcierge || this.props.disabled} - accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON} - accessibilityLabel={this.props.translate('reportActionCompose.collapse')} - > - - - - )} - {!this.props.isComposerFullSize && isFullComposerAvailable && ( - + {({openPicker}) => { + const triggerAttachmentPicker = () => { + // Set a flag to block suggestion calculation until we're finished using the file picker, + // which will stop any flickering as the file picker opens on non-native devices. + if (this.willBlurTextInputOnTapOutside) { + this.shouldBlockEmojiCalc = true; + this.shouldBlockMentionCalc = true; + } + openPicker({ + onPicked: displayFileInModal, + }); + }; + const menuItems = [ + ...this.getMoneyRequestOptions(reportParticipants), + ...this.getTaskOption(), + { + icon: Expensicons.Paperclip, + text: this.props.translate('reportActionCompose.addAttachment'), + onSelected: () => { + if (Browser.isSafari()) { + return; + } + triggerAttachmentPicker(); + }, + }, + ]; + return ( + <> + + {this.props.isComposerFullSize && ( + + { + e.preventDefault(); + this.setShouldShowSuggestionMenuToFalse(); + Report.setIsComposerFullSize(this.props.reportID, false); + }} + // Keep focus on the composer when Collapse button is clicked. + onMouseDown={(e) => e.preventDefault()} + style={styles.composerSizeButton} + disabled={isBlockedFromConcierge || this.props.disabled} + accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON} + accessibilityLabel={this.props.translate('reportActionCompose.collapse')} + > + + + + )} + {!this.props.isComposerFullSize && isFullComposerAvailable && ( + + { + e.preventDefault(); + this.setShouldShowSuggestionMenuToFalse(); + Report.setIsComposerFullSize(this.props.reportID, true); + }} + // Keep focus on the composer when Expand button is clicked. + onMouseDown={(e) => e.preventDefault()} + style={styles.composerSizeButton} + disabled={isBlockedFromConcierge || this.props.disabled} + accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON} + accessibilityLabel={this.props.translate('reportActionCompose.expand')} + > + + + + )} + { e.preventDefault(); - this.setShouldShowSuggestionMenuToFalse(); - Report.setIsComposerFullSize(this.props.reportID, true); + + // Drop focus to avoid blue focus ring. + this.actionButtonRef.current.blur(); + this.setMenuVisibility(!this.state.isMenuVisible); }} - // Keep focus on the composer when Expand button is clicked. - onMouseDown={(e) => e.preventDefault()} style={styles.composerSizeButton} disabled={isBlockedFromConcierge || this.props.disabled} accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON} - accessibilityLabel={this.props.translate('reportActionCompose.expand')} + accessibilityLabel={this.props.translate('reportActionCompose.addAction')} > - + - )} - - { - e.preventDefault(); - - // Drop focus to avoid blue focus ring. - this.actionButtonRef.current.blur(); - this.setMenuVisibility(!this.state.isMenuVisible); - }} - style={styles.composerSizeButton} - disabled={isBlockedFromConcierge || this.props.disabled} - accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON} - accessibilityLabel={this.props.translate('reportActionCompose.addAction')} - > - - - - - this.setMenuVisibility(false)} - onItemSelected={(item, index) => { - this.setMenuVisibility(false); - - // In order for the file picker to open dynamically, the click - // function must be called from within a event handler that was initiated - // by the user. - if (index === menuItems.length - 1) { - // Set a flag to block suggestion calculation until we're finished using the file picker, - // which will stop any flickering as the file picker opens on non-native devices. - if (this.willBlurTextInputOnTapOutside) { - this.shouldBlockEmojiCalc = true; - this.shouldBlockMentionCalc = true; + + this.setMenuVisibility(false)} + onItemSelected={(item, index) => { + this.setMenuVisibility(false); + + // In order for the file picker to open dynamically, the click + // function must be called from within a event handler that was initiated + // by the user on Safari. + if (index === menuItems.length - 1 && Browser.isSafari()) { + triggerAttachmentPicker(); } - openPicker({ - onPicked: displayFileInModal, - }); - } - }} - anchorPosition={styles.createMenuPositionReportActionCompose(this.props.windowHeight)} - anchorAlignment={{horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM}} - menuItems={[ - ...this.getMoneyRequestOptions(reportParticipants), - ...this.getTaskOption(), - { - icon: Expensicons.Paperclip, - text: this.props.translate('reportActionCompose.addAttachment'), - onSelected: () => { - // Set a flag to block suggestion calculation until we're finished using the file picker, - // which will stop any flickering as the file picker opens on non-native devices. - if (this.willBlurTextInputOnTapOutside) { - this.shouldBlockEmojiCalc = true; - this.shouldBlockMentionCalc = true; - } - - openPicker({ - onPicked: displayFileInModal, - }); - }, - }, - ]} - withoutOverlay - anchorRef={this.actionButtonRef} - /> - - )} + }} + anchorPosition={styles.createMenuPositionReportActionCompose(this.props.windowHeight)} + anchorAlignment={{horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM}} + menuItems={menuItems} + withoutOverlay + anchorRef={this.actionButtonRef} + /> + + ); + }}