From cfc84203babb2af859224c446a1da1e46357163a Mon Sep 17 00:00:00 2001 From: Rajat Parashar Date: Sat, 3 Jul 2021 01:02:50 +0530 Subject: [PATCH 1/9] added tapable links to user's login info --- src/pages/DetailsPage.js | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js index 937e8f0dbc93..7472b68ea931 100755 --- a/src/pages/DetailsPage.js +++ b/src/pages/DetailsPage.js @@ -1,6 +1,6 @@ import React from 'react'; import { - View, + View, Pressable, Linking, } from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; @@ -60,6 +60,7 @@ const getPhoneNumber = (details) => { const DetailsPage = ({ personalDetails, route, translate, toLocalPhone, }) => { + console.debug(route); const details = personalDetails[route.params.login]; const isSMSLogin = Str.isSMSLogin(details.login); @@ -91,11 +92,19 @@ const DetailsPage = ({ imageStyles={[styles.avatarLarge]} source={details.avatar} /> - - {details.displayName && isSMSLogin - ? toLocalPhone(details.displayName) - : (details.displayName || null)} - + (details.displayName && isSMSLogin + ? Linking.openURL(`tel:${getPhoneNumber(details)}`) + : false + )} + > + + + {details.displayName && isSMSLogin + ? toLocalPhone(details.displayName) + : (details.displayName || null)} + + {details.login ? ( @@ -103,11 +112,20 @@ const DetailsPage = ({ ? 'common.phoneNumber' : 'common.email')} - - {isSMSLogin - ? toLocalPhone(getPhoneNumber(details)) - : details.login} - + Linking.openURL( + isSMSLogin + ? `tel:${getPhoneNumber(details)}` + : `mailto:${details.login}`, + )} + > + + {isSMSLogin + ? toLocalPhone(getPhoneNumber(details)) + : details.login} + + + ) : null} {details.pronouns ? ( From 50080094c40c28851935509e24a8578d854ce364 Mon Sep 17 00:00:00 2001 From: Rajat Parashar Date: Sun, 4 Jul 2021 06:50:55 +0530 Subject: [PATCH 2/9] remove Console logs --- src/pages/DetailsPage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js index 7472b68ea931..d8ce861ff8d7 100755 --- a/src/pages/DetailsPage.js +++ b/src/pages/DetailsPage.js @@ -60,7 +60,6 @@ const getPhoneNumber = (details) => { const DetailsPage = ({ personalDetails, route, translate, toLocalPhone, }) => { - console.debug(route); const details = personalDetails[route.params.login]; const isSMSLogin = Str.isSMSLogin(details.login); From fae93e542d020b3d130eda4d9e6e760c21f5bfc6 Mon Sep 17 00:00:00 2001 From: Rajat Parashar Date: Wed, 7 Jul 2021 12:57:44 +0530 Subject: [PATCH 3/9] moved tapping behavipour to a component --- src/components/TappableCopy.js | 79 +++++++++++++++++++ src/pages/DetailsPage.js | 32 +++----- .../report/ReportActionContextMenuItem.js | 13 +++ 3 files changed, 105 insertions(+), 19 deletions(-) create mode 100644 src/components/TappableCopy.js diff --git a/src/components/TappableCopy.js b/src/components/TappableCopy.js new file mode 100644 index 000000000000..3f4727b29a64 --- /dev/null +++ b/src/components/TappableCopy.js @@ -0,0 +1,79 @@ +import React from 'react'; +import {View, Pressable, Linking} from 'react-native'; +import PropTypes from 'prop-types'; +import styles from '../styles/styles'; +import compose from '../libs/compose'; +import {Checkmark, Clipboard as ClipboardIcon} from './Icon/Expensicons'; +import Clipboard from '../libs/Clipboard'; +import ReportActionContextMenuItem from '../pages/home/report/ReportActionContextMenuItem'; +import withLocalize, {withLocalizePropTypes} from './withLocalize'; +import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; + +const propTypes = { + /** Children to wrap in TappableCopy. */ + children: PropTypes.node.isRequired, + + /** Styles to be assigned to Container */ + style: PropTypes.arrayOf(PropTypes.object), + + /** Decides Tap behaviour. */ + type: PropTypes.oneOf(['phone', 'email']), + + /** Value to be copied or passed via tap. */ + value: PropTypes.string.isRequired, + + ...windowDimensionsPropTypes, + ...withLocalizePropTypes, +}; + +const defaultProps = { + style: [], + type: undefined, +}; + +const TappableCopy = props => ( + + {props.type && props.isSmallScreenWidth + ? ( + Linking.openURL( + props.type === 'phone' + ? `tel:${props.value}` + : `mailto:${props.value}`, + )} + > + {props.children} + + ) + : props.children} + {props.type && !props.isSmallScreenWidth + && ( + + Clipboard.setString(props.value)} + /> + + )} + +); + + +TappableCopy.propTypes = propTypes; +TappableCopy.defaultProps = defaultProps; +TappableCopy.displayName = 'TappableCopy'; + +export default compose( + withWindowDimensions, + withLocalize, +)(TappableCopy); diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js index d8ce861ff8d7..1a3dadc1549b 100755 --- a/src/pages/DetailsPage.js +++ b/src/pages/DetailsPage.js @@ -1,7 +1,5 @@ import React from 'react'; -import { - View, Pressable, Linking, -} from 'react-native'; +import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; @@ -16,6 +14,7 @@ import ScreenWrapper from '../components/ScreenWrapper'; import personalDetailsPropType from './personalDetailsPropType'; import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; import compose from '../libs/compose'; +import TappableCopy from '../components/TappableCopy'; const matchType = PropTypes.shape({ params: PropTypes.shape({ @@ -69,6 +68,7 @@ const DetailsPage = ({ const timezone = moment().tz(details.timezone.selected); const GMTTime = `${timezone.toString().split(/[+-]/)[0].slice(-3)} ${timezone.zoneAbbr()}`; const currentTime = Number.isNaN(Number(timezone.zoneAbbr())) ? timezone.zoneAbbr() : GMTTime; + return ( - (details.displayName && isSMSLogin - ? Linking.openURL(`tel:${getPhoneNumber(details)}`) - : false - )} + - - + {details.displayName && isSMSLogin ? toLocalPhone(details.displayName) : (details.displayName || null)} - + {details.login ? ( @@ -111,20 +109,16 @@ const DetailsPage = ({ ? 'common.phoneNumber' : 'common.email')} - Linking.openURL( - isSMSLogin - ? `tel:${getPhoneNumber(details)}` - : `mailto:${details.login}`, - )} + {isSMSLogin ? toLocalPhone(getPhoneNumber(details)) : details.login} - - + ) : null} {details.pronouns ? ( diff --git a/src/pages/home/report/ReportActionContextMenuItem.js b/src/pages/home/report/ReportActionContextMenuItem.js index ae2905d7b274..3cc4c19b3675 100644 --- a/src/pages/home/report/ReportActionContextMenuItem.js +++ b/src/pages/home/report/ReportActionContextMenuItem.js @@ -25,12 +25,16 @@ const propTypes = { /** Callback to fire when the item is pressed */ onPress: PropTypes.func.isRequired, + + /** Automatically reset the success status */ + autoReset: PropTypes.bool, }; const defaultProps = { isMini: false, successIcon: null, successText: '', + autoReset: false, }; class ReportActionContextMenuItem extends Component { @@ -42,6 +46,12 @@ class ReportActionContextMenuItem extends Component { this.triggerPressAndUpdateSuccess = this.triggerPressAndUpdateSuccess.bind(this); } + componentWillUnmount() { + if (this.successResetTimer) { + clearTimeout(this.successResetTimer); + } + } + /** * Called on button press and mark the run */ @@ -57,6 +67,9 @@ class ReportActionContextMenuItem extends Component { this.setState({ success: true, }); + if (this.props.autoReset) { + this.successResetTimer = setTimeout(() => this.setState({success: false}), 1800); + } } } From 26123f9de08973aafa05652c4175c2000955603d Mon Sep 17 00:00:00 2001 From: Rajat Parashar Date: Thu, 8 Jul 2021 05:07:15 +0530 Subject: [PATCH 4/9] used constants --- src/components/TappableCopy.js | 6 +++--- src/pages/DetailsPage.js | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/TappableCopy.js b/src/components/TappableCopy.js index 3f4727b29a64..09fac6389d58 100644 --- a/src/components/TappableCopy.js +++ b/src/components/TappableCopy.js @@ -8,6 +8,7 @@ import Clipboard from '../libs/Clipboard'; import ReportActionContextMenuItem from '../pages/home/report/ReportActionContextMenuItem'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; +import CONST from '../CONST'; const propTypes = { /** Children to wrap in TappableCopy. */ @@ -17,7 +18,7 @@ const propTypes = { style: PropTypes.arrayOf(PropTypes.object), /** Decides Tap behaviour. */ - type: PropTypes.oneOf(['phone', 'email']), + type: PropTypes.oneOf([CONST.LOGIN_TYPE.PHONE, CONST.LOGIN_TYPE.EMAIL]), /** Value to be copied or passed via tap. */ value: PropTypes.string.isRequired, @@ -37,7 +38,7 @@ const TappableCopy = props => ( ? ( Linking.openURL( - props.type === 'phone' + props.type === CONST.LOGIN_TYPE.PHONE ? `tel:${props.value}` : `mailto:${props.value}`, )} @@ -68,7 +69,6 @@ const TappableCopy = props => ( ); - TappableCopy.propTypes = propTypes; TappableCopy.defaultProps = defaultProps; TappableCopy.displayName = 'TappableCopy'; diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js index 1a3dadc1549b..b5e96523def6 100755 --- a/src/pages/DetailsPage.js +++ b/src/pages/DetailsPage.js @@ -15,6 +15,7 @@ import personalDetailsPropType from './personalDetailsPropType'; import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; import compose from '../libs/compose'; import TappableCopy from '../components/TappableCopy'; +import CONST from '../CONST'; const matchType = PropTypes.shape({ params: PropTypes.shape({ @@ -93,7 +94,7 @@ const DetailsPage = ({ /> @@ -110,7 +111,7 @@ const DetailsPage = ({ : 'common.email')} From 4e1aae3da17f259206445a5f85b1d4e534df4930 Mon Sep 17 00:00:00 2001 From: Rajat Parashar Date: Fri, 9 Jul 2021 04:48:43 +0530 Subject: [PATCH 5/9] rename and refactor --- .../ContextMenuItem.js} | 20 +++++++++---------- src/components/TappableCopy.js | 4 ++-- .../home/report/ReportActionContextMenu.js | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) rename src/{pages/home/report/ReportActionContextMenuItem.js => components/ContextMenuItem.js} (87%) diff --git a/src/pages/home/report/ReportActionContextMenuItem.js b/src/components/ContextMenuItem.js similarity index 87% rename from src/pages/home/report/ReportActionContextMenuItem.js rename to src/components/ContextMenuItem.js index 3cc4c19b3675..265c830c4bb8 100644 --- a/src/pages/home/report/ReportActionContextMenuItem.js +++ b/src/components/ContextMenuItem.js @@ -1,11 +1,11 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import {Pressable} from 'react-native'; -import MenuItem from '../../../components/MenuItem'; -import Tooltip from '../../../components/Tooltip'; -import Icon from '../../../components/Icon'; -import styles, {getIconFillColor, getButtonBackgroundColorStyle} from '../../../styles/styles'; -import getButtonState from '../../../libs/getButtonState'; +import MenuItem from './MenuItem'; +import Tooltip from './Tooltip'; +import Icon from './Icon'; +import styles, {getIconFillColor, getButtonBackgroundColorStyle} from '../styles/styles'; +import getButtonState from '../libs/getButtonState'; const propTypes = { /** Icon Component */ @@ -37,7 +37,7 @@ const defaultProps = { autoReset: false, }; -class ReportActionContextMenuItem extends Component { +class ContextMenuItem extends Component { constructor(props) { super(props); this.state = { @@ -112,8 +112,8 @@ class ReportActionContextMenuItem extends Component { } } -ReportActionContextMenuItem.propTypes = propTypes; -ReportActionContextMenuItem.defaultProps = defaultProps; -ReportActionContextMenuItem.displayName = 'ReportActionContextMenuItem'; +ContextMenuItem.propTypes = propTypes; +ContextMenuItem.defaultProps = defaultProps; +ContextMenuItem.displayName = 'ContextMenuItem'; -export default ReportActionContextMenuItem; +export default ContextMenuItem; diff --git a/src/components/TappableCopy.js b/src/components/TappableCopy.js index 09fac6389d58..0eac56cc9261 100644 --- a/src/components/TappableCopy.js +++ b/src/components/TappableCopy.js @@ -5,7 +5,7 @@ import styles from '../styles/styles'; import compose from '../libs/compose'; import {Checkmark, Clipboard as ClipboardIcon} from './Icon/Expensicons'; import Clipboard from '../libs/Clipboard'; -import ReportActionContextMenuItem from '../pages/home/report/ReportActionContextMenuItem'; +import ContextMenuItem from './ContextMenuItem'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import CONST from '../CONST'; @@ -55,7 +55,7 @@ const TappableCopy = props => ( styles.justifyContentCenter, {right: -36, top: 0, bottom: 0}]} > - {this.contextActions.map(contextAction => _.result(contextAction, 'shouldShow', false) && ( - Date: Fri, 9 Jul 2021 06:41:32 +0530 Subject: [PATCH 6/9] comment updated --- src/pages/home/report/ReportActionContextMenu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionContextMenu.js b/src/pages/home/report/ReportActionContextMenu.js index 351bb735ffc9..8f609380ac9f 100755 --- a/src/pages/home/report/ReportActionContextMenu.js +++ b/src/pages/home/report/ReportActionContextMenu.js @@ -74,7 +74,7 @@ class ReportActionContextMenu extends React.Component { shouldShow: true, // If return value is true, we switch the `text` and `icon` on - // `ReportActionContextMenuItem` with `successText` and `successIcon` which will fallback to + // `ContextMenuItem` with `successText` and `successIcon` which will fallback to // the `text` and `icon` onPress: () => { const message = _.last(lodashGet(this.props.reportAction, 'message', null)); From 7a34f081a03e1c7526c12013a704f19d017bca89 Mon Sep 17 00:00:00 2001 From: Rajat Parashar Date: Fri, 9 Jul 2021 23:02:54 +0530 Subject: [PATCH 7/9] refactor --- .../{TappableCopy.js => CommunicationsLink.js} | 18 +++++++++--------- src/languages/en.js | 4 +++- src/pages/DetailsPage.js | 10 +++++----- .../home/report/ReportActionContextMenu.js | 4 ++-- src/styles/styles.js | 6 ++++++ 5 files changed, 25 insertions(+), 17 deletions(-) rename src/components/{TappableCopy.js => CommunicationsLink.js} (82%) diff --git a/src/components/TappableCopy.js b/src/components/CommunicationsLink.js similarity index 82% rename from src/components/TappableCopy.js rename to src/components/CommunicationsLink.js index 0eac56cc9261..90f668ec6e70 100644 --- a/src/components/TappableCopy.js +++ b/src/components/CommunicationsLink.js @@ -11,7 +11,7 @@ import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimen import CONST from '../CONST'; const propTypes = { - /** Children to wrap in TappableCopy. */ + /** Children to wrap in CommunicationsLink. */ children: PropTypes.node.isRequired, /** Styles to be assigned to Container */ @@ -32,7 +32,7 @@ const defaultProps = { type: undefined, }; -const TappableCopy = props => ( +const CommunicationsLink = props => ( {props.type && props.isSmallScreenWidth ? ( @@ -53,13 +53,13 @@ const TappableCopy = props => ( styles.pAbsolute, styles.alignItemsCenter, styles.justifyContentCenter, - {right: -36, top: 0, bottom: 0}]} + styles.communicationsLinkIcon]} > Clipboard.setString(props.value)} @@ -69,11 +69,11 @@ const TappableCopy = props => ( ); -TappableCopy.propTypes = propTypes; -TappableCopy.defaultProps = defaultProps; -TappableCopy.displayName = 'TappableCopy'; +CommunicationsLink.propTypes = propTypes; +CommunicationsLink.defaultProps = defaultProps; +CommunicationsLink.displayName = 'CommunicationsLink'; export default compose( withWindowDimensions, withLocalize, -)(TappableCopy); +)(CommunicationsLink); diff --git a/src/languages/en.js b/src/languages/en.js index d50fb421e816..7d2caa6c42d0 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -106,9 +106,11 @@ export default { youAppearToBeOffline: 'You appear to be offline.', fileUploadFailed: 'Upload Failed. File is not supported.', }, - reportActionContextMenu: { + contextMenuItem: { copyToClipboard: 'Copy to Clipboard', copied: 'Copied!', + }, + reportActionContextMenu: { copyLink: 'Copy Link', markAsUnread: 'Mark as Unread', editComment: 'Edit Comment', diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js index b5e96523def6..4fafba90b613 100755 --- a/src/pages/DetailsPage.js +++ b/src/pages/DetailsPage.js @@ -14,7 +14,7 @@ import ScreenWrapper from '../components/ScreenWrapper'; import personalDetailsPropType from './personalDetailsPropType'; import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; import compose from '../libs/compose'; -import TappableCopy from '../components/TappableCopy'; +import CommunicationsLink from '../components/CommunicationsLink'; import CONST from '../CONST'; const matchType = PropTypes.shape({ @@ -92,7 +92,7 @@ const DetailsPage = ({ imageStyles={[styles.avatarLarge]} source={details.avatar} /> - - + {details.login ? ( @@ -110,7 +110,7 @@ const DetailsPage = ({ ? 'common.phoneNumber' : 'common.email')} - @@ -119,7 +119,7 @@ const DetailsPage = ({ ? toLocalPhone(getPhoneNumber(details)) : details.login} - + ) : null} {details.pronouns ? ( diff --git a/src/pages/home/report/ReportActionContextMenu.js b/src/pages/home/report/ReportActionContextMenu.js index 8f609380ac9f..ca8fcc35b04f 100755 --- a/src/pages/home/report/ReportActionContextMenu.js +++ b/src/pages/home/report/ReportActionContextMenu.js @@ -67,9 +67,9 @@ class ReportActionContextMenu extends React.Component { this.contextActions = [ // Copy to clipboard { - text: this.props.translate('reportActionContextMenu.copyToClipboard'), + text: this.props.translate('contextMenuItem.copyToClipboard'), icon: ClipboardIcon, - successText: this.props.translate('reportActionContextMenu.copied'), + successText: this.props.translate('contextMenuItem.copied'), successIcon: Checkmark, shouldShow: true, diff --git a/src/styles/styles.js b/src/styles/styles.js index c9ffccbc8e18..29fdf32a56fc 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -1747,6 +1747,12 @@ const styles = { lineHeight: 16, ...whiteSpace.noWrap, }, + + communicationsLinkIcon: { + right: -36, + top: 0, + bottom: 0, + }, }; const baseCodeTagStyles = { From 4f3f1db397e93363b956837e8c7e89b9b0c7bec0 Mon Sep 17 00:00:00 2001 From: Rajat Parashar Date: Fri, 9 Jul 2021 23:08:19 +0530 Subject: [PATCH 8/9] rename style prop --- src/components/CommunicationsLink.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/CommunicationsLink.js b/src/components/CommunicationsLink.js index 90f668ec6e70..b5372764f591 100644 --- a/src/components/CommunicationsLink.js +++ b/src/components/CommunicationsLink.js @@ -15,7 +15,7 @@ const propTypes = { children: PropTypes.node.isRequired, /** Styles to be assigned to Container */ - style: PropTypes.arrayOf(PropTypes.object), + containerStyles: PropTypes.arrayOf(PropTypes.object), /** Decides Tap behaviour. */ type: PropTypes.oneOf([CONST.LOGIN_TYPE.PHONE, CONST.LOGIN_TYPE.EMAIL]), @@ -28,12 +28,12 @@ const propTypes = { }; const defaultProps = { - style: [], + containerStyles: [], type: undefined, }; const CommunicationsLink = props => ( - + {props.type && props.isSmallScreenWidth ? ( Date: Mon, 12 Jul 2021 20:04:22 +0530 Subject: [PATCH 9/9] simplified code --- src/components/CommunicationsLink.js | 7 +++---- src/pages/DetailsPage.js | 27 ++++++++++++++++----------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/components/CommunicationsLink.js b/src/components/CommunicationsLink.js index b5372764f591..c60ced905fc8 100644 --- a/src/components/CommunicationsLink.js +++ b/src/components/CommunicationsLink.js @@ -18,7 +18,7 @@ const propTypes = { containerStyles: PropTypes.arrayOf(PropTypes.object), /** Decides Tap behaviour. */ - type: PropTypes.oneOf([CONST.LOGIN_TYPE.PHONE, CONST.LOGIN_TYPE.EMAIL]), + type: PropTypes.oneOf([CONST.LOGIN_TYPE.PHONE, CONST.LOGIN_TYPE.EMAIL]).isRequired, /** Value to be copied or passed via tap. */ value: PropTypes.string.isRequired, @@ -29,12 +29,11 @@ const propTypes = { const defaultProps = { containerStyles: [], - type: undefined, }; const CommunicationsLink = props => ( - {props.type && props.isSmallScreenWidth + {props.isSmallScreenWidth ? ( Linking.openURL( @@ -47,7 +46,7 @@ const CommunicationsLink = props => ( ) : props.children} - {props.type && !props.isSmallScreenWidth + {!props.isSmallScreenWidth && ( - - - {details.displayName && isSMSLogin - ? toLocalPhone(details.displayName) - : (details.displayName || null)} - - + {details.displayName && isSMSLogin + ? ( + + + {toLocalPhone(details.displayName)} + + + ) : ( + + {details.displayName || null} + + )} {details.login ? (