diff --git a/ios/eSteem.xcworkspace/xcuserdata/mistik.xcuserdatad/UserInterfaceState.xcuserstate b/ios/eSteem.xcworkspace/xcuserdata/mistik.xcuserdatad/UserInterfaceState.xcuserstate index 9f7267666..3ff52e2d0 100644 Binary files a/ios/eSteem.xcworkspace/xcuserdata/mistik.xcuserdatad/UserInterfaceState.xcuserstate and b/ios/eSteem.xcworkspace/xcuserdata/mistik.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/package.json b/package.json index 0519de56f..929766680 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "react-native": "0.61.2", "react-native-actionsheet": "^2.4.2", "react-native-autocomplete-input": "^4.1.0", + "react-native-autoheight-webview": "^1.2.2", "react-native-code-push": "esteemapp/react-native-code-push", "react-native-config": "luggit/react-native-config#master", "react-native-dark-mode": "^0.1.2", diff --git a/src/components/basicUIElements/index.js b/src/components/basicUIElements/index.js index f4ff21f63..e4cf494d2 100644 --- a/src/components/basicUIElements/index.js +++ b/src/components/basicUIElements/index.js @@ -19,10 +19,12 @@ import WalletDetailsPlaceHolder from './view/placeHolder/walletDetailsPlaceHolde import WalletUnclaimedPlaceHolder from './view/placeHolder/walletUnclaimedPlaceHolder'; import ListPlaceHolder from './view/placeHolder/listPlaceHolderView'; import BoostPlaceHolder from './view/placeHolder/boostPlaceHolderView'; +import CommentPlaceHolder from './view/placeHolder/commentPlaceHolderView'; export { Card, Chip, + CommentPlaceHolder, GrayWrapper, LineBreak, ListItemPlaceHolder, diff --git a/src/components/basicUIElements/view/placeHolder/commentPlaceHolderView.js b/src/components/basicUIElements/view/placeHolder/commentPlaceHolderView.js new file mode 100644 index 000000000..7f7c51f4f --- /dev/null +++ b/src/components/basicUIElements/view/placeHolder/commentPlaceHolderView.js @@ -0,0 +1,35 @@ +import React from 'react'; +import { View } from 'react-native'; +import Placeholder from 'rn-placeholder'; + +import { ThemeContainer } from '../../../../containers'; + +import styles from './listItemPlaceHolderStyles'; + +const CommentPlaceHolderView = () => { + return ( + + {({ isDarkTheme }) => { + const color = isDarkTheme ? '#2e3d51' : '#f5f5f5'; + return ( + + + + + + ); + }} + + ); +}; + +export default CommentPlaceHolderView; diff --git a/src/components/basicUIElements/view/placeHolder/listItemPlaceHolderStyles.js b/src/components/basicUIElements/view/placeHolder/listItemPlaceHolderStyles.js index c006f721b..c489b1019 100644 --- a/src/components/basicUIElements/view/placeHolder/listItemPlaceHolderStyles.js +++ b/src/components/basicUIElements/view/placeHolder/listItemPlaceHolderStyles.js @@ -16,4 +16,7 @@ export default EStyleSheet.create({ marginTop: 3, flex: 1, }, + paragraphWithoutMargin: { + flex: 1, + }, }); diff --git a/src/components/postElements/body/view/config.js b/src/components/postElements/body/view/config.js new file mode 100644 index 000000000..30c8fc2f4 --- /dev/null +++ b/src/components/postElements/body/view/config.js @@ -0,0 +1,106 @@ +export default `document.addEventListener('click', function(event) { + let el = event.target; + // A element can be wrapped with inline element. Look parent elements. + while (el.tagName !== 'A') { + if (!el.parentNode) { + break; + } + el = el.parentNode; + } + if (!el || el.tagName !== 'A') { + window.ReactNativeWebView.postMessage('4'); + if (el.tagName) window.ReactNativeWebView.postMessage(el.tagName); + return; + } + if (el.getAttribute('target') === '_external') { + const href = el.getAttribute('href'); + const result = { + type: '_external', + href + } + window.ReactNativeWebView.postMessage(JSON.stringify(result)); + + return true; + } + if (el.classList.contains('markdown-external-link')) { + const href = el.getAttribute('data-href'); + const result = { + type: 'markdown-external-link', + href + } + window.ReactNativeWebView.postMessage(JSON.stringify(result)); + + return true; + } + if (el.classList.contains('markdown-author-link')) { + const author = el.getAttribute('data-author'); + const result = { + type: 'markdown-author-link', + author, + } + window.ReactNativeWebView.postMessage(JSON.stringify(result)); + return false; + } + + if (el.classList.contains('markdown-post-link')) { + let category = el.getAttribute('data-tag'); + let author = el.getAttribute('data-author'); + let permlink = el.getAttribute('data-permlink'); + const result = { + type: 'markdown-post-link', + category, + author, + permlink, + } + window.ReactNativeWebView.postMessage(JSON.stringify(result)); + return false; + } + if (el.classList.contains('markdown-tag-link')) { + let tag = el.getAttribute('data-tag'); + const result = { + type: 'markdown-tag-link', + tag + } + window.ReactNativeWebView.postMessage(JSON.stringify(result)); + return false; + } + if (el.classList.contains('markdown-witnesses-link')) { + + const result = { + type: 'markdown-witnesses-link' + } + window.ReactNativeWebView.postMessage(JSON.stringify(result)); + return false; + } + if (el.classList.contains('markdown-proposal-link')) { + + let proposal = el.getAttribute('data-proposal'); + const result = { + type: 'markdown-proposal-link', + proposal + } + window.ReactNativeWebView.postMessage(JSON.stringify(result)); + return false; + } + if (el.classList.contains('markdown-video-link')) { + const embedSrc = ''; + if (embedSrc) { + el.innerHTML = embedSrc; + return; + } + const videoHref = el.getAttribute('data-video-href'); + if (videoHref) { + const result = { + type: 'markdown-video-link', + videoHref + } + window.ReactNativeWebView.postMessage(JSON.stringify(result)); + + return false; + } + } + window.ReactNativeWebView.postMessage('4'); + const author = el.getAttribute('data-author').toString(); + window.ReactNativeWebView.postMessage(JSON.stringify(author)); +}) +true;`; diff --git a/src/components/postElements/body/view/postBodyView.js b/src/components/postElements/body/view/postBodyView.js index 2b0fcac67..3a3aef673 100644 --- a/src/components/postElements/body/view/postBodyView.js +++ b/src/components/postElements/body/view/postBodyView.js @@ -1,16 +1,17 @@ -import React from 'react'; -import { Dimensions, Linking, Alert, TouchableOpacity, Text } from 'react-native'; +import React, { Fragment } from 'react'; +import { Dimensions, Linking, Alert } from 'react-native'; import { withNavigation } from 'react-navigation'; import { useIntl, injectIntl } from 'react-intl'; -import HTML from 'react-native-render-html'; -import { getParentsTagsRecursively } from 'react-native-render-html/src/HTMLUtils'; +import AutoHeightWebView from 'react-native-autoheight-webview'; +import EStyleSheet from 'react-native-extended-stylesheet'; +import get from 'lodash/get'; + +import script from './config.js'; +import { PostPlaceHolder, CommentPlaceHolder } from '../../../basicUIElements'; // Constants import { default as ROUTES } from '../../../../constants/routeNames'; -// Styles -import styles from './postBodyStyles'; - const WIDTH = Dimensions.get('window').width; const PostBody = ({ @@ -23,36 +24,72 @@ const PostBody = ({ }) => { const intl = useIntl(); - const _handleOnLinkPress = (href, hrefAtr) => { - if (hrefAtr.class === 'markdown-author-link') { - if (!handleOnUserPress) { - _handleOnUserPress(hrefAtr['data-author']); - } else { - handleOnUserPress(hrefAtr['data-author']); + const _handleOnLinkPress = event => { + if ((!event && !get(event, 'nativeEvent.data'), false)) { + return; + } + + try { + const data = JSON.parse(get(event, 'nativeEvent.data')); + + const { type, href, author, category, permlink, tag, proposal, videoHref } = data; + + switch (type) { + case '_external': + case 'markdown-external-link': + _handleBrowserLink(href); + break; + case 'markdown-author-link': + if (!handleOnUserPress) { + _handleOnUserPress(author); + } else { + handleOnUserPress(author); + } + break; + case 'markdown-post-link': + if (!handleOnPostPress) { + _handleOnPostPress(permlink, author); + } else { + handleOnPostPress(permlink, author); + } + break; + case 'markdown-tag-link': + _handleTagPress(tag); + break; + case 'markdown-witnesses-link': + break; + case 'markdown-proposal-link': + break; + case 'markdown-video-link': + break; + + default: + break; } - } else if (hrefAtr.class === 'markdown-post-link') { - if (!handleOnPostPress) { - _handleOnPostPress(hrefAtr['data-permlink'], hrefAtr['data-author']); - } else { - handleOnPostPress(hrefAtr['data-permlink']); - } - } else { - _handleBrowserLink(href); + } catch (error) {} + }; + + const _handleTagPress = tag => { + if (tag) { + navigation.navigate({ + routeName: ROUTES.SCREENS.SEARCH_RESULT, + params: { + tag, + }, + }); } }; const _handleBrowserLink = async url => { - if (!url) { - return; + if (url) { + Linking.canOpenURL(url).then(supported => { + if (supported) { + Linking.openURL(url); + } else { + Alert.alert(intl.formatMessage({ id: 'alert.failed_to_open' })); + } + }); } - - Linking.canOpenURL(url).then(supported => { - if (supported) { - Linking.openURL(url); - } else { - Alert.alert(intl.formatMessage({ id: 'alert.failed_to_open' })); - } - }); }; const _handleOnPostPress = (permlink, author) => { @@ -82,117 +119,116 @@ const PostBody = ({ } }; - const _hasParentTag = (node, name) => { - if (!node.parent) { - return false; - } - - if (node.name === name) { - return true; - } - - return _hasParentTag(node.parent, name); - }; - - const _alterNode = (node, isComment) => { - if (isComment) { - if (node.name === 'img') { - node.attribs.style = `max-width: ${WIDTH - 50}px; height: 100px; width: ${WIDTH - - 50}px; text-align: center;`; - } - } else if (node.name === 'a') { - node.attribs.style = 'text-decoration: underline'; - } - - if (node.name === 'img') { - node.attribs.style = 'text-align: center;'; - if (_hasParentTag(node, 'td')) { - node.attribs.style = `max-width: ${WIDTH / 2 - 20}px; `; - } - } - - if (node.name === 'div' && node.attribs && node.attribs.class) { - const _className = node.attribs.class; - - if (_className === 'pull-right') { - node.attribs.style = 'text-align: right; align-self: flex-end;'; - } - - if (_className === 'pull-left') { - node.attribs.style = 'text-align: left; align-self: flex-start;'; - } - - if (_className === 'text-justify') { - node.attribs.style = 'text-align: justify; text-justify: inter-word; letter-spacing: 0px;'; - } - - if (_className === 'phishy') { - node.attribs.style = 'color: red'; - } - } - }; - - const _alterData = node => { - if ( - node.type === 'text' && - node.data.includes('markdown-author-link') && - node.parent && - getParentsTagsRecursively(node.parent).includes('code') - ) { - return node.data.replace(/<[^>]*>/g, ''); - } - }; - - const _initialDimensions = isComment - ? { width: WIDTH - 50, height: 80 } - : { width: WIDTH, height: 216 }; - - const _customRenderer = { - a: (htmlAttribs, children, convertedCSSStyles, passProps) => { - if (passProps.parentWrapper === 'Text') { - return ( - _handleOnLinkPress(htmlAttribs['data-href'], htmlAttribs)} - > - {children} - - ); - } - return ( - _handleOnLinkPress(htmlAttribs['data-href'], htmlAttribs)} - > - {children} - - ); - }, - br: (htmlAttribs, children, passProps) => { - return {'\n'}; - }, - }; + const test = body.replace(/ _handleOnLinkPress(evt, href, hrefAtr)} - containerStyle={isComment ? styles.commentContainer : styles.container} - textSelectable={textSelectable} - tagsStyles={isComment ? { img: { height: 120 } } : styles} - ignoredTags={['script']} - debug={false} - staticContentMaxWidth={WIDTH - 33} - imagesInitialDimensions={_initialDimensions} - baseFontStyle={styles.text} - imagesMaxWidth={isComment ? WIDTH - 50 : WIDTH} - alterNode={e => _alterNode(e, isComment)} - alterData={e => _alterData(e)} - renderers={_customRenderer} - /> + + (isComment ? : )} + startInLoadingState={true} + onShouldStartLoadWithRequest={false} + scrollEnabled={false} + /> + ); }; -export default injectIntl(withNavigation(PostBody)); +const areEqual = (prevProps, nextProps) => { + if (prevProps.body !== nextProps.body) { + return true; + } + return false; +}; + +export default React.memo(injectIntl(withNavigation(PostBody)), areEqual); diff --git a/src/components/postView/view/postDisplayView.js b/src/components/postView/view/postDisplayView.js index 52c43b16b..6e3d1aa00 100644 --- a/src/components/postView/view/postDisplayView.js +++ b/src/components/postView/view/postDisplayView.js @@ -162,7 +162,9 @@ class PostDisplayView extends PureComponent { const isGetComment = scrollHeight + 300 > postHeight; const formatedTime = post && getTimeFromNow(post.created); - if (isGetComment && !isLoadedComments) this.setState({ isLoadedComments: true }); + if (isGetComment && !isLoadedComments) { + this.setState({ isLoadedComments: true }); + } if (isPostUnavailable) { return ( diff --git a/yarn.lock b/yarn.lock index d3fdc471e..c39b7898c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7458,6 +7458,13 @@ react-native-autocomplete-input@^4.1.0: resolved "https://registry.yarnpkg.com/react-native-autocomplete-input/-/react-native-autocomplete-input-4.1.0.tgz#979ece28d891b245ecb967b6d31f1f924445b8ab" integrity sha512-Yn4GulZ9F6tde74UUGZHdVFeYWVuL7+EbUZy6kt+QHrzMc5B4OuRop1FT4RyWLpvbySW/vvqYgj9LAmlzkuEqA== +react-native-autoheight-webview@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/react-native-autoheight-webview/-/react-native-autoheight-webview-1.2.2.tgz#5b65fdad47f552cd154fc9dd916aebc69492cc24" + integrity sha512-Did1IBXxY3fmGyGn7ioT3Osu9Uk7H+4M/rc6bal83cwEjxsX0lUDhzXOMNJh4D6Wh5eRne2r7zPz52tKjeLv8g== + dependencies: + prop-types "^15.7.2" + react-native-code-push@esteemapp/react-native-code-push: version "1000.0.0-beta" resolved "https://codeload.github.com/esteemapp/react-native-code-push/tar.gz/c07b7023c1212dc5d9231a0526a869d2501cb221"