diff --git a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.js b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.js index f2f9e2e89ae7..ad8846eb6006 100644 --- a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.js +++ b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.js @@ -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); @@ -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); @@ -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: { @@ -134,7 +146,7 @@ class PopoverReportActionContextMenu extends React.Component { selection, isPopoverVisible: true, reportActionDraftMessage: draftMessage, - }, onShow); + }); }); } @@ -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, @@ -230,6 +254,7 @@ class PopoverReportActionContextMenu extends React.Component { {}) { 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() {