1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Desktop: Fixes #1196: Moved webview to separate component to more precisely handle lifecycle events and precent uncessary reloading

This commit is contained in:
Laurent Cozic 2019-02-08 23:07:01 +00:00
parent da3589149d
commit 4326902683
3 changed files with 121 additions and 75 deletions

View File

@ -36,6 +36,7 @@ const ResourceFetcher = require('lib/services/ResourceFetcher');
const { toSystemSlashes, safeFilename } = require('lib/path-utils'); const { toSystemSlashes, safeFilename } = require('lib/path-utils');
const { clipboard } = require('electron'); const { clipboard } = require('electron');
const SearchEngine = require('lib/services/SearchEngine'); const SearchEngine = require('lib/services/SearchEngine');
const NoteTextViewer = require('./NoteTextViewer.min');
require('brace/mode/markdown'); require('brace/mode/markdown');
// https://ace.c9.io/build/kitchen-sink.html // https://ace.c9.io/build/kitchen-sink.html
@ -79,6 +80,8 @@ class NoteTextComponent extends React.Component {
localSearch: Object.assign({}, this.localSearchDefaultState), localSearch: Object.assign({}, this.localSearchDefaultState),
}; };
this.webviewRef_ = React.createRef();
this.lastLoadedNoteId_ = null; this.lastLoadedNoteId_ = null;
this.webviewListeners_ = null; this.webviewListeners_ = null;
@ -260,6 +263,8 @@ class NoteTextComponent extends React.Component {
} }
this.titleField_keyDown = this.titleField_keyDown.bind(this); this.titleField_keyDown = this.titleField_keyDown.bind(this);
this.webview_ipcMessage = this.webview_ipcMessage.bind(this);
this.webview_domReady = this.webview_domReady.bind(this);
} }
// Note: // Note:
@ -460,7 +465,7 @@ class NoteTextComponent extends React.Component {
// If we are loading nothing (noteId == null), make sure to // If we are loading nothing (noteId == null), make sure to
// set webviewReady to false too because the webview component // set webviewReady to false too because the webview component
// is going to be removed in render(). // is going to be removed in render().
const webviewReady = this.webview_ && this.state.webviewReady && (noteId || props.newNote); const webviewReady = !!this.webviewRef_.current && this.state.webviewReady && (!!noteId || !!props.newNote);
// Scroll back to top when loading new note // Scroll back to top when loading new note
if (loadingNewNote) { if (loadingNewNote) {
@ -765,7 +770,7 @@ class NoteTextComponent extends React.Component {
}); });
} }
if (this.webview_) this.webview_.send('setPercentScroll', p); if (this.webviewRef_.current) this.webviewRef_.current.wrappedInstance.send('setPercentScroll', p);
} }
editor_scroll() { editor_scroll() {
@ -781,7 +786,7 @@ class NoteTextComponent extends React.Component {
} }
webview_domReady() { webview_domReady() {
if (!this.webview_) return; if (!this.webviewRef_.current) return;
this.setState({ this.setState({
webviewReady: true, webviewReady: true,
@ -790,19 +795,6 @@ class NoteTextComponent extends React.Component {
// if (Setting.value('env') === 'dev') this.webview_.openDevTools(); // if (Setting.value('env') === 'dev') this.webview_.openDevTools();
} }
webview_ref(element) {
if (this.webview_) {
if (this.webview_ === element) return;
this.destroyWebview();
}
if (!element) {
this.destroyWebview();
} else {
this.initWebview(element);
}
}
editor_ref(element) { editor_ref(element) {
if (this.editor_ === element) return; if (this.editor_ === element) return;
@ -875,35 +867,6 @@ class NoteTextComponent extends React.Component {
} }
} }
initWebview(wv) {
if (!this.webviewListeners_) {
this.webviewListeners_ = {
'dom-ready': this.webview_domReady.bind(this),
'ipc-message': this.webview_ipcMessage.bind(this),
};
}
for (let n in this.webviewListeners_) {
if (!this.webviewListeners_.hasOwnProperty(n)) continue;
const fn = this.webviewListeners_[n];
wv.addEventListener(n, fn);
}
this.webview_ = wv;
}
destroyWebview() {
if (!this.webview_) return;
for (let n in this.webviewListeners_) {
if (!this.webviewListeners_.hasOwnProperty(n)) continue;
const fn = this.webviewListeners_[n];
this.webview_.removeEventListener(n, fn);
}
this.webview_ = null;
}
aceEditor_change(body) { aceEditor_change(body) {
shared.noteComponent_change(this, 'body', body); shared.noteComponent_change(this, 'body', body);
this.scheduleHtmlUpdate(); this.scheduleHtmlUpdate();
@ -1091,7 +1054,7 @@ class NoteTextComponent extends React.Component {
} }
printTo_(target, options) { printTo_(target, options) {
if (this.props.selectedNoteIds.length !== 1 || !this.webview_) { if (this.props.selectedNoteIds.length !== 1 || !this.webviewRef_.current) {
throw new Error(_('Only one note can be printed or exported to PDF at a time.')); throw new Error(_('Only one note can be printed or exported to PDF at a time.'));
} }
@ -1113,7 +1076,7 @@ class NoteTextComponent extends React.Component {
setTimeout(() => { setTimeout(() => {
if (target === 'pdf') { if (target === 'pdf') {
this.webview_.printToPDF({}, (error, data) => { this.webviewRef_.current.wrappedInstance.printToPDF({}, (error, data) => {
restoreSettings(); restoreSettings();
if (error) { if (error) {
@ -1123,7 +1086,7 @@ class NoteTextComponent extends React.Component {
} }
}); });
} else if (target === 'printer') { } else if (target === 'printer') {
this.webview_.print(); this.webviewRef_.current.wrappedInstance.print();
restoreSettings(); restoreSettings();
} }
}, 100); }, 100);
@ -1637,7 +1600,7 @@ class NoteTextComponent extends React.Component {
if (this.props.selectedNoteIds.length > 1) { if (this.props.selectedNoteIds.length > 1) {
return this.renderMultiNotes(rootStyle); return this.renderMultiNotes(rootStyle);
} else if (!note || !!note.encryption_applied || (note && !this.props.newNote && this.props.noteId && note.id !== this.props.noteId)) { // note.id !== props.noteId is when the note has not been loaded yet, and the previous one is still in the state } else if (!note || !!note.encryption_applied) { //|| (note && !this.props.newNote && this.props.noteId && note.id !== this.props.noteId)) { // note.id !== props.noteId is when the note has not been loaded yet, and the previous one is still in the state
return this.renderNoNotes(rootStyle); return this.renderNoNotes(rootStyle);
} }
@ -1741,7 +1704,7 @@ class NoteTextComponent extends React.Component {
const htmlHasChanged = this.lastSetHtml_ !== html; const htmlHasChanged = this.lastSetHtml_ !== html;
if (htmlHasChanged) { if (htmlHasChanged) {
let options = {codeTheme: theme.codeThemeCss}; let options = {codeTheme: theme.codeThemeCss};
this.webview_.send('setHtml', html, options); this.webviewRef_.current.wrappedInstance.send('setHtml', html, options);
this.lastSetHtml_ = html; this.lastSetHtml_ = html;
} }
@ -1767,7 +1730,7 @@ class NoteTextComponent extends React.Component {
if (htmlHasChanged || keywordHash !== this.lastSetMarkers_ || !ObjectUtils.fieldsEqual(this.lastSetMarkersOptions_, markerOptions)) { if (htmlHasChanged || keywordHash !== this.lastSetMarkers_ || !ObjectUtils.fieldsEqual(this.lastSetMarkersOptions_, markerOptions)) {
this.lastSetMarkers_ = keywordHash; this.lastSetMarkers_ = keywordHash;
this.lastSetMarkersOptions_ = Object.assign({}, markerOptions); this.lastSetMarkersOptions_ = Object.assign({}, markerOptions);
this.webview_.send('setMarkers', keywords, markerOptions); this.webviewRef_.current.wrappedInstance.send('setMarkers', keywords, markerOptions);
} }
} }
@ -1799,32 +1762,13 @@ class NoteTextComponent extends React.Component {
const titleBarDate = <span style={Object.assign({}, theme.textStyle, {color: theme.colorFaded})}>{time.formatMsToLocal(note.user_updated_time)}</span> const titleBarDate = <span style={Object.assign({}, theme.textStyle, {color: theme.colorFaded})}>{time.formatMsToLocal(note.user_updated_time)}</span>
const viewer = <webview const viewer = <NoteTextViewer
style={viewerStyle} ref={this.webviewRef_}
preload="gui/note-viewer/preload.js" viewerStyle={viewerStyle}
src="gui/note-viewer/index.html" onDomReady={this.webview_domReady}
webpreferences="contextIsolation" onIpcMessage={this.webview_ipcMessage}
ref={(elem) => { this.webview_ref(elem); } }
/> />
// const markers = [{
// startRow: 2,
// startCol: 3,
// endRow: 2,
// endCol: 6,
// type: 'text',
// className: 'test-marker'
// }];
// markers={markers}
// editorProps={{$useWorker: false}}
// #note-editor .test-marker {
// background-color: red;
// color: yellow;
// position: absolute;
// }
const editorRootStyle = Object.assign({}, editorStyle); const editorRootStyle = Object.assign({}, editorStyle);
delete editorRootStyle.width; delete editorRootStyle.width;
delete editorRootStyle.height; delete editorRootStyle.height;
@ -1846,6 +1790,7 @@ class NoteTextComponent extends React.Component {
showPrintMargin={false} showPrintMargin={false}
onSelectionChange={this.aceEditor_selectionChange} onSelectionChange={this.aceEditor_selectionChange}
onFocus={this.aceEditor_focus} onFocus={this.aceEditor_focus}
readOnly={visiblePanes.indexOf('editor') < 0}
// Disable warning: "Automatically scrolling cursor into view after // Disable warning: "Automatically scrolling cursor into view after
// selection change this will be disabled in the next version set // selection change this will be disabled in the next version set

View File

@ -0,0 +1,99 @@
const React = require('react');
const { connect } = require('react-redux');
const { themeStyle } = require('../theme.js');
const { _ } = require('lib/locale.js');
class NoteTextViewerComponent extends React.Component {
constructor() {
super();
this.initialized_ = false;
this.webviewRef_ = React.createRef();
this.webviewListeners_ = null;
this.webview_domReady = this.webview_domReady.bind(this);
this.webview_ipcMessage = this.webview_ipcMessage.bind(this);
}
webview_domReady(event) {
this.props.onDomReady(event);
}
webview_ipcMessage(event) {
this.props.onIpcMessage(event);
}
initWebview() {
const wv = this.webviewRef_.current;
if (!this.webviewListeners_) {
this.webviewListeners_ = {
'dom-ready': this.webview_domReady.bind(this),
'ipc-message': this.webview_ipcMessage.bind(this),
};
}
for (let n in this.webviewListeners_) {
if (!this.webviewListeners_.hasOwnProperty(n)) continue;
const fn = this.webviewListeners_[n];
wv.addEventListener(n, fn);
}
}
destroyWebview() {
const wv = this.webviewRef_.current;
if (!wv || !this.initialized_) return;
for (let n in this.webviewListeners_) {
if (!this.webviewListeners_.hasOwnProperty(n)) continue;
const fn = this.webviewListeners_[n];
wv.removeEventListener(n, fn);
}
}
componentDidUpdate() {
if (!this.initialized_ && this.webviewRef_.current) {
this.initWebview();
this.initialized_ = true;
}
}
componentWillUnmount() {
this.destroyWebview();
}
send(channel, arg0 = null, arg1 = null, arg2 = null, arg3 = null) {
return this.webviewRef_.current.send(channel, arg0, arg1, arg2, arg3);
}
printToPDF(options, callback) {
return this.webviewRef_.current.printToPDF(options, callback);
}
print(options = {}) {
return this.webviewRef_.current.print(options);
}
render() {
return <webview
ref={this.webviewRef_}
style={this.props.viewerStyle}
preload="gui/note-viewer/preload.js"
src="gui/note-viewer/index.html"
webpreferences="contextIsolation"
/>
}
}
const mapStateToProps = (state) => {
return {
theme: state.settings.theme,
};
};
const NoteTextViewer = connect(mapStateToProps, null, null, { withRef: true })(NoteTextViewerComponent);
module.exports = NoteTextViewer;

View File

@ -19,6 +19,8 @@ class WelcomeUtils {
const tempDir = Setting.value('resourceDir'); const tempDir = Setting.value('resourceDir');
// TODO: Update mobile root.js // TODO: Update mobile root.js
// TODO: Update CLI
// TODO: Test CLI
for (let i = 0; i < folderAssets.length; i++) { for (let i = 0; i < folderAssets.length; i++) {
const folderAsset = folderAssets[i]; const folderAsset = folderAssets[i];