Skip to content

Commit

Permalink
Merge pull request #12987 from b1tjoy/b1tjoy-fix-8311
Browse files Browse the repository at this point in the history
Fix context menu when long press on url and email link
  • Loading branch information
marcochavezf authored Jan 12, 2023
2 parents 8649636 + 150074a commit 81855c2
Show file tree
Hide file tree
Showing 15 changed files with 360 additions and 138 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
import React from 'react';
import {Pressable} from 'react-native';
import * as anchorForAttachmentsOnlyPropTypes from './anchorForAttachmentsOnlyPropTypes';
import PropTypes from 'prop-types';
import {
propTypes as anchorForAttachmentsOnlyPropTypes,
defaultProps as anchorForAttachmentsOnlyDefaultProps,
} from './anchorForAttachmentsOnlyPropTypes';
import AttachmentView from '../AttachmentView';
import fileDownload from '../../libs/fileDownload';
import addEncryptedAuthTokenToURL from '../../libs/addEncryptedAuthTokenToURL';
import {ShowContextMenuContext, showContextMenuForReport} from '../ShowContextMenuContext';

const propTypes = {
/** Press in handler for the link */
onPressIn: PropTypes.func,

/** Press out handler for the link */
onPressOut: PropTypes.func,

...anchorForAttachmentsOnlyPropTypes,
};

const defaultProps = {
onPressIn: undefined,
onPressOut: undefined,
...anchorForAttachmentsOnlyDefaultProps,
};

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

return (
<Pressable
style={this.props.style}
onPress={() => {
if (this.state.isDownloading) {
return;
}
this.processDownload(source, this.props.displayName);
}}
>
<AttachmentView
sourceURL={source}
file={{name: this.props.displayName}}
shouldShowDownloadIcon
shouldShowLoadingSpinnerIcon={this.state.isDownloading}
/>
</Pressable>
<ShowContextMenuContext.Consumer>
{({
anchor,
reportID,
action,
checkIfContextMenuActive,
}) => (
<Pressable
style={this.props.style}
onPress={() => {
if (this.state.isDownloading) {
return;
}
this.processDownload(source, this.props.displayName);
}}
onPressIn={this.props.onPressIn}
onPressOut={this.props.onPressOut}
onLongPress={event => showContextMenuForReport(
event,
anchor,
reportID,
action,
checkIfContextMenuActive,
)}
>
<AttachmentView
sourceURL={source}
file={{name: this.props.displayName}}
shouldShowDownloadIcon
shouldShowLoadingSpinnerIcon={this.state.isDownloading}
/>
</Pressable>
)}
</ShowContextMenuContext.Consumer>
);
}
}

BaseAnchorForAttachmentsOnly.propTypes = anchorForAttachmentsOnlyPropTypes.propTypes;
BaseAnchorForAttachmentsOnly.defaultProps = anchorForAttachmentsOnlyPropTypes.defaultProps;
BaseAnchorForAttachmentsOnly.propTypes = propTypes;
BaseAnchorForAttachmentsOnly.defaultProps = defaultProps;

export default BaseAnchorForAttachmentsOnly;
12 changes: 10 additions & 2 deletions src/components/AnchorForAttachmentsOnly/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import React from 'react';
import * as anchorForAttachmentsOnlyPropTypes from './anchorForAttachmentsOnlyPropTypes';
import BaseAnchorForAttachmentsOnly from './BaseAnchorForAttachmentsOnly';
import * as DeviceCapabilities from '../../libs/DeviceCapabilities';
import ControlSelection from '../../libs/ControlSelection';

// eslint-disable-next-line react/jsx-props-no-spreading
const AnchorForAttachmentsOnly = props => <BaseAnchorForAttachmentsOnly {...props} />;
const AnchorForAttachmentsOnly = props => (
<BaseAnchorForAttachmentsOnly
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()}
onPressOut={() => ControlSelection.unblock()}
/>
);

AnchorForAttachmentsOnly.propTypes = anchorForAttachmentsOnlyPropTypes.propTypes;
AnchorForAttachmentsOnly.defaultProps = anchorForAttachmentsOnlyPropTypes.defaultProps;
Expand Down
24 changes: 21 additions & 3 deletions src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import _ from 'underscore';
import React from 'react';
import {StyleSheet} from 'react-native';
import PropTypes from 'prop-types';
import lodashGet from 'lodash/get';
import Str from 'expensify-common/lib/str';
import Text from '../Text';
Expand All @@ -11,13 +12,28 @@ import Tooltip from '../Tooltip';
import * as DeviceCapabilities from '../../libs/DeviceCapabilities';
import styles from '../../styles/styles';
import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions';
import {propTypes as anchorForCommentsOnlyPropTypes, defaultProps} from './anchorForCommentsOnlyPropTypes';
import {
propTypes as anchorForCommentsOnlyPropTypes,
defaultProps as anchorForCommentsOnlyDefaultProps,
} from './anchorForCommentsOnlyPropTypes';

const propTypes = {
/** Press in handler for the link */
onPressIn: PropTypes.func,

/** Press out handler for the link */
onPressOut: PropTypes.func,

...anchorForCommentsOnlyPropTypes,
...windowDimensionsPropTypes,
};

const defaultProps = {
onPressIn: undefined,
onPressOut: undefined,
...anchorForCommentsOnlyDefaultProps,
};

/*
* This is a default anchor component for regular links.
*/
Expand Down Expand Up @@ -45,6 +61,9 @@ const BaseAnchorForCommentsOnly = (props) => {
);
}
}
onPress={linkProps.onPress}
onPressIn={props.onPressIn}
onPressOut={props.onPressOut}
>
<Tooltip containerStyles={[styles.dInline]} text={Str.isValidEmail(props.displayName) ? '' : props.href}>
<Text
Expand All @@ -55,8 +74,7 @@ const BaseAnchorForCommentsOnly = (props) => {
rel: props.rel,
target: props.target,
}}
// eslint-disable-next-line react/jsx-props-no-spreading
{...linkProps}
href={linkProps.href}
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
>
Expand Down
13 changes: 11 additions & 2 deletions src/components/AnchorForCommentsOnly/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import React from 'react';
import * as anchorForCommentsOnlyPropTypes from './anchorForCommentsOnlyPropTypes';
import BaseAnchorForCommentsOnly from './BaseAnchorForCommentsOnly';
import * as DeviceCapabilities from '../../libs/DeviceCapabilities';
import ControlSelection from '../../libs/ControlSelection';

const AnchorForCommentsOnly = props => (
<BaseAnchorForCommentsOnly
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()}
onPressOut={() => ControlSelection.unblock()}
/>
);

// eslint-disable-next-line react/jsx-props-no-spreading
const AnchorForCommentsOnly = props => <BaseAnchorForCommentsOnly {...props} />;
AnchorForCommentsOnly.propTypes = anchorForCommentsOnlyPropTypes.propTypes;
AnchorForCommentsOnly.defaultProps = anchorForCommentsOnlyPropTypes.defaultProps;
AnchorForCommentsOnly.displayName = 'AnchorForCommentsOnly';
Expand Down
49 changes: 30 additions & 19 deletions src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import styles from '../../../styles/styles';
import ThumbnailImage from '../../ThumbnailImage';
import PressableWithoutFocus from '../../PressableWithoutFocus';
import CONST from '../../../CONST';
import {ShowContextMenuContext, showContextMenuForReport} from '../../ShowContextMenuContext';

const ImageRenderer = (props) => {
const htmlAttribs = props.tnode.attributes;
Expand Down Expand Up @@ -57,27 +58,37 @@ const ImageRenderer = (props) => {
imageHeight={imageHeight}
/>
) : (
<AttachmentModal
allowDownload
sourceURL={source}
isAuthTokenRequired={isAttachment}
originalFileName={originalFileName}
>
{({show}) => (
<PressableWithoutFocus
style={styles.noOutline}
onPress={show}
<ShowContextMenuContext.Consumer>
{({
anchor,
reportID,
action,
checkIfContextMenuActive,
}) => (
<AttachmentModal
allowDownload
sourceURL={source}
isAuthTokenRequired={isAttachment}
originalFileName={originalFileName}
>
<ThumbnailImage
previewSourceURL={previewSource}
style={styles.webViewStyles.tagStyles.img}
isAuthTokenRequired={isAttachment}
imageWidth={imageWidth}
imageHeight={imageHeight}
/>
</PressableWithoutFocus>
{({show}) => (
<PressableWithoutFocus
style={styles.noOutline}
onPress={show}
onLongPress={event => showContextMenuForReport(event, anchor, reportID, action, checkIfContextMenuActive)}
>
<ThumbnailImage
previewSourceURL={previewSource}
style={styles.webViewStyles.tagStyles.img}
isAuthTokenRequired={isAttachment}
imageWidth={imageWidth}
imageHeight={imageHeight}
/>
</PressableWithoutFocus>
)}
</AttachmentModal>
)}
</AttachmentModal>
</ShowContextMenuContext.Consumer>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,59 @@
import React, {forwardRef} from 'react';
import {ScrollView} from 'react-native-gesture-handler';
import {View} from 'react-native';
import {Pressable} from 'react-native';
import PropTypes from 'prop-types';
import _ from 'underscore';
import htmlRendererPropTypes from '../htmlRendererPropTypes';
import withLocalize from '../../../withLocalize';
import {ShowContextMenuContext, showContextMenuForReport} from '../../../ShowContextMenuContext';

const propTypes = {
/** Press in handler for the code block */
onPressIn: PropTypes.func,

/** Press out handler for the code block */
onPressOut: PropTypes.func,

...htmlRendererPropTypes,
};

const defaultProps = {
onPressIn: undefined,
onPressOut: undefined,
};

const BasePreRenderer = forwardRef((props, ref) => {
const TDefaultRenderer = props.TDefaultRenderer;
const defaultRendererProps = _.omit(props, ['TDefaultRenderer']);
const defaultRendererProps = _.omit(props, ['TDefaultRenderer', 'onPressIn', 'onPressOut', 'onLongPress']);

return (
<ScrollView
ref={ref}
horizontal
>
<View onStartShouldSetResponder={() => true}>
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<TDefaultRenderer {...defaultRendererProps} />
</View>
<ShowContextMenuContext.Consumer>
{({
anchor,
reportID,
action,
checkIfContextMenuActive,
}) => (
<Pressable
onPressIn={props.onPressIn}
onPressOut={props.onPressOut}
onLongPress={event => showContextMenuForReport(event, anchor, reportID, action, checkIfContextMenuActive)}
>
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<TDefaultRenderer {...defaultRendererProps} />
</Pressable>
)}
</ShowContextMenuContext.Consumer>
</ScrollView>
);
});

BasePreRenderer.displayName = 'BasePreRenderer';
BasePreRenderer.propTypes = htmlRendererPropTypes;
BasePreRenderer.propTypes = propTypes;
BasePreRenderer.defaultProps = defaultProps;

export default withLocalize(BasePreRenderer);
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import _ from 'underscore';
import withLocalize from '../../../withLocalize';
import htmlRendererPropTypes from '../htmlRendererPropTypes';
import BasePreRenderer from './BasePreRenderer';
import * as DeviceCapabilities from '../../../../libs/DeviceCapabilities';
import ControlSelection from '../../../../libs/ControlSelection';

class PreRenderer extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -58,6 +60,8 @@ class PreRenderer extends React.Component {
// eslint-disable-next-line react/jsx-props-no-spreading
{...this.props}
ref={el => this.ref = el}
onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()}
onPressOut={() => ControlSelection.unblock()}
/>
);
}
Expand Down
Loading

0 comments on commit 81855c2

Please sign in to comment.