mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Desktop: Wait for note to be saved before closing the app
This commit is contained in:
parent
93dccf62df
commit
75b28c46af
@ -4,6 +4,7 @@ const url = require('url');
|
||||
const path = require('path');
|
||||
const { dirname } = require('lib/path-utils');
|
||||
const fs = require('fs-extra');
|
||||
const { ipcMain } = require('electron');
|
||||
|
||||
class ElectronAppWrapper {
|
||||
|
||||
@ -15,6 +16,7 @@ class ElectronAppWrapper {
|
||||
this.willQuitApp_ = false;
|
||||
this.tray_ = null;
|
||||
this.buildDir_ = null;
|
||||
this.rendererProcessQuitReply_ = null;
|
||||
}
|
||||
|
||||
electronApp() {
|
||||
@ -112,9 +114,11 @@ class ElectronAppWrapper {
|
||||
// On Windows and Linux, the app is closed when the window is closed *except* if the tray icon is used. In which
|
||||
// case the app must be explicitly closed with Ctrl+Q or by right-clicking on the tray icon and selecting "Exit".
|
||||
|
||||
let isGoingToExit = false;
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
if (this.willQuitApp_) {
|
||||
this.win_ = null;
|
||||
isGoingToExit = true;
|
||||
} else {
|
||||
event.preventDefault();
|
||||
this.hide();
|
||||
@ -124,9 +128,39 @@ class ElectronAppWrapper {
|
||||
event.preventDefault();
|
||||
this.win_.hide();
|
||||
} else {
|
||||
this.win_ = null;
|
||||
isGoingToExit = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isGoingToExit) {
|
||||
if (!this.rendererProcessQuitReply_) {
|
||||
// If we haven't notified the renderer process yet, do it now
|
||||
// so that it can tell us if we can really close the app or not.
|
||||
// Search for "appClose" event for closing logic on renderer side.
|
||||
event.preventDefault();
|
||||
this.win_.webContents.send('appClose');
|
||||
} else {
|
||||
// If the renderer process has responded, check if we can close or not
|
||||
if (this.rendererProcessQuitReply_.canClose) {
|
||||
// Really quit the app
|
||||
this.rendererProcessQuitReply_ = null;
|
||||
this.win_ = null;
|
||||
} else {
|
||||
// Wait for renderer to finish task
|
||||
event.preventDefault();
|
||||
this.rendererProcessQuitReply_ = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('asynchronous-message', (event, message, args) => {
|
||||
if (message === 'appCloseReply') {
|
||||
// We got the response from the renderer process:
|
||||
// save the response and try quit again.
|
||||
this.rendererProcessQuitReply_ = args;
|
||||
this.electronApp_.quit();
|
||||
}
|
||||
});
|
||||
|
||||
// Let us register listeners on the window, so we can update the state
|
||||
|
@ -5,6 +5,7 @@ const { SideBar } = require('./SideBar.min.js');
|
||||
const { NoteList } = require('./NoteList.min.js');
|
||||
const { NoteText } = require('./NoteText.min.js');
|
||||
const NoteText2 = require('./NoteText2.js').default;
|
||||
const { stateUtils } = require('lib/reducer.js');
|
||||
const { PromptDialog } = require('./PromptDialog.min.js');
|
||||
const NoteContentPropertiesDialog = require('./NoteContentPropertiesDialog.js').default;
|
||||
const NotePropertiesDialog = require('./NotePropertiesDialog.min.js');
|
||||
@ -23,11 +24,25 @@ const VerticalResizer = require('./VerticalResizer.min');
|
||||
const PluginManager = require('lib/services/PluginManager');
|
||||
const TemplateUtils = require('lib/TemplateUtils');
|
||||
const EncryptionService = require('lib/services/EncryptionService');
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
|
||||
class MainScreenComponent extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
promptOptions: null,
|
||||
modalLayer: {
|
||||
visible: false,
|
||||
message: '',
|
||||
},
|
||||
notePropertiesDialogOptions: {},
|
||||
noteContentPropertiesDialogOptions: {},
|
||||
shareNoteDialogOptions: {},
|
||||
};
|
||||
|
||||
this.setupAppCloseHandling();
|
||||
|
||||
this.notePropertiesDialog_close = this.notePropertiesDialog_close.bind(this);
|
||||
this.noteContentPropertiesDialog_close = this.noteContentPropertiesDialog_close.bind(this);
|
||||
this.shareNoteDialog_close = this.shareNoteDialog_close.bind(this);
|
||||
@ -35,6 +50,37 @@ class MainScreenComponent extends React.Component {
|
||||
this.noteList_onDrag = this.noteList_onDrag.bind(this);
|
||||
}
|
||||
|
||||
setupAppCloseHandling() {
|
||||
this.waitForNotesSavedIID_ = null;
|
||||
|
||||
// This event is dispached from the main process when the app is about
|
||||
// to close. The renderer process must respond with the "appCloseReply"
|
||||
// and tell the main process whether the app can really be closed or not.
|
||||
// For example, it cannot be closed right away if a note is being saved.
|
||||
// If a note is being saved, we wait till it is saved and then call
|
||||
// "appCloseReply" again.
|
||||
ipcRenderer.on('appClose', () => {
|
||||
if (this.waitForNotesSavedIID_) clearInterval(this.waitForNotesSavedIID_);
|
||||
this.waitForNotesSavedIID_ = null;
|
||||
|
||||
ipcRenderer.send('asynchronous-message', 'appCloseReply', {
|
||||
canClose: !this.props.hasNotesBeingSaved,
|
||||
});
|
||||
|
||||
if (this.props.hasNotesBeingSaved) {
|
||||
this.waitForNotesSavedIID_ = setInterval(() => {
|
||||
if (!this.props.hasNotesBeingSaved) {
|
||||
clearInterval(this.waitForNotesSavedIID_);
|
||||
this.waitForNotesSavedIID_ = null;
|
||||
ipcRenderer.send('asynchronous-message', 'appCloseReply', {
|
||||
canClose: true,
|
||||
});
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sidebar_onDrag(event) {
|
||||
Setting.setValue('style.sidebar.width', this.props.sidebarWidth + event.deltaX);
|
||||
}
|
||||
@ -55,19 +101,6 @@ class MainScreenComponent extends React.Component {
|
||||
this.setState({ shareNoteDialogOptions: {} });
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({
|
||||
promptOptions: null,
|
||||
modalLayer: {
|
||||
visible: false,
|
||||
message: '',
|
||||
},
|
||||
notePropertiesDialogOptions: {},
|
||||
noteContentPropertiesDialogOptions: {},
|
||||
shareNoteDialogOptions: {},
|
||||
});
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps(newProps) {
|
||||
// Execute a command if any, and if we haven't already executed it
|
||||
if (newProps.windowCommand && newProps.windowCommand !== this.props.windowCommand) {
|
||||
@ -735,6 +768,7 @@ const mapStateToProps = state => {
|
||||
selectedNoteId: state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null,
|
||||
plugins: state.plugins,
|
||||
templates: state.templates,
|
||||
hasNotesBeingSaved: stateUtils.hasNotesBeingSaved(state),
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -22,9 +22,7 @@ const { bridge } = require('electron').remote.require('./bridge');
|
||||
async function initialize() {
|
||||
this.wcsTimeoutId_ = null;
|
||||
|
||||
bridge()
|
||||
.window()
|
||||
.on('resize', function() {
|
||||
bridge().window().on('resize', function() {
|
||||
if (this.wcsTimeoutId_) clearTimeout(this.wcsTimeoutId_);
|
||||
|
||||
this.wcsTimeoutId_ = setTimeout(() => {
|
||||
|
@ -627,7 +627,7 @@ class BaseApplication {
|
||||
initArgs = Object.assign(initArgs, extraFlags);
|
||||
|
||||
this.logger_.addTarget('file', { path: `${profileDir}/log.txt` });
|
||||
// if (Setting.value('env') === 'dev' && Setting.value('appType') === 'desktop') this.logger_.addTarget('console', { level: Logger.LEVEL_DEBUG });
|
||||
if (Setting.value('env') === 'dev' && Setting.value('appType') === 'desktop') this.logger_.addTarget('console', { level: Logger.LEVEL_DEBUG });
|
||||
this.logger_.setLevel(initArgs.logLevel);
|
||||
|
||||
reg.setLogger(this.logger_);
|
||||
|
@ -89,6 +89,13 @@ stateUtils.foldersOrder = function(stateSettings) {
|
||||
]);
|
||||
};
|
||||
|
||||
stateUtils.hasNotesBeingSaved = function(state) {
|
||||
for (const id in state.editorNoteStatuses) {
|
||||
if (state.editorNoteStatuses[id] === 'saving') return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
stateUtils.parentItem = function(state) {
|
||||
const t = state.notesParentType;
|
||||
let id = null;
|
||||
|
Loading…
Reference in New Issue
Block a user