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 { 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
|
||||||
|
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');
|
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];
|
||||||
|
Loading…
Reference in New Issue
Block a user