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:
parent
da3589149d
commit
4326902683
@ -36,6 +36,7 @@ const ResourceFetcher = require('lib/services/ResourceFetcher');
|
||||
const { toSystemSlashes, safeFilename } = require('lib/path-utils');
|
||||
const { clipboard } = require('electron');
|
||||
const SearchEngine = require('lib/services/SearchEngine');
|
||||
const NoteTextViewer = require('./NoteTextViewer.min');
|
||||
|
||||
require('brace/mode/markdown');
|
||||
// https://ace.c9.io/build/kitchen-sink.html
|
||||
@ -79,6 +80,8 @@ class NoteTextComponent extends React.Component {
|
||||
localSearch: Object.assign({}, this.localSearchDefaultState),
|
||||
};
|
||||
|
||||
this.webviewRef_ = React.createRef();
|
||||
|
||||
this.lastLoadedNoteId_ = null;
|
||||
|
||||
this.webviewListeners_ = null;
|
||||
@ -260,6 +263,8 @@ class NoteTextComponent extends React.Component {
|
||||
}
|
||||
|
||||
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:
|
||||
@ -460,7 +465,7 @@ class NoteTextComponent extends React.Component {
|
||||
// If we are loading nothing (noteId == null), make sure to
|
||||
// set webviewReady to false too because the webview component
|
||||
// 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
|
||||
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() {
|
||||
@ -781,7 +786,7 @@ class NoteTextComponent extends React.Component {
|
||||
}
|
||||
|
||||
webview_domReady() {
|
||||
if (!this.webview_) return;
|
||||
if (!this.webviewRef_.current) return;
|
||||
|
||||
this.setState({
|
||||
webviewReady: true,
|
||||
@ -790,19 +795,6 @@ class NoteTextComponent extends React.Component {
|
||||
// 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) {
|
||||
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) {
|
||||
shared.noteComponent_change(this, 'body', body);
|
||||
this.scheduleHtmlUpdate();
|
||||
@ -1091,7 +1054,7 @@ class NoteTextComponent extends React.Component {
|
||||
}
|
||||
|
||||
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.'));
|
||||
}
|
||||
|
||||
@ -1113,7 +1076,7 @@ class NoteTextComponent extends React.Component {
|
||||
|
||||
setTimeout(() => {
|
||||
if (target === 'pdf') {
|
||||
this.webview_.printToPDF({}, (error, data) => {
|
||||
this.webviewRef_.current.wrappedInstance.printToPDF({}, (error, data) => {
|
||||
restoreSettings();
|
||||
|
||||
if (error) {
|
||||
@ -1123,7 +1086,7 @@ class NoteTextComponent extends React.Component {
|
||||
}
|
||||
});
|
||||
} else if (target === 'printer') {
|
||||
this.webview_.print();
|
||||
this.webviewRef_.current.wrappedInstance.print();
|
||||
restoreSettings();
|
||||
}
|
||||
}, 100);
|
||||
@ -1637,7 +1600,7 @@ class NoteTextComponent extends React.Component {
|
||||
|
||||
if (this.props.selectedNoteIds.length > 1) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1741,7 +1704,7 @@ class NoteTextComponent extends React.Component {
|
||||
const htmlHasChanged = this.lastSetHtml_ !== html;
|
||||
if (htmlHasChanged) {
|
||||
let options = {codeTheme: theme.codeThemeCss};
|
||||
this.webview_.send('setHtml', html, options);
|
||||
this.webviewRef_.current.wrappedInstance.send('setHtml', html, options);
|
||||
this.lastSetHtml_ = html;
|
||||
}
|
||||
|
||||
@ -1767,7 +1730,7 @@ class NoteTextComponent extends React.Component {
|
||||
if (htmlHasChanged || keywordHash !== this.lastSetMarkers_ || !ObjectUtils.fieldsEqual(this.lastSetMarkersOptions_, markerOptions)) {
|
||||
this.lastSetMarkers_ = keywordHash;
|
||||
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 viewer = <webview
|
||||
style={viewerStyle}
|
||||
preload="gui/note-viewer/preload.js"
|
||||
src="gui/note-viewer/index.html"
|
||||
webpreferences="contextIsolation"
|
||||
ref={(elem) => { this.webview_ref(elem); } }
|
||||
const viewer = <NoteTextViewer
|
||||
ref={this.webviewRef_}
|
||||
viewerStyle={viewerStyle}
|
||||
onDomReady={this.webview_domReady}
|
||||
onIpcMessage={this.webview_ipcMessage}
|
||||
/>
|
||||
|
||||
// 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);
|
||||
delete editorRootStyle.width;
|
||||
delete editorRootStyle.height;
|
||||
@ -1846,6 +1790,7 @@ class NoteTextComponent extends React.Component {
|
||||
showPrintMargin={false}
|
||||
onSelectionChange={this.aceEditor_selectionChange}
|
||||
onFocus={this.aceEditor_focus}
|
||||
readOnly={visiblePanes.indexOf('editor') < 0}
|
||||
|
||||
// Disable warning: "Automatically scrolling cursor into view after
|
||||
// selection change this will be disabled in the next version set
|
||||
|
99
ElectronClient/app/gui/NoteTextViewer.jsx
Normal file
99
ElectronClient/app/gui/NoteTextViewer.jsx
Normal 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;
|
@ -19,6 +19,8 @@ class WelcomeUtils {
|
||||
const tempDir = Setting.value('resourceDir');
|
||||
|
||||
// TODO: Update mobile root.js
|
||||
// TODO: Update CLI
|
||||
// TODO: Test CLI
|
||||
|
||||
for (let i = 0; i < folderAssets.length; i++) {
|
||||
const folderAsset = folderAssets[i];
|
||||
|
Loading…
Reference in New Issue
Block a user