Desktop: Fixes #3835: Prevent crash in rare case when opening the config screen

pull/3856/head
Laurent Cozic 2020-09-29 08:40:14 +01:00
parent e0e4735b03
commit cf2442c5b2
5 changed files with 36 additions and 47 deletions

View File

@ -146,6 +146,7 @@ ElectronClient/gui/NoteList/NoteList.js
ElectronClient/gui/NoteListControls/commands/focusSearch.js ElectronClient/gui/NoteListControls/commands/focusSearch.js
ElectronClient/gui/NoteListControls/NoteListControls.js ElectronClient/gui/NoteListControls/NoteListControls.js
ElectronClient/gui/NoteListItem.js ElectronClient/gui/NoteListItem.js
ElectronClient/gui/NoteTextViewer.js
ElectronClient/gui/NoteToolbar/NoteToolbar.js ElectronClient/gui/NoteToolbar/NoteToolbar.js
ElectronClient/gui/OneDriveLoginScreen.js ElectronClient/gui/OneDriveLoginScreen.js
ElectronClient/gui/ResizableLayout/hooks/useLayoutItemSizes.js ElectronClient/gui/ResizableLayout/hooks/useLayoutItemSizes.js

1
.gitignore vendored
View File

@ -139,6 +139,7 @@ ElectronClient/gui/NoteList/NoteList.js
ElectronClient/gui/NoteListControls/commands/focusSearch.js ElectronClient/gui/NoteListControls/commands/focusSearch.js
ElectronClient/gui/NoteListControls/NoteListControls.js ElectronClient/gui/NoteListControls/NoteListControls.js
ElectronClient/gui/NoteListItem.js ElectronClient/gui/NoteListItem.js
ElectronClient/gui/NoteTextViewer.js
ElectronClient/gui/NoteToolbar/NoteToolbar.js ElectronClient/gui/NoteToolbar/NoteToolbar.js
ElectronClient/gui/OneDriveLoginScreen.js ElectronClient/gui/OneDriveLoginScreen.js
ElectronClient/gui/ResizableLayout/hooks/useLayoutItemSizes.js ElectronClient/gui/ResizableLayout/hooks/useLayoutItemSizes.js

View File

@ -9,6 +9,7 @@ import { useScrollHandler, usePrevious, cursorPositionToTextOffset, useRootSize
import Toolbar from './Toolbar'; import Toolbar from './Toolbar';
import styles_ from './styles'; import styles_ from './styles';
import { RenderedBody, defaultRenderedBody } from './utils/types'; import { RenderedBody, defaultRenderedBody } from './utils/types';
import NoteTextViewer from '../../../NoteTextViewer';
import Editor from './Editor'; import Editor from './Editor';
// @ts-ignore // @ts-ignore
@ -17,7 +18,6 @@ const { bridge } = require('electron').remote.require('./bridge');
const Note = require('lib/models/Note.js'); const Note = require('lib/models/Note.js');
const { clipboard } = require('electron'); const { clipboard } = require('electron');
const Setting = require('lib/models/Setting.js'); const Setting = require('lib/models/Setting.js');
const NoteTextViewer = require('../../../NoteTextViewer.min');
const shared = require('lib/components/shared/note-screen-shared.js'); const shared = require('lib/components/shared/note-screen-shared.js');
const Menu = bridge().Menu; const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem; const MenuItem = bridge().MenuItem;

View File

@ -2,7 +2,7 @@ const React = require('react');
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { themeStyle } = require('lib/theme'); const { themeStyle } = require('lib/theme');
const { _ } = require('lib/locale.js'); const { _ } = require('lib/locale.js');
const NoteTextViewer = require('./NoteTextViewer.min'); const NoteTextViewer = require('./NoteTextViewer').default;
const HelpButton = require('./HelpButton.min'); const HelpButton = require('./HelpButton.min');
const BaseModel = require('lib/BaseModel'); const BaseModel = require('lib/BaseModel');
const Revision = require('lib/models/Revision'); const Revision = require('lib/models/Revision');

View File

@ -1,15 +1,24 @@
const React = require('react'); import * as React from 'react';
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { reg } = require('lib/registry.js');
class NoteTextViewerComponent extends React.Component { interface Props {
constructor() { onDomReady: Function,
super(); onIpcMessage: Function,
viewerStyle: any,
}
this.initialized_ = false; class NoteTextViewerComponent extends React.Component<Props, any> {
this.domReady_ = false;
private initialized_:boolean = false;
private domReady_:boolean = false;
private webviewRef_:any;
private webviewListeners_:any = null;
constructor(props:any) {
super(props);
this.webviewRef_ = React.createRef(); this.webviewRef_ = React.createRef();
this.webviewListeners_ = null;
this.webview_domReady = this.webview_domReady.bind(this); this.webview_domReady = this.webview_domReady.bind(this);
this.webview_ipcMessage = this.webview_ipcMessage.bind(this); this.webview_ipcMessage = this.webview_ipcMessage.bind(this);
@ -17,20 +26,20 @@ class NoteTextViewerComponent extends React.Component {
this.webview_message = this.webview_message.bind(this); this.webview_message = this.webview_message.bind(this);
} }
webview_domReady(event) { webview_domReady(event:any) {
this.domReady_ = true; this.domReady_ = true;
if (this.props.onDomReady) this.props.onDomReady(event); if (this.props.onDomReady) this.props.onDomReady(event);
} }
webview_ipcMessage(event) { webview_ipcMessage(event:any) {
if (this.props.onIpcMessage) this.props.onIpcMessage(event); if (this.props.onIpcMessage) this.props.onIpcMessage(event);
} }
webview_load() { webview_load() {
this.webview_domReady(); this.webview_domReady({});
} }
webview_message(event) { webview_message(event:any) {
if (!event.data || event.data.target !== 'main') return; if (!event.data || event.data.target !== 'main') return;
const callName = event.data.name; const callName = event.data.name;
@ -78,7 +87,14 @@ class NoteTextViewerComponent extends React.Component {
wv.removeEventListener(n, fn); wv.removeEventListener(n, fn);
} }
try {
// It seems this can throw a cross-origin error in a way that is hard to replicate so just wrap
// it in try/catch since it's not critical.
// https://github.com/laurent22/joplin/issues/3835
this.webviewRef_.current.contentWindow.removeEventListener('message', this.webview_message); this.webviewRef_.current.contentWindow.removeEventListener('message', this.webview_message);
} catch (error) {
reg.logger().warn('Error destroying note viewer', error);
}
this.initialized_ = false; this.initialized_ = false;
this.domReady_ = false; this.domReady_ = false;
@ -107,7 +123,7 @@ class NoteTextViewerComponent extends React.Component {
// Wrap WebView functions // Wrap WebView functions
// ---------------------------------------------------------------- // ----------------------------------------------------------------
send(channel, arg0 = null, arg1 = null) { send(channel:string, arg0:any = null, arg1:any = null) {
const win = this.webviewRef_.current.contentWindow; const win = this.webviewRef_.current.contentWindow;
if (channel === 'setHtml') { if (channel === 'setHtml') {
@ -127,47 +143,18 @@ class NoteTextViewerComponent extends React.Component {
} }
} }
printToPDF() { // options, callback) {
// In Electron 4x, printToPDF is broken so need to use this hack:
// https://github.com/electron/electron/issues/16171#issuecomment-451090245
// return this.webviewRef_.current.printToPDF(options, callback);
// return this.webviewRef_.current.getWebContents().printToPDF(options, callback);
}
print() {
// In Electron 4x, print is broken so need to use this hack:
// https://github.com/electron/electron/issues/16219#issuecomment-451454948
// Note that this is not a perfect workaround since it means the options are ignored
// In particular it means that background images and colours won't be printed (printBackground property will be ignored)
// return this.webviewRef_.current.getWebContents().print({});
return this.webviewRef_.current.getWebContents().executeJavaScript('window.print()');
}
openDevTools() {
// return this.webviewRef_.current.openDevTools();
}
closeDevTools() {
// return this.webviewRef_.current.closeDevTools();
}
isDevToolsOpened() {
// return this.webviewRef_.current.isDevToolsOpened();
}
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// Wrap WebView functions (END) // Wrap WebView functions (END)
// ---------------------------------------------------------------- // ----------------------------------------------------------------
render() { render() {
console.info('RRRRRRRRRRRRRRRRRRRRRRR');
const viewerStyle = Object.assign({}, { border: 'none' }, this.props.viewerStyle); const viewerStyle = Object.assign({}, { border: 'none' }, this.props.viewerStyle);
return <iframe className="noteTextViewer" ref={this.webviewRef_} style={viewerStyle} src="gui/note-viewer/index.html"></iframe>; return <iframe className="noteTextViewer" ref={this.webviewRef_} style={viewerStyle} src="gui/note-viewer/index.html"></iframe>;
} }
} }
const mapStateToProps = state => { const mapStateToProps = (state:any) => {
return { return {
themeId: state.settings.theme, themeId: state.settings.theme,
}; };
@ -180,4 +167,4 @@ const NoteTextViewer = connect(
{ withRef: true } { withRef: true }
)(NoteTextViewerComponent); )(NoteTextViewerComponent);
module.exports = NoteTextViewer; export default NoteTextViewer;