From d22079a6e7c3da48643de1ad0008fa290d487da2 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 2 Dec 2020 14:51:57 -0700 Subject: [PATCH] Revert "React Native Render HTML: whitespace collapsing" --- .gitignore | 3 - config/webpack/webpack.common.js | 2 +- config/webpack/webpack.dev.js | 9 +- config/webpack/webpack.prod.js | 5 - package-lock.json | 153 ++--------------- package.json | 4 +- .../anchorForCommentsOnlyPropTypes.js | 27 --- src/components/AnchorForCommentsOnly/index.js | 26 ++- .../AnchorForCommentsOnly/index.native.js | 29 +++- .../InlineCodeBlock/index.android.js | 21 ++- src/components/InlineCodeBlock/index.ios.js | 27 ++- src/components/InlineCodeBlock/index.js | 25 ++- .../inlineCodeBlockPropTypes.js | 10 -- src/components/RenderHTML.js | 156 ------------------ .../home/report/ReportActionItemFragment.js | 113 ++++++++++++- src/styles/StyleSheet.js | 69 ++++---- 16 files changed, 241 insertions(+), 438 deletions(-) delete mode 100644 src/components/AnchorForCommentsOnly/anchorForCommentsOnlyPropTypes.js delete mode 100644 src/components/InlineCodeBlock/inlineCodeBlockPropTypes.js delete mode 100644 src/components/RenderHTML.js diff --git a/.gitignore b/.gitignore index 0c4904335d51..8efffec2c5f8 100644 --- a/.gitignore +++ b/.gitignore @@ -32,9 +32,6 @@ build/ local.properties *.iml -# Vscode -.vscode - # node.js # node_modules/ diff --git a/config/webpack/webpack.common.js b/config/webpack/webpack.common.js index 334d52593a75..28b397f7bc72 100644 --- a/config/webpack/webpack.common.js +++ b/config/webpack/webpack.common.js @@ -52,7 +52,7 @@ module.exports = { */ exclude: [ // eslint-disable-next-line max-len - /node_modules\/(?!(react-native-webview|react-native-onyx)\/).*|\.native\.js$/, + /node_modules\/(?!(react-native-render-html|react-native-webview|react-native-onyx)\/).*|\.native\.js$/, platformExclude ], }, diff --git a/config/webpack/webpack.dev.js b/config/webpack/webpack.dev.js index 4f6e0bd32fe9..871b46e53e55 100644 --- a/config/webpack/webpack.dev.js +++ b/config/webpack/webpack.dev.js @@ -16,11 +16,6 @@ module.exports = merge(common, { plugins: [ new webpack.DefinePlugin({ __REACT_WEB_CONFIG__: JSON.stringify(env), - - // React Native JavaScript environment requires the global __DEV__ variable to be accessible. - // react-native-render-html uses variable to log exclusively during development. - // See https://reactnative.dev/docs/javascript-environment - __DEV__: true, - }), - ], + }) + ] }); diff --git a/config/webpack/webpack.prod.js b/config/webpack/webpack.prod.js index 6cccd5242175..3a1604c44c3a 100644 --- a/config/webpack/webpack.prod.js +++ b/config/webpack/webpack.prod.js @@ -12,11 +12,6 @@ module.exports = merge(common, { plugins: [ new webpack.DefinePlugin({ __REACT_WEB_CONFIG__: JSON.stringify(env), - - // React Native JavaScript environment requires the global __DEV__ variable to be accessible. - // react-native-render-html uses variable to log exclusively during development. - // See https://reactnative.dev/docs/javascript-environment - __DEV__: false, }) ], }); diff --git a/package-lock.json b/package-lock.json index 3e0c72d00967..0a3957af8d8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2913,81 +2913,6 @@ "chalk": "^3.0.0" } }, - "@native-html/css-processor": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@native-html/css-processor/-/css-processor-1.6.1.tgz", - "integrity": "sha512-3l4SmYU5CIwL7f8GSssypWfFd7W/FcqVrOomhDRbaWYsxKh2T0zNcIjJbkr8ZbpXJk3qKrV1EMoTJ8vt6H8M9Q==", - "requires": { - "css-to-react-native": "^3.0.0" - } - }, - "@native-html/transient-render-engine": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@native-html/transient-render-engine/-/transient-render-engine-3.6.0.tgz", - "integrity": "sha512-fvPIzD+b2xq7+cIcFwItze3IS59eneht7h31VqRQ5CyN7mCTfcuxCmMzLDdM7/1Chn4A79tG0yEWeJQSt2565Q==", - "requires": { - "@native-html/css-processor": "1.6.1", - "@types/ramda": "^0.27.32", - "htmlparser2": "^5.0.1", - "ramda": "^0.27.1" - }, - "dependencies": { - "dom-serializer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.1.0.tgz", - "integrity": "sha512-ox7bvGXt2n+uLWtCRLybYx60IrOlWL/aCebWJk1T0d4m3y2tzf4U3ij9wBMUb6YJZpz06HCCYuyCDveE2xXmzQ==", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^3.0.0", - "entities": "^2.0.0" - } - }, - "domelementtype": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz", - "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==" - }, - "domhandler": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", - "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", - "requires": { - "domelementtype": "^2.0.1" - } - }, - "domutils": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.4.2.tgz", - "integrity": "sha512-NKbgaM8ZJOecTZsIzW5gSuplsX2IWW2mIK7xVr8hTQF2v1CJWTmLZ1HOCh5sH+IzVPAGE5IucooOkvwBRAdowA==", - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.0.1", - "domhandler": "^3.3.0" - } - }, - "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" - }, - "htmlparser2": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", - "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^3.3.0", - "domutils": "^2.4.2", - "entities": "^2.0.0" - } - }, - "ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==" - } - } - }, "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -3546,14 +3471,6 @@ "integrity": "sha512-UEyp8LwZ4Dg30kVU2Q3amHHyTn1jEdhCIE59ANed76GaT1Vp76DD3ZWSAxgCrw6wJ0TqeoBpqmfUHiUDPs//HQ==", "dev": true }, - "@types/ramda": { - "version": "0.27.32", - "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.32.tgz", - "integrity": "sha512-vdwZcWC+hlTxB//LZQLS1+VEdArImGI4yVKUpeqB8b9mBXgDFXCuQoOt8spQbi8fTyNLOdqRv6liSm2ckxWLog==", - "requires": { - "ts-toolbelt": "^6.15.1" - } - }, "@types/semver": { "version": "7.3.4", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz", @@ -5828,11 +5745,6 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, - "camelize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" - }, "caniuse-lite": { "version": "1.0.30001148", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz", @@ -6696,11 +6608,6 @@ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, - "css-color-keywords": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", - "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=" - }, "css-hot-loader": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/css-hot-loader/-/css-hot-loader-1.4.4.tgz", @@ -6851,16 +6758,6 @@ } } }, - "css-to-react-native": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz", - "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==", - "requires": { - "camelize": "^1.0.0", - "css-color-keywords": "^1.0.0", - "postcss-value-parser": "^4.0.2" - } - }, "css-what": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", @@ -7479,7 +7376,6 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "dev": true, "requires": { "domelementtype": "^2.0.1", "entities": "^2.0.0" @@ -7488,14 +7384,12 @@ "domelementtype": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz", - "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==", - "dev": true + "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==" }, "entities": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" } } }, @@ -7513,8 +7407,7 @@ "domelementtype": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" }, "domexception": { "version": "2.0.1", @@ -7537,7 +7430,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "dev": true, "requires": { "domelementtype": "1" } @@ -7546,7 +7438,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dev": true, "requires": { "dom-serializer": "0", "domelementtype": "1" @@ -8108,8 +7999,7 @@ "entities": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" }, "env-paths": { "version": "2.2.0", @@ -9612,6 +9502,11 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, "eventsource": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", @@ -11309,7 +11204,6 @@ "version": "3.10.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "dev": true, "requires": { "domelementtype": "^1.3.1", "domhandler": "^2.3.0", @@ -11323,7 +11217,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -18746,7 +18639,8 @@ "postcss-value-parser": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true }, "prelude-ls": { "version": "1.2.1", @@ -19565,20 +19459,14 @@ } }, "react-native-render-html": { - "version": "6.0.0-alpha.8", - "resolved": "https://registry.npmjs.org/react-native-render-html/-/react-native-render-html-6.0.0-alpha.8.tgz", - "integrity": "sha512-iMXnJ59mB9bV0O/w/GEqcTQ1PnV7/tjmxC8eDZv8vTIbN3wVvpgwvPWS/aTndDtvBC9mHYGG+CazH7ALqJjJMw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/react-native-render-html/-/react-native-render-html-4.2.4.tgz", + "integrity": "sha512-OiLItEzKgS7dzD9XI5bHhjcUEfpWdzH1FgexzjbBdICPfYjmmcefpcRmLZY1+HMfxJ7wL8iF1PzTF48LchGTBA==", "requires": { - "@native-html/transient-render-engine": "^3.6.0", - "@types/ramda": "^0.27.32", - "ramda": "^0.27.1" - }, - "dependencies": { - "ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==" - } + "buffer": "^4.5.1", + "events": "^1.1.0", + "html-entities": "^1.2.0", + "htmlparser2": "3.10.1" } }, "react-native-safe-area-context": { @@ -22293,11 +22181,6 @@ "utf8-byte-length": "^1.0.1" } }, - "ts-toolbelt": { - "version": "6.15.5", - "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-6.15.5.tgz", - "integrity": "sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==" - }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", diff --git a/package.json b/package.json index 56c0c0fe93cf..ec867ee343cf 100644 --- a/package.json +++ b/package.json @@ -60,9 +60,9 @@ "react-native-image-picker": "^2.3.3", "react-native-keyboard-spacer": "^0.4.1", "react-native-modal": "^11.5.6", - "react-native-onyx": "git+https://git@github.com:Expensify/react-native-onyx.git#8e29d1807382c8a1325c92858c551f6b19e1aaad", "react-native-pdf": "^6.2.2", - "react-native-render-html": "^6.0.0-alpha.8", + "react-native-onyx": "git+https://git@github.com:Expensify/react-native-onyx.git#8e29d1807382c8a1325c92858c551f6b19e1aaad", + "react-native-render-html": "^4.2.3", "react-native-safe-area-context": "^3.1.4", "react-native-web": "^0.14.0", "react-native-web-webview": "^1.0.2", diff --git a/src/components/AnchorForCommentsOnly/anchorForCommentsOnlyPropTypes.js b/src/components/AnchorForCommentsOnly/anchorForCommentsOnlyPropTypes.js deleted file mode 100644 index eb61cdc7d3a1..000000000000 --- a/src/components/AnchorForCommentsOnly/anchorForCommentsOnlyPropTypes.js +++ /dev/null @@ -1,27 +0,0 @@ -import PropTypes from 'prop-types'; - -/** - * Text based component that is passed a URL to open onPress - */ -const anchorForCommentsOnlyPropTypes = { - // 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 - 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 - target: PropTypes.string, - - - // Any children to display - children: PropTypes.node, - - // Any additional styles to apply - // eslint-disable-next-line react/forbid-prop-types - style: PropTypes.any, -}; - -export default anchorForCommentsOnlyPropTypes; diff --git a/src/components/AnchorForCommentsOnly/index.js b/src/components/AnchorForCommentsOnly/index.js index 6e70e859cf96..96244d347278 100644 --- a/src/components/AnchorForCommentsOnly/index.js +++ b/src/components/AnchorForCommentsOnly/index.js @@ -1,6 +1,28 @@ import React from 'react'; +import PropTypes from 'prop-types'; import {StyleSheet} from 'react-native'; -import anchorForCommentsOnlyPropTypes from './anchorForCommentsOnlyPropTypes'; + +/** + * Text based component that is passed a URL to open onPress + */ + +const propTypes = { + // The URL to open + href: PropTypes.string, + + // What headers to send to the linked page (usually noopener and noreferrer) + rel: PropTypes.string, + + // Used to determine where to open a link ("_blank" is passed for a new tab) + target: PropTypes.string, + + // Any children to display + children: PropTypes.node, + + // Any additional styles to apply + // eslint-disable-next-line react/forbid-prop-types + style: PropTypes.any, +}; const defaultProps = { href: '', @@ -30,7 +52,7 @@ const AnchorForCommentsOnly = ({ ); -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 index a5b184a8a6b3..d4ed3dfc944b 100644 --- a/src/components/AnchorForCommentsOnly/index.native.js +++ b/src/components/AnchorForCommentsOnly/index.native.js @@ -1,6 +1,31 @@ import React from 'react'; +import PropTypes from 'prop-types'; import {Linking, StyleSheet, Text} from 'react-native'; -import anchorForCommentsOnlyPropTypes from './anchorForCommentsOnlyPropTypes'; + +/** + * Text based component that is passed a URL to open onPress + */ + +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 + 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 + target: PropTypes.string, + + + // Any children to display + children: PropTypes.node, + + // Any additional styles to apply + // eslint-disable-next-line react/forbid-prop-types + style: PropTypes.any, +}; const defaultProps = { href: '', @@ -20,7 +45,7 @@ const AnchorForCommentsOnly = ({ Linking.openURL(href)} {...props}>{children} ); -AnchorForCommentsOnly.propTypes = anchorForCommentsOnlyPropTypes; +AnchorForCommentsOnly.propTypes = propTypes; AnchorForCommentsOnly.defaultProps = defaultProps; AnchorForCommentsOnly.displayName = 'AnchorForCommentsOnly'; diff --git a/src/components/InlineCodeBlock/index.android.js b/src/components/InlineCodeBlock/index.android.js index d68ca0031b84..00f294e3a327 100644 --- a/src/components/InlineCodeBlock/index.android.js +++ b/src/components/InlineCodeBlock/index.android.js @@ -1,19 +1,18 @@ -/* eslint-disable react/jsx-props-no-spreading */ import React from 'react'; +import PropTypes from 'prop-types'; import {View} from 'react-native'; -import inlineCodeBlockPropTypes from './inlineCodeBlockPropTypes'; +import {webViewStyles} from '../../styles/StyleSheet'; -const InlineCodeBlock = ({ - TDefaultRenderer, - defaultRendererProps, - boxModelStyle, - textStyle, -}) => ( - - +const propTypes = { + children: PropTypes.node.isRequired, +}; + +const InlineCodeBlock = ({children}) => ( + + {children} ); -InlineCodeBlock.propTypes = inlineCodeBlockPropTypes; +InlineCodeBlock.propTypes = propTypes; InlineCodeBlock.displayName = 'InlineCodeBlock'; export default InlineCodeBlock; diff --git a/src/components/InlineCodeBlock/index.ios.js b/src/components/InlineCodeBlock/index.ios.js index 65b3d26d1f19..40ce39772dc8 100644 --- a/src/components/InlineCodeBlock/index.ios.js +++ b/src/components/InlineCodeBlock/index.ios.js @@ -1,25 +1,18 @@ -/* eslint-disable react/jsx-props-no-spreading */ import React from 'react'; +import PropTypes from 'prop-types'; import {View} from 'react-native'; -import styles from '../../styles/StyleSheet'; -import inlineCodeBlockPropTypes from './inlineCodeBlockPropTypes'; +import styles, {webViewStyles} from '../../styles/StyleSheet'; -const InlineCodeBlock = ({ - TDefaultRenderer, - defaultRendererProps, - boxModelStyle, - textStyle, -}) => ( - - +const propTypes = { + children: PropTypes.node.isRequired, +}; + +const InlineCodeBlock = ({children}) => ( + + {children} ); -InlineCodeBlock.propTypes = inlineCodeBlockPropTypes; +InlineCodeBlock.propTypes = propTypes; InlineCodeBlock.displayName = 'InlineCodeBlock'; export default InlineCodeBlock; diff --git a/src/components/InlineCodeBlock/index.js b/src/components/InlineCodeBlock/index.js index aa6edab7018e..6264e55b0264 100644 --- a/src/components/InlineCodeBlock/index.js +++ b/src/components/InlineCodeBlock/index.js @@ -1,19 +1,18 @@ import React from 'react'; -import inlineCodeBlockPropTypes from './inlineCodeBlockPropTypes'; +import PropTypes from 'prop-types'; +import {Text} from 'react-native'; +import {webViewStyles} from '../../styles/StyleSheet'; -const InlineCodeBlock = ({ - TDefaultRenderer, - defaultRendererProps, - boxModelStyle, - textStyle, -}) => ( - +const propTypes = { + children: PropTypes.node.isRequired, +}; + +const InlineCodeBlock = ({children}) => ( + + {children} + ); -InlineCodeBlock.propTypes = inlineCodeBlockPropTypes; +InlineCodeBlock.propTypes = propTypes; InlineCodeBlock.displayName = 'InlineCodeBlock'; export default InlineCodeBlock; diff --git a/src/components/InlineCodeBlock/inlineCodeBlockPropTypes.js b/src/components/InlineCodeBlock/inlineCodeBlockPropTypes.js deleted file mode 100644 index f880d3a1e4ae..000000000000 --- a/src/components/InlineCodeBlock/inlineCodeBlockPropTypes.js +++ /dev/null @@ -1,10 +0,0 @@ -import PropTypes from 'prop-types'; - -const inlineCodeBlockPropTypes = { - TDefaultRenderer: PropTypes.func.isRequired, - defaultRendererProps: PropTypes.object.isRequired, - boxModelStyle: PropTypes.any.isRequired, - textStyle: PropTypes.any.isRequired -}; - -export default inlineCodeBlockPropTypes; diff --git a/src/components/RenderHTML.js b/src/components/RenderHTML.js deleted file mode 100644 index 3f05c055736c..000000000000 --- a/src/components/RenderHTML.js +++ /dev/null @@ -1,156 +0,0 @@ -/* eslint-disable react/prop-types */ -import React from 'react'; -import PropTypes from 'prop-types'; -import {useWindowDimensions} from 'react-native'; -import HTML, { - defaultHTMLElementModels, - TNodeChildrenRenderer, - splitBoxModelStyle, -} from 'react-native-render-html'; -import Config from '../CONFIG'; -import {getAuthToken} from '../libs/API'; -import {webViewStyles} from '../styles/StyleSheet'; -import fontFamily from '../styles/fontFamily'; -import AnchorForCommentsOnly from './AnchorForCommentsOnly'; -import ImageThumbnailWithModal from './ImageThumbnailWithModal'; -import InlineCodeBlock from './InlineCodeBlock'; - -const MAX_IMG_DIMENSIONS = 512; - -const EXTRA_FONTS = [ - fontFamily.GTA, - fontFamily.GTA_BOLD, - fontFamily.GTA_ITALIC, - fontFamily.MONOSPACE, - fontFamily.SYSTEM, -]; - -/** - * Compute images maximum width from the available screen width. This function - * is used by the HTML component in the default renderer for img tags to scale - * down images that would otherwise overflow horizontally. - * - * @param {number} contentWidth - The content width provided to the HTML - * component. - * @returns {number} The minimum between contentWidth and MAX_IMG_DIMENSIONS - */ -function computeImagesMaxWidth(contentWidth) { - return Math.min(MAX_IMG_DIMENSIONS, contentWidth); -} - -function AnchorRenderer({tnode, key, style}) { - const htmlAttribs = tnode.attributes; - return ( - - - - ); -} - -function CodeRenderer({ - key, style, TDefaultRenderer, ...defaultRendererProps -}) { - // We split wrapper and inner styles - // "boxModelStyle" corresponds to border, margin, padding and backgroundColor - const {boxModelStyle, otherStyle: textStyle} = splitBoxModelStyle(style); - return ( - - ); -} - -function ImgRenderer({key, tnode}) { - const htmlAttribs = tnode.attributes; - - // Attaches authTokens as a URL parameter to load image attachments - let previewSource = htmlAttribs['data-expensify-source'] - ? `${htmlAttribs.src}?authToken=${getAuthToken()}` - : htmlAttribs.src; - - let source = htmlAttribs['data-expensify-source'] - ? `${htmlAttribs['data-expensify-source']}?authToken=${getAuthToken()}` - : htmlAttribs.src; - - // Update the image URL so the images can be accessed depending on the config environment - previewSource = previewSource.replace( - Config.EXPENSIFY.URL_EXPENSIFY_COM, - Config.EXPENSIFY.URL_API_ROOT, - ); - source = source.replace( - Config.EXPENSIFY.URL_EXPENSIFY_COM, - Config.EXPENSIFY.URL_API_ROOT, - ); - - return ( - - ); -} - -// Define default element models for these renderers. -AnchorRenderer.model = defaultHTMLElementModels.a; -CodeRenderer.model = defaultHTMLElementModels.code; -ImgRenderer.model = defaultHTMLElementModels.img; - -// Define the custom render methods -const renderers = { - a: AnchorRenderer, - code: CodeRenderer, - img: ImgRenderer, -}; - -const propTypes = { - html: PropTypes.string.isRequired, - debug: PropTypes.bool, -}; - -const RenderHTML = ({html, debug = false}) => { - const {width} = useWindowDimensions(); - const containerWidth = width * 0.8; - return ( - - ); -}; - -RenderHTML.displayName = 'RenderHTML'; -RenderHTML.propTypes = propTypes; -RenderHTML.defaultProps = { - debug: false, -}; - -export default RenderHTML; diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index 0ec35ffcf925..363e9cc4bf73 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -1,11 +1,18 @@ import React from 'react'; -import {ActivityIndicator, View} from 'react-native'; +import HTML from 'react-native-render-html'; +import { + Linking, ActivityIndicator, View, Dimensions +} from 'react-native'; import PropTypes from 'prop-types'; import Str from 'expensify-common/lib/str'; import ReportActionFragmentPropTypes from './ReportActionFragmentPropTypes'; -import styles, {colors} from '../../../styles/StyleSheet'; -import RenderHTML from '../../../components/RenderHTML'; +import styles, {webViewStyles, colors} from '../../../styles/StyleSheet'; import Text from '../../../components/Text'; +import AnchorForCommentsOnly from '../../../components/AnchorForCommentsOnly'; +import InlineCodeBlock from '../../../components/InlineCodeBlock'; +import * as API from '../../../libs/API'; +import ImageThumbnailWithModal from '../../../components/ImageThumbnailWithModal'; +import Config from '../../../CONFIG'; const propTypes = { // The message fragment needing to be displayed @@ -24,8 +31,85 @@ const defaultProps = { }; class ReportActionItemFragment extends React.PureComponent { + constructor(props) { + super(props); + + // Define the custom render methods + // For tags, the attribute is used to be more cross-platform friendly + this.customRenderers = { + a: (htmlAttribs, children, convertedCSSStyles, passProps) => ( + + {children} + + ), + pre: (htmlAttribs, children, convertedCSSStyles, passProps) => ( + + {children} + + ), + code: (htmlAttribs, children, convertedCSSStyles, passProps) => ( + + {children} + + ), + blockquote: (htmlAttribs, children, convertedCSSStyles, passProps) => ( + + {children} + + ), + img: (htmlAttribs, children, convertedCSSStyles, passProps) => { + // Attaches authTokens as a URL parameter to load image attachments + let previewSource = htmlAttribs['data-expensify-source'] + ? `${htmlAttribs.src}?authToken=${API.getAuthToken()}` + : htmlAttribs.src; + + let source = htmlAttribs['data-expensify-source'] + ? `${htmlAttribs['data-expensify-source']}?authToken=${API.getAuthToken()}` + : htmlAttribs.src; + + // Update the image URL so the images can be accessed depending on the config environment + previewSource = previewSource.replace( + Config.EXPENSIFY.URL_EXPENSIFY_COM, + Config.EXPENSIFY.URL_API_ROOT + ); + source = source.replace( + Config.EXPENSIFY.URL_EXPENSIFY_COM, + Config.EXPENSIFY.URL_API_ROOT + ); + + return ( + + ); + }, + }; + } + render() { const {fragment} = this.props; + const maxImageDimensions = 512; + const windowWidth = Dimensions.get('window').width; switch (fragment.type) { case 'COMMENT': // If this is an attachment placeholder, return the placeholder component @@ -42,11 +126,24 @@ class ReportActionItemFragment extends React.PureComponent { } // Only render HTML if we have html in the fragment - return fragment.html !== fragment.text ? ( - - ) : ( - {Str.htmlDecode(fragment.text)} - ); + return fragment.html !== fragment.text + ? ( + Linking.openURL(href)} + html={fragment.html} + imagesMaxWidth={Math.min(maxImageDimensions, windowWidth * 0.8)} + imagesInitialDimensions={{width: maxImageDimensions, height: maxImageDimensions}} + /> + ) + : ( + + {Str.htmlDecode(fragment.text)} + + ); case 'TEXT': return (