From 4bb3ba4aefd02d8ba05debf2a669f21abd1f8be5 Mon Sep 17 00:00:00 2001 From: Niranjana Binoy <43930900+NiranjanaBinoy@users.noreply.github.com> Date: Mon, 7 Mar 2022 13:53:19 -0500 Subject: [PATCH] Adding new settings dropdown for Dark mode in Experimental tab (#13097) --- app/_locales/en/messages.json | 12 +++++ app/scripts/controllers/preferences.js | 10 ++++ app/scripts/controllers/preferences.test.js | 14 ++++++ app/scripts/metamask-controller.js | 1 + development/build/scripts.js | 2 + ui/pages/routes/routes.component.js | 17 ++++++- ui/pages/routes/routes.container.js | 2 + .../experimental-tab.component.js | 49 +++++++++++++++++++ .../experimental-tab.constant.js | 4 ++ .../experimental-tab.container.js | 4 ++ ui/selectors/selectors.js | 10 ++++ ui/store/actions.js | 12 +++++ 12 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 ui/pages/settings/experimental-tab/experimental-tab.constant.js diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 4d602ed13..195f13671 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -742,6 +742,9 @@ "message": "$1 has recommended this price.", "description": "$1 represents the Dapp's origin" }, + "darkTheme": { + "message": "Dark" + }, "data": { "message": "Data" }, @@ -777,6 +780,9 @@ "decryptRequest": { "message": "Decrypt request" }, + "defaultTheme": { + "message": "Default" + }, "delete": { "message": "Delete" }, @@ -3439,6 +3445,12 @@ "testFaucet": { "message": "Test Faucet" }, + "theme": { + "message": "Theme" + }, + "themeDescription": { + "message": "Choose your preferred MetaMask theme." + }, "thisWillCreate": { "message": "This will create a new wallet and Secret Recovery Phrase" }, diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 4a503e7e6..73e3b7963 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -68,6 +68,7 @@ export default class PreferencesController { ledgerTransportType: window.navigator.hid ? LEDGER_TRANSPORT_TYPES.WEBHID : LEDGER_TRANSPORT_TYPES.U2F, + theme: 'default', ...opts.initState, }; @@ -169,6 +170,15 @@ export default class PreferencesController { this.store.updateState({ eip1559V2Enabled: val }); } + /** + * Setter for the `theme` property + * + * @param {string} val - 'default' or 'dark' value based on the mode selected by user. + */ + setTheme(val) { + this.store.updateState({ theme: val }); + } + /** * Add new methodData to state, to avoid requesting this information again through Infura * diff --git a/app/scripts/controllers/preferences.test.js b/app/scripts/controllers/preferences.test.js index d6c8c59bf..10e7ba215 100644 --- a/app/scripts/controllers/preferences.test.js +++ b/app/scripts/controllers/preferences.test.js @@ -351,4 +351,18 @@ describe('preferences controller', function () { ); }); }); + + describe('setTheme', function () { + it('should default to value "default"', function () { + const state = preferencesController.store.getState(); + assert.equal(state.theme, 'default'); + }); + + it('should set the setTheme property in state', function () { + const state = preferencesController.store.getState(); + assert.equal(state.theme, 'default'); + preferencesController.setTheme('dark'); + assert.equal(preferencesController.store.getState().theme, 'dark'); + }); + }); }); diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index b7ddb2a5a..bf85bac18 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1424,6 +1424,7 @@ export default class MetamaskController extends EventEmitter { setEIP1559V2Enabled: preferencesController.setEIP1559V2Enabled.bind( preferencesController, ), + setTheme: preferencesController.setTheme.bind(preferencesController), // AssetsContractController getTokenStandardAndDetails: assetsContractController.getTokenStandardAndDetails.bind( diff --git a/development/build/scripts.js b/development/build/scripts.js index d07f7bcfc..e5e36a863 100644 --- a/development/build/scripts.js +++ b/development/build/scripts.js @@ -34,6 +34,7 @@ const metamaskrc = require('rc')('metamask', { INFURA_PROD_PROJECT_ID: process.env.INFURA_PROD_PROJECT_ID, ONBOARDING_V2: process.env.ONBOARDING_V2, COLLECTIBLES_V1: process.env.COLLECTIBLES_V1, + DARK_MODE_V1: process.env.DARK_MODE_V1, SEGMENT_HOST: process.env.SEGMENT_HOST, SEGMENT_WRITE_KEY: process.env.SEGMENT_WRITE_KEY, SEGMENT_BETA_WRITE_KEY: process.env.SEGMENT_BETA_WRITE_KEY, @@ -803,6 +804,7 @@ function getEnvironmentVariables({ buildType, devMode, testing }) { SWAPS_USE_DEV_APIS: process.env.SWAPS_USE_DEV_APIS === '1', ONBOARDING_V2: metamaskrc.ONBOARDING_V2 === '1', COLLECTIBLES_V1: metamaskrc.COLLECTIBLES_V1 === '1', + DARK_MODE_V1: metamaskrc.DARK_MODE_V1 === '1', }; } diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 2ac1b40fa..02ebdedf6 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -94,6 +94,7 @@ export default class Routes extends Component { prepareToLeaveSwaps: PropTypes.func, browserEnvironmentOs: PropTypes.string, browserEnvironmentBrowser: PropTypes.string, + theme: PropTypes.string, }; static contextTypes = { @@ -101,12 +102,22 @@ export default class Routes extends Component { metricsEvent: PropTypes.func, }; + componentDidUpdate(prevProps) { + if (process.env.DARK_MODE_V1) { + const { theme } = this.props; + if (theme !== prevProps.theme) { + document.documentElement.setAttribute('data-theme', theme); + } + } + } + UNSAFE_componentWillMount() { const { currentCurrency, pageChanged, setCurrentCurrencyToUSD, history, + theme, } = this.props; if (!currentCurrency) { setCurrentCurrencyToUSD(); @@ -117,6 +128,9 @@ export default class Routes extends Component { pageChanged(locationObj.pathname); } }); + if (process.env.DARK_MODE_V1 && theme) { + document.documentElement.setAttribute('data-theme', theme); + } } renderRoutes() { @@ -313,18 +327,19 @@ export default class Routes extends Component { isMouseUser, browserEnvironmentOs: os, browserEnvironmentBrowser: browser, + theme, } = this.props; const loadMessage = loadingMessage || isNetworkLoading ? this.getConnectingLabel(loadingMessage) : null; - return (
setMouseUserState(true)} diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index e8f6ba6ad..168b3e213 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -5,6 +5,7 @@ import { getNetworkIdentifier, getPreferences, isNetworkLoading, + getTheme, } from '../../selectors'; import { lockMetamask, @@ -36,6 +37,7 @@ function mapStateToProps(state) { browserEnvironmentContainter: state.metamask.browserEnvironment?.browser, providerId: getNetworkIdentifier(state), providerType: state.metamask.provider?.type, + theme: getTheme(state), }; } diff --git a/ui/pages/settings/experimental-tab/experimental-tab.component.js b/ui/pages/settings/experimental-tab/experimental-tab.component.js index 6d74ca459..5961432e9 100644 --- a/ui/pages/settings/experimental-tab/experimental-tab.component.js +++ b/ui/pages/settings/experimental-tab/experimental-tab.component.js @@ -5,6 +5,12 @@ import { getSettingsSectionNumber, handleSettingsRefs, } from '../../../helpers/utils/settings-search'; +import Dropdown from '../../../components/ui/dropdown'; + +import { THEME_TYPE } from './experimental-tab.constant'; + +/*eslint-disable prefer-destructuring*/ +const DARK_MODE_V1 = process.env.DARK_MODE_V1; export default class ExperimentalTab extends PureComponent { static contextTypes = { @@ -21,6 +27,8 @@ export default class ExperimentalTab extends PureComponent { openSeaEnabled: PropTypes.bool, eip1559V2Enabled: PropTypes.bool, setEIP1559V2Enabled: PropTypes.func, + theme: PropTypes.string, + setTheme: PropTypes.func, }; settingsRefs = Array( @@ -220,6 +228,46 @@ export default class ExperimentalTab extends PureComponent { ); } + renderTheme() { + if (!DARK_MODE_V1) { + return null; + } + const { t } = this.context; + const { theme, setTheme } = this.props; + + const themesOptions = [ + { + name: t('defaultTheme'), + value: THEME_TYPE.DEFAULT, + }, + { + name: t('darkTheme'), + value: THEME_TYPE.DARK, + }, + ]; + + return ( +
+
+ {this.context.t('theme')} +
+ {this.context.t('themeDescription')} +
+
+
+
+ setTheme(newTheme)} + /> +
+
+
+ ); + } + render() { return (
@@ -227,6 +275,7 @@ export default class ExperimentalTab extends PureComponent { {this.renderOpenSeaEnabledToggle()} {this.renderCollectibleDetectionToggle()} {this.renderEIP1559V2EnabledToggle()} + {this.renderTheme()}
); } diff --git a/ui/pages/settings/experimental-tab/experimental-tab.constant.js b/ui/pages/settings/experimental-tab/experimental-tab.constant.js new file mode 100644 index 000000000..8544e85e0 --- /dev/null +++ b/ui/pages/settings/experimental-tab/experimental-tab.constant.js @@ -0,0 +1,4 @@ +export const THEME_TYPE = { + DEFAULT: 'default', + DARK: 'dark', +}; diff --git a/ui/pages/settings/experimental-tab/experimental-tab.container.js b/ui/pages/settings/experimental-tab/experimental-tab.container.js index 195d01e88..c9a6d3b74 100644 --- a/ui/pages/settings/experimental-tab/experimental-tab.container.js +++ b/ui/pages/settings/experimental-tab/experimental-tab.container.js @@ -6,12 +6,14 @@ import { setUseCollectibleDetection, setOpenSeaEnabled, setEIP1559V2Enabled, + setTheme, } from '../../../store/actions'; import { getUseTokenDetection, getUseCollectibleDetection, getOpenSeaEnabled, getEIP1559V2Enabled, + getTheme, } from '../../../selectors'; import ExperimentalTab from './experimental-tab.component'; @@ -21,6 +23,7 @@ const mapStateToProps = (state) => { useCollectibleDetection: getUseCollectibleDetection(state), openSeaEnabled: getOpenSeaEnabled(state), eip1559V2Enabled: getEIP1559V2Enabled(state), + theme: getTheme(state), }; }; @@ -31,6 +34,7 @@ const mapDispatchToProps = (dispatch) => { dispatch(setUseCollectibleDetection(val)), setOpenSeaEnabled: (val) => dispatch(setOpenSeaEnabled(val)), setEIP1559V2Enabled: (val) => dispatch(setEIP1559V2Enabled(val)), + setTheme: (val) => dispatch(setTheme(val)), }; }; diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index c287422e9..a74b7facf 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -789,6 +789,16 @@ export function getOpenSeaEnabled(state) { return Boolean(state.metamask.openSeaEnabled); } +/** + * To get the `theme` value which determines which theme is selected + * + * @param {*} state + * @returns Boolean + */ +export function getTheme(state) { + return state.metamask.theme; +} + /** * To retrieve the tokenList produced by TokenListcontroller * diff --git a/ui/store/actions.js b/ui/store/actions.js index 7842b4b5d..97e17de8a 100644 --- a/ui/store/actions.js +++ b/ui/store/actions.js @@ -2325,6 +2325,18 @@ export function setEIP1559V2Enabled(val) { }; } +export function setTheme(val) { + return async (dispatch) => { + dispatch(showLoadingIndication()); + log.debug(`background.setTheme`); + try { + await promisifiedBackground.setTheme(val); + } finally { + dispatch(hideLoadingIndication()); + } + }; +} + export function setIpfsGateway(val) { return (dispatch) => { dispatch(showLoadingIndication());