1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00

Desktop: Ask to start in safe mode when the application has crashed

This commit is contained in:
Laurent Cozic 2022-04-20 17:34:58 +01:00
parent bd917ae09c
commit d9a4a9cb30
13 changed files with 64 additions and 25 deletions

View File

@ -808,6 +808,9 @@ packages/app-desktop/services/plugins/hooks/useViewIsReady.js.map
packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.d.ts packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.d.ts
packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.js packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.js
packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.js.map packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.js.map
packages/app-desktop/services/restart.d.ts
packages/app-desktop/services/restart.js
packages/app-desktop/services/restart.js.map
packages/app-desktop/services/share/invitationRespond.d.ts packages/app-desktop/services/share/invitationRespond.d.ts
packages/app-desktop/services/share/invitationRespond.js packages/app-desktop/services/share/invitationRespond.js
packages/app-desktop/services/share/invitationRespond.js.map packages/app-desktop/services/share/invitationRespond.js.map

3
.gitignore vendored
View File

@ -798,6 +798,9 @@ packages/app-desktop/services/plugins/hooks/useViewIsReady.js.map
packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.d.ts packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.d.ts
packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.js packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.js
packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.js.map packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.js.map
packages/app-desktop/services/restart.d.ts
packages/app-desktop/services/restart.js
packages/app-desktop/services/restart.js.map
packages/app-desktop/services/share/invitationRespond.d.ts packages/app-desktop/services/share/invitationRespond.d.ts
packages/app-desktop/services/share/invitationRespond.js packages/app-desktop/services/share/invitationRespond.js
packages/app-desktop/services/share/invitationRespond.js.map packages/app-desktop/services/share/invitationRespond.js.map

View File

@ -192,7 +192,7 @@ export default class ElectronAppWrapper {
// We got the response from the renderer process: // We got the response from the renderer process:
// save the response and try quit again. // save the response and try quit again.
this.rendererProcessQuitReply_ = args; this.rendererProcessQuitReply_ = args;
this.electronApp_.quit(); this.quit();
} }
}); });
@ -253,7 +253,7 @@ export default class ElectronAppWrapper {
}); });
} }
async quit() { quit() {
this.electronApp_.quit(); this.electronApp_.quit();
} }
@ -325,7 +325,7 @@ export default class ElectronAppWrapper {
if (!gotTheLock) { if (!gotTheLock) {
// Another instance is already running - exit // Another instance is already running - exit
this.electronApp_.quit(); this.quit();
return true; return true;
} }
@ -362,7 +362,7 @@ export default class ElectronAppWrapper {
}); });
this.electronApp_.on('window-all-closed', () => { this.electronApp_.on('window-all-closed', () => {
this.electronApp_.quit(); this.quit();
}); });
this.electronApp_.on('activate', () => { this.electronApp_.on('activate', () => {

View File

@ -324,6 +324,15 @@ class Application extends BaseApplication {
}, 500); }, 500);
} }
private crashDetectionHandler() {
if (!Setting.value('wasClosedSuccessfully')) {
const answer = confirm(_('The application did not close properly. Would you like to start in safe mode?'));
Setting.setValue('isSafeMode', !!answer);
}
Setting.setValue('wasClosedSuccessfully', false);
}
public async start(argv: string[]): Promise<any> { public async start(argv: string[]): Promise<any> {
// If running inside a package, the command line, instead of being "node.exe <path> <flags>" is "joplin.exe <flags>" so // If running inside a package, the command line, instead of being "node.exe <path> <flags>" is "joplin.exe <flags>" so
// insert an extra argument so that they can be processed in a consistent way everywhere. // insert an extra argument so that they can be processed in a consistent way everywhere.
@ -331,6 +340,8 @@ class Application extends BaseApplication {
argv = await super.start(argv); argv = await super.start(argv);
this.crashDetectionHandler();
await this.applySettingsSideEffects(); await this.applySettingsSideEffects();
if (Setting.value('sync.upgradeState') === Setting.SYNC_UPGRADE_STATE_MUST_DO) { if (Setting.value('sync.upgradeState') === Setting.SYNC_UPGRADE_STATE_MUST_DO) {

View File

@ -2,7 +2,7 @@ import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/
import Setting from '@joplin/lib/models/Setting'; import Setting from '@joplin/lib/models/Setting';
import { saveProfileConfig } from '@joplin/lib/services/profileConfig'; import { saveProfileConfig } from '@joplin/lib/services/profileConfig';
import { ProfileConfig } from '@joplin/lib/services/profileConfig/types'; import { ProfileConfig } from '@joplin/lib/services/profileConfig/types';
import bridge from '../services/bridge'; import restart from '../services/restart';
export const declaration: CommandDeclaration = { export const declaration: CommandDeclaration = {
name: 'switchProfile', name: 'switchProfile',
@ -20,7 +20,7 @@ export const runtime = (): CommandRuntime => {
}; };
await saveProfileConfig(`${Setting.value('rootProfileDir')}/profiles.json`, newConfig); await saveProfileConfig(`${Setting.value('rootProfileDir')}/profiles.json`, newConfig);
bridge().restart(false); await restart(false);
}, },
}; };
}; };

View File

@ -1,7 +1,7 @@
import { _ } from '@joplin/lib/locale'; import { _ } from '@joplin/lib/locale';
import Setting from '@joplin/lib/models/Setting'; import Setting from '@joplin/lib/models/Setting';
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService'; import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
import bridge from '../services/bridge'; import restart from '../services/restart';
export const declaration: CommandDeclaration = { export const declaration: CommandDeclaration = {
name: 'toggleSafeMode', name: 'toggleSafeMode',
@ -14,7 +14,7 @@ export const runtime = (): CommandRuntime => {
enabled = enabled !== null ? enabled : !Setting.value('isSafeMode'); enabled = enabled !== null ? enabled : !Setting.value('isSafeMode');
Setting.setValue('isSafeMode', enabled); Setting.setValue('isSafeMode', enabled);
await Setting.saveAll(); await Setting.saveAll();
bridge().restart(); await restart();
}, },
}; };
}; };

View File

@ -14,6 +14,7 @@ const pathUtils = require('@joplin/lib/path-utils');
import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry'; import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry';
const shared = require('@joplin/lib/components/shared/config-shared.js'); const shared = require('@joplin/lib/components/shared/config-shared.js');
import ClipperConfigScreen from '../ClipperConfigScreen'; import ClipperConfigScreen from '../ClipperConfigScreen';
import restart from '../../services/restart';
const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen'); const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen');
const settingKeyToControl: any = { const settingKeyToControl: any = {
@ -72,12 +73,12 @@ class ConfigScreenComponent extends React.Component<any, any> {
if (!confirm('This cannot be undone. Do you want to continue?')) return; if (!confirm('This cannot be undone. Do you want to continue?')) return;
Setting.setValue('sync.startupOperation', SyncStartupOperation.ClearLocalSyncState); Setting.setValue('sync.startupOperation', SyncStartupOperation.ClearLocalSyncState);
await Setting.saveAll(); await Setting.saveAll();
bridge().restart(); await restart();
} else if (key === 'sync.clearLocalDataButton') { } else if (key === 'sync.clearLocalDataButton') {
if (!confirm('This cannot be undone. Do you want to continue?')) return; if (!confirm('This cannot be undone. Do you want to continue?')) return;
Setting.setValue('sync.startupOperation', SyncStartupOperation.ClearLocalData); Setting.setValue('sync.startupOperation', SyncStartupOperation.ClearLocalData);
await Setting.saveAll(); await Setting.saveAll();
bridge().restart(); await restart();
} else if (key === 'sync.openSyncWizard') { } else if (key === 'sync.openSyncWizard') {
this.props.dispatch({ this.props.dispatch({
type: 'DIALOG_OPEN', type: 'DIALOG_OPEN',
@ -632,7 +633,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
private async restartApp() { private async restartApp() {
await Setting.saveAll(); await Setting.saveAll();
bridge().restart(); await restart();
} }
private async checkNeedRestart() { private async checkNeedRestart() {

View File

@ -2,7 +2,7 @@ import * as React from 'react';
import versionInfo from '@joplin/lib/versionInfo'; import versionInfo from '@joplin/lib/versionInfo';
import PluginService from '@joplin/lib/services/plugins/PluginService'; import PluginService from '@joplin/lib/services/plugins/PluginService';
import Setting from '@joplin/lib/models/Setting'; import Setting from '@joplin/lib/models/Setting';
import bridge from '../services/bridge'; import restart from '../services/restart';
const packageInfo = require('../packageInfo.js'); const packageInfo = require('../packageInfo.js');
const ipcRenderer = require('electron').ipcRenderer; const ipcRenderer = require('electron').ipcRenderer;
@ -75,7 +75,7 @@ export default class ErrorBoundary extends React.Component<Props, State> {
const safeMode_click = async () => { const safeMode_click = async () => {
Setting.setValue('isSafeMode', true); Setting.setValue('isSafeMode', true);
await Setting.saveAll(); await Setting.saveAll();
bridge().restart(); await restart();
}; };
try { try {

View File

@ -40,6 +40,7 @@ import { showMissingMasterKeyMessage } from '@joplin/lib/services/e2ee/utils';
import { MasterKeyEntity } from '@joplin/lib/services/e2ee/types'; import { MasterKeyEntity } from '@joplin/lib/services/e2ee/types';
import commands from './commands/index'; import commands from './commands/index';
import invitationRespond from '../../services/share/invitationRespond'; import invitationRespond from '../../services/share/invitationRespond';
import restart from '../../services/restart';
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { PromptDialog } = require('../PromptDialog.min.js'); const { PromptDialog } = require('../PromptDialog.min.js');
const NotePropertiesDialog = require('../NotePropertiesDialog.min.js'); const NotePropertiesDialog = require('../NotePropertiesDialog.min.js');
@ -267,18 +268,22 @@ class MainScreenComponent extends React.Component<Props, State> {
if (this.waitForNotesSavedIID_) shim.clearInterval(this.waitForNotesSavedIID_); if (this.waitForNotesSavedIID_) shim.clearInterval(this.waitForNotesSavedIID_);
this.waitForNotesSavedIID_ = null; this.waitForNotesSavedIID_ = null;
ipcRenderer.send('asynchronous-message', 'appCloseReply', { const sendCanClose = async (canClose: boolean) => {
canClose: !this.props.hasNotesBeingSaved, if (canClose) {
}); Setting.setValue('wasClosedSuccessfully', true);
await Setting.saveAll();
}
ipcRenderer.send('asynchronous-message', 'appCloseReply', { canClose });
};
await sendCanClose(!this.props.hasNotesBeingSaved);
if (this.props.hasNotesBeingSaved) { if (this.props.hasNotesBeingSaved) {
this.waitForNotesSavedIID_ = shim.setInterval(() => { this.waitForNotesSavedIID_ = shim.setInterval(() => {
if (!this.props.hasNotesBeingSaved) { if (!this.props.hasNotesBeingSaved) {
shim.clearInterval(this.waitForNotesSavedIID_); shim.clearInterval(this.waitForNotesSavedIID_);
this.waitForNotesSavedIID_ = null; this.waitForNotesSavedIID_ = null;
ipcRenderer.send('asynchronous-message', 'appCloseReply', { void sendCanClose(true);
canClose: true,
});
} }
}, 50); }, 50);
} }
@ -557,13 +562,13 @@ class MainScreenComponent extends React.Component<Props, State> {
const onRestartAndUpgrade = async () => { const onRestartAndUpgrade = async () => {
Setting.setValue('sync.upgradeState', Setting.SYNC_UPGRADE_STATE_MUST_DO); Setting.setValue('sync.upgradeState', Setting.SYNC_UPGRADE_STATE_MUST_DO);
await Setting.saveAll(); await Setting.saveAll();
bridge().restart(); await restart();
}; };
const onDisableSafeModeAndRestart = async () => { const onDisableSafeModeAndRestart = async () => {
Setting.setValue('isSafeMode', false); Setting.setValue('isSafeMode', false);
await Setting.saveAll(); await Setting.saveAll();
bridge().restart(); await restart();
}; };
const onInvitationRespond = async (shareUserId: string, folderId: string, masterKey: MasterKeyEntity, accept: boolean) => { const onInvitationRespond = async (shareUserId: string, folderId: string, masterKey: MasterKeyEntity, accept: boolean) => {

View File

@ -2,7 +2,7 @@ import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/
import { _ } from '@joplin/lib/locale'; import { _ } from '@joplin/lib/locale';
import { createNewProfile, saveProfileConfig } from '@joplin/lib/services/profileConfig'; import { createNewProfile, saveProfileConfig } from '@joplin/lib/services/profileConfig';
import Setting from '@joplin/lib/models/Setting'; import Setting from '@joplin/lib/models/Setting';
import bridge from '../../../services/bridge'; import restart from '../../../services/restart';
export const declaration: CommandDeclaration = { export const declaration: CommandDeclaration = {
name: 'addProfile', name: 'addProfile',
@ -22,7 +22,7 @@ export const runtime = (comp: any): CommandRuntime => {
const { newConfig, newProfile } = createNewProfile(context.state.profileConfig, answer); const { newConfig, newProfile } = createNewProfile(context.state.profileConfig, answer);
newConfig.currentProfileId = newProfile.id; newConfig.currentProfileId = newProfile.id;
await saveProfileConfig(`${Setting.value('rootProfileDir')}/profiles.json`, newConfig); await saveProfileConfig(`${Setting.value('rootProfileDir')}/profiles.json`, newConfig);
bridge().restart(false); await restart(false);
} }
comp.setState({ promptOptions: null }); comp.setState({ promptOptions: null });

View File

@ -5,7 +5,7 @@ import useSyncTargetUpgrade, { SyncTargetUpgradeResult } from '@joplin/lib/servi
const { render } = require('react-dom'); const { render } = require('react-dom');
const ipcRenderer = require('electron').ipcRenderer; const ipcRenderer = require('electron').ipcRenderer;
import Setting from '@joplin/lib/models/Setting'; import Setting from '@joplin/lib/models/Setting';
const bridge = require('@electron/remote').require('./bridge').default; import restart from '../services/restart';
function useAppCloseHandler(upgradeResult: SyncTargetUpgradeResult) { function useAppCloseHandler(upgradeResult: SyncTargetUpgradeResult) {
useEffect(function() { useEffect(function() {
@ -64,7 +64,7 @@ function useStyle() {
function useRestartOnDone(upgradeResult: SyncTargetUpgradeResult) { function useRestartOnDone(upgradeResult: SyncTargetUpgradeResult) {
useEffect(function() { useEffect(function() {
if (upgradeResult.done && !upgradeResult.error) { if (upgradeResult.done && !upgradeResult.error) {
bridge().restart(); void restart();
} }
}, [upgradeResult.done]); }, [upgradeResult.done]);
} }

View File

@ -0,0 +1,10 @@
import Setting from '@joplin/lib/models/Setting';
import bridge from './bridge';
export default async (linuxSafeRestart: boolean = true) => {
Setting.setValue('wasClosedSuccessfully', true);
await Setting.saveAll();
bridge().restart(linuxSafeRestart);
};

View File

@ -1500,6 +1500,12 @@ class Setting extends BaseModel {
public: false, public: false,
}, },
wasClosedSuccessfully: {
value: true,
type: SettingItemType.Bool,
public: false,
},
// 'featureFlag.syncAccurateTimestamps': { // 'featureFlag.syncAccurateTimestamps': {
// value: false, // value: false,
// type: SettingItemType.Bool, // type: SettingItemType.Bool,