Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 979dce5

Browse filesBrowse files
authored
[MM-66805] Use registry to get tray icon theme on Windows (#3601)
1 parent 2b8c640 commit 979dce5
Copy full SHA for 979dce5

File tree

Expand file treeCollapse file tree

5 files changed

+249
-3
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

5 files changed

+249
-3
lines changed
Open diff view settings
Collapse file

‎src/app/system/tray/tray.test.js‎

Copy file name to clipboardExpand all lines: src/app/system/tray/tray.test.js
+78Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
22
// See LICENSE.txt for license information.
33

4+
import Config from 'common/config';
45
import {handleConfigUpdate} from 'main/app/config';
56
import AutoLauncher from 'main/AutoLauncher';
67

@@ -69,6 +70,15 @@ jest.mock('app/mainWindow/mainWindow', () => ({
6970
jest.mock('app/mainWindow/modals/modalManager', () => ({
7071
isModalDisplayed: jest.fn(),
7172
}));
73+
jest.mock('common/config', () => {
74+
const mockConfig = {
75+
getWindowsSystemDarkMode: jest.fn(),
76+
};
77+
return {
78+
__esModule: true,
79+
default: mockConfig,
80+
};
81+
});
7282

7383
describe('main/tray', () => {
7484
beforeEach(() => {
@@ -195,4 +205,72 @@ describe('main/tray', () => {
195205
value: originalPlatform,
196206
});
197207
});
208+
209+
describe('win32 - use_system', () => {
210+
const originalPlatform = process.platform;
211+
212+
beforeEach(() => {
213+
Object.defineProperty(process, 'platform', {
214+
value: 'win32',
215+
});
216+
jest.clearAllMocks();
217+
});
218+
219+
afterEach(() => {
220+
Object.defineProperty(process, 'platform', {
221+
value: originalPlatform,
222+
});
223+
});
224+
225+
it('should use dark theme when system is in light mode (AppsUseLightTheme = true)', () => {
226+
Config.getWindowsSystemDarkMode.mockReturnValue(false);
227+
const theme = 'dark';
228+
const winResultDark = {
229+
normal: `windows/tray_${theme}.ico`,
230+
unread: `windows/tray_${theme}_unread.ico`,
231+
mention: `windows/tray_${theme}_mention.ico`,
232+
};
233+
234+
const result = Tray.refreshImages('use_system');
235+
236+
expect(Config.getWindowsSystemDarkMode).toHaveBeenCalled();
237+
expect(result.normal.image).toBe(winResultDark.normal);
238+
expect(result.unread.image).toBe(winResultDark.unread);
239+
expect(result.mention.image).toBe(winResultDark.mention);
240+
});
241+
242+
it('should use light theme when system is in dark mode (AppsUseLightTheme = false)', () => {
243+
Config.getWindowsSystemDarkMode.mockReturnValue(true);
244+
const theme = 'light';
245+
const winResultLight = {
246+
normal: `windows/tray_${theme}.ico`,
247+
unread: `windows/tray_${theme}_unread.ico`,
248+
mention: `windows/tray_${theme}_mention.ico`,
249+
};
250+
251+
const result = Tray.refreshImages('use_system');
252+
253+
expect(Config.getWindowsSystemDarkMode).toHaveBeenCalled();
254+
expect(result.normal.image).toBe(winResultLight.normal);
255+
expect(result.unread.image).toBe(winResultLight.unread);
256+
expect(result.mention.image).toBe(winResultLight.mention);
257+
});
258+
259+
it('should call getWindowsSystemDarkMode when trayIconTheme is use_system', () => {
260+
Config.getWindowsSystemDarkMode.mockReturnValue(false);
261+
262+
Tray.refreshImages('use_system');
263+
264+
expect(Config.getWindowsSystemDarkMode).toHaveBeenCalledTimes(1);
265+
});
266+
267+
it('should not call getWindowsSystemDarkMode when trayIconTheme is not use_system', () => {
268+
Config.getWindowsSystemDarkMode.mockReturnValue(false);
269+
270+
Tray.refreshImages('light');
271+
Tray.refreshImages('dark');
272+
273+
expect(Config.getWindowsSystemDarkMode).not.toHaveBeenCalled();
274+
});
275+
});
198276
});
Collapse file

‎src/app/system/tray/tray.ts‎

Copy file name to clipboardExpand all lines: src/app/system/tray/tray.ts
+6-3Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33

44
import path from 'path';
55

6-
import {app, nativeImage, Tray, systemPreferences, nativeTheme} from 'electron';
6+
import {app, nativeImage, Tray, systemPreferences} from 'electron';
77

88
import MainWindow from 'app/mainWindow/mainWindow';
99
import AppState from 'common/appState';
1010
import {UPDATE_APPSTATE_TOTALS} from 'common/communication';
11+
import Config from 'common/config';
1112
import {Logger} from 'common/log';
1213
import {localizeMessage} from 'main/i18nManager';
1314

@@ -45,8 +46,10 @@ export class TrayIcon {
4546
};
4647

4748
refreshImages = (trayIconTheme: string) => {
48-
const systemTheme = nativeTheme.shouldUseDarkColors ? 'light' : 'dark';
49-
const winTheme = trayIconTheme === 'use_system' ? systemTheme : trayIconTheme;
49+
let winTheme = trayIconTheme;
50+
if (trayIconTheme === 'use_system') {
51+
winTheme = Config.getWindowsSystemDarkMode() ? 'light' : 'dark';
52+
}
5053

5154
switch (process.platform) {
5255
case 'win32':
Collapse file

‎src/common/config/RegistryConfig.test.js‎

Copy file name to clipboardExpand all lines: src/common/config/RegistryConfig.test.js
+140Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,36 @@ jest.mock('registry-js', () => {
162162
throw new Error('Registry access error in CU');
163163
}
164164
return [];
165+
} else if (hive === 'theme-light-hive') {
166+
if (key.includes('Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize')) {
167+
return [
168+
{
169+
name: 'AppsUseLightTheme',
170+
data: 1,
171+
},
172+
];
173+
}
174+
return [];
175+
} else if (hive === 'theme-dark-hive') {
176+
if (key.includes('Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize')) {
177+
return [
178+
{
179+
name: 'AppsUseLightTheme',
180+
data: 0,
181+
},
182+
];
183+
}
184+
return [];
185+
} else if (hive === 'theme-undefined-hive') {
186+
if (key.includes('Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize')) {
187+
return [];
188+
}
189+
return [];
190+
} else if (hive === 'theme-error-hive') {
191+
if (key.includes('Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize')) {
192+
throw new Error('Registry access error');
193+
}
194+
return [];
165195
}
166196

167197
return [];
@@ -501,4 +531,114 @@ describe('common/config/RegistryConfig', () => {
501531
});
502532
});
503533
});
534+
535+
describe('getAppsUseLightTheme', () => {
536+
let registryConfig;
537+
let originalPlatform;
538+
539+
beforeEach(() => {
540+
registryConfig = new RegistryConfig();
541+
originalPlatform = process.platform;
542+
});
543+
544+
afterEach(() => {
545+
Object.defineProperty(process, 'platform', {
546+
value: originalPlatform,
547+
});
548+
});
549+
550+
it('should return true when AppsUseLightTheme is 1 (light mode)', () => {
551+
Object.defineProperty(process, 'platform', {
552+
value: 'win32',
553+
});
554+
555+
const originalFn = registryConfig.getRegistryEntryValues;
556+
registryConfig.getRegistryEntryValues = (hive, key, name) => {
557+
if (hive === 'HKEY_CURRENT_USER' && key.includes('Themes\\Personalize')) {
558+
return originalFn.apply(registryConfig, ['theme-light-hive', key, name, false]);
559+
}
560+
return originalFn.apply(registryConfig, [hive, key, name, false]);
561+
};
562+
563+
const result = registryConfig.getAppsUseLightTheme();
564+
expect(result).toBe(true);
565+
});
566+
567+
it('should return false when AppsUseLightTheme is 0 (dark mode)', () => {
568+
Object.defineProperty(process, 'platform', {
569+
value: 'win32',
570+
});
571+
572+
const originalFn = registryConfig.getRegistryEntryValues;
573+
registryConfig.getRegistryEntryValues = (hive, key, name) => {
574+
if (hive === 'HKEY_CURRENT_USER' && key.includes('Themes\\Personalize')) {
575+
return originalFn.apply(registryConfig, ['theme-dark-hive', key, name, false]);
576+
}
577+
return originalFn.apply(registryConfig, [hive, key, name, false]);
578+
};
579+
580+
const result = registryConfig.getAppsUseLightTheme();
581+
expect(result).toBe(false);
582+
});
583+
584+
it('should return true (default) when AppsUseLightTheme is undefined', () => {
585+
Object.defineProperty(process, 'platform', {
586+
value: 'win32',
587+
});
588+
589+
const originalFn = registryConfig.getRegistryEntryValues;
590+
registryConfig.getRegistryEntryValues = (hive, key, name) => {
591+
if (hive === 'HKEY_CURRENT_USER' && key.includes('Themes\\Personalize')) {
592+
return originalFn.apply(registryConfig, ['theme-undefined-hive', key, name, false]);
593+
}
594+
return originalFn.apply(registryConfig, [hive, key, name, false]);
595+
};
596+
597+
const result = registryConfig.getAppsUseLightTheme();
598+
expect(result).toBe(true);
599+
});
600+
601+
it('should return true (default) on non-Windows platforms', () => {
602+
Object.defineProperty(process, 'platform', {
603+
value: 'darwin',
604+
});
605+
606+
const result = registryConfig.getAppsUseLightTheme();
607+
expect(result).toBe(true);
608+
});
609+
610+
it('should return true (default) when registry access throws an error', () => {
611+
Object.defineProperty(process, 'platform', {
612+
value: 'win32',
613+
});
614+
615+
const originalFn = registryConfig.getRegistryEntryValues;
616+
registryConfig.getRegistryEntryValues = (hive, key, name) => {
617+
if (hive === 'HKEY_CURRENT_USER' && key.includes('Themes\\Personalize')) {
618+
return originalFn.apply(registryConfig, ['theme-error-hive', key, name, false]);
619+
}
620+
return originalFn.apply(registryConfig, [hive, key, name, false]);
621+
};
622+
623+
const result = registryConfig.getAppsUseLightTheme();
624+
expect(result).toBe(true);
625+
});
626+
627+
it('should correctly read AppsUseLightTheme from HKEY_CURRENT_USER', () => {
628+
Object.defineProperty(process, 'platform', {
629+
value: 'win32',
630+
});
631+
632+
const originalFn = registryConfig.getRegistryEntryValues;
633+
registryConfig.getRegistryEntryValues = (hive, key, name) => {
634+
if (hive === 'HKEY_CURRENT_USER' && key.includes('Themes\\Personalize') && name === 'AppsUseLightTheme') {
635+
return 0;
636+
}
637+
return originalFn.apply(registryConfig, [hive, key, name, false]);
638+
};
639+
640+
const result = registryConfig.getAppsUseLightTheme();
641+
expect(result).toBe(false);
642+
});
643+
});
504644
});
Collapse file

‎src/common/config/RegistryConfig.ts‎

Copy file name to clipboardExpand all lines: src/common/config/RegistryConfig.ts
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,27 @@ export default class RegistryConfig extends EventEmitter {
105105
return value === undefined ? value : value === 1;
106106
}
107107

108+
/**
109+
* Retrieves the current Windows theme preference (AppsUseLightTheme)
110+
* @returns true if light mode is enabled, false if dark mode is enabled
111+
*/
112+
getAppsUseLightTheme(): boolean {
113+
if (process.platform !== 'win32') {
114+
return true;
115+
}
116+
try {
117+
const value = this.getRegistryEntryValues(
118+
HKEY.HKEY_CURRENT_USER,
119+
'Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize',
120+
'AppsUseLightTheme',
121+
);
122+
return value === undefined ? true : value === 1;
123+
} catch (error) {
124+
log.debug('Error reading AppsUseLightTheme from registry', error);
125+
return true;
126+
}
127+
}
128+
108129
/**
109130
* Initiates retrieval of a specific key in the Windows registry
110131
*
Collapse file

‎src/common/config/index.ts‎

Copy file name to clipboardExpand all lines: src/common/config/index.ts
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,10 @@ export class Config extends EventEmitter {
265265
return this.combinedData?.themeSyncing ?? true;
266266
}
267267

268+
getWindowsSystemDarkMode = () => {
269+
return !this.registryConfig.getAppsUseLightTheme();
270+
};
271+
268272
/**
269273
* Gets the servers from registry into the config object and reload
270274
*

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.