import { startCase } from 'lodash'; import PropTypes from 'prop-types'; import React, { PureComponent } from 'react'; import { addUrlProtocolPrefix, getEnvironmentType, } from '../../../../app/scripts/lib/util'; import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app'; import { MetaMetricsEventCategory, MetaMetricsEventKeyType, MetaMetricsEventName, } from '../../../../shared/constants/metametrics'; import { IPFS_DEFAULT_GATEWAY_URL } from '../../../../shared/constants/network'; import { AUTO_DETECT_TOKEN_LEARN_MORE_LINK, COINGECKO_LINK, CONSENSYS_PRIVACY_LINK, CRYPTOCOMPARE_LINK, ETHERSCAN_PRIVACY_LINK, PRIVACY_POLICY_LINK, } from '../../../../shared/lib/ui-utils'; import SRPQuiz from '../../../components/app/srp-quiz-modal/SRPQuiz'; import { BUTTON_SIZES, Button, } from '../../../components/component-library/button'; import TextField from '../../../components/ui/text-field'; import ToggleButton from '../../../components/ui/toggle-button'; import { ADD_POPULAR_CUSTOM_NETWORK } from '../../../helpers/constants/routes'; import { getNumberOfSettingsInSection, handleSettingsRefs, } from '../../../helpers/utils/settings-search'; import { Box, Text } from '../../../components/component-library'; import { TextColor, TextVariant, } from '../../../helpers/constants/design-system'; export default class SecurityTab extends PureComponent { static contextTypes = { t: PropTypes.func, trackEvent: PropTypes.func, }; static propTypes = { warning: PropTypes.string, history: PropTypes.object, participateInMetaMetrics: PropTypes.bool.isRequired, setParticipateInMetaMetrics: PropTypes.func.isRequired, showIncomingTransactions: PropTypes.bool.isRequired, setShowIncomingTransactionsFeatureFlag: PropTypes.func.isRequired, setUsePhishDetect: PropTypes.func.isRequired, usePhishDetect: PropTypes.bool.isRequired, useTokenDetection: PropTypes.bool.isRequired, setUseTokenDetection: PropTypes.func.isRequired, setIpfsGateway: PropTypes.func.isRequired, ipfsGateway: PropTypes.string.isRequired, useMultiAccountBalanceChecker: PropTypes.bool.isRequired, setUseMultiAccountBalanceChecker: PropTypes.func.isRequired, useCurrencyRateCheck: PropTypes.bool.isRequired, setUseCurrencyRateCheck: PropTypes.func.isRequired, useAddressBarEnsResolution: PropTypes.bool.isRequired, setUseAddressBarEnsResolution: PropTypes.func.isRequired, }; state = { ipfsGateway: this.props.ipfsGateway, ipfsGatewayError: '', srpQuizModalVisible: false, ipfsToggle: false, }; settingsRefCounter = 0; settingsRefs = Array( getNumberOfSettingsInSection( this.context.t, this.context.t('securityAndPrivacy'), ), ) .fill(undefined) .map(() => { return React.createRef(); }); componentDidUpdate() { const { t } = this.context; handleSettingsRefs(t, t('securityAndPrivacy'), this.settingsRefs); } componentDidMount() { const { t } = this.context; handleSettingsRefs(t, t('securityAndPrivacy'), this.settingsRefs); } toggleSetting(value, eventName, eventAction, toggleMethod) { this.context.trackEvent({ category: MetaMetricsEventCategory.Settings, event: eventName, properties: { action: eventAction, legacy_event: true, }, }); toggleMethod(!value); } hideSrpQuizModal = () => this.setState({ srpQuizModalVisible: false }); renderSeedWords() { const { t } = this.context; return ( <>
{t('secretRecoveryPhrase')}
{this.state.srpQuizModalVisible && ( )}
); } renderIncomingTransactionsOptIn() { const { t } = this.context; const { showIncomingTransactions, setShowIncomingTransactionsFeatureFlag } = this.props; return (
{t('showIncomingTransactions')}
{t('showIncomingTransactionsDescription', [ // TODO: Update to use real link {t('etherscan')} , // TODO: Update to use real link {t('privacyMsg')} , ])}
setShowIncomingTransactionsFeatureFlag(!value) } offLabel={t('off')} onLabel={t('on')} />
); } renderPhishingDetectionToggle() { const { t } = this.context; const { usePhishDetect, setUsePhishDetect } = this.props; return (
{t('usePhishingDetection')}
{t('usePhishingDetectionDescription')}
setUsePhishDetect(!value)} offLabel={t('off')} onLabel={t('on')} />
); } renderMetaMetricsOptIn() { const { t } = this.context; const { participateInMetaMetrics, setParticipateInMetaMetrics } = this.props; return (
{t('participateInMetaMetrics')}
{t('participateInMetaMetricsDescription')}
setParticipateInMetaMetrics(!value)} offLabel={t('off')} onLabel={t('on')} />
); } renderChooseYourNetworkButton() { const { t } = this.context; return (
{t('chooseYourNetwork')}
{t('chooseYourNetworkDescription', [ // TODO: Update to use real link {t('privacyMsg')} , ])}
); } renderIpfsGatewayControl() { const { t } = this.context; const { ipfsGatewayError } = this.state; const { useAddressBarEnsResolution, setUseAddressBarEnsResolution } = this.props; const handleIpfsGatewaySave = (gateway) => { const url = gateway ? new URL(addUrlProtocolPrefix(gateway)) : ''; const { host } = url; this.props.setIpfsGateway(host); }; const handleIpfsGatewayChange = (url) => { this.setState(() => { let ipfsError = ''; try { const urlObj = new URL(addUrlProtocolPrefix(url)); if (!urlObj.host) { throw new Error(); } // don't allow the use of this gateway if (urlObj.host === 'gateway.ipfs.io') { throw new Error('Forbidden gateway'); } } catch (error) { ipfsError = error.message === 'Forbidden gateway' ? t('forbiddenIpfsGateway') : t('invalidIpfsGateway'); } handleIpfsGatewaySave(url); return { ipfsGateway: url, ipfsGatewayError: ipfsError, }; }); }; const handleIpfsToggle = (url) => { url?.length < 1 ? handleIpfsGatewayChange(IPFS_DEFAULT_GATEWAY_URL) : handleIpfsGatewayChange(''); }; return (
{t('addCustomIPFSGateway')}
{t('addCustomIPFSGatewayDescription')}
{ handleIpfsToggle(value); this.setState({ ipfsToggle: Boolean(value) }); }} offLabel={t('off')} onLabel={t('on')} />
{!this.state.ipfsToggle && (
handleIpfsGatewayChange(e.target.value)} error={ipfsGatewayError} fullWidth margin="dense" />
)}
{t('ensDomainsSettingTitle')}
{t('ensDomainsSettingDescriptionIntro')} {t('ensDomainsSettingDescriptionPoint1')} {t('ensDomainsSettingDescriptionPoint2')} {t('ensDomainsSettingDescriptionPoint3')} {t('ensDomainsSettingDescriptionOutro')}
setUseAddressBarEnsResolution(!value)} offLabel={t('off')} onLabel={t('on')} />
); } renderAutoDetectTokensToggle() { const { t } = this.context; const { useTokenDetection, setUseTokenDetection } = this.props; return (
{t('autoDetectTokens')}
{t('autoDetectTokensDescription', [ // TODO: Update to use real link {startCase(t('learnMore'))} , ])}
{ this.toggleSetting( value, MetaMetricsEventName.KeyAutoDetectTokens, MetaMetricsEventName.KeyAutoDetectTokens, setUseTokenDetection, ); }} offLabel={t('off')} onLabel={t('on')} />
); } renderBatchAccountBalanceRequestsToggle() { const { t } = this.context; const { useMultiAccountBalanceChecker, setUseMultiAccountBalanceChecker } = this.props; return (
{t('useMultiAccountBalanceChecker')}
{t('useMultiAccountBalanceCheckerSettingDescription')}
{ this.toggleSetting( value, MetaMetricsEventName.KeyBatchAccountBalanceRequests, MetaMetricsEventName.KeyBatchAccountBalanceRequests, setUseMultiAccountBalanceChecker, ); }} offLabel={t('off')} onLabel={t('on')} />
); } renderCurrencyRateCheckToggle() { const { t } = this.context; const { useCurrencyRateCheck, setUseCurrencyRateCheck } = this.props; return (
{t('currencyRateCheckToggle')}
{t('currencyRateCheckToggleDescription', [ {t('coingecko')} , {t('cryptoCompare')} , {t('privacyMsg')} , ])}
setUseCurrencyRateCheck(!value)} offLabel={t('off')} onLabel={t('on')} />
); } render() { const { warning } = this.props; return (
{warning &&
{warning}
} {this.context.t('security')} {this.renderSeedWords()} {this.context.t('privacy')}
Alerts
{this.renderPhishingDetectionToggle()}
{this.context.t('transactions')}
{this.renderCurrencyRateCheckToggle()} {this.renderIncomingTransactionsOptIn()}
{this.context.t('networkProvider')}
{this.renderChooseYourNetworkButton()} {this.renderIpfsGatewayControl()}
{this.context.t('tokenAutoDetection')}
{this.renderAutoDetectTokensToggle()} {this.renderBatchAccountBalanceRequestsToggle()}
{this.context.t('metrics')}
{this.renderMetaMetricsOptIn()}
); } }