From c37372ff9dc78ad3962ef33d0b7ecb6a3e41abc7 Mon Sep 17 00:00:00 2001 From: Rajat Parashar Date: Fri, 26 Mar 2021 01:05:39 +0530 Subject: [PATCH 1/5] new: Copy to clipboard functionality --- config/webpack/webpack.common.js | 1 + package-lock.json | 5 + package.json | 1 + src/CONST.js | 1 + src/libs/Clipboard/index.js | 52 +++++++ src/libs/Clipboard/index.native.js | 16 +++ .../home/report/ReportActionContextMenu.js | 30 +++- .../report/ReportActionContextMenuItem.js | 131 +++++++++++++----- src/pages/home/report/ReportActionItem.js | 6 +- .../getReportActionContextMenuItemStyles.js | 2 + 10 files changed, 202 insertions(+), 43 deletions(-) create mode 100644 src/libs/Clipboard/index.js create mode 100644 src/libs/Clipboard/index.native.js diff --git a/config/webpack/webpack.common.js b/config/webpack/webpack.common.js index 6f52e4ea0a13..46c85ef524de 100644 --- a/config/webpack/webpack.common.js +++ b/config/webpack/webpack.common.js @@ -127,6 +127,7 @@ const webpackConfig = { alias: { 'react-native-config': 'react-web-config', 'react-native$': 'react-native-web', + '@react-native-community/clipboard': 'react-native-web', }, // React Native libraries may have web-specific module implementations that appear with the extension `.web.js` diff --git a/package-lock.json b/package-lock.json index 958e73b2af7d..90ab8198af1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3757,6 +3757,11 @@ "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-4.10.1.tgz", "integrity": "sha512-ael2f1onoPF3vF7YqHGWy7NnafzGu+yp88BbFbP0ydoCP2xGSUzmZVw0zakPTC040Id+JQ9WeFczujMkDy6jYQ==" }, + "@react-native-community/clipboard": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@react-native-community/clipboard/-/clipboard-1.5.1.tgz", + "integrity": "sha512-AHAmrkLEH5UtPaDiRqoULERHh3oNv7Dgs0bTC0hO5Z2GdNokAMPT5w8ci8aMcRemcwbtdHjxChgtjbeA38GBdA==" + }, "@react-native-community/eslint-config": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@react-native-community/eslint-config/-/eslint-config-2.0.0.tgz", diff --git a/package.json b/package.json index c93867846ed1..01849d3419ed 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@babel/preset-flow": "^7.12.13", "@react-native-community/async-storage": "^1.11.0", "@react-native-community/cli": "4.13.1", + "@react-native-community/clipboard": "^1.5.1", "@react-native-community/masked-view": "^0.1.10", "@react-native-community/netinfo": "^5.9.10", "@react-native-community/progress-bar-android": "^1.0.4", diff --git a/src/CONST.js b/src/CONST.js index 2888611097d3..087cf65b440c 100644 --- a/src/CONST.js +++ b/src/CONST.js @@ -9,6 +9,7 @@ const CONST = { DEFAULT: 'default', HOVERED: 'hovered', PRESSED: 'pressed', + COMPLETE: 'complete', }, CLOUDFRONT_URL, PDF_VIEWER_URL: '/pdf/web/viewer.html', diff --git a/src/libs/Clipboard/index.js b/src/libs/Clipboard/index.js new file mode 100644 index 000000000000..a97a857832fe --- /dev/null +++ b/src/libs/Clipboard/index.js @@ -0,0 +1,52 @@ +// on Web/desktop this import will be replaced with `react-native-web` so we have to import the named export; +import {Clipboard as base} from '@react-native-community/clipboard'; + + +class Clipboard extends base { + /** + * Enable copying the images on Web/Desktop + * + * @static + * @param {String} html + * @returns {Boolean} + * @memberof Clipboard + */ + static setImage(html) { + let success = false; + const body = document.body; + + if (body) { + // add the text to a hidden node + const node = document.createElement('div'); + node.contentEditable = true; + node.innerHTML = html; + node.style.opacity = '0'; + node.style.position = 'absolute'; + node.style.whiteSpace = 'pre-wrap'; + node.style.userSelect = 'auto'; + body.appendChild(node); + + // select the text + const selection = window.getSelection(); + selection.removeAllRanges(); + const range = document.createRange(); + range.selectNodeContents(node); + selection.addRange(range); + + // attempt to copy + try { + document.execCommand('copy'); + success = true; + // eslint-disable-next-line no-empty + } catch (e) {} + + // remove selection and node + selection.removeAllRanges(); + body.removeChild(node); + } + + return success; + } +} + +export default Clipboard; diff --git a/src/libs/Clipboard/index.native.js b/src/libs/Clipboard/index.native.js new file mode 100644 index 000000000000..34ee00452fc4 --- /dev/null +++ b/src/libs/Clipboard/index.native.js @@ -0,0 +1,16 @@ +import base from '@react-native-community/clipboard'; + +class Clipboard extends base { + /** + * Noop on native platforms + * + * @static + * @returns {Boolean} + * @memberof Clipboard + */ + static setImage() { + return null; + } +} + +export default Clipboard; diff --git a/src/pages/home/report/ReportActionContextMenu.js b/src/pages/home/report/ReportActionContextMenu.js index 80156f9443e3..a8a24e5e915a 100644 --- a/src/pages/home/report/ReportActionContextMenu.js +++ b/src/pages/home/report/ReportActionContextMenu.js @@ -1,11 +1,16 @@ +import _ from 'underscore'; import React from 'react'; import {View} from 'react-native'; +import Str from 'expensify-common/lib/str'; import PropTypes from 'prop-types'; +import lodashGet from 'lodash/get'; import { - Clipboard, LinkCopy, Mail, Pencil, Trashcan, + Clipboard as ClipboardIcon, LinkCopy, Mail, Pencil, Trashcan, Checkmark, } from '../../../components/Icon/Expensicons'; import getReportActionContextMenuStyles from '../../../styles/getReportActionContextMenuStyles'; import ReportActionContextMenuItem from './ReportActionContextMenuItem'; +import ReportActionPropTypes from './ReportActionPropTypes'; +import Clipboard from '../../../libs/Clipboard'; /** * A list of all the context actions in this menu. @@ -14,7 +19,20 @@ const CONTEXT_ACTIONS = [ // Copy to clipboard { text: 'Copy to Clipboard', - icon: Clipboard, + icon: ClipboardIcon, + successText: 'Copied!', + successIcon: Checkmark, + onPress: (action) => { + const lastMessage = _.last(lodashGet(action, 'message', null)); + const html = lodashGet(lastMessage, 'html', ''); + const isImage = lastMessage && /]+)\/>/gi.test(html); + if (isImage) { + Clipboard.setImage(html); + } else { + Clipboard.setString(action.message[0].text); + } + return true; + }, }, // Copy chat link @@ -47,9 +65,8 @@ const propTypes = { // eslint-disable-next-line react/no-unused-prop-types reportID: PropTypes.number.isRequired, - // The ID of the report action this context menu is attached to. - // eslint-disable-next-line react/no-unused-prop-types - reportActionID: PropTypes.number.isRequired, + // The report action this context menu is attached to. + reportAction: PropTypes.shape(ReportActionPropTypes).isRequired, // If true, this component will be a small, row-oriented menu that displays icons but not text. // If false, this component will be a larger, column-oriented menu that displays icons alongside text in each row. @@ -72,7 +89,10 @@ const ReportActionContextMenu = (props) => { Str.result(contextAction.onPress, props.reportAction)} key={contextAction.text} /> ))} diff --git a/src/pages/home/report/ReportActionContextMenuItem.js b/src/pages/home/report/ReportActionContextMenuItem.js index 6b60ce74d9f5..1702973711d1 100644 --- a/src/pages/home/report/ReportActionContextMenuItem.js +++ b/src/pages/home/report/ReportActionContextMenuItem.js @@ -1,4 +1,4 @@ -import React, {memo} from 'react'; +import React, {Component} from 'react'; import PropTypes from 'prop-types'; import {Pressable, Text} from 'react-native'; import Tooltip from '../../../components/Tooltip'; @@ -12,9 +12,15 @@ import styles from '../../../styles/styles'; * * @param {Boolean} [isHovered] * @param {Boolean} [isPressed] + * @param {Boolean} [isComplete] * @returns {String} */ -function getButtonState(isHovered = false, isPressed = false) { +function getButtonState(isHovered = false, isPressed = false, isComplete = false) { + // We force this one over other states + if (isComplete) { + return CONST.BUTTON_STATES.COMPLETE; + } + if (isPressed) { return CONST.BUTTON_STATES.PRESSED; } @@ -29,52 +35,107 @@ function getButtonState(isHovered = false, isPressed = false) { const propTypes = { icon: PropTypes.elementType.isRequired, text: PropTypes.string.isRequired, + successIcon: PropTypes.elementType, + successText: PropTypes.string, isMini: PropTypes.bool, + onPress: PropTypes.func.isRequired, }; const defaultProps = { isMini: false, + successIcon: null, + successText: '', }; -const ReportActionContextMenuItem = (props) => { - const {getButtonStyle, getIconFillColor} = getReportActionContextMenuItemStyles(props.isMini); - return ( - props.isMini - ? ( - - getButtonStyle(getButtonState(hovered, pressed))}> +class ReportActionContextMenuItem extends Component { + constructor(props) { + super(props); + this.state = { + hasRun: false, + }; + this.onPress = this.onPress.bind(this); + } + + /** + * Called on button press and mark the run + * + * @memberof ReportActionContextMenuItem + */ + onPress() { + const pressResult = this.props.onPress(); + if (!(pressResult instanceof Promise)) { + if (pressResult) { + this.setState({ + hasRun: true, + }); + } + return; + } + pressResult.then((result) => { + if (result) { + this.setState({ + hasRun: true, + }); + } + }); + } + + render() { + const {getButtonStyle, getIconFillColor} = getReportActionContextMenuItemStyles(this.props.isMini); + const icon = this.state.hasRun ? this.props.successIcon || this.props.icon : this.props.icon; + const text = this.state.hasRun ? this.props.successText || this.props.text : this.props.text; + return ( + this.props.isMini + ? ( + + getButtonStyle( + getButtonState(hovered, pressed, this.state.hasRun), + ) + } + > + {({hovered, pressed}) => ( + + )} + + + ) : ( + getButtonStyle( + getButtonState(hovered, pressed, this.state.hasRun), + ) + } + > {({hovered, pressed}) => ( - + <> + + + {text} + + )} - - ) : ( - getButtonStyle(getButtonState(hovered, pressed))}> - {({hovered, pressed}) => ( - <> - - - {props.text} - - - )} - - ) - ); -}; + ) + ); + } +} ReportActionContextMenuItem.propTypes = propTypes; ReportActionContextMenuItem.defaultProps = defaultProps; ReportActionContextMenuItem.displayName = 'ReportActionContextMenuItem'; -export default memo(ReportActionContextMenuItem); +export default ReportActionContextMenuItem; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index a0d920a60185..ad67362eabfc 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -117,7 +117,7 @@ class ReportActionItem extends Component { )} > diff --git a/src/styles/getReportActionContextMenuItemStyles.js b/src/styles/getReportActionContextMenuItemStyles.js index c53ab29df6e6..949eb44c8c71 100644 --- a/src/styles/getReportActionContextMenuItemStyles.js +++ b/src/styles/getReportActionContextMenuItemStyles.js @@ -33,6 +33,8 @@ function getIconFillColor(buttonState = CONST.BUTTON_STATES.DEFAULT) { return themeColors.text; case CONST.BUTTON_STATES.PRESSED: return themeColors.heading; + case CONST.BUTTON_STATES.COMPLETE: + return themeColors.iconSuccessFill; case CONST.BUTTON_STATES.DEFAULT: default: return themeColors.icon; From e31c1e73a1223b3ee09fc9310134aff105abb6ff Mon Sep 17 00:00:00 2001 From: Rajat Parashar Date: Sat, 27 Mar 2021 02:14:06 +0530 Subject: [PATCH 2/5] refactor code --- src/libs/Clipboard/index.js | 51 +------------------ src/libs/Clipboard/index.native.js | 15 +----- .../home/report/ReportActionContextMenu.js | 14 +++-- .../report/ReportActionContextMenuItem.js | 33 ++++-------- src/pages/home/report/ReportActionItem.js | 2 +- 5 files changed, 24 insertions(+), 91 deletions(-) diff --git a/src/libs/Clipboard/index.js b/src/libs/Clipboard/index.js index a97a857832fe..8e93fe929adf 100644 --- a/src/libs/Clipboard/index.js +++ b/src/libs/Clipboard/index.js @@ -1,52 +1,5 @@ // on Web/desktop this import will be replaced with `react-native-web` so we have to import the named export; -import {Clipboard as base} from '@react-native-community/clipboard'; - - -class Clipboard extends base { - /** - * Enable copying the images on Web/Desktop - * - * @static - * @param {String} html - * @returns {Boolean} - * @memberof Clipboard - */ - static setImage(html) { - let success = false; - const body = document.body; - - if (body) { - // add the text to a hidden node - const node = document.createElement('div'); - node.contentEditable = true; - node.innerHTML = html; - node.style.opacity = '0'; - node.style.position = 'absolute'; - node.style.whiteSpace = 'pre-wrap'; - node.style.userSelect = 'auto'; - body.appendChild(node); - - // select the text - const selection = window.getSelection(); - selection.removeAllRanges(); - const range = document.createRange(); - range.selectNodeContents(node); - selection.addRange(range); - - // attempt to copy - try { - document.execCommand('copy'); - success = true; - // eslint-disable-next-line no-empty - } catch (e) {} - - // remove selection and node - selection.removeAllRanges(); - body.removeChild(node); - } - - return success; - } -} +// according to the `react-native-web`; +import {Clipboard} from '@react-native-community/clipboard'; export default Clipboard; diff --git a/src/libs/Clipboard/index.native.js b/src/libs/Clipboard/index.native.js index 34ee00452fc4..db249165a421 100644 --- a/src/libs/Clipboard/index.native.js +++ b/src/libs/Clipboard/index.native.js @@ -1,16 +1,3 @@ -import base from '@react-native-community/clipboard'; - -class Clipboard extends base { - /** - * Noop on native platforms - * - * @static - * @returns {Boolean} - * @memberof Clipboard - */ - static setImage() { - return null; - } -} +import Clipboard from '@react-native-community/clipboard'; export default Clipboard; diff --git a/src/pages/home/report/ReportActionContextMenu.js b/src/pages/home/report/ReportActionContextMenu.js index a8a24e5e915a..976f0c3f1599 100644 --- a/src/pages/home/report/ReportActionContextMenu.js +++ b/src/pages/home/report/ReportActionContextMenu.js @@ -1,7 +1,6 @@ import _ from 'underscore'; import React from 'react'; import {View} from 'react-native'; -import Str from 'expensify-common/lib/str'; import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; import { @@ -22,13 +21,14 @@ const CONTEXT_ACTIONS = [ icon: ClipboardIcon, successText: 'Copied!', successIcon: Checkmark, + + // If return value is true, we switch the `text` and `icon` on + // `ReportActionContextMenuItem` with `successText` and `successIcon` onPress: (action) => { const lastMessage = _.last(lodashGet(action, 'message', null)); const html = lodashGet(lastMessage, 'html', ''); const isImage = lastMessage && /]+)\/>/gi.test(html); - if (isImage) { - Clipboard.setImage(html); - } else { + if (!isImage) { Clipboard.setString(action.message[0].text); } return true; @@ -39,24 +39,28 @@ const CONTEXT_ACTIONS = [ { text: 'Copy Link', icon: LinkCopy, + onPress: () => { }, }, // Mark as Unread { text: 'Mark as Unread', icon: Mail, + onPress: () => { }, }, // Edit Comment { text: 'Edit Comment', icon: Pencil, + onPress: () => { }, }, // Delete Comment { text: 'Delete Comment', icon: Trashcan, + onPress: () => { }, }, ]; @@ -92,7 +96,7 @@ const ReportActionContextMenu = (props) => { successIcon={contextAction.successIcon} successText={contextAction.successText} isMini={props.isMini} - onPress={() => Str.result(contextAction.onPress, props.reportAction)} + onPress={() => contextAction.onPress(props.reportAction)} key={contextAction.text} /> ))} diff --git a/src/pages/home/report/ReportActionContextMenuItem.js b/src/pages/home/report/ReportActionContextMenuItem.js index 1702973711d1..6847cea179dc 100644 --- a/src/pages/home/report/ReportActionContextMenuItem.js +++ b/src/pages/home/report/ReportActionContextMenuItem.js @@ -16,7 +16,6 @@ import styles from '../../../styles/styles'; * @returns {String} */ function getButtonState(isHovered = false, isPressed = false, isComplete = false) { - // We force this one over other states if (isComplete) { return CONST.BUTTON_STATES.COMPLETE; } @@ -51,7 +50,7 @@ class ReportActionContextMenuItem extends Component { constructor(props) { super(props); this.state = { - hasRun: false, + success: false, }; this.onPress = this.onPress.bind(this); } @@ -63,27 +62,17 @@ class ReportActionContextMenuItem extends Component { */ onPress() { const pressResult = this.props.onPress(); - if (!(pressResult instanceof Promise)) { - if (pressResult) { - this.setState({ - hasRun: true, - }); - } - return; + if (pressResult) { + this.setState({ + success: true, + }); } - pressResult.then((result) => { - if (result) { - this.setState({ - hasRun: true, - }); - } - }); } render() { const {getButtonStyle, getIconFillColor} = getReportActionContextMenuItemStyles(this.props.isMini); - const icon = this.state.hasRun ? this.props.successIcon || this.props.icon : this.props.icon; - const text = this.state.hasRun ? this.props.successText || this.props.text : this.props.text; + const icon = this.state.success ? this.props.successIcon || this.props.icon : this.props.icon; + const text = this.state.success ? this.props.successText || this.props.text : this.props.text; return ( this.props.isMini ? ( @@ -92,14 +81,14 @@ class ReportActionContextMenuItem extends Component { onPress={this.onPress} style={ ({hovered, pressed}) => getButtonStyle( - getButtonState(hovered, pressed, this.state.hasRun), + getButtonState(hovered, pressed, this.state.success), ) } > {({hovered, pressed}) => ( )} @@ -109,7 +98,7 @@ class ReportActionContextMenuItem extends Component { onPress={this.onPress} style={ ({hovered, pressed}) => getButtonStyle( - getButtonState(hovered, pressed, this.state.hasRun), + getButtonState(hovered, pressed, this.state.success), ) } > @@ -117,7 +106,7 @@ class ReportActionContextMenuItem extends Component { <> )} > From 4a4481d12a60dc5dff6ab8b16cf410d8d916444a Mon Sep 17 00:00:00 2001 From: Rajat Parashar Date: Tue, 30 Mar 2021 14:47:22 +0530 Subject: [PATCH 3/5] attachment check updated --- src/pages/home/report/ReportActionContextMenu.js | 12 ++++++++---- src/pages/home/report/ReportActionContextMenuItem.js | 3 +++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/pages/home/report/ReportActionContextMenu.js b/src/pages/home/report/ReportActionContextMenu.js index 976f0c3f1599..f19e0d5f55b4 100644 --- a/src/pages/home/report/ReportActionContextMenu.js +++ b/src/pages/home/report/ReportActionContextMenu.js @@ -23,13 +23,17 @@ const CONTEXT_ACTIONS = [ successIcon: Checkmark, // If return value is true, we switch the `text` and `icon` on - // `ReportActionContextMenuItem` with `successText` and `successIcon` + // `ReportActionContextMenuItem` with `successText` and `successIcon` which will fallback to + // the `text` and `icon` onPress: (action) => { const lastMessage = _.last(lodashGet(action, 'message', null)); const html = lodashGet(lastMessage, 'html', ''); - const isImage = lastMessage && /]+)\/>/gi.test(html); - if (!isImage) { - Clipboard.setString(action.message[0].text); + const text = lodashGet(lastMessage, 'text', ''); + const isAttachment = text === '[Attachment]'; + if (!isAttachment) { + Clipboard.setString(text); + } else { + Clipboard.setString(html); } return true; }, diff --git a/src/pages/home/report/ReportActionContextMenuItem.js b/src/pages/home/report/ReportActionContextMenuItem.js index 6847cea179dc..64622f0694eb 100644 --- a/src/pages/home/report/ReportActionContextMenuItem.js +++ b/src/pages/home/report/ReportActionContextMenuItem.js @@ -61,6 +61,9 @@ class ReportActionContextMenuItem extends Component { * @memberof ReportActionContextMenuItem */ onPress() { + if (this.state.success) { + return; + } const pressResult = this.props.onPress(); if (pressResult) { this.setState({ From 002e5c11bbfe7c5dbf6fd9e6fc377cd098eaa388 Mon Sep 17 00:00:00 2001 From: Rajat Parashar Date: Wed, 31 Mar 2021 15:03:21 +0530 Subject: [PATCH 4/5] refactor code --- config/webpack/webpack.common.js | 1 - src/libs/Clipboard/index.js | 5 ++--- src/libs/reportUtils.js | 17 ++++++++++++++++- .../home/report/ReportActionContextMenu.js | 13 ++++++------- .../home/report/ReportActionContextMenuItem.js | 17 +++++++++-------- 5 files changed, 33 insertions(+), 20 deletions(-) diff --git a/config/webpack/webpack.common.js b/config/webpack/webpack.common.js index 46c85ef524de..6f52e4ea0a13 100644 --- a/config/webpack/webpack.common.js +++ b/config/webpack/webpack.common.js @@ -127,7 +127,6 @@ const webpackConfig = { alias: { 'react-native-config': 'react-web-config', 'react-native$': 'react-native-web', - '@react-native-community/clipboard': 'react-native-web', }, // React Native libraries may have web-specific module implementations that appear with the extension `.web.js` diff --git a/src/libs/Clipboard/index.js b/src/libs/Clipboard/index.js index 8e93fe929adf..808e75b765ee 100644 --- a/src/libs/Clipboard/index.js +++ b/src/libs/Clipboard/index.js @@ -1,5 +1,4 @@ -// on Web/desktop this import will be replaced with `react-native-web` so we have to import the named export; -// according to the `react-native-web`; -import {Clipboard} from '@react-native-community/clipboard'; +// on Web/desktop this import will be replaced with `react-native-web` +import {Clipboard} from 'react-native-web'; export default Clipboard; diff --git a/src/libs/reportUtils.js b/src/libs/reportUtils.js index 538076d6c183..0e09a063f1af 100644 --- a/src/libs/reportUtils.js +++ b/src/libs/reportUtils.js @@ -1,5 +1,6 @@ import _ from 'underscore'; import Str from 'expensify-common/lib/str'; +import lodashGet from 'lodash/get'; /** * Returns the concatenated title for the PrimaryLogins of a report @@ -11,7 +12,21 @@ function getReportParticipantsTitle(logins) { return _.map(logins, login => Str.removeSMSDomain(login)).join(', '); } +/** + * Check whether a report action is Attachment is not. + * + * @param {Object} reportAction action from a Report + * @returns {Boolean} + */ +function isReportActionAttachment(reportAction) { + if (_.has(reportAction, 'isAttachment')) { + return reportAction.isAttachment; + } + const lastMessage = _.last(lodashGet(reportAction, 'message', null)); + return lodashGet(lastMessage, 'text', '') === '[Attachment]'; +} + export { - // eslint-disable-next-line import/prefer-default-export getReportParticipantsTitle, + isReportActionAttachment, }; diff --git a/src/pages/home/report/ReportActionContextMenu.js b/src/pages/home/report/ReportActionContextMenu.js index f19e0d5f55b4..18c42d8f81e7 100644 --- a/src/pages/home/report/ReportActionContextMenu.js +++ b/src/pages/home/report/ReportActionContextMenu.js @@ -10,6 +10,7 @@ import getReportActionContextMenuStyles from '../../../styles/getReportActionCon import ReportActionContextMenuItem from './ReportActionContextMenuItem'; import ReportActionPropTypes from './ReportActionPropTypes'; import Clipboard from '../../../libs/Clipboard'; +import {isReportActionAttachment} from '../../../libs/reportUtils'; /** * A list of all the context actions in this menu. @@ -29,13 +30,11 @@ const CONTEXT_ACTIONS = [ const lastMessage = _.last(lodashGet(action, 'message', null)); const html = lodashGet(lastMessage, 'html', ''); const text = lodashGet(lastMessage, 'text', ''); - const isAttachment = text === '[Attachment]'; - if (!isAttachment) { + if (!isReportActionAttachment(action)) { Clipboard.setString(text); } else { Clipboard.setString(html); } - return true; }, }, @@ -43,28 +42,28 @@ const CONTEXT_ACTIONS = [ { text: 'Copy Link', icon: LinkCopy, - onPress: () => { }, + onPress: () => {}, }, // Mark as Unread { text: 'Mark as Unread', icon: Mail, - onPress: () => { }, + onPress: () => {}, }, // Edit Comment { text: 'Edit Comment', icon: Pencil, - onPress: () => { }, + onPress: () => {}, }, // Delete Comment { text: 'Delete Comment', icon: Trashcan, - onPress: () => { }, + onPress: () => {}, }, ]; diff --git a/src/pages/home/report/ReportActionContextMenuItem.js b/src/pages/home/report/ReportActionContextMenuItem.js index 64622f0694eb..1ce45419e983 100644 --- a/src/pages/home/report/ReportActionContextMenuItem.js +++ b/src/pages/home/report/ReportActionContextMenuItem.js @@ -52,20 +52,21 @@ class ReportActionContextMenuItem extends Component { this.state = { success: false, }; - this.onPress = this.onPress.bind(this); + this.triggerPressAndUpdateSuccess = this.triggerPressAndUpdateSuccess.bind(this); } /** * Called on button press and mark the run - * - * @memberof ReportActionContextMenuItem */ - onPress() { + triggerPressAndUpdateSuccess() { if (this.state.success) { return; } - const pressResult = this.props.onPress(); - if (pressResult) { + this.props.onPress(); + + // We only set the success state when we have icon or text to represent the success state + // We may want to replace this check by checking the Result from OnPress Callback in future. + if (this.props.successIcon || this.props.successText) { this.setState({ success: true, }); @@ -81,7 +82,7 @@ class ReportActionContextMenuItem extends Component { ? ( getButtonStyle( getButtonState(hovered, pressed, this.state.success), @@ -98,7 +99,7 @@ class ReportActionContextMenuItem extends Component { ) : ( getButtonStyle( getButtonState(hovered, pressed, this.state.success), From 4bf4a2ae8524e761fa3ba88a7b5691ed56dcc9bb Mon Sep 17 00:00:00 2001 From: Rajat Parashar Date: Fri, 2 Apr 2021 01:50:55 +0530 Subject: [PATCH 5/5] removed [Attachment] manual checks --- src/libs/actions/Report.js | 3 ++- src/libs/reportUtils.js | 13 ++++--------- src/pages/home/report/ReportActionContextMenu.js | 13 ++++++++----- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 2bf879dacd4e..87323ccc073e 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -17,6 +17,7 @@ import Timing from './Timing'; import * as API from '../API'; import CONST from '../../CONST'; import Log from '../Log'; +import {isReportMessageAttachment} from '../reportUtils'; let currentUserEmail; let currentUserAccountID; @@ -376,7 +377,7 @@ function updateReportWithNewAction(reportID, reportAction) { // Add the action into Onyx reportActionsToMerge[reportAction.sequenceNumber] = { ...reportAction, - isAttachment: messageText === '[Attachment]', + isAttachment: isReportMessageAttachment(messageText), loading: false, }; diff --git a/src/libs/reportUtils.js b/src/libs/reportUtils.js index 0e09a063f1af..a8e5575059b4 100644 --- a/src/libs/reportUtils.js +++ b/src/libs/reportUtils.js @@ -1,6 +1,5 @@ import _ from 'underscore'; import Str from 'expensify-common/lib/str'; -import lodashGet from 'lodash/get'; /** * Returns the concatenated title for the PrimaryLogins of a report @@ -15,18 +14,14 @@ function getReportParticipantsTitle(logins) { /** * Check whether a report action is Attachment is not. * - * @param {Object} reportAction action from a Report + * @param {Object} reportMessageText report action's message as text * @returns {Boolean} */ -function isReportActionAttachment(reportAction) { - if (_.has(reportAction, 'isAttachment')) { - return reportAction.isAttachment; - } - const lastMessage = _.last(lodashGet(reportAction, 'message', null)); - return lodashGet(lastMessage, 'text', '') === '[Attachment]'; +function isReportMessageAttachment(reportMessageText) { + return reportMessageText === '[Attachment]'; } export { getReportParticipantsTitle, - isReportActionAttachment, + isReportMessageAttachment, }; diff --git a/src/pages/home/report/ReportActionContextMenu.js b/src/pages/home/report/ReportActionContextMenu.js index 18c42d8f81e7..e667ba5c65f9 100644 --- a/src/pages/home/report/ReportActionContextMenu.js +++ b/src/pages/home/report/ReportActionContextMenu.js @@ -10,7 +10,7 @@ import getReportActionContextMenuStyles from '../../../styles/getReportActionCon import ReportActionContextMenuItem from './ReportActionContextMenuItem'; import ReportActionPropTypes from './ReportActionPropTypes'; import Clipboard from '../../../libs/Clipboard'; -import {isReportActionAttachment} from '../../../libs/reportUtils'; +import {isReportMessageAttachment} from '../../../libs/reportUtils'; /** * A list of all the context actions in this menu. @@ -27,10 +27,13 @@ const CONTEXT_ACTIONS = [ // `ReportActionContextMenuItem` with `successText` and `successIcon` which will fallback to // the `text` and `icon` onPress: (action) => { - const lastMessage = _.last(lodashGet(action, 'message', null)); - const html = lodashGet(lastMessage, 'html', ''); - const text = lodashGet(lastMessage, 'text', ''); - if (!isReportActionAttachment(action)) { + const message = _.last(lodashGet(action, 'message', null)); + const html = lodashGet(message, 'html', ''); + const text = lodashGet(message, 'text', ''); + const isAttachment = _.has(action, 'isAttachment') + ? action.isAttachment + : isReportMessageAttachment(text); + if (!isAttachment) { Clipboard.setString(text); } else { Clipboard.setString(html);