fix(electron): optimize electron open/close on mac (#6224)

1. never close main window on mac to allow it to be quickly open
1. make the browser show a bit faster
2. brought up app window when clicking some menu items
This commit is contained in:
pengx17 2024-03-20 11:02:22 +00:00
parent 4f5907766f
commit 65ab6c89bf
No known key found for this signature in database
GPG Key ID: 23F23D9E8B3971ED
5 changed files with 67 additions and 24 deletions

View File

@ -2,6 +2,7 @@ import { app, Menu } from 'electron';
import { isMacOS } from '../../shared/utils';
import { revealLogFile } from '../logger';
import { initAndShowMainWindow } from '../main-window';
import { checkForUpdates } from '../updater';
import { applicationMenuSubjects } from './subject';
@ -42,7 +43,9 @@ export function createApplicationMenu() {
id: MENUITEM_NEW_PAGE,
label: 'New Doc',
accelerator: isMac ? 'Cmd+N' : 'Ctrl+N',
click: () => {
click: async () => {
await initAndShowMainWindow();
// fixme: if the window is just created, the new page action will not be triggered
applicationMenuSubjects.newPageAction.next();
},
},
@ -100,7 +103,12 @@ export function createApplicationMenu() {
{ type: 'separator' },
{ role: 'front' },
{ type: 'separator' },
{ role: 'window' },
{
role: 'window',
click: async () => {
await initAndShowMainWindow();
},
},
]
: [{ role: 'close' }]),
],
@ -125,6 +133,7 @@ export function createApplicationMenu() {
{
label: 'Check for Updates',
click: async () => {
await initAndShowMainWindow();
await checkForUpdates();
},
},

View File

@ -52,9 +52,7 @@ if (!isSingleInstance) {
* Shout down background process if all windows was closed
*/
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
app.quit();
});
/**

View File

@ -27,6 +27,14 @@ const getWindowAdditionalArguments = async () => {
];
};
function closeAllWindows() {
BrowserWindow.getAllWindows().forEach(w => {
if (!w.isDestroyed()) {
w.destroy();
}
});
}
async function createWindow(additionalArguments: string[]) {
logger.info('create window');
const mainWindowState = electronWindowState({
@ -82,12 +90,7 @@ async function createWindow(additionalArguments: string[]) {
* @see https://github.com/electron/electron/issues/25012
*/
browserWindow.on('ready-to-show', () => {
if (IS_DEV) {
// do not gain focus in dev mode
browserWindow.showInactive();
} else {
browserWindow.show();
}
helperConnectionUnsub?.();
helperConnectionUnsub = helperProcessManager.connectRenderer(
browserWindow.webContents
);
@ -96,15 +99,26 @@ async function createWindow(additionalArguments: string[]) {
});
browserWindow.on('close', e => {
e.preventDefault();
// close and destroy all windows
BrowserWindow.getAllWindows().forEach(w => {
if (!w.isDestroyed()) {
w.destroy();
}
});
helperConnectionUnsub?.();
// TODO: gracefully close the app, for example, ask user to save unsaved changes
e.preventDefault();
if (!isMacOS()) {
closeAllWindows();
} else {
// hide window on macOS
// application quit will be handled by closing the hidden window
//
// explanation:
// - closing the top window (by clicking close button or CMD-w)
// - will be captured in "close" event here
// - hiding the app to make the app open faster when user click the app icon
// - quit the app by "cmd+q" or right click on the dock icon and select "quit"
// - all browser windows will capture the "close" event
// - the hidden window will close all windows
// - "window-all-closed" event will be emitted and eventually quit the app
browserWindow.hide();
}
helperConnectionUnsub?.();
helperConnectionUnsub = undefined;
});
browserWindow.on('leave-full-screen', () => {
@ -148,15 +162,37 @@ async function createWindow(additionalArguments: string[]) {
// singleton
let browserWindow$: Promise<BrowserWindow> | undefined;
// a hidden window that prevents the app from quitting on MacOS
let hiddenMacWindow: BrowserWindow | undefined;
/**
* Init main BrowserWindow. Will create a new window if it's not created yet.
*/
export async function initMainWindow() {
export async function initAndShowMainWindow() {
if (!browserWindow$ || (await browserWindow$.then(w => w.isDestroyed()))) {
const additionalArguments = await getWindowAdditionalArguments();
browserWindow$ = createWindow(additionalArguments);
}
const mainWindow = await browserWindow$;
if (IS_DEV) {
// do not gain focus in dev mode
mainWindow.showInactive();
} else {
mainWindow.show();
}
if (!hiddenMacWindow && isMacOS()) {
hiddenMacWindow = new BrowserWindow({
show: false,
width: 100,
height: 100,
});
hiddenMacWindow.on('close', () => {
closeAllWindows();
});
}
return mainWindow;
}

View File

@ -4,7 +4,7 @@ import { getLinkPreview } from 'link-preview-js';
import { isMacOS } from '../../shared/utils';
import { persistentConfig } from '../config-storage/persist';
import { logger } from '../logger';
import { getMainWindow, initMainWindow } from '../main-window';
import { getMainWindow, initAndShowMainWindow } from '../main-window';
import { getOnboardingWindow } from '../onboarding';
import type { NamespaceHandlers } from '../type';
import { launchStage } from '../windows-manager/stage';
@ -58,7 +58,7 @@ export const uiHandlers = {
try {
const onboarding = await getOnboardingWindow();
onboarding?.hide();
await initMainWindow();
await initAndShowMainWindow();
// need to destroy onboarding window after main window is ready
// otherwise the main window will be closed as well
onboarding?.destroy();

View File

@ -1,5 +1,5 @@
import { logger } from '../logger';
import { initMainWindow } from '../main-window';
import { initAndShowMainWindow } from '../main-window';
import {
getOnboardingWindow,
getOrCreateOnboardingWindow,
@ -12,7 +12,7 @@ import { launchStage } from './stage';
export async function launch() {
const stage = launchStage.value;
if (stage === 'main') {
initMainWindow().catch(e => {
initAndShowMainWindow().catch(e => {
logger.error('Failed to restore or create window:', e);
});