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:
committed by
Facebook GitHub Bot
parent
cef1468db7
commit
9a4d94c971
@@ -7,8 +7,9 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Button} from '../ui';
|
import {Button, FlexColumn} from '../ui';
|
||||||
import React, {Component, useContext} from 'react';
|
import React, {Component, useContext} from 'react';
|
||||||
|
import {Radio} from 'antd';
|
||||||
import {updateSettings, Action} from '../reducers/settings';
|
import {updateSettings, Action} from '../reducers/settings';
|
||||||
import {
|
import {
|
||||||
Action as LauncherAction,
|
Action as LauncherAction,
|
||||||
@@ -245,18 +246,23 @@ class SettingsSheet extends Component<Props, State> {
|
|||||||
}));
|
}));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ToggledSection
|
<FlexColumn style={{paddingLeft: 15, paddingBottom: 10}}>
|
||||||
label="Enable dark theme"
|
Theme Selection
|
||||||
toggled={darkMode}
|
<Radio.Group
|
||||||
onChange={(enabled) => {
|
value={darkMode}
|
||||||
this.setState((prevState) => ({
|
onChange={(event) => {
|
||||||
updatedSettings: {
|
this.setState((prevState) => ({
|
||||||
...prevState.updatedSettings,
|
updatedSettings: {
|
||||||
darkMode: enabled,
|
...prevState.updatedSettings,
|
||||||
},
|
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
|
<ToggledSection
|
||||||
label="React Native keyboard shortcuts"
|
label="React Native keyboard shortcuts"
|
||||||
toggled={reactNative.shortcuts.enabled}
|
toggled={reactNative.shortcuts.enabled}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ import styled from '@emotion/styled';
|
|||||||
import {CopyOutlined} from '@ant-design/icons';
|
import {CopyOutlined} from '@ant-design/icons';
|
||||||
import {getVersionString} from './utils/versionString';
|
import {getVersionString} from './utils/versionString';
|
||||||
import {PersistGate} from 'redux-persist/integration/react';
|
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') {
|
if (process.env.NODE_ENV === 'development' && os.platform() === 'darwin') {
|
||||||
// By default Node.JS has its internal certificate storage and doesn't use
|
// By default Node.JS has its internal certificate storage and doesn't use
|
||||||
@@ -213,14 +213,26 @@ function init() {
|
|||||||
sideEffect(
|
sideEffect(
|
||||||
store,
|
store,
|
||||||
{name: 'loadTheme', fireImmediately: false, throttleMs: 500},
|
{name: 'loadTheme', fireImmediately: false, throttleMs: 500},
|
||||||
(state) => ({
|
(state) => {
|
||||||
dark: state.settingsState.darkMode,
|
const theme = state.settingsState.darkMode;
|
||||||
}),
|
let shouldUseDarkMode = remote.nativeTheme.shouldUseDarkColors;
|
||||||
(theme) => {
|
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
|
document.getElementById('flipper-theme-import') as HTMLLinkElement
|
||||||
).href = `themes/${theme.dark ? 'dark' : 'light'}.css`;
|
).href = `themes/${result.shouldUseDarkMode ? 'dark' : 'light'}.css`;
|
||||||
ipcRenderer.send('setTheme', theme.dark ? 'dark' : 'light');
|
ipcRenderer.send('setTheme', result.theme);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export type Settings = {
|
|||||||
openDevMenu: string;
|
openDevMenu: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
darkMode: boolean;
|
darkMode: string;
|
||||||
showWelcomeAtStartup: boolean;
|
showWelcomeAtStartup: boolean;
|
||||||
suppressPluginErrors: boolean;
|
suppressPluginErrors: boolean;
|
||||||
};
|
};
|
||||||
@@ -78,7 +78,7 @@ const initialState: Settings = {
|
|||||||
openDevMenu: 'Alt+Shift+D',
|
openDevMenu: 'Alt+Shift+D',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
darkMode: false,
|
darkMode: 'auto',
|
||||||
showWelcomeAtStartup: true,
|
showWelcomeAtStartup: true,
|
||||||
suppressPluginErrors: false,
|
suppressPluginErrors: false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {useStore} from './useStore';
|
import {useStore} from './useStore';
|
||||||
|
import {remote} from 'electron';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This hook returns whether dark mode is currently being used.
|
* 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
|
* which will provide colors that reflect the theme
|
||||||
*/
|
*/
|
||||||
export function useIsDarkMode(): boolean {
|
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;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,9 +109,7 @@ if (argv['disable-gpu'] || process.env.FLIPPER_DISABLE_GPU === '1') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
process.env.CONFIG = JSON.stringify(config);
|
process.env.CONFIG = JSON.stringify(config);
|
||||||
if (config.darkMode) {
|
nativeTheme.themeSource = config.darkMode || 'light';
|
||||||
nativeTheme.themeSource = 'dark';
|
|
||||||
}
|
|
||||||
|
|
||||||
// possible reference to main app window
|
// possible reference to main app window
|
||||||
let win: BrowserWindow;
|
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;
|
nativeTheme.themeSource = mode;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -382,7 +380,7 @@ function createWindow() {
|
|||||||
configPath,
|
configPath,
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
...config,
|
...config,
|
||||||
darkMode: nativeTheme.themeSource === 'dark',
|
darkMode: nativeTheme.themeSource,
|
||||||
lastWindowPosition: {
|
lastWindowPosition: {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export type Config = {
|
|||||||
launcherMsg?: string | undefined;
|
launcherMsg?: string | undefined;
|
||||||
updaterEnabled?: boolean;
|
updaterEnabled?: boolean;
|
||||||
launcherEnabled?: boolean;
|
launcherEnabled?: boolean;
|
||||||
darkMode?: boolean;
|
darkMode: 'system' | 'light' | 'dark';
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function setup(argv: any) {
|
export default function setup(argv: any) {
|
||||||
@@ -38,6 +38,7 @@ export default function setup(argv: any) {
|
|||||||
let config: Config = {
|
let config: Config = {
|
||||||
pluginPaths: [],
|
pluginPaths: [],
|
||||||
disabledPlugins: [],
|
disabledPlugins: [],
|
||||||
|
darkMode: 'light',
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user