Avoid calling tray.destroy()
when quitting
This commit is contained in:
parent
dff941adc7
commit
3aa488c3d5
3 changed files with 50 additions and 3 deletions
|
@ -7,6 +7,13 @@ import { Menu, Tray, app, nativeImage } from 'electron';
|
|||
import * as log from '../ts/logging/log';
|
||||
import type { LocaleMessagesType } from '../ts/types/I18N';
|
||||
|
||||
export type SystemTrayServiceOptionsType = Readonly<{
|
||||
messages: LocaleMessagesType;
|
||||
|
||||
// For testing
|
||||
createTrayInstance?: (icon: NativeImage) => Tray;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* A class that manages an [Electron `Tray` instance][0]. It's responsible for creating
|
||||
* and destroying a `Tray`, and listening to the associated `BrowserWindow`'s visibility
|
||||
|
@ -23,14 +30,19 @@ export class SystemTrayService {
|
|||
|
||||
private isEnabled = false;
|
||||
|
||||
private isQuitting = false;
|
||||
|
||||
private unreadCount = 0;
|
||||
|
||||
private boundRender: typeof SystemTrayService.prototype.render;
|
||||
|
||||
constructor({ messages }: Readonly<{ messages: LocaleMessagesType }>) {
|
||||
private createTrayInstance: (icon: NativeImage) => Tray;
|
||||
|
||||
constructor({ messages, createTrayInstance }: SystemTrayServiceOptionsType) {
|
||||
log.info('System tray service: created');
|
||||
this.messages = messages;
|
||||
this.boundRender = this.render.bind(this);
|
||||
this.createTrayInstance = createTrayInstance || (icon => new Tray(icon));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,6 +104,19 @@ export class SystemTrayService {
|
|||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Workaround for: https://github.com/electron/electron/issues/32581#issuecomment-1020359931
|
||||
*
|
||||
* Tray is automatically destroyed when app quits so we shouldn't destroy it
|
||||
* twice when all windows will close.
|
||||
*/
|
||||
markShouldQuit(): void {
|
||||
log.info('System tray service: markShouldQuit');
|
||||
|
||||
this.tray = undefined;
|
||||
this.isQuitting = true;
|
||||
}
|
||||
|
||||
private render(): void {
|
||||
if (this.isEnabled && this.browserWindow) {
|
||||
this.renderEnabled();
|
||||
|
@ -101,6 +126,11 @@ export class SystemTrayService {
|
|||
}
|
||||
|
||||
private renderEnabled() {
|
||||
if (this.isQuitting) {
|
||||
log.info('System tray service: not rendering the tray, quitting');
|
||||
return;
|
||||
}
|
||||
|
||||
log.info('System tray service: rendering the tray');
|
||||
|
||||
this.tray = this.tray || this.createTray();
|
||||
|
@ -177,7 +207,7 @@ export class SystemTrayService {
|
|||
log.info('System tray service: creating the tray');
|
||||
|
||||
// This icon may be swiftly overwritten.
|
||||
const result = new Tray(getDefaultIcon());
|
||||
const result = this.createTrayInstance(getDefaultIcon());
|
||||
|
||||
// Note: "When app indicator is used on Linux, the click event is ignored." This
|
||||
// doesn't mean that the click event is always ignored on Linux; it depends on how
|
||||
|
|
|
@ -1729,6 +1729,7 @@ app.on('before-quit', () => {
|
|||
shouldQuit: windowState.shouldQuit(),
|
||||
});
|
||||
|
||||
systemTrayService?.markShouldQuit();
|
||||
windowState.markShouldQuit();
|
||||
|
||||
if (mainWindow) {
|
||||
|
|
|
@ -8,6 +8,7 @@ import { BrowserWindow, Tray, nativeImage } from 'electron';
|
|||
import * as path from 'path';
|
||||
import { MINUTE } from '../../util/durations';
|
||||
|
||||
import type { SystemTrayServiceOptionsType } from '../../../app/SystemTrayService';
|
||||
import { SystemTrayService } from '../../../app/SystemTrayService';
|
||||
|
||||
describe('SystemTrayService', function thisNeeded() {
|
||||
|
@ -23,7 +24,9 @@ describe('SystemTrayService', function thisNeeded() {
|
|||
*
|
||||
* This only affects these tests, not the "real" code.
|
||||
*/
|
||||
function newService(): SystemTrayService {
|
||||
function newService(
|
||||
options?: Partial<SystemTrayServiceOptionsType>
|
||||
): SystemTrayService {
|
||||
const result = new SystemTrayService({
|
||||
messages: {
|
||||
hide: { message: 'Hide' },
|
||||
|
@ -31,6 +34,7 @@ describe('SystemTrayService', function thisNeeded() {
|
|||
show: { message: 'Show' },
|
||||
signalDesktop: { message: 'Signal' },
|
||||
},
|
||||
...options,
|
||||
});
|
||||
servicesCreated.add(result);
|
||||
return result;
|
||||
|
@ -258,4 +262,16 @@ describe('SystemTrayService', function thisNeeded() {
|
|||
sinon.assert.calledWith(setImageStub, sinon.match.string);
|
||||
sinon.assert.calledWith(setImageStub, sinon.match.instanceOf(NativeImage));
|
||||
});
|
||||
|
||||
it('should not create new Tray after markShouldQuit', () => {
|
||||
const createTrayInstance = sandbox.stub();
|
||||
|
||||
const service = newService({ createTrayInstance });
|
||||
|
||||
service.setMainWindow(new BrowserWindow({ show: false }));
|
||||
service.markShouldQuit();
|
||||
service.setEnabled(true);
|
||||
|
||||
sinon.assert.notCalled(createTrayInstance);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue