From 05da3f720c1527a61c09ca616b68793e15029ce3 Mon Sep 17 00:00:00 2001 From: Jony Bursztyn Date: Wed, 30 Oct 2024 09:01:29 -0400 Subject: [PATCH] feat: add privacy mode (#28021) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Adds a privacy mode toggle (an eye icon next to the main balance) that hides all sensitive information/token balances **UPDATE** Here is feedback from @amandaye0h and has been currently implemented in this PR [Figma](https://www.figma.com/design/aMYisczaJyEsYl1TYdcPUL/Portfolio-View?node-id=6219-62460&t=aeTv5cenoUPUrg1c-4) [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28021?quickstart=1) ## **Related issues** Fixes: https://github.com/MetaMask/MetaMask-planning/issues/3416 https://github.com/MetaMask/MetaMask-planning/issues/3418 https://github.com/MetaMask/MetaMask-planning/issues/3419 ## **Manual testing steps** 1. Go to the Wallet page 2. Click on the new Eye icon next to the balance 3. All balances should be hidden 4. Click on the Eye icon once again 5. All balances should be shown ## **Screenshots/Recordings** ### **Before** ### **After** https://github.com/user-attachments/assets/2950ac0c-593d-4daa-aa5d-3e6c3a2d5598 https://github.com/user-attachments/assets/6371c2a2-04fa-48a3-8744-991a1540d5f2 Screenshot 2024-10-22 at 18 43 19 ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: vinnyhoward Co-authored-by: David Walsh --- app/scripts/constants/sentry-state.ts | 1 + .../preferences-controller.test.ts | 2 + .../controllers/preferences-controller.ts | 2 + app/scripts/fixtures/with-preferences.js | 1 + test/e2e/fixture-builder.js | 1 + test/e2e/tests/metrics/errors.spec.js | 1 + .../tests/privacy-mode/privacy-mode.spec.js | 106 ++++++++++++++++++ .../data/onboarding-completion-route.json | 3 +- .../asset-list/native-token/native-token.tsx | 4 +- .../app/assets/token-cell/token-cell.test.tsx | 5 +- .../app/assets/token-cell/token-cell.tsx | 4 +- .../row/__snapshots__/currency.test.tsx.snap | 2 + .../__snapshots__/currency-input.test.js.snap | 4 + ...transaction-gas-fee.component.test.js.snap | 2 + ...-preferenced-currency-display.test.js.snap | 1 + .../aggregated-percentage-overview.test.tsx | 4 + .../aggregated-percentage-overview.tsx | 21 +++- .../app/wallet-overview/coin-overview.tsx | 64 +++++++---- ui/components/app/wallet-overview/index.scss | 6 +- .../sensitive-text/sensitive-text.types.ts | 1 - .../account-list-item.test.js.snap | 3 + .../asset-balance-text.test.tsx.snap | 1 + .../connect-accounts-modal.test.tsx.snap | 2 + .../__snapshots__/connections.test.tsx.snap | 2 + .../send/__snapshots__/send.test.js.snap | 2 + .../__snapshots__/your-accounts.test.tsx.snap | 12 ++ .../token-list-item/token-list-item.tsx | 22 +++- .../currency-display.component.test.js.snap | 3 + .../currency-display.component.js | 24 ++-- ui/ducks/metamask/metamask.js | 1 + .../prepare-bridge-page.test.tsx.snap | 4 + .../confirm-gas-display.test.js.snap | 2 + .../confirm-legacy-gas-display.test.js.snap | 3 + .../confirm-detail-row.component.test.js.snap | 2 + .../multi-layer-fee-message.test.js.snap | 2 + .../confirm-send-ether.test.js.snap | 3 + .../confirm-transaction-base.test.js.snap | 3 + .../remove-snap-account.test.js.snap | 2 + ui/store/actions.ts | 4 + 39 files changed, 284 insertions(+), 48 deletions(-) create mode 100644 test/e2e/tests/privacy-mode/privacy-mode.spec.js diff --git a/app/scripts/constants/sentry-state.ts b/app/scripts/constants/sentry-state.ts index 864f6045b340..1c5dfcfa7ceb 100644 --- a/app/scripts/constants/sentry-state.ts +++ b/app/scripts/constants/sentry-state.ts @@ -243,6 +243,7 @@ export const SENTRY_BACKGROUND_STATE = { showNativeTokenAsMainBalance: true, petnamesEnabled: true, showConfirmationAdvancedDetails: true, + privacyMode: false, }, useExternalServices: false, selectedAddress: false, diff --git a/app/scripts/controllers/preferences-controller.test.ts b/app/scripts/controllers/preferences-controller.test.ts index 9c28ed7c43a0..9215ff8571a7 100644 --- a/app/scripts/controllers/preferences-controller.test.ts +++ b/app/scripts/controllers/preferences-controller.test.ts @@ -730,6 +730,7 @@ describe('preferences controller', () => { expect(controller.state.preferences).toStrictEqual({ autoLockTimeLimit: undefined, showExtensionInFullSizeView: false, + privacyMode: false, showFiatInTestnets: false, showTestNetworks: false, smartTransactionsOptInStatus: null, @@ -764,6 +765,7 @@ describe('preferences controller', () => { useNativeCurrencyAsPrimaryCurrency: true, hideZeroBalanceTokens: false, petnamesEnabled: true, + privacyMode: false, redesignedConfirmationsEnabled: true, redesignedTransactionsEnabled: true, shouldShowAggregatedBalancePopover: true, diff --git a/app/scripts/controllers/preferences-controller.ts b/app/scripts/controllers/preferences-controller.ts index 536ec33b34eb..ee18403c210b 100644 --- a/app/scripts/controllers/preferences-controller.ts +++ b/app/scripts/controllers/preferences-controller.ts @@ -112,6 +112,7 @@ export type Preferences = { redesignedTransactionsEnabled: boolean; featureNotificationsEnabled: boolean; showMultiRpcModal: boolean; + privacyMode: boolean; isRedesignedConfirmationsDeveloperEnabled: boolean; showConfirmationAdvancedDetails: boolean; tokenSortConfig: { @@ -214,6 +215,7 @@ export const getDefaultPreferencesControllerState = isRedesignedConfirmationsDeveloperEnabled: false, showConfirmationAdvancedDetails: false, showMultiRpcModal: false, + privacyMode: false, shouldShowAggregatedBalancePopover: true, // by default user should see popover; tokenSortConfig: { key: 'tokenFiatAmount', diff --git a/app/scripts/fixtures/with-preferences.js b/app/scripts/fixtures/with-preferences.js index 8d1e4293e8a4..c3a482ef8f94 100644 --- a/app/scripts/fixtures/with-preferences.js +++ b/app/scripts/fixtures/with-preferences.js @@ -13,6 +13,7 @@ export const FIXTURES_PREFERENCES = { showNftAutodetectModal: false, isRedesignedConfirmationsDeveloperEnabled: false, showConfirmationAdvancedDetails: false, + privacyMode: false, }, featureFlags: { sendHexData: true, diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index 0cd05c1dde13..4d7e1873bff4 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -75,6 +75,7 @@ function onboardingFixture() { hideZeroBalanceTokens: false, showExtensionInFullSizeView: false, showFiatInTestnets: false, + privacyMode: false, showTestNetworks: false, smartTransactionsOptInStatus: false, showNativeTokenAsMainBalance: true, diff --git a/test/e2e/tests/metrics/errors.spec.js b/test/e2e/tests/metrics/errors.spec.js index 142810394c28..66de2461abc7 100644 --- a/test/e2e/tests/metrics/errors.spec.js +++ b/test/e2e/tests/metrics/errors.spec.js @@ -881,6 +881,7 @@ describe('Sentry errors', function () { preferences: { autoLockTimeLimit: true, // Initialized as undefined showConfirmationAdvancedDetails: true, + privacyMode: false, }, smartTransactionsState: { fees: { diff --git a/test/e2e/tests/privacy-mode/privacy-mode.spec.js b/test/e2e/tests/privacy-mode/privacy-mode.spec.js new file mode 100644 index 000000000000..a4d2c2245752 --- /dev/null +++ b/test/e2e/tests/privacy-mode/privacy-mode.spec.js @@ -0,0 +1,106 @@ +const { strict: assert } = require('assert'); +const { + withFixtures, + unlockWallet, + defaultGanacheOptions, +} = require('../../helpers'); +const FixtureBuilder = require('../../fixture-builder'); + +describe('Privacy Mode', function () { + it('should activate privacy mode, then deactivate it', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder().withPreferencesController().build(), + ganacheOptions: defaultGanacheOptions, + title: this.test.fullTitle(), + }, + async ({ driver }) => { + async function checkForHeaderValue(value) { + const balanceElement = await driver.findElement( + '[data-testid="eth-overview__primary-currency"] .currency-display-component__text', + ); + const surveyText = await balanceElement.getText(); + assert.equal( + surveyText, + value, + `Header balance should be "${value}"`, + ); + } + + async function checkForTokenValue(value) { + const balanceElement = await driver.findElement( + '[data-testid="multichain-token-list-item-secondary-value"]', + ); + const surveyText = await balanceElement.getText(); + assert.equal(surveyText, value, `Token balance should be "${value}"`); + } + + async function checkForPrivacy() { + await checkForHeaderValue('••••••'); + await checkForTokenValue('•••••••••'); + } + + async function checkForNoPrivacy() { + await checkForHeaderValue('25'); + await checkForTokenValue('25 ETH'); + } + + async function togglePrivacy() { + const balanceElement = await driver.findElement( + '[data-testid="eth-overview__primary-currency"] .currency-display-component__text', + ); + const initialText = await balanceElement.getText(); + + await driver.clickElement('[data-testid="sensitive-toggle"]'); + await driver.wait(async () => { + const currentText = await balanceElement.getText(); + return currentText !== initialText; + }, 2e3); + } + + await unlockWallet(driver); + await checkForNoPrivacy(); + await togglePrivacy(); + await checkForPrivacy(); + await togglePrivacy(); + await checkForNoPrivacy(); + }, + ); + }); + + it('should hide fiat balance and token balance when privacy mode is activated', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder().withPreferencesController().build(), + ganacheOptions: defaultGanacheOptions, + title: this.test.fullTitle(), + }, + async ({ driver }) => { + await unlockWallet(driver); + + async function togglePrivacy() { + const balanceElement = await driver.findElement( + '[data-testid="eth-overview__primary-currency"] .currency-display-component__text', + ); + const initialText = await balanceElement.getText(); + + await driver.clickElement('[data-testid="sensitive-toggle"]'); + await driver.wait(async () => { + const currentText = await balanceElement.getText(); + return currentText !== initialText; + }, 2e3); + } + + await togglePrivacy(); + await driver.clickElement('[data-testid="account-menu-icon"]'); + const valueText = await driver.findElement( + '[data-testid="account-value-and-suffix"]', + ); + const valueTextContent = await valueText.getText(); + + assert.equal(valueTextContent, '••••••'); + }, + ); + }); +}); diff --git a/test/integration/data/onboarding-completion-route.json b/test/integration/data/onboarding-completion-route.json index e651e9c2ce29..e47d1379b2eb 100644 --- a/test/integration/data/onboarding-completion-route.json +++ b/test/integration/data/onboarding-completion-route.json @@ -227,7 +227,8 @@ "hideZeroBalanceTokens": false, "petnamesEnabled": true, "redesignedConfirmationsEnabled": true, - "featureNotificationsEnabled": false + "featureNotificationsEnabled": false, + "privacyMode": false }, "preventPollingOnNetworkRestart": false, "previousAppVersion": "", diff --git a/ui/components/app/assets/asset-list/native-token/native-token.tsx b/ui/components/app/assets/asset-list/native-token/native-token.tsx index cf0191b3de66..e63a2902a552 100644 --- a/ui/components/app/assets/asset-list/native-token/native-token.tsx +++ b/ui/components/app/assets/asset-list/native-token/native-token.tsx @@ -8,11 +8,11 @@ import { getMultichainIsMainnet, getMultichainSelectedAccountCachedBalance, } from '../../../../../selectors/multichain'; +import { getPreferences } from '../../../../../selectors'; import { TokenListItem } from '../../../../multichain'; import { useIsOriginalNativeTokenSymbol } from '../../../../../hooks/useIsOriginalNativeTokenSymbol'; import { AssetListProps } from '../asset-list'; import { useNativeTokenBalance } from './use-native-token-balance'; -// import { getPreferences } from '../../../../../selectors'; const NativeToken = ({ onClickAsset }: AssetListProps) => { const nativeCurrency = useSelector(getMultichainNativeCurrency); @@ -20,6 +20,7 @@ const NativeToken = ({ onClickAsset }: AssetListProps) => { const { chainId, ticker, type, rpcUrl } = useSelector( getMultichainCurrentNetwork, ); + const { privacyMode } = useSelector(getPreferences); const isOriginalNativeSymbol = useIsOriginalNativeTokenSymbol( chainId, ticker, @@ -52,6 +53,7 @@ const NativeToken = ({ onClickAsset }: AssetListProps) => { isNativeCurrency isStakeable={isStakeable} showPercentage + privacyMode={privacyMode} /> ); }; diff --git a/ui/components/app/assets/token-cell/token-cell.test.tsx b/ui/components/app/assets/token-cell/token-cell.test.tsx index 882c80964d5b..5cb4b30aea49 100644 --- a/ui/components/app/assets/token-cell/token-cell.test.tsx +++ b/ui/components/app/assets/token-cell/token-cell.test.tsx @@ -5,7 +5,7 @@ import { fireEvent } from '@testing-library/react'; import { useSelector } from 'react-redux'; import { renderWithProvider } from '../../../../../test/lib/render-helpers'; import { useTokenFiatAmount } from '../../../../hooks/useTokenFiatAmount'; -import { getTokenList } from '../../../../selectors'; +import { getTokenList, getPreferences } from '../../../../selectors'; import { getMultichainCurrentChainId, getMultichainIsEvm, @@ -98,6 +98,9 @@ describe('Token Cell', () => { }; const useSelectorMock = useSelector; (useSelectorMock as jest.Mock).mockImplementation((selector) => { + if (selector === getPreferences) { + return { privacyMode: false }; + } if (selector === getTokenList) { return MOCK_GET_TOKEN_LIST; } diff --git a/ui/components/app/assets/token-cell/token-cell.tsx b/ui/components/app/assets/token-cell/token-cell.tsx index 5f5b43d6c098..3a042de1ebb8 100644 --- a/ui/components/app/assets/token-cell/token-cell.tsx +++ b/ui/components/app/assets/token-cell/token-cell.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useSelector } from 'react-redux'; -import { getTokenList } from '../../../../selectors'; +import { getTokenList, getPreferences } from '../../../../selectors'; import { useTokenFiatAmount } from '../../../../hooks/useTokenFiatAmount'; import { TokenListItem } from '../../../multichain'; import { isEqualCaseInsensitive } from '../../../../../shared/modules/string-utils'; @@ -23,6 +23,7 @@ export default function TokenCell({ onClick, }: TokenCellProps) { const tokenList = useSelector(getTokenList); + const { privacyMode } = useSelector(getPreferences); const tokenData = Object.values(tokenList).find( (token) => isEqualCaseInsensitive(token.symbol, symbol) && @@ -51,6 +52,7 @@ export default function TokenCell({ isOriginalTokenSymbol={isOriginalTokenSymbol} address={address} showPercentage + privacyMode={privacyMode} /> ); } diff --git a/ui/components/app/confirm/info/row/__snapshots__/currency.test.tsx.snap b/ui/components/app/confirm/info/row/__snapshots__/currency.test.tsx.snap index e98ec1921081..9f7014dea03e 100644 --- a/ui/components/app/confirm/info/row/__snapshots__/currency.test.tsx.snap +++ b/ui/components/app/confirm/info/row/__snapshots__/currency.test.tsx.snap @@ -12,6 +12,7 @@ exports[`ConfirmInfoRowCurrency should display in currency passed 1`] = ` > $82.65 @@ -37,6 +38,7 @@ exports[`ConfirmInfoRowCurrency should display value in user preferred currency > 0.14861879 diff --git a/ui/components/app/currency-input/__snapshots__/currency-input.test.js.snap b/ui/components/app/currency-input/__snapshots__/currency-input.test.js.snap index f9823b5af9ac..2482a916a5a9 100644 --- a/ui/components/app/currency-input/__snapshots__/currency-input.test.js.snap +++ b/ui/components/app/currency-input/__snapshots__/currency-input.test.js.snap @@ -36,6 +36,7 @@ exports[`CurrencyInput Component rendering should disable unit input 1`] = ` > $0.00 @@ -89,6 +90,7 @@ exports[`CurrencyInput Component rendering should render properly with a fiat va > 0.004327880204275946 @@ -183,6 +185,7 @@ exports[`CurrencyInput Component rendering should render properly with an ETH va > $231.06 @@ -237,6 +240,7 @@ exports[`CurrencyInput Component rendering should render properly without a suff > $0.00 diff --git a/ui/components/app/modals/cancel-transaction/cancel-transaction-gas-fee/__snapshots__/cancel-transaction-gas-fee.component.test.js.snap b/ui/components/app/modals/cancel-transaction/cancel-transaction-gas-fee/__snapshots__/cancel-transaction-gas-fee.component.test.js.snap index 179a3821cad4..f98b3a231970 100644 --- a/ui/components/app/modals/cancel-transaction/cancel-transaction-gas-fee/__snapshots__/cancel-transaction-gas-fee.component.test.js.snap +++ b/ui/components/app/modals/cancel-transaction/cancel-transaction-gas-fee/__snapshots__/cancel-transaction-gas-fee.component.test.js.snap @@ -11,6 +11,7 @@ exports[`CancelTransactionGasFee Component should render 1`] = ` > <0.000001 @@ -26,6 +27,7 @@ exports[`CancelTransactionGasFee Component should render 1`] = ` > <0.000001 diff --git a/ui/components/app/user-preferenced-currency-display/__snapshots__/user-preferenced-currency-display.test.js.snap b/ui/components/app/user-preferenced-currency-display/__snapshots__/user-preferenced-currency-display.test.js.snap index b29efce542e3..4a9fc4d3cf7a 100644 --- a/ui/components/app/user-preferenced-currency-display/__snapshots__/user-preferenced-currency-display.test.js.snap +++ b/ui/components/app/user-preferenced-currency-display/__snapshots__/user-preferenced-currency-display.test.js.snap @@ -8,6 +8,7 @@ exports[`UserPreferencedCurrencyDisplay Component rendering should match snapsho > 0 diff --git a/ui/components/app/wallet-overview/aggregated-percentage-overview.test.tsx b/ui/components/app/wallet-overview/aggregated-percentage-overview.test.tsx index 95e0d92fa2b8..8da096151908 100644 --- a/ui/components/app/wallet-overview/aggregated-percentage-overview.test.tsx +++ b/ui/components/app/wallet-overview/aggregated-percentage-overview.test.tsx @@ -7,6 +7,7 @@ import { getSelectedAccount, getShouldHideZeroBalanceTokens, getTokensMarketData, + getPreferences, } from '../../../selectors'; import { useAccountTotalFiatBalance } from '../../../hooks/useAccountTotalFiatBalance'; import { AggregatedPercentageOverview } from './aggregated-percentage-overview'; @@ -22,6 +23,7 @@ jest.mock('../../../ducks/locale/locale', () => ({ jest.mock('../../../selectors', () => ({ getCurrentCurrency: jest.fn(), getSelectedAccount: jest.fn(), + getPreferences: jest.fn(), getShouldHideZeroBalanceTokens: jest.fn(), getTokensMarketData: jest.fn(), })); @@ -32,6 +34,7 @@ jest.mock('../../../hooks/useAccountTotalFiatBalance', () => ({ const mockGetIntlLocale = getIntlLocale as unknown as jest.Mock; const mockGetCurrentCurrency = getCurrentCurrency as jest.Mock; +const mockGetPreferences = getPreferences as jest.Mock; const mockGetSelectedAccount = getSelectedAccount as unknown as jest.Mock; const mockGetShouldHideZeroBalanceTokens = getShouldHideZeroBalanceTokens as jest.Mock; @@ -159,6 +162,7 @@ describe('AggregatedPercentageOverview', () => { beforeEach(() => { mockGetIntlLocale.mockReturnValue('en-US'); mockGetCurrentCurrency.mockReturnValue('USD'); + mockGetPreferences.mockReturnValue({ privacyMode: false }); mockGetSelectedAccount.mockReturnValue(selectedAccountMock); mockGetShouldHideZeroBalanceTokens.mockReturnValue(false); mockGetTokensMarketData.mockReturnValue(marketDataMock); diff --git a/ui/components/app/wallet-overview/aggregated-percentage-overview.tsx b/ui/components/app/wallet-overview/aggregated-percentage-overview.tsx index 94555d3bc0cd..8c609610daa1 100644 --- a/ui/components/app/wallet-overview/aggregated-percentage-overview.tsx +++ b/ui/components/app/wallet-overview/aggregated-percentage-overview.tsx @@ -7,6 +7,7 @@ import { getSelectedAccount, getShouldHideZeroBalanceTokens, getTokensMarketData, + getPreferences, } from '../../../selectors'; import { useAccountTotalFiatBalance } from '../../../hooks/useAccountTotalFiatBalance'; @@ -19,7 +20,7 @@ import { TextColor, TextVariant, } from '../../../helpers/constants/design-system'; -import { Box, Text } from '../../component-library'; +import { Box, SensitiveText } from '../../component-library'; import { getCalculatedTokenAmount1dAgo } from '../../../helpers/utils/util'; // core already has this exported type but its not yet available in this version @@ -34,6 +35,7 @@ export const AggregatedPercentageOverview = () => { useSelector(getTokensMarketData); const locale = useSelector(getIntlLocale); const fiatCurrency = useSelector(getCurrentCurrency); + const { privacyMode } = useSelector(getPreferences); const selectedAccount = useSelector(getSelectedAccount); const shouldHideZeroBalanceTokens = useSelector( getShouldHideZeroBalanceTokens, @@ -110,7 +112,7 @@ export const AggregatedPercentageOverview = () => { let color = TextColor.textDefault; - if (isValidAmount(amountChange)) { + if (!privacyMode && isValidAmount(amountChange)) { if ((amountChange as number) === 0) { color = TextColor.textDefault; } else if ((amountChange as number) > 0) { @@ -118,26 +120,33 @@ export const AggregatedPercentageOverview = () => { } else { color = TextColor.errorDefault; } + } else { + color = TextColor.textAlternative; } + return ( - {formattedAmountChange} - - + {formattedPercentChange} - + ); }; diff --git a/ui/components/app/wallet-overview/coin-overview.tsx b/ui/components/app/wallet-overview/coin-overview.tsx index 2de787ef23c0..9f267c96a53d 100644 --- a/ui/components/app/wallet-overview/coin-overview.tsx +++ b/ui/components/app/wallet-overview/coin-overview.tsx @@ -28,6 +28,7 @@ import { JustifyContent, TextAlign, TextVariant, + IconColor, } from '../../../helpers/constants/design-system'; ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) import { getPortfolioUrl } from '../../../helpers/utils/portfolio'; @@ -61,7 +62,10 @@ import Spinner from '../../ui/spinner'; import { PercentageAndAmountChange } from '../../multichain/token-list-item/price/percentage-and-amount-change/percentage-and-amount-change'; import { getMultichainIsEvm } from '../../../selectors/multichain'; import { useAccountTotalFiatBalance } from '../../../hooks/useAccountTotalFiatBalance'; -import { setAggregatedBalancePopoverShown } from '../../../store/actions'; +import { + setAggregatedBalancePopoverShown, + setPrivacyMode, +} from '../../../store/actions'; import { useTheme } from '../../../hooks/useTheme'; import { getSpecificSettingsRoute } from '../../../helpers/utils/settings-search'; import { useI18nContext } from '../../../hooks/useI18nContext'; @@ -128,7 +132,7 @@ export const CoinOverview = ({ const shouldShowPopover = useSelector(getShouldShowAggregatedBalancePopover); const isTestnet = useSelector(getIsTestnet); - const { showFiatInTestnets } = useSelector(getPreferences); + const { showFiatInTestnets, privacyMode } = useSelector(getPreferences); const selectedAccount = useSelector(getSelectedAccount); const shouldHideZeroBalanceTokens = useSelector( @@ -163,6 +167,10 @@ export const CoinOverview = ({ dispatch(setAggregatedBalancePopoverShown()); }; + const handleSensitiveToggle = () => { + dispatch(setPrivacyMode(!privacyMode)); + }; + const [referenceElement, setReferenceElement] = useState(null); const setBoxRef = (ref: HTMLSpanElement | null) => { @@ -253,26 +261,38 @@ export const CoinOverview = ({ ref={setBoxRef} > {balanceToDisplay ? ( - + <> + + + ) : ( )} diff --git a/ui/components/app/wallet-overview/index.scss b/ui/components/app/wallet-overview/index.scss index 4759af1ffa8c..a790e8b7ba2e 100644 --- a/ui/components/app/wallet-overview/index.scss +++ b/ui/components/app/wallet-overview/index.scss @@ -70,7 +70,8 @@ display: flex; max-width: inherit; justify-content: center; - flex-wrap: wrap; + align-items: center; + flex-wrap: nowrap; } &__primary-balance { @@ -134,7 +135,8 @@ display: flex; max-width: inherit; justify-content: center; - flex-wrap: wrap; + align-items: center; + flex-wrap: nowrap; } &__primary-balance { diff --git a/ui/components/component-library/sensitive-text/sensitive-text.types.ts b/ui/components/component-library/sensitive-text/sensitive-text.types.ts index 3834190df864..1ea8270d377f 100644 --- a/ui/components/component-library/sensitive-text/sensitive-text.types.ts +++ b/ui/components/component-library/sensitive-text/sensitive-text.types.ts @@ -30,7 +30,6 @@ export type SensitiveTextProps = Omit< * @default false */ isHidden?: boolean; - /** * Determines the length of the hidden text (number of asterisks). * Can be a predefined SensitiveTextLength or a custom string number. diff --git a/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap b/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap index 51f6f2e905f9..e320bd1de0e3 100644 --- a/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap +++ b/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap @@ -242,6 +242,7 @@ exports[`AccountListItem renders AccountListItem component and shows account nam > $100,000.00 @@ -538,6 +539,7 @@ exports[`AccountListItem renders AccountListItem component and shows account nam > 0.006 @@ -581,6 +583,7 @@ exports[`AccountListItem renders AccountListItem component and shows account nam > 0.006 diff --git a/ui/components/multichain/asset-picker-amount/asset-balance/__snapshots__/asset-balance-text.test.tsx.snap b/ui/components/multichain/asset-picker-amount/asset-balance/__snapshots__/asset-balance-text.test.tsx.snap index 9c0bd9c49482..a0c808186082 100644 --- a/ui/components/multichain/asset-picker-amount/asset-balance/__snapshots__/asset-balance-text.test.tsx.snap +++ b/ui/components/multichain/asset-picker-amount/asset-balance/__snapshots__/asset-balance-text.test.tsx.snap @@ -8,6 +8,7 @@ exports[`AssetBalanceText matches snapshot 1`] = ` > prefix-fiat value diff --git a/ui/components/multichain/connect-accounts-modal/__snapshots__/connect-accounts-modal.test.tsx.snap b/ui/components/multichain/connect-accounts-modal/__snapshots__/connect-accounts-modal.test.tsx.snap index d53c8e7d8d8a..b4a4836db2d6 100644 --- a/ui/components/multichain/connect-accounts-modal/__snapshots__/connect-accounts-modal.test.tsx.snap +++ b/ui/components/multichain/connect-accounts-modal/__snapshots__/connect-accounts-modal.test.tsx.snap @@ -358,6 +358,7 @@ exports[`Connect More Accounts Modal should render correctly 1`] = ` > 0 @@ -401,6 +402,7 @@ exports[`Connect More Accounts Modal should render correctly 1`] = ` > 0 diff --git a/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap b/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap index afd02098086f..ad2dc490d7c0 100644 --- a/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap +++ b/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap @@ -297,6 +297,7 @@ exports[`Connections Content should render correctly 1`] = ` > 966.988 @@ -340,6 +341,7 @@ exports[`Connections Content should render correctly 1`] = ` > 966.988 diff --git a/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap b/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap index 814dc934fc9a..7b0605b7ea60 100644 --- a/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap +++ b/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap @@ -474,6 +474,7 @@ exports[`SendPage render and initialization should render correctly even when a > $0.00 @@ -517,6 +518,7 @@ exports[`SendPage render and initialization should render correctly even when a > 0 diff --git a/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap b/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap index 9fbf7e29879b..71431a330f94 100644 --- a/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap +++ b/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap @@ -248,6 +248,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 966.988 @@ -291,6 +292,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 966.988 @@ -545,6 +547,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -588,6 +591,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -842,6 +846,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -885,6 +890,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -1148,6 +1154,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -1191,6 +1198,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -1445,6 +1453,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -1488,6 +1497,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -1755,6 +1765,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -1798,6 +1809,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 diff --git a/ui/components/multichain/token-list-item/token-list-item.tsx b/ui/components/multichain/token-list-item/token-list-item.tsx index 0c3c46114541..bf3968963465 100644 --- a/ui/components/multichain/token-list-item/token-list-item.tsx +++ b/ui/components/multichain/token-list-item/token-list-item.tsx @@ -34,6 +34,8 @@ import { ModalFooter, ModalHeader, ModalOverlay, + SensitiveText, + SensitiveTextLength, Text, } from '../../component-library'; import { @@ -82,6 +84,7 @@ type TokenListItemProps = { address?: string | null; showPercentage?: boolean; isPrimaryTokenSymbolHidden?: boolean; + privacyMode?: boolean; }; export const TokenListItem = ({ @@ -99,6 +102,7 @@ export const TokenListItem = ({ isStakeable = false, address = null, showPercentage = false, + privacyMode = false, }: TokenListItemProps) => { const t = useI18nContext(); const isEvm = useSelector(getMultichainIsEvm); @@ -375,17 +379,19 @@ export const TokenListItem = ({ ariaLabel={''} /> - {primary}{' '} {isNativeCurrency || isPrimaryTokenSymbolHidden ? '' : tokenSymbol} - + ) : ( - {secondary} - - + {primary}{' '} {isNativeCurrency || isPrimaryTokenSymbolHidden ? '' : tokenSymbol} - + )} diff --git a/ui/components/ui/currency-display/__snapshots__/currency-display.component.test.js.snap b/ui/components/ui/currency-display/__snapshots__/currency-display.component.test.js.snap index 44ba7be60b6f..eeb40144894b 100644 --- a/ui/components/ui/currency-display/__snapshots__/currency-display.component.test.js.snap +++ b/ui/components/ui/currency-display/__snapshots__/currency-display.component.test.js.snap @@ -8,6 +8,7 @@ exports[`CurrencyDisplay Component should match default snapshot 1`] = ` > @@ -21,6 +22,7 @@ exports[`CurrencyDisplay Component should render text with a className 1`] = ` > $123.45 @@ -36,6 +38,7 @@ exports[`CurrencyDisplay Component should render text with a prefix 1`] = ` > - $123.45 diff --git a/ui/components/ui/currency-display/currency-display.component.js b/ui/components/ui/currency-display/currency-display.component.js index ca9322661d79..a0bb114409f6 100644 --- a/ui/components/ui/currency-display/currency-display.component.js +++ b/ui/components/ui/currency-display/currency-display.component.js @@ -1,9 +1,11 @@ import React from 'react'; +import { useSelector } from 'react-redux'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { useCurrencyDisplay } from '../../../hooks/useCurrencyDisplay'; import { EtherDenomination } from '../../../../shared/constants/common'; -import { Text, Box } from '../../component-library'; +import { getPreferences } from '../../../selectors'; +import { SensitiveText, Box } from '../../component-library'; import { AlignItems, Display, @@ -35,6 +37,7 @@ export default function CurrencyDisplay({ isAggregatedFiatOverviewBalance = false, ...props }) { + const { privacyMode } = useSelector(getPreferences); const [title, parts] = useCurrencyDisplay(value, { account, displayValue, @@ -68,26 +71,33 @@ export default function CurrencyDisplay({ {prefixComponent} ) : null} - {parts.prefix} {parts.value} - + {parts.suffix ? ( - {parts.suffix} - + ) : null} ); diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index 05cc6d46cb27..d7fa8211b3b7 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -50,6 +50,7 @@ const initialState = { smartTransactionsOptInStatus: false, petnamesEnabled: true, featureNotificationsEnabled: false, + privacyMode: false, showMultiRpcModal: false, }, firstTimeFlowType: null, diff --git a/ui/pages/bridge/prepare/__snapshots__/prepare-bridge-page.test.tsx.snap b/ui/pages/bridge/prepare/__snapshots__/prepare-bridge-page.test.tsx.snap index b406cafe0941..4284c1893d7c 100644 --- a/ui/pages/bridge/prepare/__snapshots__/prepare-bridge-page.test.tsx.snap +++ b/ui/pages/bridge/prepare/__snapshots__/prepare-bridge-page.test.tsx.snap @@ -107,6 +107,7 @@ exports[`PrepareBridgePage should render the component, with initial state 1`] = > $0.00 @@ -191,6 +192,7 @@ exports[`PrepareBridgePage should render the component, with initial state 1`] = > $0.00 @@ -316,6 +318,7 @@ exports[`PrepareBridgePage should render the component, with inputs set 1`] = ` > $0.00 @@ -444,6 +447,7 @@ exports[`PrepareBridgePage should render the component, with inputs set 1`] = ` > $0.00 diff --git a/ui/pages/confirmations/components/confirm-gas-display/__snapshots__/confirm-gas-display.test.js.snap b/ui/pages/confirmations/components/confirm-gas-display/__snapshots__/confirm-gas-display.test.js.snap index b13f1f6d31e9..e35c865829b8 100644 --- a/ui/pages/confirmations/components/confirm-gas-display/__snapshots__/confirm-gas-display.test.js.snap +++ b/ui/pages/confirmations/components/confirm-gas-display/__snapshots__/confirm-gas-display.test.js.snap @@ -51,6 +51,7 @@ exports[`ConfirmGasDisplay should match snapshot 1`] = ` > 0.001197 @@ -113,6 +114,7 @@ exports[`ConfirmGasDisplay should match snapshot 1`] = ` > 0.00147 diff --git a/ui/pages/confirmations/components/confirm-gas-display/confirm-legacy-gas-display/__snapshots__/confirm-legacy-gas-display.test.js.snap b/ui/pages/confirmations/components/confirm-gas-display/confirm-legacy-gas-display/__snapshots__/confirm-legacy-gas-display.test.js.snap index f6e40da8118c..db005f8c02e0 100644 --- a/ui/pages/confirmations/components/confirm-gas-display/confirm-legacy-gas-display/__snapshots__/confirm-legacy-gas-display.test.js.snap +++ b/ui/pages/confirmations/components/confirm-gas-display/confirm-legacy-gas-display/__snapshots__/confirm-legacy-gas-display.test.js.snap @@ -51,6 +51,7 @@ exports[`ConfirmLegacyGasDisplay should match snapshot 1`] = ` > 0.000021 @@ -67,6 +68,7 @@ exports[`ConfirmLegacyGasDisplay should match snapshot 1`] = ` > 0.000021 @@ -100,6 +102,7 @@ exports[`ConfirmLegacyGasDisplay should match snapshot 1`] = ` > 0.000021 diff --git a/ui/pages/confirmations/components/confirm-page-container/confirm-detail-row/__snapshots__/confirm-detail-row.component.test.js.snap b/ui/pages/confirmations/components/confirm-page-container/confirm-detail-row/__snapshots__/confirm-detail-row.component.test.js.snap index 8a3053f67d88..073d23aebf88 100644 --- a/ui/pages/confirmations/components/confirm-page-container/confirm-detail-row/__snapshots__/confirm-detail-row.component.test.js.snap +++ b/ui/pages/confirmations/components/confirm-page-container/confirm-detail-row/__snapshots__/confirm-detail-row.component.test.js.snap @@ -27,6 +27,7 @@ exports[`Confirm Detail Row Component should match snapshot 1`] = ` 0 @@ -47,6 +48,7 @@ exports[`Confirm Detail Row Component should match snapshot 1`] = ` 0 diff --git a/ui/pages/confirmations/components/multilayer-fee-message/__snapshots__/multi-layer-fee-message.test.js.snap b/ui/pages/confirmations/components/multilayer-fee-message/__snapshots__/multi-layer-fee-message.test.js.snap index 362926d71ce8..bf7516245e8b 100644 --- a/ui/pages/confirmations/components/multilayer-fee-message/__snapshots__/multi-layer-fee-message.test.js.snap +++ b/ui/pages/confirmations/components/multilayer-fee-message/__snapshots__/multi-layer-fee-message.test.js.snap @@ -105,6 +105,7 @@ exports[`Multi layer fee message when balance and token price checker is enabled > $0.00 @@ -152,6 +153,7 @@ exports[`Multi layer fee message when balance and token price checker is enabled > $0.56 diff --git a/ui/pages/confirmations/confirm-send-ether/__snapshots__/confirm-send-ether.test.js.snap b/ui/pages/confirmations/confirm-send-ether/__snapshots__/confirm-send-ether.test.js.snap index 2bbceca19ec8..0da1e036c9f0 100644 --- a/ui/pages/confirmations/confirm-send-ether/__snapshots__/confirm-send-ether.test.js.snap +++ b/ui/pages/confirmations/confirm-send-ether/__snapshots__/confirm-send-ether.test.js.snap @@ -306,6 +306,7 @@ exports[`ConfirmSendEther should render correct information for for confirm send 0 @@ -469,6 +470,7 @@ exports[`ConfirmSendEther should render correct information for for confirm send > 0.000021 @@ -522,6 +524,7 @@ exports[`ConfirmSendEther should render correct information for for confirm send > 0.00021 diff --git a/ui/pages/confirmations/confirm-transaction-base/__snapshots__/confirm-transaction-base.test.js.snap b/ui/pages/confirmations/confirm-transaction-base/__snapshots__/confirm-transaction-base.test.js.snap index c89fa090cd3d..3bd2313850a4 100644 --- a/ui/pages/confirmations/confirm-transaction-base/__snapshots__/confirm-transaction-base.test.js.snap +++ b/ui/pages/confirmations/confirm-transaction-base/__snapshots__/confirm-transaction-base.test.js.snap @@ -264,6 +264,7 @@ exports[`Confirm Transaction Base should match snapshot 1`] = ` > 0.0001 @@ -407,6 +408,7 @@ exports[`Confirm Transaction Base should match snapshot 1`] = ` > 0.000021 @@ -440,6 +442,7 @@ exports[`Confirm Transaction Base should match snapshot 1`] = ` > 0.000021 diff --git a/ui/pages/confirmations/confirmation/templates/__snapshots__/remove-snap-account.test.js.snap b/ui/pages/confirmations/confirmation/templates/__snapshots__/remove-snap-account.test.js.snap index a541bb5f7ae3..317071e6be26 100644 --- a/ui/pages/confirmations/confirmation/templates/__snapshots__/remove-snap-account.test.js.snap +++ b/ui/pages/confirmations/confirmation/templates/__snapshots__/remove-snap-account.test.js.snap @@ -334,6 +334,7 @@ exports[`remove-snap-account confirmation should match snapshot 1`] = ` > 966.988 @@ -377,6 +378,7 @@ exports[`remove-snap-account confirmation should match snapshot 1`] = ` > 966.988 diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 82054a80f3cd..f8e232f3a519 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -3089,6 +3089,10 @@ export function setRedesignedConfirmationsEnabled(value: boolean) { return setPreference('redesignedConfirmationsEnabled', value); } +export function setPrivacyMode(value: boolean) { + return setPreference('privacyMode', value, false); +} + export function setRedesignedTransactionsEnabled(value: boolean) { return setPreference('redesignedTransactionsEnabled', value); }