Skip to content

Commit 81855c2

Browse files
authored
Merge pull request #12987 from b1tjoy/b1tjoy-fix-8311
Fix context menu when long press on url and email link
2 parents 8649636 + 150074a commit 81855c2

File tree

15 files changed

+360
-138
lines changed

15 files changed

+360
-138
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
11
import React from 'react';
22
import {Pressable} from 'react-native';
3-
import * as anchorForAttachmentsOnlyPropTypes from './anchorForAttachmentsOnlyPropTypes';
3+
import PropTypes from 'prop-types';
4+
import {
5+
propTypes as anchorForAttachmentsOnlyPropTypes,
6+
defaultProps as anchorForAttachmentsOnlyDefaultProps,
7+
} from './anchorForAttachmentsOnlyPropTypes';
48
import AttachmentView from '../AttachmentView';
59
import fileDownload from '../../libs/fileDownload';
610
import addEncryptedAuthTokenToURL from '../../libs/addEncryptedAuthTokenToURL';
11+
import {ShowContextMenuContext, showContextMenuForReport} from '../ShowContextMenuContext';
12+
13+
const propTypes = {
14+
/** Press in handler for the link */
15+
onPressIn: PropTypes.func,
16+
17+
/** Press out handler for the link */
18+
onPressOut: PropTypes.func,
19+
20+
...anchorForAttachmentsOnlyPropTypes,
21+
};
22+
23+
const defaultProps = {
24+
onPressIn: undefined,
25+
onPressOut: undefined,
26+
...anchorForAttachmentsOnlyDefaultProps,
27+
};
728

829
class BaseAnchorForAttachmentsOnly extends React.Component {
930
constructor(props) {
@@ -30,27 +51,45 @@ class BaseAnchorForAttachmentsOnly extends React.Component {
3051
const source = addEncryptedAuthTokenToURL(this.props.source);
3152

3253
return (
33-
<Pressable
34-
style={this.props.style}
35-
onPress={() => {
36-
if (this.state.isDownloading) {
37-
return;
38-
}
39-
this.processDownload(source, this.props.displayName);
40-
}}
41-
>
42-
<AttachmentView
43-
sourceURL={source}
44-
file={{name: this.props.displayName}}
45-
shouldShowDownloadIcon
46-
shouldShowLoadingSpinnerIcon={this.state.isDownloading}
47-
/>
48-
</Pressable>
54+
<ShowContextMenuContext.Consumer>
55+
{({
56+
anchor,
57+
reportID,
58+
action,
59+
checkIfContextMenuActive,
60+
}) => (
61+
<Pressable
62+
style={this.props.style}
63+
onPress={() => {
64+
if (this.state.isDownloading) {
65+
return;
66+
}
67+
this.processDownload(source, this.props.displayName);
68+
}}
69+
onPressIn={this.props.onPressIn}
70+
onPressOut={this.props.onPressOut}
71+
onLongPress={event => showContextMenuForReport(
72+
event,
73+
anchor,
74+
reportID,
75+
action,
76+
checkIfContextMenuActive,
77+
)}
78+
>
79+
<AttachmentView
80+
sourceURL={source}
81+
file={{name: this.props.displayName}}
82+
shouldShowDownloadIcon
83+
shouldShowLoadingSpinnerIcon={this.state.isDownloading}
84+
/>
85+
</Pressable>
86+
)}
87+
</ShowContextMenuContext.Consumer>
4988
);
5089
}
5190
}
5291

53-
BaseAnchorForAttachmentsOnly.propTypes = anchorForAttachmentsOnlyPropTypes.propTypes;
54-
BaseAnchorForAttachmentsOnly.defaultProps = anchorForAttachmentsOnlyPropTypes.defaultProps;
92+
BaseAnchorForAttachmentsOnly.propTypes = propTypes;
93+
BaseAnchorForAttachmentsOnly.defaultProps = defaultProps;
5594

5695
export default BaseAnchorForAttachmentsOnly;

src/components/AnchorForAttachmentsOnly/index.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
import React from 'react';
22
import * as anchorForAttachmentsOnlyPropTypes from './anchorForAttachmentsOnlyPropTypes';
33
import BaseAnchorForAttachmentsOnly from './BaseAnchorForAttachmentsOnly';
4+
import * as DeviceCapabilities from '../../libs/DeviceCapabilities';
5+
import ControlSelection from '../../libs/ControlSelection';
46

5-
// eslint-disable-next-line react/jsx-props-no-spreading
6-
const AnchorForAttachmentsOnly = props => <BaseAnchorForAttachmentsOnly {...props} />;
7+
const AnchorForAttachmentsOnly = props => (
8+
<BaseAnchorForAttachmentsOnly
9+
// eslint-disable-next-line react/jsx-props-no-spreading
10+
{...props}
11+
onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()}
12+
onPressOut={() => ControlSelection.unblock()}
13+
/>
14+
);
715

816
AnchorForAttachmentsOnly.propTypes = anchorForAttachmentsOnlyPropTypes.propTypes;
917
AnchorForAttachmentsOnly.defaultProps = anchorForAttachmentsOnlyPropTypes.defaultProps;

src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly.js

+21-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import _ from 'underscore';
22
import React from 'react';
33
import {StyleSheet} from 'react-native';
4+
import PropTypes from 'prop-types';
45
import lodashGet from 'lodash/get';
56
import Str from 'expensify-common/lib/str';
67
import Text from '../Text';
@@ -11,13 +12,28 @@ import Tooltip from '../Tooltip';
1112
import * as DeviceCapabilities from '../../libs/DeviceCapabilities';
1213
import styles from '../../styles/styles';
1314
import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions';
14-
import {propTypes as anchorForCommentsOnlyPropTypes, defaultProps} from './anchorForCommentsOnlyPropTypes';
15+
import {
16+
propTypes as anchorForCommentsOnlyPropTypes,
17+
defaultProps as anchorForCommentsOnlyDefaultProps,
18+
} from './anchorForCommentsOnlyPropTypes';
1519

1620
const propTypes = {
21+
/** Press in handler for the link */
22+
onPressIn: PropTypes.func,
23+
24+
/** Press out handler for the link */
25+
onPressOut: PropTypes.func,
26+
1727
...anchorForCommentsOnlyPropTypes,
1828
...windowDimensionsPropTypes,
1929
};
2030

31+
const defaultProps = {
32+
onPressIn: undefined,
33+
onPressOut: undefined,
34+
...anchorForCommentsOnlyDefaultProps,
35+
};
36+
2137
/*
2238
* This is a default anchor component for regular links.
2339
*/
@@ -45,6 +61,9 @@ const BaseAnchorForCommentsOnly = (props) => {
4561
);
4662
}
4763
}
64+
onPress={linkProps.onPress}
65+
onPressIn={props.onPressIn}
66+
onPressOut={props.onPressOut}
4867
>
4968
<Tooltip containerStyles={[styles.dInline]} text={Str.isValidEmail(props.displayName) ? '' : props.href}>
5069
<Text
@@ -55,8 +74,7 @@ const BaseAnchorForCommentsOnly = (props) => {
5574
rel: props.rel,
5675
target: props.target,
5776
}}
58-
// eslint-disable-next-line react/jsx-props-no-spreading
59-
{...linkProps}
77+
href={linkProps.href}
6078
// eslint-disable-next-line react/jsx-props-no-spreading
6179
{...rest}
6280
>

src/components/AnchorForCommentsOnly/index.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
import React from 'react';
22
import * as anchorForCommentsOnlyPropTypes from './anchorForCommentsOnlyPropTypes';
33
import BaseAnchorForCommentsOnly from './BaseAnchorForCommentsOnly';
4+
import * as DeviceCapabilities from '../../libs/DeviceCapabilities';
5+
import ControlSelection from '../../libs/ControlSelection';
6+
7+
const AnchorForCommentsOnly = props => (
8+
<BaseAnchorForCommentsOnly
9+
// eslint-disable-next-line react/jsx-props-no-spreading
10+
{...props}
11+
onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()}
12+
onPressOut={() => ControlSelection.unblock()}
13+
/>
14+
);
415

5-
// eslint-disable-next-line react/jsx-props-no-spreading
6-
const AnchorForCommentsOnly = props => <BaseAnchorForCommentsOnly {...props} />;
716
AnchorForCommentsOnly.propTypes = anchorForCommentsOnlyPropTypes.propTypes;
817
AnchorForCommentsOnly.defaultProps = anchorForCommentsOnlyPropTypes.defaultProps;
918
AnchorForCommentsOnly.displayName = 'AnchorForCommentsOnly';

src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js

+30-19
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import styles from '../../../styles/styles';
66
import ThumbnailImage from '../../ThumbnailImage';
77
import PressableWithoutFocus from '../../PressableWithoutFocus';
88
import CONST from '../../../CONST';
9+
import {ShowContextMenuContext, showContextMenuForReport} from '../../ShowContextMenuContext';
910

1011
const ImageRenderer = (props) => {
1112
const htmlAttribs = props.tnode.attributes;
@@ -57,27 +58,37 @@ const ImageRenderer = (props) => {
5758
imageHeight={imageHeight}
5859
/>
5960
) : (
60-
<AttachmentModal
61-
allowDownload
62-
sourceURL={source}
63-
isAuthTokenRequired={isAttachment}
64-
originalFileName={originalFileName}
65-
>
66-
{({show}) => (
67-
<PressableWithoutFocus
68-
style={styles.noOutline}
69-
onPress={show}
61+
<ShowContextMenuContext.Consumer>
62+
{({
63+
anchor,
64+
reportID,
65+
action,
66+
checkIfContextMenuActive,
67+
}) => (
68+
<AttachmentModal
69+
allowDownload
70+
sourceURL={source}
71+
isAuthTokenRequired={isAttachment}
72+
originalFileName={originalFileName}
7073
>
71-
<ThumbnailImage
72-
previewSourceURL={previewSource}
73-
style={styles.webViewStyles.tagStyles.img}
74-
isAuthTokenRequired={isAttachment}
75-
imageWidth={imageWidth}
76-
imageHeight={imageHeight}
77-
/>
78-
</PressableWithoutFocus>
74+
{({show}) => (
75+
<PressableWithoutFocus
76+
style={styles.noOutline}
77+
onPress={show}
78+
onLongPress={event => showContextMenuForReport(event, anchor, reportID, action, checkIfContextMenuActive)}
79+
>
80+
<ThumbnailImage
81+
previewSourceURL={previewSource}
82+
style={styles.webViewStyles.tagStyles.img}
83+
isAuthTokenRequired={isAttachment}
84+
imageWidth={imageWidth}
85+
imageHeight={imageHeight}
86+
/>
87+
</PressableWithoutFocus>
88+
)}
89+
</AttachmentModal>
7990
)}
80-
</AttachmentModal>
91+
</ShowContextMenuContext.Consumer>
8192
);
8293
};
8394

Original file line numberDiff line numberDiff line change
@@ -1,28 +1,59 @@
11
import React, {forwardRef} from 'react';
22
import {ScrollView} from 'react-native-gesture-handler';
3-
import {View} from 'react-native';
3+
import {Pressable} from 'react-native';
4+
import PropTypes from 'prop-types';
45
import _ from 'underscore';
56
import htmlRendererPropTypes from '../htmlRendererPropTypes';
67
import withLocalize from '../../../withLocalize';
8+
import {ShowContextMenuContext, showContextMenuForReport} from '../../../ShowContextMenuContext';
9+
10+
const propTypes = {
11+
/** Press in handler for the code block */
12+
onPressIn: PropTypes.func,
13+
14+
/** Press out handler for the code block */
15+
onPressOut: PropTypes.func,
16+
17+
...htmlRendererPropTypes,
18+
};
19+
20+
const defaultProps = {
21+
onPressIn: undefined,
22+
onPressOut: undefined,
23+
};
724

825
const BasePreRenderer = forwardRef((props, ref) => {
926
const TDefaultRenderer = props.TDefaultRenderer;
10-
const defaultRendererProps = _.omit(props, ['TDefaultRenderer']);
27+
const defaultRendererProps = _.omit(props, ['TDefaultRenderer', 'onPressIn', 'onPressOut', 'onLongPress']);
1128

1229
return (
1330
<ScrollView
1431
ref={ref}
1532
horizontal
1633
>
17-
<View onStartShouldSetResponder={() => true}>
18-
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
19-
<TDefaultRenderer {...defaultRendererProps} />
20-
</View>
34+
<ShowContextMenuContext.Consumer>
35+
{({
36+
anchor,
37+
reportID,
38+
action,
39+
checkIfContextMenuActive,
40+
}) => (
41+
<Pressable
42+
onPressIn={props.onPressIn}
43+
onPressOut={props.onPressOut}
44+
onLongPress={event => showContextMenuForReport(event, anchor, reportID, action, checkIfContextMenuActive)}
45+
>
46+
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
47+
<TDefaultRenderer {...defaultRendererProps} />
48+
</Pressable>
49+
)}
50+
</ShowContextMenuContext.Consumer>
2151
</ScrollView>
2252
);
2353
});
2454

2555
BasePreRenderer.displayName = 'BasePreRenderer';
26-
BasePreRenderer.propTypes = htmlRendererPropTypes;
56+
BasePreRenderer.propTypes = propTypes;
57+
BasePreRenderer.defaultProps = defaultProps;
2758

2859
export default withLocalize(BasePreRenderer);

src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer/index.js

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import _ from 'underscore';
33
import withLocalize from '../../../withLocalize';
44
import htmlRendererPropTypes from '../htmlRendererPropTypes';
55
import BasePreRenderer from './BasePreRenderer';
6+
import * as DeviceCapabilities from '../../../../libs/DeviceCapabilities';
7+
import ControlSelection from '../../../../libs/ControlSelection';
68

79
class PreRenderer extends React.Component {
810
constructor(props) {
@@ -58,6 +60,8 @@ class PreRenderer extends React.Component {
5860
// eslint-disable-next-line react/jsx-props-no-spreading
5961
{...this.props}
6062
ref={el => this.ref = el}
63+
onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()}
64+
onPressOut={() => ControlSelection.unblock()}
6165
/>
6266
);
6367
}

0 commit comments

Comments
 (0)