Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ class PopoverReportActionContextMenu extends React.Component {
vertical: 0,
},
};
this.onPopoverShow = () => {};
this.onPopoverHide = () => {};
this.onPopoverHideActionCallback = () => {};
this.contextMenuAnchor = undefined;
this.showContextMenu = this.showContextMenu.bind(this);
this.hideContextMenu = this.hideContextMenu.bind(this);
Expand All @@ -46,6 +48,7 @@ class PopoverReportActionContextMenu extends React.Component {
this.confirmDeleteAndHideModal = this.confirmDeleteAndHideModal.bind(this);
this.hideDeleteModal = this.hideDeleteModal.bind(this);
this.showDeleteModal = this.showDeleteModal.bind(this);
this.runAndResetOnPopoverShow = this.runAndResetOnPopoverShow.bind(this);
this.runAndResetOnPopoverHide = this.runAndResetOnPopoverHide.bind(this);
this.getContextMenuMeasuredLocation = this.getContextMenuMeasuredLocation.bind(this);
this.isActiveReportAction = this.isActiveReportAction.bind(this);
Expand Down Expand Up @@ -117,7 +120,16 @@ class PopoverReportActionContextMenu extends React.Component {
) {
const nativeEvent = event.nativeEvent || {};
this.contextMenuAnchor = contextMenuAnchor;
this.onPopoverHide = onHide;

// Singleton behaviour of ContextMenu creates race conditions when user requests multiple contextMenus.
// But it is possible that every new request registers new callbacks thus instanceID is used to corelate those callbacks
this.instanceID = Math.random().toString(36).substr(2, 5);

// Register the onHide callback only when Popover is shown to remove the race conditions when there are mutltiple popover open requests
this.onPopoverShow = () => {
onShow();
this.onPopoverHide = onHide;
};
this.getContextMenuMeasuredLocation().then(({x, y}) => {
this.setState({
cursorRelativePosition: {
Expand All @@ -134,7 +146,7 @@ class PopoverReportActionContextMenu extends React.Component {
selection,
isPopoverVisible: true,
reportActionDraftMessage: draftMessage,
}, onShow);
});
});
}

Expand All @@ -159,22 +171,34 @@ class PopoverReportActionContextMenu extends React.Component {
}

/**
* After Popover hides, call the registered onPopoverHide callback and reset it
* After Popover shows, call the registered onPopoverShow callback and reset it
*/
runAndResetOnPopoverShow() {
this.onPopoverShow();

// After we have called the action, reset it.
this.onPopoverShow = () => {};
}

/**
* After Popover hides, call the registered onPopoverHide & onPopoverHideActionCallback callback and reset it
*/
runAndResetOnPopoverHide() {
this.onPopoverHide();
this.onPopoverHideActionCallback();

// After we have called the action, reset it.
this.onPopoverHide = () => {};
this.onPopoverHideActionCallback = () => {};
}

/**
* Hide the ReportActionContextMenu modal popover.
* @param {Function} onHideCallback Callback to be called after popover is completely hidden
* @param {Function} onHideActionCallback Callback to be called after popover is completely hidden
*/
hideContextMenu(onHideCallback) {
if (_.isFunction(onHideCallback)) {
this.onPopoverHide = onHideCallback;
hideContextMenu(onHideActionCallback) {
if (_.isFunction(onHideActionCallback)) {
this.onPopoverHideActionCallback = onHideActionCallback;
}
this.setState({
reportID: 0,
Expand Down Expand Up @@ -230,6 +254,7 @@ class PopoverReportActionContextMenu extends React.Component {
<PopoverWithMeasuredContent
isVisible={this.state.isPopoverVisible}
onClose={this.hideContextMenu}
onModalShow={this.runAndResetOnPopoverShow}
onModalHide={this.runAndResetOnPopoverHide}
anchorPosition={this.state.popoverAnchorPosition}
animationIn="fadeIn"
Expand Down
11 changes: 10 additions & 1 deletion src/pages/home/report/ContextMenu/ReportActionContextMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,16 @@ function hideContextMenu(shouldDelay, onHideCallback = () => {}) {

return;
}
setTimeout(() => contextMenuRef.current.hideContextMenu(onHideCallback), 800);

// Save the active instanceID for which hide action was called.
// If menu is being closed with a delay, check that whether the same instance exists or a new was created.
// If instance is not same, cancel the hide action
const instanceID = contextMenuRef.current.instanceID;
setTimeout(() => {
if (contextMenuRef.current.instanceID === instanceID) {
contextMenuRef.current.hideContextMenu(onHideCallback);
}
}, 800);
}

function hideDeleteModal() {
Expand Down