Skip to content

Commit

Permalink
Merge pull request #3441 from parasharrajat/edit-box
Browse files Browse the repository at this point in the history
Set the focus on Edit Box after edit action.
  • Loading branch information
roryabraham authored Jun 11, 2021
2 parents 1ceed16 + 65662dd commit 34612d7
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 17 deletions.
8 changes: 6 additions & 2 deletions src/components/Modal/BaseModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ class BaseModal extends PureComponent {
*/
hideModalAndRemoveEventListeners() {
this.unsubscribeFromKeyEvents();
setModalVisibility(false);
if (this.props.shouldSetModalVisibility) {
setModalVisibility(false);
}
this.props.onModalHide();
}

Expand Down Expand Up @@ -79,7 +81,9 @@ class BaseModal extends PureComponent {
onBackButtonPress={this.props.onClose}
onModalShow={() => {
this.subscribeToKeyEvents();
setModalVisibility(true);
if (this.props.shouldSetModalVisibility) {
setModalVisibility(true);
}
this.props.onModalShow();
}}
onModalHide={this.hideModalAndRemoveEventListeners}
Expand Down
4 changes: 4 additions & 0 deletions src/components/Modal/ModalPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import CONST from '../../CONST';
import {windowDimensionsPropTypes} from '../withWindowDimensions';

const propTypes = {
/** Should we announce the Modal visibility changes? */
shouldSetModalVisibility: PropTypes.bool,

/** Callback method fired when the user requests to close the modal */
onClose: PropTypes.func.isRequired,

Expand Down Expand Up @@ -49,6 +52,7 @@ const propTypes = {
};

const defaultProps = {
shouldSetModalVisibility: true,
onSubmit: null,
type: '',
onModalHide: () => {},
Expand Down
6 changes: 3 additions & 3 deletions src/libs/Navigation/AppNavigator/AuthScreens.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ Onyx.connect({
const RootStack = createCustomModalStackNavigator();

// We want to delay the re-rendering for components(e.g. ReportActionCompose)
// that depends on modal visibility until Modal is completely closed or its transition has ended
// When modal screen is focused and animation transition is ended, update modal visibility in Onyx
// that depends on modal visibility until Modal is completely closed and its focused
// When modal screen is focused, update modal visibility in Onyx
// https://reactnavigation.org/docs/navigation-events/
const modalScreenListeners = {
transitionEnd: () => {
focus: () => {
setModalVisibility(true);
},
beforeRemove: () => {
Expand Down
37 changes: 37 additions & 0 deletions src/libs/ReportActionComposeFocusManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import _ from 'underscore';

let focusCallback = null;

/**
* Register a callback to be called when focus is requested.
* Typical uses of this would be call the focus on the ReportActionComposer.
*
* @param {Function} callback callback to register
*/
function onComposerFocus(callback) {
focusCallback = callback;
}

/**
* Request focus on the ReportActionComposer
*
*/
function focus() {
if (_.isFunction(focusCallback)) {
focusCallback();
}
}

/**
* Clear the registered focus callback
*
*/
function clear() {
focusCallback = null;
}

export default {
onComposerFocus,
focus,
clear,
};
5 changes: 4 additions & 1 deletion src/pages/home/report/ReportActionCompose.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import Navigation from '../../../libs/Navigation/Navigation';
import ROUTES from '../../../ROUTES';
import ReportActionPropTypes from './ReportActionPropTypes';
import {canEditReportAction} from '../../../libs/reportUtils';
import ReportActionComposeFocusManager from '../../../libs/ReportActionComposeFocusManager';

const propTypes = {
/** Beta features list */
Expand Down Expand Up @@ -150,6 +151,7 @@ class ReportActionCompose extends React.Component {
}

componentDidMount() {
ReportActionComposeFocusManager.onComposerFocus(this.focus);
Dimensions.addEventListener('change', this.measureEmojiPopoverAnchorPosition);
}

Expand All @@ -165,6 +167,7 @@ class ReportActionCompose extends React.Component {
}

componentWillUnmount() {
ReportActionComposeFocusManager.clear();
Dimensions.removeEventListener('change', this.measureEmojiPopoverAnchorPosition);
}

Expand Down Expand Up @@ -203,7 +206,7 @@ class ReportActionCompose extends React.Component {
* Focus the composer text input
*/
focus() {
if (this.textInput) {
if (this.shouldFocusInputOnScreenFocus && this.props.isFocused && this.textInput) {
// There could be other animations running while we trigger manual focus.
// This prevents focus from making those animations janky.
InteractionManager.runAfterInteractions(() => {
Expand Down
27 changes: 17 additions & 10 deletions src/pages/home/report/ReportActionContextMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import compose from '../../../libs/compose';
import {isReportMessageAttachment, canEditReportAction} from '../../../libs/reportUtils';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import ConfirmModal from '../../../components/ConfirmModal';
import ReportActionComposeFocusManager from '../../../libs/ReportActionComposeFocusManager';

const propTypes = {
/** The ID of the report this report action is attached to. */
Expand Down Expand Up @@ -87,7 +88,7 @@ class ReportActionContextMenu extends React.Component {
} else {
Clipboard.setString(html);
}
this.hidePopover(true);
this.hidePopover(true, ReportActionComposeFocusManager.focus);
},
},

Expand All @@ -106,7 +107,7 @@ class ReportActionContextMenu extends React.Component {
onPress: () => {
updateLastReadActionID(this.props.reportID, this.props.reportAction.sequenceNumber);
setNewMarkerPosition(this.props.reportID, this.props.reportAction.sequenceNumber);
this.hidePopover(true);
this.hidePopover(true, ReportActionComposeFocusManager.focus);
},
},

Expand All @@ -115,21 +116,26 @@ class ReportActionContextMenu extends React.Component {
icon: Pencil,
shouldShow: () => canEditReportAction(this.props.reportAction),
onPress: () => {
this.hidePopover();
saveReportActionDraft(
const editAction = () => saveReportActionDraft(
this.props.reportID,
this.props.reportAction.reportActionID,
_.isEmpty(this.props.draftMessage) ? this.getActionText() : '',
);

if (this.props.isMini) {
// No popover to hide, call editAction immediately
editAction();
} else {
// Hide popover, then call editAction
this.hidePopover(false, editAction);
}
},
},
{
text: this.props.translate('reportActionContextMenu.deleteComment'),
icon: Trashcan,
shouldShow: () => canEditReportAction(this.props.reportAction),
onPress: () => {
this.setState({isDeleteCommentConfirmModalVisible: true});
},
onPress: () => this.setState({isDeleteCommentConfirmModalVisible: true}),
},
];

Expand Down Expand Up @@ -165,14 +171,15 @@ class ReportActionContextMenu extends React.Component {
* Hides the popover menu with an optional delay
*
* @param {Boolean} shouldDelay whether the menu should close after a delay
* @param {Function} [onHideCallback=() => {}] Callback to be called after Popover Menu is hidden
* @memberof ReportActionContextMenu
*/
hidePopover(shouldDelay) {
hidePopover(shouldDelay, onHideCallback = () => {}) {
if (!shouldDelay) {
this.props.hidePopover();
this.props.hidePopover(onHideCallback);
return;
}
setTimeout(this.props.hidePopover, 800);
setTimeout(() => this.props.hidePopover(onHideCallback), 800);
}

render() {
Expand Down
9 changes: 8 additions & 1 deletion src/pages/home/report/ReportActionItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class ReportActionItem extends Component {
constructor(props) {
super(props);

this.onPopoverHide = () => {};
this.state = {
isPopoverVisible: false,
cursorPosition: {
Expand Down Expand Up @@ -173,8 +174,12 @@ class ReportActionItem extends Component {

/**
* Hide the ReportActionContextMenu modal popover.
* @param {Function} onHideCallback Callback to be called after popover is completely hidden
*/
hidePopover() {
hidePopover(onHideCallback) {
if (_.isFunction(onHideCallback)) {
this.onPopoverHide = onHideCallback;
}
this.setState({isPopoverVisible: false});
}

Expand Down Expand Up @@ -268,10 +273,12 @@ class ReportActionItem extends Component {
<PopoverWithMeasuredContent
isVisible={this.state.isPopoverVisible}
onClose={this.hidePopover}
onModalHide={this.onPopoverHide}
anchorPosition={this.state.popoverAnchorPosition}
animationIn="fadeIn"
animationOutTiming={1}
measureContent={this.measureContent}
shouldSetModalVisibility={false}
>
<ReportActionContextMenu
isVisible
Expand Down
2 changes: 2 additions & 0 deletions src/pages/home/report/ReportActionItemMessageEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {scrollToIndex} from '../../../libs/ReportScrollManager';
import toggleReportActionComposeView from '../../../libs/toggleReportActionComposeView';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
import Button from '../../../components/Button';
import ReportActionComposeFocusManager from '../../../libs/ReportActionComposeFocusManager';

const propTypes = {
/** All the data of the action */
Expand Down Expand Up @@ -74,6 +75,7 @@ class ReportActionItemMessageEdit extends React.Component {
deleteDraft() {
saveReportActionDraft(this.props.reportID, this.props.action.reportActionID, '');
toggleReportActionComposeView(true, this.props.isSmallScreenWidth);
ReportActionComposeFocusManager.focus();
}

/**
Expand Down

0 comments on commit 34612d7

Please sign in to comment.