Skip to content

Commit be9d80d

Browse files
MonilBhavsarAndrewGable
authored and
AndrewGable
committed
Merge pull request #4297 from parasharrajat/context-menu
[NO QA] Fix crash (cherry picked from commit 6f1b536)
1 parent 865bba5 commit be9d80d

File tree

1 file changed

+261
-0
lines changed

1 file changed

+261
-0
lines changed
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
import React from 'react';
2+
import {
3+
Dimensions,
4+
} from 'react-native';
5+
import _ from 'underscore';
6+
import {
7+
deleteReportComment,
8+
} from '../../../../libs/actions/Report';
9+
import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize';
10+
import PopoverWithMeasuredContent from '../../../../components/PopoverWithMeasuredContent';
11+
import BaseReportActionContextMenu from './BaseReportActionContextMenu';
12+
import ConfirmModal from '../../../../components/ConfirmModal';
13+
14+
const propTypes = {
15+
...withLocalizePropTypes,
16+
};
17+
18+
class PopoverReportActionContextMenu extends React.Component {
19+
constructor(props) {
20+
super(props);
21+
22+
this.state = {
23+
reportID: 0,
24+
reportAction: {},
25+
selection: '',
26+
reportActionDraftMessage: '',
27+
isPopoverVisible: false,
28+
isDeleteCommentConfirmModalVisible: false,
29+
cursorRelativePosition: {
30+
horizontal: 0,
31+
vertical: 0,
32+
},
33+
34+
// The horizontal and vertical position (relative to the screen) where the popover will display.
35+
popoverAnchorPosition: {
36+
horizontal: 0,
37+
vertical: 0,
38+
},
39+
};
40+
this.onPopoverHide = () => {};
41+
this.contextMenuAnchor = undefined;
42+
this.showContextMenu = this.showContextMenu.bind(this);
43+
this.hideContextMenu = this.hideContextMenu.bind(this);
44+
this.measureContent = this.measureContent.bind(this);
45+
this.measureContextMenuAnchorPosition = this.measureContextMenuAnchorPosition.bind(this);
46+
this.confirmDeleteAndHideModal = this.confirmDeleteAndHideModal.bind(this);
47+
this.hideDeleteModal = this.hideDeleteModal.bind(this);
48+
this.showDeleteModal = this.showDeleteModal.bind(this);
49+
this.runAndResetOnPopoverHide = this.runAndResetOnPopoverHide.bind(this);
50+
this.getContextMenuMeasuredLocation = this.getContextMenuMeasuredLocation.bind(this);
51+
this.isActiveReportAction = this.isActiveReportAction.bind(this);
52+
}
53+
54+
componentDidMount() {
55+
Dimensions.addEventListener('change', this.measureContextMenuAnchorPosition);
56+
}
57+
58+
shouldComponentUpdate(nextProps, nextState) {
59+
return this.state.isPopoverVisible !== nextState.isPopoverVisible
60+
|| this.state.popoverAnchorPosition !== nextState.popoverAnchorPosition
61+
|| this.state.isDeleteCommentConfirmModalVisible !== nextState.isDeleteCommentConfirmModalVisible;
62+
}
63+
64+
componentWillUnmount() {
65+
Dimensions.removeEventListener('change', this.measureContextMenuAnchorPosition);
66+
}
67+
68+
/**
69+
* Get the Context menu anchor position
70+
* We calculate the achor coordinates from measureInWindow async method
71+
*
72+
* @returns {Promise<Object>}
73+
*/
74+
getContextMenuMeasuredLocation() {
75+
return new Promise((resolve) => {
76+
if (this.contextMenuAnchor) {
77+
this.contextMenuAnchor.measureInWindow((x, y) => resolve({x, y}));
78+
} else {
79+
resolve({x: 0, y: 0});
80+
}
81+
});
82+
}
83+
84+
/**
85+
* Whether Context Menu is active for the Report Action.
86+
*
87+
* @param {Number|String} actionID
88+
* @return {Boolean}
89+
*/
90+
isActiveReportAction(actionID) {
91+
return this.state.reportAction.reportActionID === actionID;
92+
}
93+
94+
/**
95+
* Show the ReportActionContextMenu modal popover.
96+
*
97+
* @param {Object} [event] - A press event.
98+
* @param {string} [selection] - A copy text.
99+
* @param {Element} contextMenuAnchor - popoverAnchor
100+
* @param {Number} reportID - Active Report Id
101+
* @param {Object} reportAction - ReportAction for ContextMenu
102+
* @param {String} draftMessage - ReportAction Draftmessage
103+
* @param {Function} [onShow] - Run a callback when Menu is shown
104+
* @param {Function} [onHide] - Run a callback when Menu is hidden
105+
*/
106+
showContextMenu(
107+
event,
108+
selection,
109+
contextMenuAnchor,
110+
reportID,
111+
reportAction,
112+
draftMessage,
113+
onShow = () => {},
114+
onHide = () => {},
115+
) {
116+
const nativeEvent = event.nativeEvent || {};
117+
this.contextMenuAnchor = contextMenuAnchor;
118+
this.onPopoverHide = onHide;
119+
this.getContextMenuMeasuredLocation().then(({x, y}) => {
120+
this.setState({
121+
cursorRelativePosition: {
122+
horizontal: nativeEvent.pageX - x,
123+
vertical: nativeEvent.pageY - y,
124+
},
125+
popoverAnchorPosition: {
126+
horizontal: nativeEvent.pageX,
127+
vertical: nativeEvent.pageY,
128+
},
129+
reportID,
130+
reportAction,
131+
selection,
132+
isPopoverVisible: true,
133+
reportActionDraftMessage: draftMessage,
134+
}, onShow);
135+
});
136+
}
137+
138+
/**
139+
* This gets called on Dimensions change to find the anchor coordinates for the action context menu.
140+
*/
141+
measureContextMenuAnchorPosition() {
142+
if (!this.state.isPopoverVisible) {
143+
return;
144+
}
145+
this.getContextMenuMeasuredLocation().then(({x, y}) => {
146+
if (!x || !y) {
147+
return;
148+
}
149+
this.setState(prev => ({
150+
popoverAnchorPosition: {
151+
horizontal: prev.cursorRelativePosition.horizontal + x,
152+
vertical: prev.cursorRelativePosition.vertical + y,
153+
},
154+
}));
155+
});
156+
}
157+
158+
/**
159+
* After Popover hides, call the registered onPopoverHide callback and reset it
160+
*/
161+
runAndResetOnPopoverHide() {
162+
this.onPopoverHide();
163+
164+
// After we have called the action, reset it.
165+
this.onPopoverHide = () => {};
166+
}
167+
168+
/**
169+
* Hide the ReportActionContextMenu modal popover.
170+
* @param {Function} onHideCallback Callback to be called after popover is completely hidden
171+
*/
172+
hideContextMenu(onHideCallback) {
173+
if (_.isFunction(onHideCallback)) {
174+
this.onPopoverHide = onHideCallback;
175+
}
176+
this.setState({
177+
reportID: 0,
178+
reportAction: {},
179+
selection: '',
180+
reportActionDraftMessage: '',
181+
isPopoverVisible: false,
182+
});
183+
}
184+
185+
/**
186+
* Used to calculate the Context Menu Dimensions
187+
*
188+
* @returns {JSX}
189+
*/
190+
measureContent() {
191+
return (
192+
<BaseReportActionContextMenu
193+
isVisible
194+
selection={this.state.selection}
195+
reportID={this.state.reportID}
196+
reportAction={this.state.reportAction}
197+
/>
198+
);
199+
}
200+
201+
confirmDeleteAndHideModal() {
202+
deleteReportComment(this.state.reportID, this.state.reportAction);
203+
this.setState({isDeleteCommentConfirmModalVisible: false});
204+
}
205+
206+
hideDeleteModal() {
207+
this.setState({
208+
reportID: 0,
209+
reportAction: {},
210+
isDeleteCommentConfirmModalVisible: false,
211+
});
212+
}
213+
214+
/**
215+
* Opens the Confirm delete action modal
216+
* @param {Number} reportID
217+
* @param {Object} reportAction
218+
*/
219+
showDeleteModal(reportID, reportAction) {
220+
this.setState({reportID, reportAction, isDeleteCommentConfirmModalVisible: true});
221+
}
222+
223+
render() {
224+
return (
225+
<>
226+
<PopoverWithMeasuredContent
227+
isVisible={this.state.isPopoverVisible}
228+
onClose={this.hideContextMenu}
229+
onModalHide={this.runAndResetOnPopoverHide}
230+
anchorPosition={this.state.popoverAnchorPosition}
231+
animationIn="fadeIn"
232+
animationOutTiming={1}
233+
measureContent={this.measureContent}
234+
shouldSetModalVisibility={false}
235+
fullscreen={false}
236+
>
237+
<BaseReportActionContextMenu
238+
isVisible
239+
reportID={this.state.reportID}
240+
reportAction={this.state.reportAction}
241+
draftMessage={this.state.reportActionDraftMessage}
242+
/>
243+
</PopoverWithMeasuredContent>
244+
<ConfirmModal
245+
title={this.props.translate('reportActionContextMenu.deleteComment')}
246+
isVisible={this.state.isDeleteCommentConfirmModalVisible}
247+
onConfirm={this.confirmDeleteAndHideModal}
248+
onCancel={this.hideDeleteModal}
249+
prompt={this.props.translate('reportActionContextMenu.deleteConfirmation')}
250+
confirmText={this.props.translate('common.delete')}
251+
cancelText={this.props.translate('common.cancel')}
252+
/>
253+
</>
254+
);
255+
}
256+
}
257+
258+
PopoverReportActionContextMenu.propTypes = propTypes;
259+
PopoverReportActionContextMenu.displayName = 'PopoverReportActionContextMenu';
260+
261+
export default withLocalize(PopoverReportActionContextMenu);

0 commit comments

Comments
 (0)