- {showTestnetMessageInDropdown ? (
+ {hideElementsForOnboarding ? null : (
+
+ )}
+ {showTestnetMessageInDropdown && !hideElementsForOnboarding ? (
{t('toggleTestNetworks', [
,
+ ...accountModalStyle,
+ },
NEW_ACCOUNT: {
contents:
,
mobileModalStyle: {
diff --git a/ui/helpers/utils/ipfs.js b/ui/helpers/utils/ipfs.js
new file mode 100644
index 000000000..9c917de2e
--- /dev/null
+++ b/ui/helpers/utils/ipfs.js
@@ -0,0 +1,3 @@
+export function addUrlProtocolPrefix(urlString) {
+ return urlString.match(/^https?:\/\//u) ? urlString : `https://${urlString}`;
+}
diff --git a/ui/pages/onboarding-flow/add-network-modal/index.js b/ui/pages/onboarding-flow/add-network-modal/index.js
new file mode 100644
index 000000000..45b7ec5b6
--- /dev/null
+++ b/ui/pages/onboarding-flow/add-network-modal/index.js
@@ -0,0 +1,43 @@
+import React from 'react';
+import { useDispatch } from 'react-redux';
+import { useI18nContext } from '../../../hooks/useI18nContext';
+
+import { hideModal } from '../../../store/actions';
+
+import Typography from '../../../components/ui/typography/typography';
+import Box from '../../../components/ui/box/box';
+import {
+ TEXT_ALIGN,
+ TYPOGRAPHY,
+ FONT_WEIGHT,
+} from '../../../helpers/constants/design-system';
+
+import NetworksForm from '../../settings/networks-tab/networks-form/networks-form';
+
+export default function AddNetworkModal() {
+ const dispatch = useDispatch();
+ const t = useI18nContext();
+
+ const closeCallback = () =>
+ dispatch(hideModal({ name: 'ONBOARDING_ADD_NETWORK' }));
+
+ return (
+ <>
+
+
+ {t('onboardingMetametricsModalTitle')}
+
+
+
+ >
+ );
+}
diff --git a/ui/pages/onboarding-flow/privacy-settings/index.scss b/ui/pages/onboarding-flow/privacy-settings/index.scss
index 0a623482b..6eae5901f 100644
--- a/ui/pages/onboarding-flow/privacy-settings/index.scss
+++ b/ui/pages/onboarding-flow/privacy-settings/index.scss
@@ -56,6 +56,15 @@
}
}
+ &__network {
+ position: relative;
+
+ .dropdown-menu-item {
+ font-size: 14px !important;
+ padding: 8px !important;
+ }
+ }
+
& button {
max-width: 50%;
padding: 15px;
diff --git a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js
index 9b732c0d9..3067db6e8 100644
--- a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js
+++ b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js
@@ -1,22 +1,32 @@
import React, { useState } from 'react';
-import { useHistory } from 'react-router-dom';
-import { useDispatch } from 'react-redux';
+import { useHistory } from 'react-router-dom';
+import { useDispatch, useSelector } from 'react-redux';
+
+import Box from '../../../components/ui/box/box';
import Button from '../../../components/ui/button';
import Typography from '../../../components/ui/typography';
import {
+ COLORS,
FONT_WEIGHT,
TYPOGRAPHY,
} from '../../../helpers/constants/design-system';
import { useI18nContext } from '../../../hooks/useI18nContext';
+import { addUrlProtocolPrefix } from '../../../helpers/utils/ipfs';
import {
setCompletedOnboarding,
setFeatureFlag,
setUseMultiAccountBalanceChecker,
setUsePhishDetect,
setUseTokenDetection,
+ showModal,
+ setIpfsGateway,
+ showNetworkDropdown,
} from '../../../store/actions';
import { ONBOARDING_PIN_EXTENSION_ROUTE } from '../../../helpers/constants/routes';
+import { Icon, TextField } from '../../../components/component-library';
+import NetworkDropdown from '../../../components/app/dropdowns/network-dropdown';
+import NetworkDisplay from '../../../components/app/network-display/network-display';
import { Setting } from './setting';
export default function PrivacySettings() {
@@ -31,6 +41,12 @@ export default function PrivacySettings() {
isMultiAccountBalanceCheckerEnabled,
setMultiAccountBalanceCheckerEnabled,
] = useState(true);
+ const [ipfsURL, setIPFSURL] = useState('');
+ const [ipfsError, setIPFSError] = useState(null);
+
+ const networks = useSelector(
+ (state) => state.metamask.frequentRpcListDetail || [],
+ );
const handleSubmit = () => {
dispatch(
@@ -42,9 +58,28 @@ export default function PrivacySettings() {
setUseMultiAccountBalanceChecker(isMultiAccountBalanceCheckerEnabled),
);
dispatch(setCompletedOnboarding());
+
+ if (ipfsURL && !ipfsError) {
+ const { host } = new URL(addUrlProtocolPrefix(ipfsURL));
+ dispatch(setIpfsGateway(host));
+ }
+
history.push(ONBOARDING_PIN_EXTENSION_ROUTE);
};
+ const handleIPFSChange = (url) => {
+ setIPFSURL(url);
+ try {
+ const { host } = new URL(addUrlProtocolPrefix(url));
+ if (!host || host === 'gateway.ipfs.io') {
+ throw new Error();
+ }
+ setIPFSError(null);
+ } catch (error) {
+ setIPFSError(t('onboardingAdvancedPrivacyIPFSInvalid'));
+ }
+ };
+
return (
<>
@@ -118,6 +153,94 @@ export default function PrivacySettings() {
title={t('useMultiAccountBalanceChecker')}
description={t('useMultiAccountBalanceCheckerDescription')}
/>
+
+ {t('onboardingAdvancedPrivacyNetworkDescription', [
+
+ {t('privacyMsg')}
+ ,
+ ])}
+
+
+ {networks.length > 1 ? (
+
+ <>
+ dispatch(showNetworkDropdown())}
+ />
+ {
+ dispatch(
+ showModal({ name: 'ONBOARDING_ADD_NETWORK' }),
+ );
+ }}
+ />
+ >
+
+ ) : null}
+ {networks.length === 1 ? (
+ {
+ e.preventDefault();
+ dispatch(showModal({ name: 'ONBOARDING_ADD_NETWORK' }));
+ }}
+ icon={ }
+ >
+ {t('onboardingAdvancedPrivacyNetworkButton')}
+
+ ) : null}
+
+ >
+ }
+ />
+
+ {t('onboardingAdvancedPrivacyIPFSDescription')}
+
+ {
+ handleIPFSChange(e.target.value);
+ }}
+ />
+ {ipfsURL ? (
+
+ {ipfsError || t('onboardingAdvancedPrivacyIPFSValid')}
+
+ ) : null}
+
+ >
+ }
+ />
{t('done')}
diff --git a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js
index d67d5a69e..43a74eb15 100644
--- a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js
+++ b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js
@@ -11,6 +11,7 @@ import PrivacySettings from './privacy-settings';
describe('Privacy Settings Onboarding View', () => {
const mockStore = {
metamask: {
+ frequentRpcListDetail: [],
provider: {
type: 'test',
},
diff --git a/ui/pages/onboarding-flow/privacy-settings/setting.js b/ui/pages/onboarding-flow/privacy-settings/setting.js
index f2951fc97..e0a137b29 100644
--- a/ui/pages/onboarding-flow/privacy-settings/setting.js
+++ b/ui/pages/onboarding-flow/privacy-settings/setting.js
@@ -9,7 +9,13 @@ import {
FONT_WEIGHT,
} from '../../../helpers/constants/design-system';
-export const Setting = ({ value, setValue, title, description }) => {
+export const Setting = ({
+ value,
+ setValue,
+ title,
+ description,
+ showToggle = true,
+}) => {
return (
@@ -18,9 +24,11 @@ export const Setting = ({ value, setValue, title, description }) => {
{description}
-
- setValue(!val)} />
-
+ {showToggle ? (
+
+ setValue(!val)} />
+
+ ) : null}
);
};
@@ -30,4 +38,5 @@ Setting.propTypes = {
setValue: PropTypes.func,
title: PropTypes.string,
description: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
+ showToggle: PropTypes.bool,
};
diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js
index 4b12fd4b7..ecfed0fe8 100644
--- a/ui/pages/routes/routes.component.js
+++ b/ui/pages/routes/routes.component.js
@@ -115,6 +115,7 @@ export default class Routes extends Component {
portfolioTooltipIsBeingShown: PropTypes.bool,
forgottenPassword: PropTypes.bool,
isCurrentProviderCustom: PropTypes.bool,
+ completedOnboarding: PropTypes.bool,
};
static contextTypes = {
@@ -379,6 +380,7 @@ export default class Routes extends Component {
shouldShowSeedPhraseReminder,
portfolioTooltipIsBeingShown,
isCurrentProviderCustom,
+ completedOnboarding,
} = this.props;
const loadMessage =
loadingMessage || isNetworkLoading
@@ -391,6 +393,7 @@ export default class Routes extends Component {
!isTestNet &&
!isNetworkUsed &&
!isCurrentProviderCustom &&
+ completedOnboarding &&
allAccountsOnNetworkAreEmpty;
const windowType = getEnvironmentType();
@@ -436,7 +439,7 @@ export default class Routes extends Component {
{process.env.ONBOARDING_V2 && this.showOnboardingHeader() && (
)}
-
+ {completedOnboarding ? : null}
{isLoading ? : null}
diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js
index 486889289..dcc0e81c3 100644
--- a/ui/pages/routes/routes.container.js
+++ b/ui/pages/routes/routes.container.js
@@ -29,6 +29,7 @@ function mapStateToProps(state) {
const { appState } = state;
const { alertOpen, alertMessage, isLoading, loadingMessage } = appState;
const { autoLockTimeLimit = 0 } = getPreferences(state);
+ const { completedOnboarding } = state.metamask;
return {
alertOpen,
@@ -55,6 +56,7 @@ function mapStateToProps(state) {
portfolioTooltipIsBeingShown: getShowPortfolioTooltip(state),
forgottenPassword: state.metamask.forgottenPassword,
isCurrentProviderCustom: isCurrentProviderCustom(state),
+ completedOnboarding,
};
}
diff --git a/ui/pages/settings/advanced-tab/advanced-tab.component.js b/ui/pages/settings/advanced-tab/advanced-tab.component.js
index 330c8b871..0b3e2a615 100644
--- a/ui/pages/settings/advanced-tab/advanced-tab.component.js
+++ b/ui/pages/settings/advanced-tab/advanced-tab.component.js
@@ -14,6 +14,7 @@ import {
getNumberOfSettingsInSection,
handleSettingsRefs,
} from '../../../helpers/utils/settings-search';
+import { addUrlProtocolPrefix } from '../../../helpers/utils/ipfs';
import {
LEDGER_TRANSPORT_TYPES,
@@ -842,10 +843,3 @@ export default class AdvancedTab extends PureComponent {
);
}
}
-
-function addUrlProtocolPrefix(urlString) {
- if (!urlString.match(/(^http:\/\/)|(^https:\/\/)/u)) {
- return `https://${urlString}`;
- }
- return urlString;
-}
diff --git a/ui/pages/settings/networks-tab/networks-form/networks-form.js b/ui/pages/settings/networks-tab/networks-form/networks-form.js
index ceb9cc70b..ccc9ebdfa 100644
--- a/ui/pages/settings/networks-tab/networks-form/networks-form.js
+++ b/ui/pages/settings/networks-tab/networks-form/networks-form.js
@@ -5,7 +5,6 @@ import React, {
useRef,
useState,
} from 'react';
-import { useHistory } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import PropTypes from 'prop-types';
import validUrl from 'valid-url';
@@ -29,10 +28,6 @@ import {
showModal,
setNewNetworkAdded,
} from '../../../../store/actions';
-import {
- DEFAULT_ROUTE,
- NETWORKS_ROUTE,
-} from '../../../../helpers/constants/routes';
import fetchWithCache from '../../../../../shared/lib/fetch-with-cache';
import { usePrevious } from '../../../../hooks/usePrevious';
import { MetaMetricsContext } from '../../../../contexts/metametrics';
@@ -86,10 +81,11 @@ const NetworksForm = ({
isCurrentRpcTarget,
networksToRender,
selectedNetwork,
+ cancelCallback,
+ submitCallback,
}) => {
const t = useI18nContext();
const trackEvent = useContext(MetaMetricsContext);
- const history = useHistory();
const dispatch = useDispatch();
const { label, labelKey, viewOnly, rpcPrefs } = selectedNetwork;
const selectedNetworkName = label || (labelKey && t(labelKey));
@@ -544,7 +540,8 @@ const NetworksForm = ({
},
});
dispatch(setNewNetworkAdded(networkName));
- history.push(DEFAULT_ROUTE);
+
+ submitCallback?.();
}
} catch (error) {
setIsSubmitting(false);
@@ -555,7 +552,7 @@ const NetworksForm = ({
const onCancel = () => {
if (addNewNetwork) {
dispatch(setSelectedSettingsRpcUrl(''));
- history.push(NETWORKS_ROUTE);
+ cancelCallback?.();
} else {
resetForm();
}
@@ -709,6 +706,8 @@ NetworksForm.propTypes = {
isCurrentRpcTarget: PropTypes.bool,
networksToRender: PropTypes.array.isRequired,
selectedNetwork: PropTypes.object,
+ cancelCallback: PropTypes.func,
+ submitCallback: PropTypes.func,
};
NetworksForm.defaultProps = {
diff --git a/ui/pages/settings/networks-tab/networks-tab-content/networks-tab-content.js b/ui/pages/settings/networks-tab/networks-tab-content/networks-tab-content.js
index 313bc0ff5..6c43f720f 100644
--- a/ui/pages/settings/networks-tab/networks-tab-content/networks-tab-content.js
+++ b/ui/pages/settings/networks-tab/networks-tab-content/networks-tab-content.js
@@ -1,10 +1,16 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
+import { useHistory } from 'react-router-dom';
import NetworksForm from '../networks-form';
import NetworksList from '../networks-list';
import { getProvider } from '../../../../selectors';
+import {
+ DEFAULT_ROUTE,
+ NETWORKS_ROUTE,
+} from '../../../../helpers/constants/routes';
+
const NetworksTabContent = ({
networkDefaultedToProvider,
networkIsSelected,
@@ -13,6 +19,7 @@ const NetworksTabContent = ({
shouldRenderNetworkForm,
}) => {
const provider = useSelector(getProvider);
+ const history = useHistory();
return (
<>
@@ -27,6 +34,8 @@ const NetworksTabContent = ({
isCurrentRpcTarget={provider.rpcUrl === selectedNetwork.rpcUrl}
networksToRender={networksToRender}
selectedNetwork={selectedNetwork}
+ submitCallback={() => history.push(DEFAULT_ROUTE)}
+ cancelCallback={() => history.push(NETWORKS_ROUTE)}
/>
) : null}
>
diff --git a/ui/pages/settings/networks-tab/networks-tab.js b/ui/pages/settings/networks-tab/networks-tab.js
index 572acad3f..287b187df 100644
--- a/ui/pages/settings/networks-tab/networks-tab.js
+++ b/ui/pages/settings/networks-tab/networks-tab.js
@@ -7,6 +7,8 @@ import { useI18nContext } from '../../../hooks/useI18nContext';
import {
ADD_POPULAR_CUSTOM_NETWORK,
NETWORKS_FORM_ROUTE,
+ DEFAULT_ROUTE,
+ NETWORKS_ROUTE,
} from '../../../helpers/constants/routes';
import { setSelectedSettingsRpcUrl } from '../../../store/actions';
import Button from '../../../components/ui/button';
@@ -106,6 +108,8 @@ const NetworksTab = ({ addNewNetwork }) => {
history.push(DEFAULT_ROUTE)}
+ cancelCallback={() => history.push(NETWORKS_ROUTE)}
/>
) : (
<>