[MM-48080] Update tray icon instantly when the settings change (#2376)

* Update wording and update tray icon instantly when the settings change

* Add tests

* Include new file
This commit is contained in:
Tasos Boulis 2022-11-14 21:40:18 +02:00 committed by GitHub
parent 4171774aee
commit e1f5250c0c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 208 additions and 7 deletions

View file

@ -22,6 +22,7 @@
"FOCALBOARD",
"gsettings",
"ICONNAME",
"inputflash",
"loadscreen",
"mmjstool",
"NOSERVERS",
@ -29,6 +30,7 @@
"officedocument",
"openxmlformats",
"presentationml",
"showunreadbadge",
"spreadsheetml",
"textbox",
"UNCLOSEABLE",

View file

@ -196,9 +196,9 @@
"renderer.components.settingsPage.showUnreadBadge.description": "Regardless of this setting, mentions are always indicated with a red badge and item count on the {taskbar} icon.",
"renderer.components.settingsPage.startAppOnLogin": "Start app on login",
"renderer.components.settingsPage.startAppOnLogin.description": "If enabled, the app starts automatically when you log in to your machine.",
"renderer.components.settingsPage.trayIcon.color": "Icon color: ",
"renderer.components.settingsPage.trayIcon.show": "Show icon in the notification area",
"renderer.components.settingsPage.trayIcon.show.darwin": "Show {appName} icon in the menu bar",
"renderer.components.settingsPage.trayIcon.theme": "Icon theme: ",
"renderer.components.settingsPage.trayIcon.theme.dark": "Dark",
"renderer.components.settingsPage.trayIcon.theme.light": "Light",
"renderer.components.settingsPage.trayIcon.theme.systemDefault": "Use system default",

View file

@ -41,7 +41,9 @@ jest.mock('main/AutoLauncher', () => ({
jest.mock('main/badge', () => ({
setUnreadBadgeSetting: jest.fn(),
}));
jest.mock('main/tray/tray', () => ({}));
jest.mock('main/tray/tray', () => ({
refreshTrayImages: jest.fn(),
}));
jest.mock('main/windows/windowManager', () => ({
handleUpdateConfig: jest.fn(),
sendToRenderer: jest.fn(),

View file

@ -67,6 +67,10 @@ export function handleConfigUpdate(newConfig: CombinedConfig) {
setLoggingLevel(newConfig.logLevel as LogLevel);
handleUpdateMenuEvent();
if (newConfig.trayIconTheme) {
refreshTrayImages(newConfig.trayIconTheme);
}
ipcMain.emit(EMIT_CONFIGURATION, true, newConfig);
}

193
src/main/tray/tray.test.js Normal file
View file

@ -0,0 +1,193 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {handleConfigUpdate} from 'main/app/config';
import AutoLauncher from 'main/AutoLauncher';
import * as tray from './tray';
jest.mock('path', () => ({
join: (a, b) => b,
resolve: (a, b, c) => (c || b),
}));
jest.mock('electron', () => {
class NativeImageMock {
image;
constructor(path) {
this.image = path;
return this;
}
static createFromPath(path) {
return new NativeImageMock(path);
}
setTemplateImage = () => jest.fn();
}
return {
app: {
getAppPath: () => '/path/to/app',
isReady: jest.fn(),
setPath: jest.fn(),
},
ipcMain: {
emit: jest.fn(),
handle: jest.fn(),
on: jest.fn(),
},
nativeImage: NativeImageMock,
nativeTheme: {
shouldUseDarkColors: true, // the value doesn't matter
},
};
});
jest.mock('main/app/utils', () => ({
handleUpdateMenuEvent: jest.fn(),
updateSpellCheckerLocales: jest.fn(),
updateServerInfos: jest.fn(),
setLoggingLevel: jest.fn(),
}));
jest.mock('main/app/intercom', () => ({
handleMainWindowIsShown: jest.fn(),
}));
jest.mock('main/AutoLauncher', () => ({
enable: jest.fn(),
disable: jest.fn(),
}));
jest.mock('main/badge', () => ({
setUnreadBadgeSetting: jest.fn(),
}));
jest.mock('main/windows/windowManager', () => ({
handleUpdateConfig: jest.fn(),
sendToRenderer: jest.fn(),
initializeCurrentServerName: jest.fn(),
}));
describe('main/tray', () => {
beforeEach(() => {
AutoLauncher.enable.mockResolvedValue({});
AutoLauncher.disable.mockResolvedValue({});
});
describe('config changes', () => {
let spy;
beforeAll(() => {
spy = jest.spyOn(tray, 'refreshTrayImages').mockImplementation();
});
afterAll(() => {
spy.mockRestore();
});
it('should update the tray icon color immediately when the config is updated', () => {
handleConfigUpdate({
trayIconTheme: 'light',
});
expect(tray.refreshTrayImages).toHaveBeenCalledWith('light');
});
it('should update the tray icon color immediately when the config is updated', () => {
handleConfigUpdate({
trayIconTheme: 'dark',
});
expect(tray.refreshTrayImages).toHaveBeenCalledWith('dark');
});
});
describe('darwin', () => {
const darwinResultAllThemes = {
normal: 'osx/menuIcons/MenuIcon16Template.png',
unread: 'osx/menuIcons/MenuIconUnread16Template.png',
mention: 'osx/menuIcons/MenuIconUnread16Template.png',
};
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'darwin',
});
const result = tray.refreshTrayImages('light');
it.each(Object.keys(result))('match "%s"', (a) => {
expect(result[a].image).toBe(darwinResultAllThemes[a]);
});
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
});
describe('win32 - light', () => {
const theme = 'light';
const winResultLight = {
normal: `windows/tray_${theme}.ico`,
unread: `windows/tray_${theme}_unread.ico`,
mention: `windows/tray_${theme}_mention.ico`,
};
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'win32',
});
const result = tray.refreshTrayImages('light');
it.each(Object.keys(result))('match "%s"', (a) => {
expect(result[a].image).toBe(winResultLight[a]);
});
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
});
describe('win32 - dark', () => {
const theme = 'dark';
const winResultDark = {
normal: `windows/tray_${theme}.ico`,
unread: `windows/tray_${theme}_unread.ico`,
mention: `windows/tray_${theme}_mention.ico`,
};
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'win32',
});
const result = tray.refreshTrayImages('dark');
it.each(Object.keys(result))('match "%s"', (a) => {
expect(result[a].image).toBe(winResultDark[a]);
});
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
});
describe('linux - light', () => {
const theme = 'light';
const linuxResultLight = {
normal: `top_bar_${theme}_16.png`,
unread: `top_bar_${theme}_unread_16.png`,
mention: `top_bar_${theme}_mention_16.png`,
};
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'linux',
});
const result = tray.refreshTrayImages('light');
it.each(Object.keys(result))('match "%s"', (a) => {
expect(result[a].image).toBe(linuxResultLight[a]);
});
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
});
describe('linux - dark', () => {
const theme = 'dark';
const linuxResultDark = {
normal: `top_bar_${theme}_16.png`,
unread: `top_bar_${theme}_unread_16.png`,
mention: `top_bar_${theme}_mention_16.png`,
};
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'linux',
});
const result = tray.refreshTrayImages('dark');
it.each(Object.keys(result))('match "%s"', (a) => {
expect(result[a].image).toBe(linuxResultDark[a]);
});
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
});
});

View file

@ -74,8 +74,8 @@ export function refreshTrayImages(trayIconTheme: string) {
return trayImages;
}
export function setupTray(icontheme: string) {
refreshTrayImages(icontheme);
export function setupTray(iconTheme: string) {
refreshTrayImages(iconTheme);
trayIcon = new Tray(trayImages.normal);
if (process.platform === 'darwin') {
systemPreferences.subscribeNotification('AppleInterfaceThemeChangedNotification', () => {

View file

@ -633,7 +633,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
options.push(
<div
style={settingsPage.container}
key='containerInputSpellchekerURL'
key='containerInputSpellcheckerURL'
>
<input
disabled={!this.state.useSpellChecker}
@ -855,8 +855,8 @@ class SettingsPage extends React.PureComponent<Props, State> {
style={{marginLeft: '20px'}}
>
<FormattedMessage
id='renderer.components.settingsPage.trayIcon.theme'
defaultMessage='Icon theme: '
id='renderer.components.settingsPage.trayIcon.color'
defaultMessage='Icon color: '
/>
{window.process.platform === 'win32' &&
<>