diff --git a/ElectronClient/app/gui/NoteText.jsx b/ElectronClient/app/gui/NoteText.jsx index 49f8bae845..4143afd93f 100644 --- a/ElectronClient/app/gui/NoteText.jsx +++ b/ElectronClient/app/gui/NoteText.jsx @@ -1,20 +1,49 @@ -//const { BaseModel } = require('lib/base-model.js'); - const React = require('react'); const { connect } = require('react-redux'); +const { MdToHtml } = require('lib/markdown-utils.js'); class NoteTextComponent extends React.Component { componentWillMount() { + this.mdToHtml_ = new MdToHtml(); + this.setState({ note: null, + webviewReady: false, }); } + componentDidMount() { + this.webview_.addEventListener('dom-ready', this.webview_domReady.bind(this)); + } + + componentWillUnmount() { + this.mdToHtml_ = null; + this.webview_.addEventListener('dom-ready', this.webview_domReady.bind(this)); + } + componentWillReceiveProps(nextProps) { if (nextProps.noteId) this.reloadNote(); } + webview_domReady() { + this.setState({ + webviewReady: true, + }); + + this.webview_.openDevTools(); + + this.webview_.addEventListener('ipc-message', (event) => { + const msg = event.channel; + + if (msg.indexOf('checkboxclick:') === 0) { + const newBody = this.mdToHtml_.handleCheckboxClick(msg, this.state.note.body); + // this.saveOneProperty('body', newBody); + //if (onCheckboxChange) onCheckboxChange(newBody); + } + }) + } + async reloadNote() { const note = this.props.noteId ? await Note.load(this.props.noteId) : null; this.setState({ @@ -26,9 +55,27 @@ class NoteTextComponent extends React.Component { const note = this.state.note; const body = note ? note.body : 'no note'; + if (this.state.webviewReady) { + const mdOptions = { + onResourceLoaded: () => { + this.forceUpdate(); + }, + postMessageSyntax: 'ipcRenderer.sendToHost', + }; + + const html = this.mdToHtml_.render(note ? note.body : '', {}, mdOptions); + + this.webview_.send('setHtml', html); + } + + const webviewStyle = { + width: this.props.style.width, + height: this.props.style.height, + }; + return (
- { body } + this.webview_ = elem} />
); } diff --git a/ElectronClient/app/note-content.html b/ElectronClient/app/note-content.html new file mode 100644 index 0000000000..a2bc264602 --- /dev/null +++ b/ElectronClient/app/note-content.html @@ -0,0 +1,8 @@ +
+ + \ No newline at end of file diff --git a/ElectronClient/app/package-lock.json b/ElectronClient/app/package-lock.json index cb170fea40..9188d3532f 100644 --- a/ElectronClient/app/package-lock.json +++ b/ElectronClient/app/package-lock.json @@ -2041,6 +2041,11 @@ "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", "dev": true }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=" + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -2604,6 +2609,11 @@ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", "dev": true }, + "marked": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz", + "integrity": "sha1-ssbGGPzOzk74bE/Gy4p8v1rtqNc=" + }, "mem": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", diff --git a/ElectronClient/app/package.json b/ElectronClient/app/package.json index 51915cb17c..5bf9cf06e8 100644 --- a/ElectronClient/app/package.json +++ b/ElectronClient/app/package.json @@ -27,7 +27,9 @@ "dependencies": { "app-module-path": "^2.2.0", "fs-extra": "^4.0.2", + "html-entities": "^1.2.1", "lodash": "^4.17.4", + "marked": "^0.3.6", "moment": "^2.19.1", "node-fetch": "^1.7.3", "promise": "^8.0.1", diff --git a/ReactNativeClient/lib/components/note-body-viewer.js b/ReactNativeClient/lib/components/note-body-viewer.js index 587da735ea..93ec92547e 100644 --- a/ReactNativeClient/lib/components/note-body-viewer.js +++ b/ReactNativeClient/lib/components/note-body-viewer.js @@ -3,7 +3,7 @@ const { WebView, View, Linking } = require('react-native'); const { globalStyle } = require('lib/components/global-style.js'); const { Resource } = require('lib/models/resource.js'); const { reg } = require('lib/registry.js'); -const { markdownUtils, MdToHtml } = require('lib/markdown-utils.js'); +const { MdToHtml } = require('lib/markdown-utils.js'); class NoteBodyViewer extends Component { diff --git a/ReactNativeClient/lib/markdown-utils.js b/ReactNativeClient/lib/markdown-utils.js index 53549467d9..ea7ccf7dd2 100644 --- a/ReactNativeClient/lib/markdown-utils.js +++ b/ReactNativeClient/lib/markdown-utils.js @@ -9,12 +9,16 @@ class MdToHtml { render(body, style, options = null) { if (!options) options = {}; + if (!options.postMessageSyntax) options.postMessageSyntax = 'postMessage'; + + // ipcRenderer.sendToHost('pong') + const { Resource } = require('lib/models/resource.js'); const Entities = require('html-entities').AllHtmlEntities; const htmlentities = (new Entities()).encode; const { shim } = require('lib/shim.js'); - const loadResource = async function(id) { + const loadResource = async (id) => { const resource = await Resource.load(id); resource.base64 = await shim.readLocalFileBase64(Resource.fullPath(resource)); @@ -91,7 +95,7 @@ class MdToHtml { if (Resource.isResourceUrl(href)) { return '[Resource not yet supported: ' + htmlentities(text) + ']'; } else { - const js = "postMessage(" + JSON.stringify(href) + "); return false;"; + const js = options.postMessageSyntax + "(" + JSON.stringify(href) + "); return false;"; let output = "" + htmlentities(text) + ''; return output; } @@ -104,7 +108,7 @@ class MdToHtml { const resourceId = Resource.urlToId(href); if (!this.loadedResources_[resourceId]) { - this.loadResource(resourceId); + loadResource(resourceId); return ''; } @@ -131,7 +135,7 @@ class MdToHtml { let elementId = 1; while (html.indexOf('°°JOP°') >= 0) { html = html.replace(/°°JOP°CHECKBOX°([A-Z]+)°(\d+)°°/, function(v, type, index) { - const js = "postMessage('checkboxclick:" + type + ':' + index + "'); this.textContent = this.textContent == '☐' ? '☑' : '☐'; return false;"; + const js = options.postMessageSyntax + "('checkboxclick:" + type + ':' + index + "'); this.textContent = this.textContent == '☐' ? '☑' : '☐'; return false;"; return '' + (type == 'NOTICK' ? '☐' : '☑') + ''; }); } @@ -139,7 +143,8 @@ class MdToHtml { //let scriptHtml = ''; let scriptHtml = ''; - html = '' + html + scriptHtml + ''; + //html = '' + html + scriptHtml + ''; + html = '' + html + scriptHtml + ''; return html; } diff --git a/ReactNativeClient/lib/shim-init-node.js b/ReactNativeClient/lib/shim-init-node.js index 5c69614808..2633a3335a 100644 --- a/ReactNativeClient/lib/shim-init-node.js +++ b/ReactNativeClient/lib/shim-init-node.js @@ -55,6 +55,11 @@ function shimInit() { const nodeFetch = require('node-fetch'); + shim.readLocalFileBase64 = (path) => { + const data = fs.readFileSync(path); + return new Buffer(data).toString('base64'); + } + shim.fetch = async function(url, options = null) { if (!options) options = {}; if (!options.timeout) options.timeout = 1000 * 120; // ms diff --git a/ReactNativeClient/lib/shim.js b/ReactNativeClient/lib/shim.js index ed747930cf..d812aae637 100644 --- a/ReactNativeClient/lib/shim.js +++ b/ReactNativeClient/lib/shim.js @@ -13,7 +13,7 @@ shim.fetch = typeof fetch !== 'undefined' ? fetch : null; shim.FormData = typeof FormData !== 'undefined' ? FormData : null; shim.fs = null; shim.FileApiDriverLocal = null; -shim.readLocalFileBase64 = () => { throw new Error('Not implemented'); } +shim.readLocalFileBase64 = (path) => { throw new Error('Not implemented'); } shim.uploadBlob = () => { throw new Error('Not implemented'); } shim.setInterval = function(fn, interval) { return setInterval(fn, interval);