diff --git a/src/components/EmojiPicker/EmojiPickerButton.js b/src/components/EmojiPicker/EmojiPickerButton.js index 58958d1fd360..57b391007eae 100644 --- a/src/components/EmojiPicker/EmojiPickerButton.js +++ b/src/components/EmojiPicker/EmojiPickerButton.js @@ -36,7 +36,7 @@ const EmojiPickerButton = (props) => { StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed)), ])} disabled={props.isDisabled} - onPress={() => EmojiPickerAction.showEmojiPicker(props.onModalHide, props.onEmojiSelected, emojiPopoverAnchor)} + onPress={() => EmojiPickerAction.showEmojiPicker(props.onModalHide, props.onEmojiSelected, emojiPopoverAnchor, undefined, props.onWillShow)} nativeID={props.nativeID} > {({hovered, pressed}) => ( diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/index.js similarity index 90% rename from src/pages/home/report/ReportActionCompose.js rename to src/pages/home/report/ReportActionCompose/index.js index 9fb4f6a22e1a..73b3b8bd3e06 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/index.js @@ -9,48 +9,49 @@ import { import _ from 'underscore'; import lodashGet from 'lodash/get'; import {withOnyx} from 'react-native-onyx'; -import styles from '../../../styles/styles'; -import themeColors from '../../../styles/themes/default'; -import Composer from '../../../components/Composer'; -import ONYXKEYS from '../../../ONYXKEYS'; -import Icon from '../../../components/Icon'; -import * as Expensicons from '../../../components/Icon/Expensicons'; -import AttachmentPicker from '../../../components/AttachmentPicker'; -import * as Report from '../../../libs/actions/Report'; -import ReportTypingIndicator from './ReportTypingIndicator'; -import AttachmentModal from '../../../components/AttachmentModal'; -import compose from '../../../libs/compose'; -import PopoverMenu from '../../../components/PopoverMenu'; -import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; -import withDrawerState from '../../../components/withDrawerState'; -import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; -import willBlurTextInputOnTapOutside from '../../../libs/willBlurTextInputOnTapOutside'; -import CONST from '../../../CONST'; -import Navigation from '../../../libs/Navigation/Navigation'; -import ROUTES from '../../../ROUTES'; -import reportActionPropTypes from './reportActionPropTypes'; -import * as ReportUtils from '../../../libs/ReportUtils'; -import ReportActionComposeFocusManager from '../../../libs/ReportActionComposeFocusManager'; -import participantPropTypes from '../../../components/participantPropTypes'; -import ParticipantLocalTime from './ParticipantLocalTime'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../../components/withCurrentUserPersonalDetails'; -import {withNetwork, withPersonalDetails} from '../../../components/OnyxProvider'; -import * as User from '../../../libs/actions/User'; -import Tooltip from '../../../components/Tooltip'; -import EmojiPickerButton from '../../../components/EmojiPicker/EmojiPickerButton'; -import * as DeviceCapabilities from '../../../libs/DeviceCapabilities'; -import toggleReportActionComposeView from '../../../libs/toggleReportActionComposeView'; -import OfflineIndicator from '../../../components/OfflineIndicator'; -import ExceededCommentLength from '../../../components/ExceededCommentLength'; -import withNavigationFocus from '../../../components/withNavigationFocus'; -import * as EmojiUtils from '../../../libs/EmojiUtils'; -import ReportDropUI from './ReportDropUI'; -import DragAndDrop from '../../../components/DragAndDrop'; -import reportPropTypes from '../../reportPropTypes'; -import EmojiSuggestions from '../../../components/EmojiSuggestions'; -import withKeyboardState, {keyboardStatePropTypes} from '../../../components/withKeyboardState'; -import ArrowKeyFocusManager from '../../../components/ArrowKeyFocusManager'; -import KeyboardShortcut from '../../../libs/KeyboardShortcut'; +import styles from '../../../../styles/styles'; +import themeColors from '../../../../styles/themes/default'; +import Composer from '../../../../components/Composer'; +import ONYXKEYS from '../../../../ONYXKEYS'; +import Icon from '../../../../components/Icon'; +import * as Expensicons from '../../../../components/Icon/Expensicons'; +import AttachmentPicker from '../../../../components/AttachmentPicker'; +import * as Report from '../../../../libs/actions/Report'; +import ReportTypingIndicator from '../ReportTypingIndicator'; +import AttachmentModal from '../../../../components/AttachmentModal'; +import compose from '../../../../libs/compose'; +import PopoverMenu from '../../../../components/PopoverMenu'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; +import withDrawerState from '../../../../components/withDrawerState'; +import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; +import willBlurTextInputOnTapOutside from '../../../../libs/willBlurTextInputOnTapOutside'; +import CONST from '../../../../CONST'; +import Navigation from '../../../../libs/Navigation/Navigation'; +import ROUTES from '../../../../ROUTES'; +import reportActionPropTypes from '../reportActionPropTypes'; +import * as ReportUtils from '../../../../libs/ReportUtils'; +import ReportActionComposeFocusManager from '../../../../libs/ReportActionComposeFocusManager'; +import participantPropTypes from '../../../../components/participantPropTypes'; +import ParticipantLocalTime from '../ParticipantLocalTime'; +import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../../../components/withCurrentUserPersonalDetails'; +import {withNetwork, withPersonalDetails} from '../../../../components/OnyxProvider'; +import * as User from '../../../../libs/actions/User'; +import Tooltip from '../../../../components/Tooltip'; +import EmojiPickerButton from '../../../../components/EmojiPicker/EmojiPickerButton'; +import * as DeviceCapabilities from '../../../../libs/DeviceCapabilities'; +import toggleReportActionComposeView from '../../../../libs/toggleReportActionComposeView'; +import OfflineIndicator from '../../../../components/OfflineIndicator'; +import ExceededCommentLength from '../../../../components/ExceededCommentLength'; +import withNavigationFocus from '../../../../components/withNavigationFocus'; +import * as EmojiUtils from '../../../../libs/EmojiUtils'; +import ReportDropUI from '../ReportDropUI'; +import DragAndDrop from '../../../../components/DragAndDrop'; +import reportPropTypes from '../../../reportPropTypes'; +import EmojiSuggestions from '../../../../components/EmojiSuggestions'; +import withKeyboardState, {keyboardStatePropTypes} from '../../../../components/withKeyboardState'; +import ArrowKeyFocusManager from '../../../../components/ArrowKeyFocusManager'; +import KeyboardShortcut from '../../../../libs/KeyboardShortcut'; +import KeyDownAction from './keyDownAction'; const propTypes = { /** Beta features list */ @@ -163,6 +164,8 @@ class ReportActionCompose extends React.Component { this.setIsFullComposerAvailable = this.setIsFullComposerAvailable.bind(this); this.focus = this.focus.bind(this); this.addEmojiToTextBox = this.addEmojiToTextBox.bind(this); + this.replaceSelectionWithInput = this.replaceSelectionWithInput.bind(this); + this.keydownListener = this.keydownListener.bind(this); this.onSelectionChange = this.onSelectionChange.bind(this); this.isEmojiCode = this.isEmojiCode.bind(this); this.setTextInputRef = this.setTextInputRef.bind(this); @@ -201,10 +204,13 @@ class ReportActionCompose extends React.Component { isEmojiPickerLarge: false, composerHeight: 0, hasExceededMaxCommentLength: false, + isEmojiPickerVisible: false, }; } componentDidMount() { + KeyDownAction.listenKeyDown(this.keydownListener); + // This callback is used in the contextMenuActions to manage giving focus back to the compose input. // TODO: we should clean up this convoluted code and instead move focus management to something like ReportFooter.js or another higher up component ReportActionComposeFocusManager.onComposerFocus(() => { @@ -260,6 +266,9 @@ class ReportActionCompose extends React.Component { } componentWillUnmount() { + if (this.keydownListener) { + KeyDownAction.removeListenKeyDown(this.keydownListener); + } ReportActionComposeFocusManager.clear(); if (this.unsubscribeEscapeKey) { @@ -465,23 +474,48 @@ class ReportActionCompose extends React.Component { return _.size(this.props.reportActions) === 1; } + keydownListener(e) { + if (this.state.isFocused || this.state.isEmojiPickerVisible || this.props.modal.isVisible || this.props.isSmallScreenWidth) { + return; + } + + // if the key pressed is non-character keys like Enter, Shift, ... do not focus + if (e.key.length > 1) { + return; + } + + // if we're typing on another input/text area, do not focus + if (e.target.nodeName === 'INPUT' || e.target.nodeName === 'TEXTAREA') { + return; + } + + this.focus(); + this.replaceSelectionWithInput(e.key); + } + /** - * Callback for the emoji picker to add whatever emoji is chosen into the main input - * - * @param {String} emoji + * @param {String} text */ - addEmojiToTextBox(emoji) { - const emojiWithSpace = `${emoji} `; + replaceSelectionWithInput(text) { const newComment = this.comment.slice(0, this.state.selection.start) - + emojiWithSpace + + text + this.comment.slice(this.state.selection.end, this.comment.length); this.setState(prevState => ({ selection: { - start: prevState.selection.start + emojiWithSpace.length, - end: prevState.selection.start + emojiWithSpace.length, + start: prevState.selection.start + text.length, + end: prevState.selection.start + text.length, }, - })); - this.updateComment(newComment); + }), this.updateComment(newComment)); + } + + /** + * Callback for the emoji picker to add whatever emoji is chosen into the main input + * + * @param {String} emoji + */ + addEmojiToTextBox(emoji) { + const emojiWithSpace = `${emoji} `; + this.replaceSelectionWithInput(emojiWithSpace); } /** @@ -870,8 +904,12 @@ class ReportActionCompose extends React.Component { {DeviceCapabilities.canUseTouchScreen() && this.props.isMediumScreenWidth ? null : ( this.focus(true)} + onModalHide={() => { + this.focus(true); + this.setState({isEmojiPickerVisible: false}); + }} onEmojiSelected={this.addEmojiToTextBox} + onWillShow={() => this.setState({isEmojiPickerVisible: true})} /> )}