Skip to content

Commit a7e3fd0

Browse files
authored
feat(styles): Add darkreader dark theme option (#2582)
1 parent 4ec3ed8 commit a7e3fd0

File tree

8 files changed

+377
-13
lines changed

8 files changed

+377
-13
lines changed

package-lock.json

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import AmpersandModel from 'ampersand-model';
2+
3+
export type CompassPreferencesModel = typeof AmpersandModel;
4+
5+
export = CompassPreferencesModel;
6+
7+
export enum THEMES {
8+
DARK = 'DARK',
9+
LIGHT = 'LIGHT',
10+
OS_THEME = 'OS_THEME'
11+
};

packages/compass-preferences-model/lib/model.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ var electronApp = electron.remote ? electron.remote.app : undefined;
77

88
var debug = require('debug')('mongodb-compass:models:preferences');
99

10+
var THEMES = {
11+
DARK: 'DARK',
12+
LIGHT: 'LIGHT',
13+
OS_THEME: 'OS_THEME'
14+
};
15+
1016
var preferencesProps = {
1117
/**
1218
* String identifier for this set of preferences. Default is `General`.
@@ -45,6 +51,15 @@ var preferencesProps = {
4551
required: true,
4652
default: false
4753
},
54+
/**
55+
* Stores the theme preference for the user.
56+
* @type {String}
57+
*/
58+
theme: {
59+
type: 'string',
60+
required: true,
61+
default: THEMES.LIGHT
62+
},
4863
/**
4964
* Stores a unique anonymous user ID (uuid) for the current user
5065
* @type {String}
@@ -296,3 +311,4 @@ var Preferences = Model.extend(storageMixin, {
296311
});
297312

298313
module.exports = Preferences;
314+
module.exports.THEMES = THEMES;

packages/compass/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@
203203
"compass-preferences-model": "^1.15.0",
204204
"compass-user-model": "^1.15.0",
205205
"cross-env": "^7.0.3",
206+
"darkreader": "^4.9.40",
206207
"debug": "4.3.0",
207208
"debug-menu": "^0.3.0",
208209
"depcheck": "^1.4.1",

packages/compass/src/app/index.js

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ var webvitals = require('web-vitals');
3434

3535
var semver = require('semver');
3636

37-
var Preferences = require('compass-preferences-model');
37+
const Preferences = require('compass-preferences-model');
38+
const { THEMES } = Preferences;
3839
var User = require('compass-user-model');
3940

4041
require('./menu-renderer');
@@ -46,6 +47,7 @@ marky.stop('Migrations');
4647
var React = require('react');
4748
var ReactDOM = require('react-dom');
4849
var { Action } = require('@mongodb-js/hadron-plugin-manager');
50+
const darkreader = require('darkreader');
4951

5052
ipc.once('app:launched', function() {
5153
console.log('in app:launched');
@@ -65,6 +67,35 @@ window.addEventListener('error', (event) => {
6567
{ message: event.message, stack: '<no stack available>' });
6668
});
6769

70+
const darkreaderOptions = { brightness: 100, contrast: 90, sepia: 10 };
71+
function enableDarkTheme() {
72+
darkreader.enable(darkreaderOptions);
73+
}
74+
75+
function disableDarkTheme() {
76+
darkreader.disable();
77+
}
78+
79+
function loadTheme(theme) {
80+
// Update main Compass when we've loaded the theme for setting app menus.
81+
ipc.call('window:theme-loaded', theme);
82+
83+
if (theme === THEMES.OS_THEME
84+
&& electron.remote.nativeTheme.shouldUseDarkColors
85+
) {
86+
enableDarkTheme();
87+
return;
88+
}
89+
90+
// Update our view based on the provided theme.
91+
if (theme === THEMES.DARK) {
92+
enableDarkTheme();
93+
return;
94+
}
95+
96+
disableDarkTheme();
97+
}
98+
6899
/**
69100
* The top-level application singleton that brings everything together!
70101
*/
@@ -356,6 +387,22 @@ app.extend({
356387
if (err) {
357388
throw err;
358389
}
390+
391+
// Get theme from the preferences and set accordingly.
392+
loadTheme(app.preferences.theme);
393+
ipc.on('app:darkreader-enable', () => {
394+
enableDarkTheme();
395+
});
396+
ipc.on('app:darkreader-disable', () => {
397+
disableDarkTheme();
398+
});
399+
ipc.on('app:save-theme', (_, theme) => {
400+
// Save the new theme on the user's preferences.
401+
app.preferences.save({
402+
theme
403+
});
404+
});
405+
359406
Action.pluginActivationCompleted.listen(() => {
360407
ipc.call('compass:loading:change-status', {
361408
status: 'activating plugins'

packages/compass/src/main/menu.spec.ts

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import EventEmitter from 'events';
2-
import { BrowserWindow, ipcMain, Menu } from 'electron';
2+
import { BrowserWindow, ipcMain, Menu, app } from 'electron';
33
import { expect } from 'chai';
4+
import sinon from 'sinon';
5+
import type processType from 'process';
6+
47
import type { CompassApplication } from './application';
58
import type { CompassMenu as _CompassMenu } from './menu';
69

@@ -99,6 +102,10 @@ describe('CompassMenu', function () {
99102
});
100103

101104
describe('getTemplate', function () {
105+
afterEach(function() {
106+
sinon.restore();
107+
});
108+
102109
it('should generate a menu template that can be passed to the Electron Menu without errors', function () {
103110
expect(() => {
104111
const template = CompassMenu.getTemplate(0);
@@ -151,6 +158,129 @@ describe('CompassMenu', function () {
151158
});
152159
});
153160

161+
it('should generate the about and theme options on darwin', function () {
162+
sinon.stub(process, 'platform').value('darwin');
163+
164+
expect(
165+
serializable(
166+
// Contains functions, so we can't easily deep equal it without
167+
// converting to serializable format
168+
CompassMenu.getTemplate(0).find((item) => item.label === app.getName())
169+
)
170+
).to.deep.eq({
171+
label: app.getName(),
172+
submenu: [
173+
{
174+
label: `About ${app.getName()}`,
175+
role: 'about',
176+
},
177+
{
178+
type: 'separator',
179+
},
180+
{
181+
label: 'Theme',
182+
submenu: [{
183+
checked: false,
184+
label: 'Use OS Theme (Preview)',
185+
type: 'checkbox',
186+
}, {
187+
checked: false,
188+
label: 'Dark Theme (Preview)',
189+
type: 'checkbox',
190+
}, {
191+
checked: true,
192+
label: 'Light Theme',
193+
type: 'checkbox',
194+
}]
195+
},
196+
{
197+
type: 'separator',
198+
},
199+
{
200+
label: 'Hide',
201+
accelerator: 'Command+H',
202+
role: 'hide',
203+
},
204+
{
205+
label: 'Hide Others',
206+
accelerator: 'Command+Shift+H',
207+
role: 'hideOthers',
208+
},
209+
{
210+
label: 'Show All',
211+
role: 'unhide',
212+
},
213+
{
214+
type: 'separator',
215+
},
216+
{
217+
label: 'Quit',
218+
accelerator: 'CmdOrCtrl+Q'
219+
}
220+
],
221+
});
222+
});
223+
224+
it('should generate the about and theme options on non darwin', function () {
225+
sinon.stub(process, 'platform').value('linux');
226+
227+
expect(
228+
serializable(
229+
// Contains functions, so we can't easily deep equal it without
230+
// converting to serializable format
231+
CompassMenu.getTemplate(0).find((item) => item.label === '&Help')
232+
)
233+
).to.deep.eq({
234+
label: '&Help',
235+
submenu: [
236+
{
237+
label: `&Online ${app.getName()} Help`,
238+
accelerator: 'F1',
239+
},
240+
{
241+
label: `${app.getName()} &Overview`
242+
},
243+
{
244+
label: '&Privacy Settings'
245+
},
246+
{
247+
label: '&Plugins'
248+
},
249+
{
250+
label: '&License'
251+
},
252+
{
253+
label: '&Open Log File'
254+
},
255+
{
256+
type: 'separator'
257+
},
258+
{
259+
label: 'Theme',
260+
submenu: [{
261+
checked: false,
262+
label: 'Use OS Theme (Preview)',
263+
type: 'checkbox',
264+
}, {
265+
checked: false,
266+
label: 'Dark Theme (Preview)',
267+
type: 'checkbox',
268+
}, {
269+
checked: true,
270+
label: 'Light Theme',
271+
type: 'checkbox',
272+
}]
273+
},
274+
{
275+
type: 'separator',
276+
},
277+
{
278+
label: `&About ${app.getName()}`,
279+
}
280+
]
281+
});
282+
});
283+
154284
it('should generate a menu template without collection submenu if `showCollection` is `false`', function () {
155285
expect(
156286
CompassMenu.getTemplate(0).find((item) => item.label === '&Collection')

0 commit comments

Comments
 (0)