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 * as log from '../ts/logging/log';
|
||||||
import type { LocaleMessagesType } from '../ts/types/I18N';
|
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
|
* 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
|
* and destroying a `Tray`, and listening to the associated `BrowserWindow`'s visibility
|
||||||
|
@ -23,14 +30,19 @@ export class SystemTrayService {
|
||||||
|
|
||||||
private isEnabled = false;
|
private isEnabled = false;
|
||||||
|
|
||||||
|
private isQuitting = false;
|
||||||
|
|
||||||
private unreadCount = 0;
|
private unreadCount = 0;
|
||||||
|
|
||||||
private boundRender: typeof SystemTrayService.prototype.render;
|
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');
|
log.info('System tray service: created');
|
||||||
this.messages = messages;
|
this.messages = messages;
|
||||||
this.boundRender = this.render.bind(this);
|
this.boundRender = this.render.bind(this);
|
||||||
|
this.createTrayInstance = createTrayInstance || (icon => new Tray(icon));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,6 +104,19 @@ export class SystemTrayService {
|
||||||
this.render();
|
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 {
|
private render(): void {
|
||||||
if (this.isEnabled && this.browserWindow) {
|
if (this.isEnabled && this.browserWindow) {
|
||||||
this.renderEnabled();
|
this.renderEnabled();
|
||||||
|
@ -101,6 +126,11 @@ export class SystemTrayService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderEnabled() {
|
private renderEnabled() {
|
||||||
|
if (this.isQuitting) {
|
||||||
|
log.info('System tray service: not rendering the tray, quitting');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
log.info('System tray service: rendering the tray');
|
log.info('System tray service: rendering the tray');
|
||||||
|
|
||||||
this.tray = this.tray || this.createTray();
|
this.tray = this.tray || this.createTray();
|
||||||
|
@ -177,7 +207,7 @@ export class SystemTrayService {
|
||||||
log.info('System tray service: creating the tray');
|
log.info('System tray service: creating the tray');
|
||||||
|
|
||||||
// This icon may be swiftly overwritten.
|
// 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
|
// 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
|
// 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(),
|
shouldQuit: windowState.shouldQuit(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
systemTrayService?.markShouldQuit();
|
||||||
windowState.markShouldQuit();
|
windowState.markShouldQuit();
|
||||||
|
|
||||||
if (mainWindow) {
|
if (mainWindow) {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { BrowserWindow, Tray, nativeImage } from 'electron';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { MINUTE } from '../../util/durations';
|
import { MINUTE } from '../../util/durations';
|
||||||
|
|
||||||
|
import type { SystemTrayServiceOptionsType } from '../../../app/SystemTrayService';
|
||||||
import { SystemTrayService } from '../../../app/SystemTrayService';
|
import { SystemTrayService } from '../../../app/SystemTrayService';
|
||||||
|
|
||||||
describe('SystemTrayService', function thisNeeded() {
|
describe('SystemTrayService', function thisNeeded() {
|
||||||
|
@ -23,7 +24,9 @@ describe('SystemTrayService', function thisNeeded() {
|
||||||
*
|
*
|
||||||
* This only affects these tests, not the "real" code.
|
* This only affects these tests, not the "real" code.
|
||||||
*/
|
*/
|
||||||
function newService(): SystemTrayService {
|
function newService(
|
||||||
|
options?: Partial<SystemTrayServiceOptionsType>
|
||||||
|
): SystemTrayService {
|
||||||
const result = new SystemTrayService({
|
const result = new SystemTrayService({
|
||||||
messages: {
|
messages: {
|
||||||
hide: { message: 'Hide' },
|
hide: { message: 'Hide' },
|
||||||
|
@ -31,6 +34,7 @@ describe('SystemTrayService', function thisNeeded() {
|
||||||
show: { message: 'Show' },
|
show: { message: 'Show' },
|
||||||
signalDesktop: { message: 'Signal' },
|
signalDesktop: { message: 'Signal' },
|
||||||
},
|
},
|
||||||
|
...options,
|
||||||
});
|
});
|
||||||
servicesCreated.add(result);
|
servicesCreated.add(result);
|
||||||
return result;
|
return result;
|
||||||
|
@ -258,4 +262,16 @@ describe('SystemTrayService', function thisNeeded() {
|
||||||
sinon.assert.calledWith(setImageStub, sinon.match.string);
|
sinon.assert.calledWith(setImageStub, sinon.match.string);
|
||||||
sinon.assert.calledWith(setImageStub, sinon.match.instanceOf(NativeImage));
|
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
Add a link
Reference in a new issue