Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Andrew 3313 link context menu #3931

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,48 @@ import Text from '../../Text';
import {propTypes, defaultProps} from '../anchorForCommentsOnlyPropTypes';
import PressableWithSecondaryInteraction from '../../PressableWithSecondaryInteraction';
import {showContextMenu} from '../../../pages/home/report/ContextMenu/ReportActionContextMenu';
import {contextMenuTypes} from '../../../pages/home/report/ContextMenu/ContextMenuActions';
import {CONTEXT_MENU_TYPES} from '../../../pages/home/report/ContextMenu/ContextMenuActions';

const linkRef = React.createRef();

/*
* This is a default anchor component for regular links.
*/
* This is a default anchor component for regular links.
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
*/
const BaseAnchorForCommentsOnly = ({
href,
rel,
target,
children,
style,
...props
}) => (
<PressableWithSecondaryInteraction
onSecondaryInteraction={
(event) => {
showContextMenu(
contextMenuTypes.link,
event,
href,
linkRef.current,
);
}) => {
let linkRef;
return (
<PressableWithSecondaryInteraction
onSecondaryInteraction={
(event) => {
showContextMenu(
CONTEXT_MENU_TYPES.link,
event,
href,
linkRef.current,
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
);
}
}
}
>
<Text
ref={linkRef}
style={StyleSheet.flatten(style)}
accessibilityRole="link"
href={href}
hrefAttrs={{rel, target}}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
>
{children}
</Text>
</PressableWithSecondaryInteraction>
);
<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
Expand Up @@ -5,10 +5,7 @@ import fileDownload from '../../../libs/fileDownload';
import Text from '../../Text';
import PressableWithSecondaryInteraction from '../../PressableWithSecondaryInteraction';
import {showContextMenu} from '../../../pages/home/report/ContextMenu/ReportActionContextMenu';
import {contextMenuTypes} from '../../../pages/home/report/ContextMenu/ContextMenuActions';

const linkRef = React.createRef();

import {CONTEXT_MENU_TYPES} from '../../../pages/home/report/ContextMenu/ContextMenuActions';

/*
* This is a default anchor component for regular links.
Expand All @@ -19,30 +16,33 @@ const BaseAnchorForCommentsOnly = ({
style,
shouldDownloadFile,
...props
}) => (
<PressableWithSecondaryInteraction
onSecondaryInteraction={
(event) => {
showContextMenu(
contextMenuTypes.link,
event,
href,
linkRef.current,
);
}) => {
let linkRef;
return (
<PressableWithSecondaryInteraction
onSecondaryInteraction={
(event) => {
showContextMenu(
CONTEXT_MENU_TYPES.link,
event,
href,
linkRef.current,
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
);
}
}
}
onPress={() => (shouldDownloadFile ? fileDownload(href) : Linking.openURL(href))}
>
<Text
ref={linkRef}
style={StyleSheet.flatten(style)}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
onPress={() => (shouldDownloadFile ? fileDownload(href) : Linking.openURL(href))}
>
{children}
</Text>
</PressableWithSecondaryInteraction>
);
<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
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
1 change: 1 addition & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export default {
},
reportActionContextMenu: {
copyToClipboard: 'Copy to Clipboard',
openLink: 'Open Link',
copied: 'Copied!',
copyLink: 'Copy Link',
copyURLToClipboard: 'Copy URL to Clipboard',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export default {
},
reportActionContextMenu: {
copyToClipboard: 'Copiar al Portapapeles',
openLink: 'Abrir Enlace',
copied: '¡Copiado!',
copyLink: 'Copiar Enlace',
copyURLToClipboard: 'Copiar URL al Portapapeles',
Expand Down
46 changes: 21 additions & 25 deletions src/pages/home/report/ContextMenu/BaseReportActionContextMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import withLocalize, {withLocalizePropTypes} from '../../../../components/withLo
import ContextMenuActions from './ContextMenuActions';

const propTypes = {
/** String representing the context menu type [LINK, REPORT_ACTION] which controls context menu choices */
type: PropTypes.string,
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
...GenericReportActionContextMenuPropTypes,
...withLocalizePropTypes,
Expand All @@ -23,33 +24,28 @@ class BaseReportActionContextMenu extends React.Component {
}

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

return this.props.isVisible && (
<View style={this.wrapperStyle}>
{_.map(
ContextMenuActions, (contextAction) => {
if (!contextAction.shouldShow(this.props.type, this.props.reportAction)) {
return;
}
return (
<ContextMenuItem
icon={contextAction.icon}
text={this.props.translate(contextAction.textTranslateKey)}
successIcon={contextAction.successIcon}
successText={contextAction.successTextTranslateKey
? this.props.translate(contextAction.successTextTranslateKey)
: undefined}
isMini={this.props.isMini}
key={contextAction.textTranslateKey}
onPress={() => contextAction.onPress(!this.props.isMini, {
reportAction: this.props.reportAction,
reportID: this.props.reportID,
draftMessage: this.props.draftMessage,
selection: this.props.selection,
})}
/>
);
},
)}
{ContextMenuActions.filter(shouldShowFilter).map(contextAction => (
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
<ContextMenuItem
icon={contextAction.icon}
text={this.props.translate(contextAction.textTranslateKey)}
successIcon={contextAction.successIcon}
successText={contextAction.successTextTranslateKey
? this.props.translate(contextAction.successTextTranslateKey)
: undefined}
isMini={this.props.isMini}
key={contextAction.textTranslateKey}
onPress={() => contextAction.onPress(!this.props.isMini, {
reportAction: this.props.reportAction,
reportID: this.props.reportID,
draftMessage: this.props.draftMessage,
selection: this.props.selection,
})}
/>
))}
</View>
);
}
Expand Down
27 changes: 19 additions & 8 deletions src/pages/home/report/ContextMenu/ContextMenuActions.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {Linking} from 'react-native';
import _ from 'underscore';
import lodashGet from 'lodash/get';
import Str from 'expensify-common/lib/str';
import {
Clipboard as ClipboardIcon, LinkCopy, Mail, Pencil, Trashcan, Checkmark,
Clipboard as ClipboardIcon, LinkCopy, Mail, Pencil, Trashcan, Checkmark, Link,
} from '../../../../components/Icon/Expensicons';
import {
setNewMarkerPosition, updateLastReadActionID, saveReportActionDraft,
Expand All @@ -22,20 +23,28 @@ function getActionText(reportAction) {
return lodashGet(message, 'html', '');
}

export const contextMenuTypes = {
export const CONTEXT_MENU_TYPES = {
link: 'LINK',
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
image: 'IMAGE',
reportAction: 'REPORT_ACTION',
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
};

// A list of all the context actions in this menu.
export default [
{
textTranslateKey: 'reportActionContextMenu.openLink',
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
icon: Link,
shouldShow: type => type === CONTEXT_MENU_TYPES.link,
onPress: (closePopover, {selection}) => {
Linking.openURL(selection);
hideContextMenu(false, ReportActionComposeFocusManager.focus);
},
},
{
textTranslateKey: 'reportActionContextMenu.copyURLToClipboard',
icon: ClipboardIcon,
successTextTranslateKey: 'reportActionContextMenu.copied',
successIcon: Checkmark,
shouldShow: type => type === contextMenuTypes.link,
shouldShow: type => type === CONTEXT_MENU_TYPES.link,
onPress: (closePopover, {selection}) => {
Clipboard.setString(selection);
hideContextMenu(true, ReportActionComposeFocusManager.focus);
Expand All @@ -46,7 +55,7 @@ export default [
icon: ClipboardIcon,
successTextTranslateKey: 'reportActionContextMenu.copied',
successIcon: Checkmark,
shouldShow: type => type === contextMenuTypes.reportAction,
shouldShow: type => type === CONTEXT_MENU_TYPES.reportAction,

// 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 @@ -80,7 +89,7 @@ export default [
textTranslateKey: 'reportActionContextMenu.markAsUnread',
icon: Mail,
successIcon: Checkmark,
shouldShow: type => type === contextMenuTypes.reportAction,
shouldShow: type => type === CONTEXT_MENU_TYPES.reportAction,
onPress: (closePopover, {reportAction, reportID}) => {
updateLastReadActionID(reportID, reportAction.sequenceNumber);
setNewMarkerPosition(reportID, reportAction.sequenceNumber);
Expand All @@ -93,7 +102,9 @@ export default [
{
textTranslateKey: 'reportActionContextMenu.editComment',
icon: Pencil,
shouldShow: (type, reportAction) => type === contextMenuTypes.reportAction && canEditReportAction(reportAction),
shouldShow: (type, reportAction) => (
type === CONTEXT_MENU_TYPES.reportAction && canEditReportAction(reportAction)
),
onPress: (closePopover, {reportID, reportAction, draftMessage}) => {
const editAction = () => saveReportActionDraft(
reportID,
Expand All @@ -114,7 +125,7 @@ export default [
{
textTranslateKey: 'reportActionContextMenu.deleteComment',
icon: Trashcan,
shouldShow: (type, reportAction) => type === contextMenuTypes.reportAction
shouldShow: (type, reportAction) => type === CONTEXT_MENU_TYPES.reportAction
&& canDeleteReportAction(reportAction),
onPress: (closePopover, {reportID, reportAction}) => {
if (closePopover) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class PopoverReportActionContextMenu extends React.Component {
/**
* Show the ReportActionContextMenu modal popover.
*
* @param {string} type - context menu type [LINK, IMAGE, REPORT_ACTION]
* @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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const contextMenuRef = React.createRef();
/**
* Show the ReportActionContextMenu modal popover.
*
* @param {string} type - the context menu type to who [LINK, IMAGE, REPORT_ACTION]
* @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 Down
5 changes: 3 additions & 2 deletions src/pages/home/report/ReportActionItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import ControlSelection from '../../../libs/ControlSelection';
import canUseTouchScreen from '../../../libs/canUseTouchscreen';
import MiniReportActionContextMenu from './ContextMenu/MiniReportActionContextMenu';
import {isActiveReportAction, showContextMenu} from './ContextMenu/ReportActionContextMenu';
import {contextMenuTypes} from './ContextMenu/ContextMenuActions';
import {CONTEXT_MENU_TYPES} from './ContextMenu/ContextMenuActions';
import {withReportActionsDrafts} from '../../../components/OnyxProvider';

const propTypes = {
/** The ID of the report this action is on. */
Expand Down Expand Up @@ -91,7 +92,7 @@ class ReportActionItem extends Component {
return;
}
showContextMenu(
contextMenuTypes.reportAction,
CONTEXT_MENU_TYPES.reportAction,
event,
selection,
this.popoverAnchor,
Expand Down