Add option for automatic dark theme (#2759)

Summary:
Add an setting option to allow automatic dark theme

A feature requested by swrobel https://github.com/facebook/flipper/issues/2708

## Changelog

### UI change
Replace the `Enable Dark Theme` Toggle button with a Radio group containing three Radio buttons.

The available options are `Dark`, `Light` and `Use System Default`, of which `Use System Default` is the default value

<img width="548" alt="Screenshot 2021-08-31 at 3 32 44 PM" src="https://user-images.githubusercontent.com/410850/131462798-4e74f757-41fc-4a3f-ba28-53d00fc1cf03.png">

### Data structure change

The `darkMode` property of [Settings](c0cd32564a/desktop/app/src/reducers/settings.tsx (L20)) is changed from `boolean` to `string`, the available values are `dark`, `light` and `auto`

Pull Request resolved: https://github.com/facebook/flipper/pull/2759

Test Plan:
### Test 1
- Step: Choose `Dark` in the Settings page and click on `Apply and Restart` button
- Expect: The application is displayed in Dark mode
### Test 2
- Step: Choose `Light` in the Settings page and click on `Apply and Restart` button
- Expect: The application is displayed in Light mode
### Test 3
- Step: Choose `Use System Default` in the Settings page and click on `Apply and Restart` button
- Expect: The application is displayed in System Default mode

Reviewed By: mweststrate

Differential Revision: D30666966

Pulled By: passy

fbshipit-source-id: a63e91f2d0dbff96890e267062cb75bffd0007f4
This commit is contained in:
ZHANG Qichuan
2021-09-08 03:27:36 -07:00
committed by Facebook GitHub Bot
parent cef1468db7
commit 9a4d94c971
6 changed files with 57 additions and 29 deletions

View File

@@ -7,8 +7,9 @@
* @format
*/
import {Button} from '../ui';
import {Button, FlexColumn} from '../ui';
import React, {Component, useContext} from 'react';
import {Radio} from 'antd';
import {updateSettings, Action} from '../reducers/settings';
import {
Action as LauncherAction,
@@ -245,18 +246,23 @@ class SettingsSheet extends Component<Props, State> {
}));
}}
/>
<ToggledSection
label="Enable dark theme"
toggled={darkMode}
onChange={(enabled) => {
<FlexColumn style={{paddingLeft: 15, paddingBottom: 10}}>
Theme Selection
<Radio.Group
value={darkMode}
onChange={(event) => {
this.setState((prevState) => ({
updatedSettings: {
...prevState.updatedSettings,
darkMode: enabled,
darkMode: event.target.value,
},
}));
}}
/>
}}>
<Radio.Button value="dark">Dark</Radio.Button>
<Radio.Button value="light">Light</Radio.Button>
<Radio.Button value="system">Use System Setting</Radio.Button>
</Radio.Group>
</FlexColumn>
<ToggledSection
label="React Native keyboard shortcuts"
toggled={reactNative.shortcuts.enabled}

View File

@@ -49,7 +49,7 @@ import styled from '@emotion/styled';
import {CopyOutlined} from '@ant-design/icons';
import {getVersionString} from './utils/versionString';
import {PersistGate} from 'redux-persist/integration/react';
import {ipcRenderer} from 'electron';
import {ipcRenderer, remote} from 'electron';
if (process.env.NODE_ENV === 'development' && os.platform() === 'darwin') {
// By default Node.JS has its internal certificate storage and doesn't use
@@ -213,14 +213,26 @@ function init() {
sideEffect(
store,
{name: 'loadTheme', fireImmediately: false, throttleMs: 500},
(state) => ({
dark: state.settingsState.darkMode,
}),
(theme) => {
(state) => {
const theme = state.settingsState.darkMode;
let shouldUseDarkMode = remote.nativeTheme.shouldUseDarkColors;
if (theme === 'dark') {
shouldUseDarkMode = true;
} else if (theme === 'light') {
shouldUseDarkMode = false;
} else if (theme === 'system') {
shouldUseDarkMode = remote.nativeTheme.shouldUseDarkColors;
}
return {
shouldUseDarkMode: shouldUseDarkMode,
theme: theme,
};
},
(result) => {
(
document.getElementById('flipper-theme-import') as HTMLLinkElement
).href = `themes/${theme.dark ? 'dark' : 'light'}.css`;
ipcRenderer.send('setTheme', theme.dark ? 'dark' : 'light');
).href = `themes/${result.shouldUseDarkMode ? 'dark' : 'light'}.css`;
ipcRenderer.send('setTheme', result.theme);
},
);
}

View File

@@ -43,7 +43,7 @@ export type Settings = {
openDevMenu: string;
};
};
darkMode: boolean;
darkMode: string;
showWelcomeAtStartup: boolean;
suppressPluginErrors: boolean;
};
@@ -78,7 +78,7 @@ const initialState: Settings = {
openDevMenu: 'Alt+Shift+D',
},
},
darkMode: false,
darkMode: 'auto',
showWelcomeAtStartup: true,
suppressPluginErrors: false,
};

View File

@@ -8,6 +8,7 @@
*/
import {useStore} from './useStore';
import {remote} from 'electron';
/**
* This hook returns whether dark mode is currently being used.
@@ -15,5 +16,15 @@ import {useStore} from './useStore';
* which will provide colors that reflect the theme
*/
export function useIsDarkMode(): boolean {
return useStore((state) => state.settingsState.darkMode);
return useStore((state) => {
const darkMode = state.settingsState.darkMode;
if (darkMode === 'dark') {
return true;
} else if (darkMode === 'light') {
return false;
} else if (darkMode === 'system') {
return remote.nativeTheme.shouldUseDarkColors;
}
return false;
});
}

View File

@@ -109,9 +109,7 @@ if (argv['disable-gpu'] || process.env.FLIPPER_DISABLE_GPU === '1') {
}
process.env.CONFIG = JSON.stringify(config);
if (config.darkMode) {
nativeTheme.themeSource = 'dark';
}
nativeTheme.themeSource = config.darkMode || 'light';
// possible reference to main app window
let win: BrowserWindow;
@@ -290,7 +288,7 @@ ipcMain.on('getLaunchTime', (event) => {
}
});
ipcMain.on('setTheme', (_e, mode: 'light' | 'dark') => {
ipcMain.on('setTheme', (_e, mode: 'light' | 'dark' | 'system') => {
nativeTheme.themeSource = mode;
});
@@ -382,7 +380,7 @@ function createWindow() {
configPath,
JSON.stringify({
...config,
darkMode: nativeTheme.themeSource === 'dark',
darkMode: nativeTheme.themeSource,
lastWindowPosition: {
x,
y,

View File

@@ -24,7 +24,7 @@ export type Config = {
launcherMsg?: string | undefined;
updaterEnabled?: boolean;
launcherEnabled?: boolean;
darkMode?: boolean;
darkMode: 'system' | 'light' | 'dark';
};
export default function setup(argv: any) {
@@ -38,6 +38,7 @@ export default function setup(argv: any) {
let config: Config = {
pluginPaths: [],
disabledPlugins: [],
darkMode: 'light',
};
try {