From 074bcc9f0c86d7ccbe30d8c5a84deddd0edce478 Mon Sep 17 00:00:00 2001 From: Rory Abraham Date: Fri, 21 May 2021 10:56:26 -0700 Subject: [PATCH 1/2] Download E.cash attachments immediately --- .../AnchorWithAuthToken.js | 50 ++++++++++++++++ .../BaseAnchorForCommentsOnly/index.js | 33 +++++++++++ .../BaseAnchorForCommentsOnly/index.native.js | 30 ++++++++++ .../anchorForCommentsOnlyPropTypes.js | 29 +++++++--- src/components/AnchorForCommentsOnly/index.js | 57 ++++++++++--------- .../AnchorForCommentsOnly/index.native.js | 27 --------- src/components/RenderHTML.js | 4 ++ 7 files changed, 167 insertions(+), 63 deletions(-) create mode 100644 src/components/AnchorForCommentsOnly/AnchorWithAuthToken.js create mode 100644 src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.js create mode 100644 src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.native.js delete mode 100644 src/components/AnchorForCommentsOnly/index.native.js diff --git a/src/components/AnchorForCommentsOnly/AnchorWithAuthToken.js b/src/components/AnchorForCommentsOnly/AnchorWithAuthToken.js new file mode 100644 index 000000000000..14440fc60dee --- /dev/null +++ b/src/components/AnchorForCommentsOnly/AnchorWithAuthToken.js @@ -0,0 +1,50 @@ +import _ from 'underscore'; +import React from 'react'; +import PropTypes from 'prop-types'; +import {withOnyx} from 'react-native-onyx'; +import ONYXKEYS from '../../ONYXKEYS'; +import { + propTypes as anchorForCommentsOnlyPropTypes, + defaultProps as anchorForCommentsOnlyDefaultProps, +} from './anchorForCommentsOnlyPropTypes'; +import BaseAnchorForCommentsOnly from './BaseAnchorForCommentsOnly'; +import addAuthTokenToURL from '../../libs/addAuthTokenToURL'; + +const propTypes = { + /** Session info for the currently logged in user. */ + session: PropTypes.shape({ + /** Currently logged in user authToken */ + authToken: PropTypes.string, + }), + + ...anchorForCommentsOnlyPropTypes, +}; + +const defaultProps = { + session: { + authToken: null, + }, + ...anchorForCommentsOnlyDefaultProps, +}; + +const AnchorWithAuthToken = (props) => { + const urlWithAuthToken = addAuthTokenToURL({ + url: props.href, + authToken: props.session.authToken, + }); + const propsToPass = _.omit(props, 'session'); + propsToPass.href = urlWithAuthToken; + propsToPass.shouldDownloadFile = true; + // eslint-disable-next-line react/jsx-props-no-spreading + return ; +}; + +AnchorWithAuthToken.propTypes = propTypes; +AnchorWithAuthToken.defaultProps = defaultProps; +AnchorWithAuthToken.displayName = 'AnchorWithAuthToken'; + +export default withOnyx({ + session: { + key: ONYXKEYS.SESSION, + }, +})(AnchorWithAuthToken); diff --git a/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.js b/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.js new file mode 100644 index 000000000000..37faebd2ee02 --- /dev/null +++ b/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.js @@ -0,0 +1,33 @@ +import React from 'react'; +import {StyleSheet, Text} from 'react-native'; +import {propTypes, defaultProps} from '../anchorForCommentsOnlyPropTypes'; + +/* + * This is a default anchor component for regular links. + */ +const BaseAnchorForCommentsOnly = ({ + href, + rel, + target, + children, + style, + ...props +}) => ( + + {children} + +); + +BaseAnchorForCommentsOnly.propTypes = propTypes; +BaseAnchorForCommentsOnly.defaultProps = defaultProps; +BaseAnchorForCommentsOnly.displayName = 'BaseAnchorForCommentsOnly'; + +export default BaseAnchorForCommentsOnly; diff --git a/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.native.js b/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.native.js new file mode 100644 index 000000000000..9a63b54b1804 --- /dev/null +++ b/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.native.js @@ -0,0 +1,30 @@ +import React from 'react'; +import {Linking, StyleSheet, Text} from 'react-native'; +import {propTypes, defaultProps} from '../anchorForCommentsOnlyPropTypes'; +import fileDownload from '../../../libs/fileDownload'; + +/* + * This is a default anchor component for regular links. + */ +const BaseAnchorForCommentsOnly = ({ + href, + children, + style, + shouldDownloadFile, + ...props +}) => ( + (shouldDownloadFile ? fileDownload(href) : Linking.openURL(href))} + // eslint-disable-next-line react/jsx-props-no-spreading + {...props} + > + {children} + +); + +BaseAnchorForCommentsOnly.propTypes = propTypes; +BaseAnchorForCommentsOnly.defaultProps = defaultProps; +BaseAnchorForCommentsOnly.displayName = 'BaseAnchorForCommentsOnly'; + +export default BaseAnchorForCommentsOnly; diff --git a/src/components/AnchorForCommentsOnly/anchorForCommentsOnlyPropTypes.js b/src/components/AnchorForCommentsOnly/anchorForCommentsOnlyPropTypes.js index eb61cdc7d3a1..04fddf8e02bf 100644 --- a/src/components/AnchorForCommentsOnly/anchorForCommentsOnlyPropTypes.js +++ b/src/components/AnchorForCommentsOnly/anchorForCommentsOnlyPropTypes.js @@ -3,25 +3,36 @@ import PropTypes from 'prop-types'; /** * Text based component that is passed a URL to open onPress */ -const anchorForCommentsOnlyPropTypes = { - // The URL to open +const propTypes = { + /** The URL to open */ href: PropTypes.string, - // What headers to send to the linked page (usually noopener and noreferrer) - // This is unused in native, but is here for parity with web + /** What headers to send to the linked page (usually noopener and noreferrer) + This is unused in native, but is here for parity with web */ rel: PropTypes.string, - // Used to determine where to open a link ("_blank" is passed for a new tab) - // This is unused in native, but is here for parity with web + /** Used to determine where to open a link ("_blank" is passed for a new tab) + This is unused in native, but is here for parity with web */ target: PropTypes.string, + /** Should the link be treated as a file download or a regular hyperlink? (relevant to native platforms only) */ + shouldDownloadFile: PropTypes.bool, - // Any children to display + /** Any children to display */ children: PropTypes.node, - // Any additional styles to apply + /** Any additional styles to apply */ // eslint-disable-next-line react/forbid-prop-types style: PropTypes.any, }; -export default anchorForCommentsOnlyPropTypes; +const defaultProps = { + href: '', + rel: '', + target: '', + shouldDownloadFile: false, + children: null, + style: {}, +}; + +export {propTypes, defaultProps}; diff --git a/src/components/AnchorForCommentsOnly/index.js b/src/components/AnchorForCommentsOnly/index.js index 3edda2184b8c..b13d1a257bf2 100644 --- a/src/components/AnchorForCommentsOnly/index.js +++ b/src/components/AnchorForCommentsOnly/index.js @@ -1,37 +1,40 @@ +import _ from 'underscore'; import React from 'react'; -import {StyleSheet, Text} from 'react-native'; -import anchorForCommentsOnlyPropTypes from './anchorForCommentsOnlyPropTypes'; +import PropTypes from 'prop-types'; +import { + propTypes as anchorForCommentsOnlyPropTypes, + defaultProps as anchorForCommentsOnlyDefaultProps, +} from './anchorForCommentsOnlyPropTypes'; +import AnchorWithAuthToken from './AnchorWithAuthToken'; +import BaseAnchorForCommentsOnly from './BaseAnchorForCommentsOnly'; + +const propTypes = { + /** Do we need an auth token to view this link or download the remote resource? */ + isAuthTokenRequired: PropTypes.bool, + + ...anchorForCommentsOnlyPropTypes, +}; const defaultProps = { - href: '', - rel: '', - target: '', - children: null, - style: {}, + isAuthTokenRequired: false, + ...anchorForCommentsOnlyDefaultProps, }; -const AnchorForCommentsOnly = ({ - href, - rel, - target, - children, - style, - ...props -}) => ( - { + const propsToPass = _.omit(props, 'isAuthTokenRequired'); + return props.isAuthTokenRequired // eslint-disable-next-line react/jsx-props-no-spreading - {...props} - > - {children} - -); + ? + // eslint-disable-next-line react/jsx-props-no-spreading + : ; +}; -AnchorForCommentsOnly.propTypes = anchorForCommentsOnlyPropTypes; +AnchorForCommentsOnly.propTypes = propTypes; AnchorForCommentsOnly.defaultProps = defaultProps; AnchorForCommentsOnly.displayName = 'AnchorForCommentsOnly'; diff --git a/src/components/AnchorForCommentsOnly/index.native.js b/src/components/AnchorForCommentsOnly/index.native.js deleted file mode 100644 index a5b184a8a6b3..000000000000 --- a/src/components/AnchorForCommentsOnly/index.native.js +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import {Linking, StyleSheet, Text} from 'react-native'; -import anchorForCommentsOnlyPropTypes from './anchorForCommentsOnlyPropTypes'; - -const defaultProps = { - href: '', - rel: null, - target: null, - children: null, - style: {}, -}; - -const AnchorForCommentsOnly = ({ - href, - children, - style, - ...props -}) => ( - // eslint-disable-next-line react/jsx-props-no-spreading - Linking.openURL(href)} {...props}>{children} -); - -AnchorForCommentsOnly.propTypes = anchorForCommentsOnlyPropTypes; -AnchorForCommentsOnly.defaultProps = defaultProps; -AnchorForCommentsOnly.displayName = 'AnchorForCommentsOnly'; - -export default AnchorForCommentsOnly; diff --git a/src/components/RenderHTML.js b/src/components/RenderHTML.js index 38214a6582d7..13df9ea60f68 100755 --- a/src/components/RenderHTML.js +++ b/src/components/RenderHTML.js @@ -43,9 +43,13 @@ function computeImagesMaxWidth(contentWidth) { function AnchorRenderer({tnode, key, style}) { const htmlAttribs = tnode.attributes; + + // An auth token is needed to download Expensify chat attachments + const isAttachment = Boolean(htmlAttribs['data-expensify-source']); return ( Date: Fri, 21 May 2021 11:48:06 -0700 Subject: [PATCH 2/2] Update outdated comment --- src/components/AnchorForCommentsOnly/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AnchorForCommentsOnly/index.js b/src/components/AnchorForCommentsOnly/index.js index b13d1a257bf2..6a5daf6a672b 100644 --- a/src/components/AnchorForCommentsOnly/index.js +++ b/src/components/AnchorForCommentsOnly/index.js @@ -21,7 +21,7 @@ const defaultProps = { }; /* - * This component acts as a switch between AnchorWithAuthToken and DefaultAnchor. + * This component acts as a switch between AnchorWithAuthToken and default BaseAnchorForCommentsOnly. * It is an optimization so that we can attach an auth token to a URL when one is required, * without using Onyx.connect on links that don't need an authToken. */