From c47c7cd925293dfa4d7eb00bf4fd19b0a9003908 Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Tue, 7 Jan 2020 08:34:20 +0100 Subject: [PATCH] basic markdown rendering --- package-lock.json | 157 ++++++++++++++++++++++++++++++++++- package.json | 3 +- src/component/uriBar/view.js | 2 +- src/page/file/view.js | 71 ++++++++++++++-- 4 files changed, 222 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 38906d1..2c85fd3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10332,9 +10332,9 @@ } }, "redux-persist": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-5.10.0.tgz", - "integrity": "sha512-sSJAzNq7zka3qVHKce1hbvqf0Vf5DuTVm7dr4GtsqQVOexnrvbV47RWFiPxQ8fscnyiuWyD2O92DOxPl0tGCRg==" + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz", + "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==" }, "redux-persist-filesystem-storage": { "version": "2.1.0", @@ -10887,6 +10887,157 @@ "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==" }, + "showdown": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-1.9.1.tgz", + "integrity": "sha512-9cGuS382HcvExtf5AHk7Cb4pAeQQ+h0eTr33V1mu+crYWV4KvWAw6el92bDrqGEk5d46Ai/fhbEUwqJ/mTCNEA==", + "requires": { + "yargs": "^14.2" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yargs": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.2.tgz", + "integrity": "sha512-/4ld+4VV5RnrynMhPZJ/ZpOCGSCeghMykZ3BhdFBDa9Wy/RH6uEGNWDJog+aUlq+9OM1CFTgtYRW5Is1Po9NOA==", + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.0" + } + }, + "yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", diff --git a/package.json b/package.json index 6a5c6d6..f4ae5db 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,8 @@ "redux-persist-transform-filter": "0.0.18", "redux-thunk": "^2.3.0", "rn-fetch-blob": "0.12.0", - "seedrandom": "3.0.3" + "seedrandom": "3.0.3", + "showdown": "1.9.1" }, "devDependencies": { "@babel/core": "^7.6.2", diff --git a/src/component/uriBar/view.js b/src/component/uriBar/view.js index a9ba9a3..80c25a7 100644 --- a/src/component/uriBar/view.js +++ b/src/component/uriBar/view.js @@ -89,7 +89,7 @@ class UriBar extends React.PureComponent { Keyboard.dismiss(); if (SEARCH_TYPES.SEARCH === type) { - this.setState({ currentValue: value }, () => this.setCaretPosition(value)); + this.setState({ currentValue: value }); updateSearchQuery(value); if (onSearchSubmitted) { diff --git a/src/page/file/view.js b/src/page/file/view.js index 53342ce..f21f53f 100644 --- a/src/page/file/view.js +++ b/src/page/file/view.js @@ -45,6 +45,8 @@ import Video from 'react-native-video'; import FileRewardsDriver from 'component/fileRewardsDriver'; import filePageStyle from 'styles/filePage'; import uriBarStyle from 'styles/uriBar'; +import RNFS from 'react-native-fs'; +import showdown from 'showdown'; import _ from 'lodash'; class FilePage extends React.PureComponent { @@ -58,6 +60,10 @@ class FilePage extends React.PureComponent { startTime = null; + webView = null; + + converter = null; + constructor(props) { super(props); this.state = { @@ -712,7 +718,7 @@ class FilePage extends React.PureComponent { } }; - openFile = (localFileUri, mediaType) => { + openFile = (localFileUri, mediaType, contentType) => { const { pushDrawerStack } = this.props; const isWebViewable = mediaType === 'text'; @@ -739,12 +745,55 @@ class FilePage extends React.PureComponent { { showWebView: true, }, - () => pushDrawerStack(Constants.DRAWER_ROUTE_FILE_VIEW), + () => { + pushDrawerStack(Constants.DRAWER_ROUTE_FILE_VIEW); + }, ); } } }; + handleWebViewLoad = () => { + const { contentType, fileInfo } = this.props; + const localFileUri = this.localUriForFileInfo(fileInfo); + if (this.webView && ['text/markdown', 'text/md'].includes(contentType)) { + RNFS.readFile(localFileUri, 'utf8').then(markdown => { + if (this.webView) { + if (!this.converter) { + this.converter = new showdown.Converter(); + } + const html = this.converter.makeHtml(markdown); + this.webView.injectJavaScript( + 'document.getElementById("content").innerHTML = \'' + + html.replace(/\n/g, '').replace(/'/g, "\\'") + + "'; true;", + ); + } + }); + } + }; + + buildWebViewSource = () => { + const { contentType, fileInfo } = this.props; + const localFileUri = this.localUriForFileInfo(fileInfo); + + if (['text/markdown', 'text/md'].includes(contentType)) { + const html = + '' + + '' + + ' ' + + ' ' + + ' ' + + ' ' + + '
' + + ' ' + + ''; + return { html }; + } + + return { uri: localFileUri }; + }; + render() { const { balance, @@ -780,7 +829,7 @@ class FilePage extends React.PureComponent { {isResolvingUri && ( - + {__('Loading decentralized data...')} @@ -893,7 +942,7 @@ class FilePage extends React.PureComponent { if (this.state.downloadPressed && canOpen && !this.state.autoOpened) { // automatically open a web viewable or image file after the download button is pressed - this.setState({ autoOpened: true }, () => this.openFile(localFileUri, mediaType)); + this.setState({ autoOpened: true }, () => this.openFile(localFileUri, mediaType, contentType)); } if (isChannel) { @@ -906,7 +955,17 @@ class FilePage extends React.PureComponent { {innerContent} {this.state.showWebView && isWebViewable && ( - + { + this.webView = ref; + }} + allowFileAccess + javaScriptEnabled + originWhiteList={['*']} + source={this.buildWebViewSource()} + style={filePageStyle.viewer} + onLoad={this.handleWebViewLoad} + /> )} {this.state.showImageViewer && ( this.openFile(localFileUri, mediaType)} + openFile={() => this.openFile(localFileUri, mediaType, contentType)} isPlayable={isPlayable} isViewable={isViewable} onFileActionPress={this.onFileDownloadButtonPressed}