Skip to content

Commit

Permalink
Merge pull request #3931 from Drewfergusson/andrew-3313-link-context-…
Browse files Browse the repository at this point in the history
…menu

Andrew 3313 link context menu
  • Loading branch information
roryabraham authored Aug 11, 2021
2 parents 1adc252 + 9cb6058 commit b858fff
Show file tree
Hide file tree
Showing 13 changed files with 127 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import React from 'react';
import {StyleSheet} from 'react-native';
import lodashGet from 'lodash/get';
import Text from '../../Text';
import {propTypes, defaultProps} from '../anchorForCommentsOnlyPropTypes';
import PressableWithSecondaryInteraction from '../../PressableWithSecondaryInteraction';
import {showContextMenu} from '../../../pages/home/report/ContextMenu/ReportActionContextMenu';
import {CONTEXT_MENU_TYPES} from '../../../pages/home/report/ContextMenu/ContextMenuActions';


/*
* This is a default anchor component for regular links.
Expand All @@ -13,18 +18,35 @@ const BaseAnchorForCommentsOnly = ({
children,
style,
...props
}) => (
<Text
style={StyleSheet.flatten(style)}
accessibilityRole="link"
href={href}
hrefAttrs={{rel, target}}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
>
{children}
</Text>
);
}) => {
let linkRef;
return (
<PressableWithSecondaryInteraction
onSecondaryInteraction={
(event) => {
showContextMenu(
CONTEXT_MENU_TYPES.LINK,
event,
href,
lodashGet(linkRef, 'current'),
);
}
}
>
<Text
ref={el => linkRef = el}
style={StyleSheet.flatten(style)}
accessibilityRole="link"
href={href}
hrefAttrs={{rel, target}}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
>
{children}
</Text>
</PressableWithSecondaryInteraction>
);
};

BaseAnchorForCommentsOnly.propTypes = propTypes;
BaseAnchorForCommentsOnly.defaultProps = defaultProps;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import React from 'react';
import lodashGet from 'lodash/get';
import {Linking, StyleSheet} from 'react-native';
import {propTypes, defaultProps} from '../anchorForCommentsOnlyPropTypes';
import fileDownload from '../../../libs/fileDownload';
import Text from '../../Text';
import PressableWithSecondaryInteraction from '../../PressableWithSecondaryInteraction';
import {showContextMenu} from '../../../pages/home/report/ContextMenu/ReportActionContextMenu';
import {CONTEXT_MENU_TYPES} from '../../../pages/home/report/ContextMenu/ContextMenuActions';

/*
* This is a default anchor component for regular links.
Expand All @@ -13,16 +17,33 @@ const BaseAnchorForCommentsOnly = ({
style,
shouldDownloadFile,
...props
}) => (
<Text
style={StyleSheet.flatten(style)}
onPress={() => (shouldDownloadFile ? fileDownload(href) : Linking.openURL(href))}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
>
{children}
</Text>
);
}) => {
let linkRef;
return (
<PressableWithSecondaryInteraction
onSecondaryInteraction={
(event) => {
showContextMenu(
CONTEXT_MENU_TYPES.LINK,
event,
href,
lodashGet(linkRef, 'current'),
);
}
}
onPress={() => (shouldDownloadFile ? fileDownload(href) : Linking.openURL(href))}
>
<Text
ref={el => linkRef = el}
style={StyleSheet.flatten(style)}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
>
{children}
</Text>
</PressableWithSecondaryInteraction>
);
};

BaseAnchorForCommentsOnly.propTypes = propTypes;
BaseAnchorForCommentsOnly.defaultProps = defaultProps;
Expand Down
4 changes: 2 additions & 2 deletions src/components/CommunicationsLink.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ const CommunicationsLink = props => (
{props.children}
<ContextMenuItem
icon={ClipboardIcon}
text={props.translate('contextMenuItem.copyToClipboard')}
text={props.translate('reportActionContextMenu.copyToClipboard')}
successIcon={Checkmark}
successText={props.translate('contextMenuItem.copied')}
successText={props.translate('reportActionContextMenu.copied')}
isMini
autoReset
onPress={() => Clipboard.setString(props.value)}
Expand Down
2 changes: 2 additions & 0 deletions src/components/PressableWithSecondaryInteraction/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class PressableWithSecondaryInteraction extends Component {
*/
executeSecondaryInteractionOnContextMenu(e) {
const selection = window.getSelection().toString();
e.stopPropagation();
if (this.props.preventDefaultContentMenu) {
e.preventDefault();
}
Expand All @@ -44,6 +45,7 @@ class PressableWithSecondaryInteraction extends Component {
delayLongPress={200}
onLongPress={this.props.onSecondaryInteraction}
onPressOut={this.props.onPressOut}
onPress={this.props.onPress}
ref={el => this.pressableRef = el}
// eslint-disable-next-line react/jsx-props-no-spreading
{...defaultPressableProps}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {propTypes, defaultProps} from './pressableWithSecondaryInteractionPropTy
const PressableWithSecondaryInteraction = props => (
<Pressable
ref={props.forwardedRef}
onPress={props.onPress}
onPressIn={props.onPressIn}
delayLongPress={200}
onLongPress={(e) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import PropTypes from 'prop-types';

const propTypes = {
/** The function that should be called when this pressable is pressed */
onPress: PropTypes.func,

/** The function that should be called when this pressable is pressedIn */
onPressIn: PropTypes.func,

Expand Down
5 changes: 2 additions & 3 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,11 @@ export default {
roomIsArchived: 'This chat room has been deleted',
localTime: ({user, time}) => `It's ${time} for ${user}`,
},
contextMenuItem: {
reportActionContextMenu: {
copyToClipboard: 'Copy to Clipboard',
copied: 'Copied!',
},
reportActionContextMenu: {
copyLink: 'Copy Link',
copyURLToClipboard: 'Copy URL to Clipboard',
markAsUnread: 'Mark as Unread',
editComment: 'Edit Comment',
deleteComment: 'Delete Comment',
Expand Down
5 changes: 2 additions & 3 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,11 @@ export default {
roomIsArchived: 'Esta sala de chat ha sido eliminada',
localTime: ({user, time}) => `Son las ${time} para ${user}`,
},
contextMenuItem: {
reportActionContextMenu: {
copyToClipboard: 'Copiar al Portapapeles',
copied: '¡Copiado!',
},
reportActionContextMenu: {
copyLink: 'Copiar Enlace',
copyURLToClipboard: 'Copiar URL al Portapapeles',
markAsUnread: 'Marcar como no leído',
editComment: 'Editar Commentario',
deleteComment: 'Eliminar Comentario',
Expand Down
16 changes: 12 additions & 4 deletions src/pages/home/report/ContextMenu/BaseReportActionContextMenu.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
import React from 'react';
import {View} from 'react-native';
import _ from 'underscore';
import PropTypes from 'prop-types';
import getReportActionContextMenuStyles from '../../../../styles/getReportActionContextMenuStyles';
import ContextMenuItem from '../../../../components/ContextMenuItem';
import {
propTypes as GenericReportActionContextMenuPropTypes,
defaultProps,
defaultProps as GenericReportActionContextMenuDefaultProps,
} from './GenericReportActionContextMenuPropTypes';
import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize';
import ContextMenuActions from './ContextMenuActions';
import ContextMenuActions, {CONTEXT_MENU_TYPES} from './ContextMenuActions';

const propTypes = {
/** String representing the context menu type [LINK, REPORT_ACTION] which controls context menu choices */
type: PropTypes.string,
...GenericReportActionContextMenuPropTypes,
...withLocalizePropTypes,
};

const defaultProps = {
type: CONTEXT_MENU_TYPES.REPORT_ACTION,
...GenericReportActionContextMenuDefaultProps,
};
class BaseReportActionContextMenu extends React.Component {
constructor(props) {
super(props);

this.wrapperStyle = getReportActionContextMenuStyles(this.props.isMini);
}

render() {
const shouldShowFilter = contextAction => contextAction.shouldShow(this.props.type, this.props.reportAction);

return this.props.isVisible && (
<View style={this.wrapperStyle}>
{_.map(ContextMenuActions, contextAction => contextAction.shouldShow(this.props.reportAction) && (
{_.map(_.filter(ContextMenuActions, shouldShowFilter), contextAction => (
<ContextMenuItem
icon={contextAction.icon}
text={this.props.translate(contextAction.textTranslateKey)}
Expand Down
32 changes: 25 additions & 7 deletions src/pages/home/report/ContextMenu/ContextMenuActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,30 @@ function getActionText(reportAction) {
return lodashGet(message, 'html', '');
}

export const CONTEXT_MENU_TYPES = {
LINK: 'LINK',
REPORT_ACTION: 'REPORT_ACTION',
};

// A list of all the context actions in this menu.
export default [
// Copy to clipboard
{
textTranslateKey: 'contextMenuItem.copyToClipboard',
textTranslateKey: 'reportActionContextMenu.copyURLToClipboard',
icon: ClipboardIcon,
successTextTranslateKey: 'reportActionContextMenu.copied',
successIcon: Checkmark,
shouldShow: type => type === CONTEXT_MENU_TYPES.LINK,
onPress: (closePopover, {selection}) => {
Clipboard.setString(selection);
hideContextMenu(true, ReportActionComposeFocusManager.focus);
},
},
{
textTranslateKey: 'reportActionContextMenu.copyToClipboard',
icon: ClipboardIcon,
successTextTranslateKey: 'contextMenuItem.copied',
successTextTranslateKey: 'reportActionContextMenu.copied',
successIcon: Checkmark,
shouldShow: () => true,
shouldShow: type => type === CONTEXT_MENU_TYPES.REPORT_ACTION,

// If return value is true, we switch the `text` and `icon` on
// `ContextMenuItem` with `successText` and `successIcon` which will fallback to
Expand Down Expand Up @@ -64,7 +79,7 @@ export default [
textTranslateKey: 'reportActionContextMenu.markAsUnread',
icon: Mail,
successIcon: Checkmark,
shouldShow: () => true,
shouldShow: type => type === CONTEXT_MENU_TYPES.REPORT_ACTION,
onPress: (closePopover, {reportAction, reportID}) => {
updateLastReadActionID(reportID, reportAction.sequenceNumber);
setNewMarkerPosition(reportID, reportAction.sequenceNumber);
Expand All @@ -77,7 +92,9 @@ export default [
{
textTranslateKey: 'reportActionContextMenu.editComment',
icon: Pencil,
shouldShow: reportAction => canEditReportAction(reportAction),
shouldShow: (type, reportAction) => (
type === CONTEXT_MENU_TYPES.REPORT_ACTION && canEditReportAction(reportAction)
),
onPress: (closePopover, {reportID, reportAction, draftMessage}) => {
const editAction = () => saveReportActionDraft(
reportID,
Expand All @@ -98,7 +115,8 @@ export default [
{
textTranslateKey: 'reportActionContextMenu.deleteComment',
icon: Trashcan,
shouldShow: reportAction => canDeleteReportAction(reportAction),
shouldShow: (type, reportAction) => type === CONTEXT_MENU_TYPES.REPORT_ACTION
&& canDeleteReportAction(reportAction),
onPress: (closePopover, {reportID, reportAction}) => {
if (closePopover) {
// Hide popover, then call showDeleteConfirmModal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class PopoverReportActionContextMenu extends React.Component {
/**
* Show the ReportActionContextMenu modal popover.
*
* @param {string} type - context menu type [LINK, REPORT_ACTION]
* @param {Object} [event] - A press event.
* @param {string} [selection] - A copy text.
* @param {Element} contextMenuAnchor - popoverAnchor
Expand All @@ -104,6 +105,7 @@ class PopoverReportActionContextMenu extends React.Component {
* @param {Function} [onHide] - Run a callback when Menu is hidden
*/
showContextMenu(
type,
event,
selection,
contextMenuAnchor,
Expand All @@ -126,6 +128,7 @@ class PopoverReportActionContextMenu extends React.Component {
horizontal: nativeEvent.pageX,
vertical: nativeEvent.pageY,
},
type,
reportID,
reportAction,
selection,
Expand Down Expand Up @@ -190,6 +193,7 @@ class PopoverReportActionContextMenu extends React.Component {
measureContent() {
return (
<BaseReportActionContextMenu
type={this.state.type}
isVisible
selection={this.state.selection}
reportID={this.state.reportID}
Expand Down Expand Up @@ -236,6 +240,7 @@ class PopoverReportActionContextMenu extends React.Component {
>
<BaseReportActionContextMenu
isVisible
type={this.state.type}
reportID={this.state.reportID}
reportAction={this.state.reportAction}
draftMessage={this.state.reportActionDraftMessage}
Expand Down
9 changes: 6 additions & 3 deletions src/pages/home/report/ContextMenu/ReportActionContextMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const contextMenuRef = React.createRef();
/**
* Show the ReportActionContextMenu modal popover.
*
* @param {string} type - the context menu type to display [LINK, REPORT_ACTION]
* @param {Object} [event] - A press event.
* @param {string} [selection] - A copy text.
* @param {Element} contextMenuAnchor - popoverAnchor
Expand All @@ -15,19 +16,21 @@ const contextMenuRef = React.createRef();
* @param {Function} [onHide=() => {}] - Run a callback when Menu is hidden
*/
function showContextMenu(
type,
event,
selection,
contextMenuAnchor,
reportID,
reportAction,
draftMessage,
reportID = 0,
reportAction = {},
draftMessage = '',
onShow = () => {},
onHide = () => {},
) {
if (!contextMenuRef.current) {
return;
}
contextMenuRef.current.showContextMenu(
type,
event,
selection,
contextMenuAnchor,
Expand Down
2 changes: 2 additions & 0 deletions src/pages/home/report/ReportActionItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import ControlSelection from '../../../libs/ControlSelection';
import canUseTouchScreen from '../../../libs/canUseTouchscreen';
import MiniReportActionContextMenu from './ContextMenu/MiniReportActionContextMenu';
import {isActiveReportAction, showContextMenu} from './ContextMenu/ReportActionContextMenu';
import {CONTEXT_MENU_TYPES} from './ContextMenu/ContextMenuActions';
import {withReportActionsDrafts} from '../../../components/OnyxProvider';

const propTypes = {
Expand Down Expand Up @@ -91,6 +92,7 @@ class ReportActionItem extends Component {
return;
}
showContextMenu(
CONTEXT_MENU_TYPES.REPORT_ACTION,
event,
selection,
this.popoverAnchor,
Expand Down

0 comments on commit b858fff

Please sign in to comment.