Skip to content
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Rebrand "Profile syncing" to "Backup and sync", adding a dedicated settings menu and more ([#32129](https://github.com/MetaMask/metamask-extension/pull/32129))
- Show UI error to user if vault should exist, but is missing ([#31404](https://github.com/MetaMask/metamask-extension/pull/31404))
- Add new security preference: `skipDeepLinkInterstitial` ([#33634](https://github.com/MetaMask/metamask-extension/pull/33634))
- Add RPC (sub)domain tracking to transaction event metrics for RPC endpoints usage ([#32076](https://github.com/MetaMask/metamask-extension/pull/32076))
- Integrate dynamic content banners ([#32101](https://github.com/MetaMask/metamask-extension/pull/32101))
- Add ability to update remote mode delegations and remove the now redundant "Update to a smart account" step on setup flows ([#32713](https://github.com/MetaMask/metamask-extension/pull/32713))
Expand Down
6 changes: 6 additions & 0 deletions app/_locales/en/messages.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions app/_locales/en_GB/messages.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions app/scripts/controllers/preferences-controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,7 @@ describe('preferences controller', () => {
useNativeCurrencyAsPrimaryCurrency: true,
hideZeroBalanceTokens: false,
petnamesEnabled: true,
skipDeepLinkInterstitial: false,
dismissSmartAccountSuggestionEnabled: false,
featureNotificationsEnabled: false,
showConfirmationAdvancedDetails: false,
Expand Down Expand Up @@ -697,6 +698,7 @@ describe('preferences controller', () => {
useNativeCurrencyAsPrimaryCurrency: true,
hideZeroBalanceTokens: false,
petnamesEnabled: true,
skipDeepLinkInterstitial: false,
privacyMode: false,
dismissSmartAccountSuggestionEnabled: false,
featureNotificationsEnabled: false,
Expand Down
2 changes: 2 additions & 0 deletions app/scripts/controllers/preferences-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export type Preferences = {
};
tokenNetworkFilter: Record<string, boolean>;
dismissSmartAccountSuggestionEnabled: boolean;
skipDeepLinkInterstitial: boolean;
smartAccountOptIn: boolean;
};

Expand Down Expand Up @@ -202,6 +203,7 @@ export const getDefaultPreferencesControllerState =
sortCallback: 'stringNumeric',
},
tokenNetworkFilter: {},
skipDeepLinkInterstitial: false,
},
// ENS decentralized website resolution
ipfsGateway: IPFS_DEFAULT_GATEWAY_URL,
Expand Down
1 change: 1 addition & 0 deletions test/e2e/fixture-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ function onboardingFixture() {
[ETHERSCAN_SUPPORTED_CHAIN_IDS.MEGAETH_TESTNET]: true,
[ETHERSCAN_SUPPORTED_CHAIN_IDS.MONAD_TESTNET]: true,
},
skipDeepLinkInterstitial: false,
},
SelectedNetworkController: {
domains: {},
Expand Down
10 changes: 9 additions & 1 deletion ui/helpers/constants/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,14 +204,22 @@ const SETTINGS_CONSTANTS = [
route: `${SECURITY_ROUTE}#reveal-secretrecovery`,
icon: 'fa fa-lock',
},
// securityAndPrivacy settingsRefs[3]
// securityAndPrivacy settingsRefs[2]
{
tabMessage: (t) => t('securityAndPrivacy'),
sectionMessage: (t) => t('usePhishingDetection'),
descriptionMessage: (t) => t('usePhishingDetectionDescription'),
route: `${SECURITY_ROUTE}#phishing-detection`,
icon: 'fa fa-lock',
},
// securityAndPrivacy settingsRefs[3]
{
tabMessage: (t) => t('securityAndPrivacy'),
sectionMessage: (t) => t('skipDeepLinkInterstitial'),
descriptionMessage: (t) => t('skipDeepLinkInterstitialDescription'),
route: `${SECURITY_ROUTE}#skip-deep-link-interstitial`,
icon: 'fa fa-lock',
},
// securityAndPrivacy settingsRefs[4]
{
tabMessage: (t) => t('securityAndPrivacy'),
Expand Down
2 changes: 1 addition & 1 deletion ui/helpers/utils/settings-search.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ describe('Settings Search Utils', () => {
it('returns "Security & privacy" section count', () => {
expect(
getNumberOfSettingRoutesInTab(t, t('securityAndPrivacy')),
).toStrictEqual(20);
).toStrictEqual(21);
});

it('returns "Network" section count', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,82 @@ exports[`Security Tab should match snapshot 1`] = `
</div>
</div>
</div>
<div
class="settings-page__content-padded"
>
<div
class="mm-box settings-page__content-row mm-box--display-flex mm-box--gap-4 mm-box--flex-direction-column"
data-testid="setting-skip-deep-link-interstitial"
id="skip-deep-link-interstitial"
>
<div
class="mm-box settings-page__content-row mm-box--display-flex mm-box--gap-4 mm-box--flex-direction-row mm-box--justify-content-space-between"
>
<div
class="settings-page__content-item"
>
<span>
Don't show interstitial screen when opening deep links
</span>
<div
class="settings-page__content-description"
>
Enabling this option will skip the interstitial screen that is shown when opening deep links in MetaMask. A "deep link" is a link like https://link.metamask.io/home that will cause MetaMask to open; these links can be obfuscated by others. The interstitial screen is designed to protect you from accidentally opening pages within MetaMask that might display your accounts, tokens, history, balances, settings, or other potentially sensitive information. This setting only applies to links signed by MetaMask.
</div>
</div>
<div
class="settings-page__content-item-col"
data-testid="skipDeepLinkInterstitial"
>
<label
class="toggle-button toggle-button--off"
tabindex="0"
>
<div
style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;"
>
<div
style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(133, 139, 154);"
>
<div
style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;"
/>
<div
style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgba(255, 255, 255, 0.6); bottom: 0px; margin-top: auto; margin-bottom: auto; padding-right: 5px; line-height: 0; width: 26px; height: 20px; opacity: 1;"
/>
</div>
<div
style="position: absolute; height: 100%; top: 0px; left: 0px; display: flex; flex: 1; align-self: stretch; align-items: center; justify-content: flex-start;"
>
<div
style="width: 18px; height: 18px; display: flex; align-self: center; box-shadow: var(--shadow-size-xs) var(--color-shadow-default); border-radius: 50%; box-sizing: border-box; position: relative; background-color: rgb(255, 255, 255); left: 3px;"
/>
</div>
<input
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: absolute; width: 1px;"
type="checkbox"
value="false"
/>
</div>
<div
class="toggle-button__status"
>
<span
class="toggle-button__label-off"
>
Off
</span>
<span
class="toggle-button__label-on"
>
On
</span>
</div>
</label>
</div>
</div>
</div>
</div>
<div>
<span
class="settings-page__security-tab-sub-header"
Expand Down
56 changes: 54 additions & 2 deletions ui/pages/settings/security-tab/security-tab.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ export default class SecurityTab extends PureComponent {
securityAlertsEnabled: PropTypes.bool,
useExternalServices: PropTypes.bool,
toggleExternalServices: PropTypes.func,
setSkipDeepLinkInterstitial: PropTypes.func.isRequired,
skipDeepLinkInterstitial: PropTypes.bool,
setSecurityAlertsEnabled: PropTypes.func,
metaMetricsDataDeletionId: PropTypes.string,
hdEntropyIndex: PropTypes.number,
Expand Down Expand Up @@ -267,7 +269,7 @@ export default class SecurityTab extends PureComponent {
</div>
<div className="settings-page__content-padded">
<Box
ref={this.settingsRefs[3]}
ref={this.settingsRefs[2]}
className="settings-page__content-row"
display={Display.Flex}
flexDirection={FlexDirection.Row}
Expand Down Expand Up @@ -311,7 +313,7 @@ export default class SecurityTab extends PureComponent {

return (
<Box
ref={this.settingsRefs[3]}
ref={this.settingsRefs[4]}
className="settings-page__content-row"
display={Display.Flex}
flexDirection={FlexDirection.Row}
Expand Down Expand Up @@ -1133,6 +1135,53 @@ export default class SecurityTab extends PureComponent {
);
}

renderSkipDeepLinkInterstitial() {
const { t } = this.context;
const { skipDeepLinkInterstitial, setSkipDeepLinkInterstitial } =
this.props;

return (
<>
<Box
ref={this.settingsRefs[3]}
className="settings-page__content-row"
data-testid="setting-skip-deep-link-interstitial"
display={Display.Flex}
flexDirection={FlexDirection.Column}
gap={4}
id="skip-deep-link-interstitial"
>
<Box
className="settings-page__content-row"
gap={4}
display={Display.Flex}
flexDirection={FlexDirection.Row}
justifyContent={JustifyContent.spaceBetween}
>
<div className="settings-page__content-item">
<span>{t('skipDeepLinkInterstitial')}</span>
<div className="settings-page__content-description">
{t('skipDeepLinkInterstitialDescription')}
</div>
</div>

<div
className="settings-page__content-item-col"
data-testid="skipDeepLinkInterstitial"
>
<ToggleButton
value={skipDeepLinkInterstitial}
onToggle={(value) => setSkipDeepLinkInterstitial(!value)}
offLabel={t('off')}
onLabel={t('on')}
/>
</div>
</Box>
</Box>
</>
);
}

renderDataCollectionWarning = () => {
const { t } = this.context;

Expand Down Expand Up @@ -1203,6 +1252,9 @@ export default class SecurityTab extends PureComponent {
<div className="settings-page__content-padded">
{this.renderPhishingDetectionToggle()}
</div>
<div className="settings-page__content-padded">
{this.renderSkipDeepLinkInterstitial()}
</div>

<div>
<span className="settings-page__security-tab-sub-header">
Expand Down
7 changes: 7 additions & 0 deletions ui/pages/settings/security-tab/security-tab.container.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ import {
setUseTransactionSimulations,
setSecurityAlertsEnabled,
updateDataDeletionTaskStatus,
setSkipDeepLinkInterstitial,
} from '../../../store/actions';
import {
getIsSecurityAlertsEnabled,
getMetaMetricsDataDeletionId,
getHDEntropyIndex,
getPreferences,
} from '../../../selectors/selectors';
import { getNetworkConfigurationsByChainId } from '../../../../shared/modules/selectors/networks';
import { openBasicFunctionalityModal } from '../../../ducks/app/app';
Expand All @@ -51,6 +53,8 @@ const mapStateToProps = (state) => {
useExternalNameSources,
} = metamask;

const { skipDeepLinkInterstitial } = getPreferences(state);

const networkConfigurations = getNetworkConfigurationsByChainId(state);

return {
Expand All @@ -73,6 +77,7 @@ const mapStateToProps = (state) => {
useTransactionSimulations: metamask.useTransactionSimulations,
metaMetricsDataDeletionId: getMetaMetricsDataDeletionId(state),
hdEntropyIndex: getHDEntropyIndex(state),
skipDeepLinkInterstitial: Boolean(skipDeepLinkInterstitial),
isSeedPhraseBackedUp: getIsPrimarySeedPhraseBackedUp(state),
};
};
Expand All @@ -96,6 +101,8 @@ const mapDispatchToProps = (dispatch) => {
dispatch(setUseSafeChainsListValidation(val)),
setBasicFunctionalityModalOpen: () =>
dispatch(openBasicFunctionalityModal()),
setSkipDeepLinkInterstitial: (val) =>
dispatch(setSkipDeepLinkInterstitial(val)),
setOpenSeaEnabled: (val) => dispatch(setOpenSeaEnabled(val)),
setUseNftDetection: (val) => dispatch(setUseNftDetection(val)),
setUse4ByteResolution: (value) => {
Expand Down
4 changes: 4 additions & 0 deletions ui/pages/settings/security-tab/security-tab.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,10 @@ describe('Security Tab', () => {
).toBe(true);
});

it('toggles skipDeepLinkInterstitial', async () => {
expect(toggleCheckbox('skipDeepLinkInterstitial', false)).toBe(true);
});

it('clicks "Add Custom Network"', async () => {
const user = userEvent.setup();
renderWithProviders(<SecurityTab />, mockStore);
Expand Down
10 changes: 10 additions & 0 deletions ui/store/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6664,6 +6664,16 @@ export async function isRelaySupported(chainId: Hex): Promise<boolean> {
]);
}

/**
* Sets the preference for skipping the interstitial page when opening a deep link.
*
* @param value - Whether to skip the interstitial page when opening a deep link.
* @returns A promise that resolves when the preference is set.
*/
export function setSkipDeepLinkInterstitial(value: boolean) {
return setPreference('skipDeepLinkInterstitial', value, false);
}

/**
* Asks the UI to reload the browser extension safely.
*
Expand Down
Loading