mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Adding popular custom network integration (#14557)
* Initial push * Refactored the code * Additional code * Removed the unused message * Added a tooltip * Fixed tests * Lint fix * Added style to a tooltip * Fix e2e test failure * Lint fix and code revert * Fix e2e test * Fixed paddings * Fixed paddings * CSS fix * Minified svg files * Applied requested changes * Fixed theme issue * Code revert * Added back overridden code * Icon problem fixed * Lint fix * Replaced H3 with H4 * Added unit test * Added breadcrumbs * Added const props for networks * Lint fix * Lint fix * Added toggle button for showing the custom network list and resolved few issues * Fixed routes * Refactored a piece of code * Enabled searching for the newly created option * Fixed unit test * Updated theme
This commit is contained in:
parent
3a31326199
commit
43f7a44c25
26
app/_locales/en/messages.json
generated
26
app/_locales/en/messages.json
generated
@ -157,6 +157,9 @@
|
|||||||
"addMemo": {
|
"addMemo": {
|
||||||
"message": "Add memo"
|
"message": "Add memo"
|
||||||
},
|
},
|
||||||
|
"addMoreNetworks": {
|
||||||
|
"message": "add more networks manually"
|
||||||
|
},
|
||||||
"addNetwork": {
|
"addNetwork": {
|
||||||
"message": "Add Network"
|
"message": "Add Network"
|
||||||
},
|
},
|
||||||
@ -1954,6 +1957,9 @@
|
|||||||
"network": {
|
"network": {
|
||||||
"message": "Network:"
|
"message": "Network:"
|
||||||
},
|
},
|
||||||
|
"networkAddedSuccessfully": {
|
||||||
|
"message": "Network added successfully!"
|
||||||
|
},
|
||||||
"networkDetails": {
|
"networkDetails": {
|
||||||
"message": "Network Details"
|
"message": "Network Details"
|
||||||
},
|
},
|
||||||
@ -2891,6 +2897,12 @@
|
|||||||
"showAdvancedGasInlineDescription": {
|
"showAdvancedGasInlineDescription": {
|
||||||
"message": "Select this to show gas price and limit controls directly on the send and confirm screens."
|
"message": "Select this to show gas price and limit controls directly on the send and confirm screens."
|
||||||
},
|
},
|
||||||
|
"showCustomNetworkList": {
|
||||||
|
"message": "Show Custom Network List"
|
||||||
|
},
|
||||||
|
"showCustomNetworkListDescription": {
|
||||||
|
"message": "Select this to show a list of networks with prefilled details when adding a new network."
|
||||||
|
},
|
||||||
"showFiatConversionInTestnets": {
|
"showFiatConversionInTestnets": {
|
||||||
"message": "Show Conversion on test networks"
|
"message": "Show Conversion on test networks"
|
||||||
},
|
},
|
||||||
@ -3000,6 +3012,9 @@
|
|||||||
"snapsToggle": {
|
"snapsToggle": {
|
||||||
"message": "A snap will only run if it is enabled"
|
"message": "A snap will only run if it is enabled"
|
||||||
},
|
},
|
||||||
|
"someNetworksMayPoseSecurity": {
|
||||||
|
"message": "Some networks may pose security and/or privacy risks. Understand the risks before adding & using a network."
|
||||||
|
},
|
||||||
"somethingWentWrong": {
|
"somethingWentWrong": {
|
||||||
"message": "Oops! Something went wrong."
|
"message": "Oops! Something went wrong."
|
||||||
},
|
},
|
||||||
@ -3562,6 +3577,10 @@
|
|||||||
"switchNetworks": {
|
"switchNetworks": {
|
||||||
"message": "Switch Networks"
|
"message": "Switch Networks"
|
||||||
},
|
},
|
||||||
|
"switchToNetwork": {
|
||||||
|
"message": "Switch to $1",
|
||||||
|
"description": "$1 represents the custom network that has previously been added"
|
||||||
|
},
|
||||||
"switchToThisAccount": {
|
"switchToThisAccount": {
|
||||||
"message": "Switch to this account"
|
"message": "Switch to this account"
|
||||||
},
|
},
|
||||||
@ -4008,6 +4027,9 @@
|
|||||||
"walletCreationSuccessTitle": {
|
"walletCreationSuccessTitle": {
|
||||||
"message": "Wallet creation successful"
|
"message": "Wallet creation successful"
|
||||||
},
|
},
|
||||||
|
"wantToAddThisNetwork": {
|
||||||
|
"message": "Want to add this network?"
|
||||||
|
},
|
||||||
"warning": {
|
"warning": {
|
||||||
"message": "Warning"
|
"message": "Warning"
|
||||||
},
|
},
|
||||||
@ -4070,6 +4092,10 @@
|
|||||||
"yesLetsTry": {
|
"yesLetsTry": {
|
||||||
"message": "Yes, let's try"
|
"message": "Yes, let's try"
|
||||||
},
|
},
|
||||||
|
"youHaveAddedAll": {
|
||||||
|
"message": "You've added all the popular networks. You can discover more networks $1 Or you can $2",
|
||||||
|
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
|
||||||
|
},
|
||||||
"youNeedToAllowCameraAccess": {
|
"youNeedToAllowCameraAccess": {
|
||||||
"message": "You need to allow camera access to use this feature."
|
"message": "You need to allow camera access to use this feature."
|
||||||
},
|
},
|
||||||
|
1
app/images/fantom-opera.svg
Normal file
1
app/images/fantom-opera.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle fill="#1969FF" cx="512" cy="512" r="512"/><path d="M480.953 162.82c17.25-9.093 43.496-9.093 60.746 0l176.016 92.795c10.39 5.477 16.095 13.638 17.117 22.063H735V744.11c-.23 9.19-5.988 18.32-17.285 24.275L541.7 861.18c-17.25 9.093-43.497 9.093-60.746 0l-176.017-92.795c-11.249-5.93-16.647-15.123-16.914-24.275a32.372 32.372 0 0 1-.001-2.35V280.82a24 24 0 0 1 0-1.937v-1.204h.08c.781-8.518 6.228-16.47 16.835-22.063l176.017-92.795ZM707 537l-165.355 87.46c-17.225 9.111-43.433 9.111-60.658 0L316 537.195v205.474l164.987 86.802c9.75 5.217 19.888 10.3 29.76 10.521l.569.008c9.852.032 19.418-4.978 29.117-9.72L707 741.92V537ZM260.424 734c0 17.88 2.06 29.633 6.15 37.912 3.389 6.863 8.475 12.107 17.761 18.489l.53.362c2.038 1.387 4.283 2.839 7.016 4.545l3.223 1.992 9.896 6.025L290.806 827l-11.076-6.75-1.862-1.153c-3.202-1.995-5.857-3.707-8.333-5.392-26.467-18.003-36.337-37.63-36.532-78.461L233 734h27.424ZM498 413c-1.28.44-2.481.951-3.575 1.53l-175.748 93.094c-.185.097-.36.194-.528.29L318 508l.276.159.4.217 175.749 93.094c1.094.579 2.294 1.09 3.575 1.53V413Zm28 0v190a25.085 25.085 0 0 0 3.576-1.53l175.747-93.094c.184-.097.36-.194.528-.29L706 508l-.276-.159-.401-.217-175.747-93.094A25.085 25.085 0 0 0 526 413Zm181-102-158 83 158 83V311Zm-391 0v166l158-83-158-83Zm213.422-123.373c-9.147-4.836-25.697-4.836-34.844 0l-175.9 92.997c-.185.098-.362.194-.529.29L318 281l.276.158.401.218 175.9 92.996c9.148 4.837 25.698 4.837 34.845 0l175.9-92.996c.185-.098.361-.194.528-.29L706 281l-.276-.158-.402-.218-175.9-92.997ZM733.194 197l11.076 6.75 1.862 1.152c3.202 1.995 5.857 3.709 8.333 5.393 26.467 18.003 36.337 37.63 36.532 78.461L791 290h-27.424c0-17.882-2.06-29.633-6.15-37.913-3.388-6.862-8.474-12.107-17.76-18.488l-.531-.362a212.559 212.559 0 0 0-7.016-4.545l-3.223-1.992-9.896-6.025L733.194 197Z" fill="#FFF" fill-rule="nonzero"/></g></svg>
|
After Width: | Height: | Size: 1.9 KiB |
1
app/images/harmony-one.svg
Normal file
1
app/images/harmony-one.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300"><defs><linearGradient id="a" x1="71.37" y1="228.63" x2="228.63" y2="71.37" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#00aee9"/><stop offset="1" stop-color="#69fabd"/></linearGradient></defs><path d="M201.17 60a38.81 38.81 0 0 0-38.84 38.71v42.92c-4 .27-8.09.44-12.33.44s-8.31.17-12.33.41V98.71a38.84 38.84 0 0 0-77.67 0v102.58a38.84 38.84 0 0 0 77.67 0v-42.92c4-.27 8.09-.44 12.33-.44s8.31-.17 12.33-.41v43.77a38.84 38.84 0 0 0 77.67 0V98.71A38.81 38.81 0 0 0 201.17 60ZM98.83 75.86a22.91 22.91 0 0 1 22.92 22.85v45.45a130.64 130.64 0 0 0-33 9.33 60 60 0 0 0-12.8 7.64V98.71a22.91 22.91 0 0 1 22.88-22.85Zm22.92 125.43a22.92 22.92 0 0 1-45.84 0V191c0-9.09 7.2-17.7 19.27-23.06a113 113 0 0 1 26.57-7.77Zm79.42 22.85a22.91 22.91 0 0 1-22.92-22.85v-45.45a130.64 130.64 0 0 0 33-9.33 60 60 0 0 0 12.8-7.64v62.42a22.91 22.91 0 0 1-22.88 22.85Zm3.65-92.14a113 113 0 0 1-26.57 7.77V98.71a22.92 22.92 0 0 1 45.84 0V109c0 9.05-7.2 17.66-19.27 23Z" style="fill:url(#a)"/><path style="fill:none" d="M0 0h300v300H0z"/></svg>
|
After Width: | Height: | Size: 1.1 KiB |
1
app/images/info-fox.svg
Normal file
1
app/images/info-fox.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 78 KiB |
@ -69,6 +69,7 @@ export default class PreferencesController {
|
|||||||
? LEDGER_TRANSPORT_TYPES.WEBHID
|
? LEDGER_TRANSPORT_TYPES.WEBHID
|
||||||
: LEDGER_TRANSPORT_TYPES.U2F,
|
: LEDGER_TRANSPORT_TYPES.U2F,
|
||||||
theme: 'light',
|
theme: 'light',
|
||||||
|
customNetworkListEnabled: false,
|
||||||
...opts.initState,
|
...opts.initState,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -179,6 +180,17 @@ export default class PreferencesController {
|
|||||||
this.store.updateState({ theme: val });
|
this.store.updateState({ theme: val });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setter for the `customNetworkListEnabled` property
|
||||||
|
*
|
||||||
|
* @param customNetworkListEnabled
|
||||||
|
*/
|
||||||
|
setCustomNetworkListEnabled(customNetworkListEnabled) {
|
||||||
|
this.store.updateState({
|
||||||
|
customNetworkListEnabled,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add new methodData to state, to avoid requesting this information again through Infura
|
* Add new methodData to state, to avoid requesting this information again through Infura
|
||||||
*
|
*
|
||||||
|
@ -1572,7 +1572,8 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
setCustomRpc: this.setCustomRpc.bind(this),
|
setCustomRpc: this.setCustomRpc.bind(this),
|
||||||
updateAndSetCustomRpc: this.updateAndSetCustomRpc.bind(this),
|
updateAndSetCustomRpc: this.updateAndSetCustomRpc.bind(this),
|
||||||
delCustomRpc: this.delCustomRpc.bind(this),
|
delCustomRpc: this.delCustomRpc.bind(this),
|
||||||
|
addCustomNetwork: this.addCustomNetwork.bind(this),
|
||||||
|
requestUserApproval: this.requestUserApproval.bind(this),
|
||||||
// PreferencesController
|
// PreferencesController
|
||||||
setSelectedAddress: preferencesController.setSelectedAddress.bind(
|
setSelectedAddress: preferencesController.setSelectedAddress.bind(
|
||||||
preferencesController,
|
preferencesController,
|
||||||
@ -1609,7 +1610,9 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
preferencesController,
|
preferencesController,
|
||||||
),
|
),
|
||||||
setTheme: preferencesController.setTheme.bind(preferencesController),
|
setTheme: preferencesController.setTheme.bind(preferencesController),
|
||||||
|
setCustomNetworkListEnabled: preferencesController.setCustomNetworkListEnabled.bind(
|
||||||
|
preferencesController,
|
||||||
|
),
|
||||||
// AssetsContractController
|
// AssetsContractController
|
||||||
getTokenStandardAndDetails: this.getTokenStandardAndDetails.bind(this),
|
getTokenStandardAndDetails: this.getTokenStandardAndDetails.bind(this),
|
||||||
|
|
||||||
@ -2026,6 +2029,43 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async requestUserApproval(customRpc, originIsMetaMask) {
|
||||||
|
try {
|
||||||
|
await this.approvalController.addAndShowApprovalRequest({
|
||||||
|
origin: 'metamask',
|
||||||
|
type: 'wallet_addEthereumChain',
|
||||||
|
requestData: {
|
||||||
|
chainId: customRpc.chainId,
|
||||||
|
blockExplorerUrl: customRpc.rpcPrefs.blockExplorerUrl,
|
||||||
|
chainName: customRpc.nickname,
|
||||||
|
rpcUrl: customRpc.rpcUrl,
|
||||||
|
ticker: customRpc.ticker,
|
||||||
|
imageUrl: customRpc.rpcPrefs.imageUrl,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (
|
||||||
|
!(originIsMetaMask && error.message === 'User rejected the request.')
|
||||||
|
) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async addCustomNetwork(customRpc) {
|
||||||
|
const { chainId, chainName, rpcUrl, ticker, blockExplorerUrl } = customRpc;
|
||||||
|
|
||||||
|
await this.preferencesController.addToFrequentRpcList(
|
||||||
|
rpcUrl,
|
||||||
|
chainId,
|
||||||
|
ticker,
|
||||||
|
chainName,
|
||||||
|
{
|
||||||
|
blockExplorerUrl,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new Vault and restore an existent keyring.
|
* Create a new Vault and restore an existent keyring.
|
||||||
*
|
*
|
||||||
|
@ -28,6 +28,9 @@ export const POLYGON_CHAIN_ID = '0x89';
|
|||||||
export const AVALANCHE_CHAIN_ID = '0xa86a';
|
export const AVALANCHE_CHAIN_ID = '0xa86a';
|
||||||
export const FANTOM_CHAIN_ID = '0xfa';
|
export const FANTOM_CHAIN_ID = '0xfa';
|
||||||
export const CELO_CHAIN_ID = '0xa4ec';
|
export const CELO_CHAIN_ID = '0xa4ec';
|
||||||
|
export const ARBITRUM_CHAIN_ID = '0xa4b1';
|
||||||
|
export const HARMONY_CHAIN_ID = '0x63564c40';
|
||||||
|
export const PALM_CHAIN_ID = '0x2a15c308d';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The largest possible chain ID we can handle.
|
* The largest possible chain ID we can handle.
|
||||||
@ -43,7 +46,14 @@ export const GOERLI_DISPLAY_NAME = 'Goerli';
|
|||||||
export const LOCALHOST_DISPLAY_NAME = 'Localhost 8545';
|
export const LOCALHOST_DISPLAY_NAME = 'Localhost 8545';
|
||||||
export const BSC_DISPLAY_NAME = 'Binance Smart Chain';
|
export const BSC_DISPLAY_NAME = 'Binance Smart Chain';
|
||||||
export const POLYGON_DISPLAY_NAME = 'Polygon';
|
export const POLYGON_DISPLAY_NAME = 'Polygon';
|
||||||
export const AVALANCHE_DISPLAY_NAME = 'Avalanche';
|
export const AVALANCHE_DISPLAY_NAME = 'Avalanche Network C-Chain';
|
||||||
|
export const ARBITRUM_DISPLAY_NAME = 'Arbitrum One';
|
||||||
|
export const BNB_DISPLAY_NAME =
|
||||||
|
'BNB Smart Chain (previously Binance Smart Chain Mainnet)';
|
||||||
|
export const OPTIMISM_DISPLAY_NAME = 'Optimism';
|
||||||
|
export const FANTOM_DISPLAY_NAME = 'Fantom Opera';
|
||||||
|
export const HARMONY_DISPLAY_NAME = 'Harmony Mainnet Shard 0';
|
||||||
|
export const PALM_DISPLAY_NAME = 'Palm';
|
||||||
|
|
||||||
const infuraProjectId = process.env.INFURA_PROJECT_ID;
|
const infuraProjectId = process.env.INFURA_PROJECT_ID;
|
||||||
export const getRpcUrl = ({ network, excludeProjectId = false }) =>
|
export const getRpcUrl = ({ network, excludeProjectId = false }) =>
|
||||||
@ -64,12 +74,20 @@ export const MATIC_SYMBOL = 'MATIC';
|
|||||||
export const AVALANCHE_SYMBOL = 'AVAX';
|
export const AVALANCHE_SYMBOL = 'AVAX';
|
||||||
export const FANTOM_SYMBOL = 'FTM';
|
export const FANTOM_SYMBOL = 'FTM';
|
||||||
export const CELO_SYMBOL = 'CELO';
|
export const CELO_SYMBOL = 'CELO';
|
||||||
|
export const ARBITRUM_SYMBOL = 'AETH';
|
||||||
|
export const HARMONY_SYMBOL = 'ONE';
|
||||||
|
export const PALM_SYMBOL = 'PALM';
|
||||||
|
|
||||||
export const ETH_TOKEN_IMAGE_URL = './images/eth_logo.svg';
|
export const ETH_TOKEN_IMAGE_URL = './images/eth_logo.svg';
|
||||||
export const TEST_ETH_TOKEN_IMAGE_URL = './images/black-eth-logo.svg';
|
export const TEST_ETH_TOKEN_IMAGE_URL = './images/black-eth-logo.svg';
|
||||||
export const BNB_TOKEN_IMAGE_URL = './images/bnb.png';
|
export const BNB_TOKEN_IMAGE_URL = './images/bnb.png';
|
||||||
export const MATIC_TOKEN_IMAGE_URL = './images/matic-token.png';
|
export const MATIC_TOKEN_IMAGE_URL = './images/matic-token.png';
|
||||||
export const AVAX_TOKEN_IMAGE_URL = './images/avax-token.png';
|
export const AVAX_TOKEN_IMAGE_URL = './images/avax-token.png';
|
||||||
|
export const AETH_TOKEN_IMAGE_URL = './images/arbitrum.svg';
|
||||||
|
export const FTM_TOKEN_IMAGE_URL = './images/fantom-opera.svg';
|
||||||
|
export const HARMONY_ONE_TOKEN_IMAGE_URL = './images/harmony-one.svg';
|
||||||
|
export const OPTIMISM_TOKEN_IMAGE_URL = './images/optimism.svg';
|
||||||
|
export const PALM_TOKEN_IMAGE_URL = './images/palm.svg';
|
||||||
|
|
||||||
export const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET, GOERLI];
|
export const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET, GOERLI];
|
||||||
|
|
||||||
@ -166,6 +184,12 @@ export const CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP = {
|
|||||||
[AVALANCHE_CHAIN_ID]: AVAX_TOKEN_IMAGE_URL,
|
[AVALANCHE_CHAIN_ID]: AVAX_TOKEN_IMAGE_URL,
|
||||||
[BSC_CHAIN_ID]: BNB_TOKEN_IMAGE_URL,
|
[BSC_CHAIN_ID]: BNB_TOKEN_IMAGE_URL,
|
||||||
[POLYGON_CHAIN_ID]: MATIC_TOKEN_IMAGE_URL,
|
[POLYGON_CHAIN_ID]: MATIC_TOKEN_IMAGE_URL,
|
||||||
|
[ARBITRUM_CHAIN_ID]: AETH_TOKEN_IMAGE_URL,
|
||||||
|
[BSC_CHAIN_ID]: BNB_TOKEN_IMAGE_URL,
|
||||||
|
[FANTOM_CHAIN_ID]: FTM_TOKEN_IMAGE_URL,
|
||||||
|
[HARMONY_CHAIN_ID]: HARMONY_ONE_TOKEN_IMAGE_URL,
|
||||||
|
[OPTIMISM_CHAIN_ID]: OPTIMISM_TOKEN_IMAGE_URL,
|
||||||
|
[PALM_CHAIN_ID]: PALM_TOKEN_IMAGE_URL,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CHAIN_ID_TO_NETWORK_ID_MAP = Object.values(
|
export const CHAIN_ID_TO_NETWORK_ID_MAP = Object.values(
|
||||||
@ -309,3 +333,86 @@ export const BUYABLE_CHAINS_MAP = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const FEATURED_RPCS = [
|
||||||
|
{
|
||||||
|
chainId: ARBITRUM_CHAIN_ID,
|
||||||
|
nickname: ARBITRUM_DISPLAY_NAME,
|
||||||
|
rpcUrl: `https://arbitrum-mainnet.infura.io/v3/${infuraProjectId}`,
|
||||||
|
ticker: ARBITRUM_SYMBOL,
|
||||||
|
rpcPrefs: {
|
||||||
|
blockExplorerUrl: 'https://explorer.arbitrum.io',
|
||||||
|
imageUrl: AETH_TOKEN_IMAGE_URL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: AVALANCHE_CHAIN_ID,
|
||||||
|
nickname: AVALANCHE_DISPLAY_NAME,
|
||||||
|
rpcUrl: 'https://api.avax.network/ext/bc/C/rpc',
|
||||||
|
ticker: AVALANCHE_SYMBOL,
|
||||||
|
rpcPrefs: {
|
||||||
|
blockExplorerUrl: 'https://snowtrace.io/',
|
||||||
|
imageUrl: AVAX_TOKEN_IMAGE_URL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: BSC_CHAIN_ID,
|
||||||
|
nickname: BNB_DISPLAY_NAME,
|
||||||
|
rpcUrl: 'https://bsc-dataseed.binance.org/',
|
||||||
|
ticker: BNB_SYMBOL,
|
||||||
|
rpcPrefs: {
|
||||||
|
blockExplorerUrl: 'https://bscscan.com/',
|
||||||
|
imageUrl: BNB_TOKEN_IMAGE_URL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: FANTOM_CHAIN_ID,
|
||||||
|
nickname: FANTOM_DISPLAY_NAME,
|
||||||
|
rpcUrl: 'https://rpc.ftm.tools/',
|
||||||
|
ticker: FANTOM_SYMBOL,
|
||||||
|
rpcPrefs: {
|
||||||
|
blockExplorerUrl: 'https://ftmscan.com/',
|
||||||
|
imageUrl: FTM_TOKEN_IMAGE_URL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: HARMONY_CHAIN_ID,
|
||||||
|
nickname: HARMONY_DISPLAY_NAME,
|
||||||
|
rpcUrl: 'https://api.harmony.one/',
|
||||||
|
ticker: HARMONY_SYMBOL,
|
||||||
|
rpcPrefs: {
|
||||||
|
blockExplorerUrl: 'https://explorer.harmony.one/',
|
||||||
|
imageUrl: HARMONY_ONE_TOKEN_IMAGE_URL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: OPTIMISM_CHAIN_ID,
|
||||||
|
nickname: OPTIMISM_DISPLAY_NAME,
|
||||||
|
rpcUrl: `https://optimism-mainnet.infura.io/v3/${infuraProjectId}`,
|
||||||
|
ticker: ETH_SYMBOL,
|
||||||
|
rpcPrefs: {
|
||||||
|
blockExplorerUrl: 'https://optimistic.etherscan.io/',
|
||||||
|
imageUrl: OPTIMISM_TOKEN_IMAGE_URL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: PALM_CHAIN_ID,
|
||||||
|
nickname: PALM_DISPLAY_NAME,
|
||||||
|
rpcUrl: `https://palm-mainnet.infura.io/v3/${infuraProjectId}`,
|
||||||
|
ticker: PALM_SYMBOL,
|
||||||
|
rpcPrefs: {
|
||||||
|
blockExplorerUrl: 'https://explorer.palm.io/',
|
||||||
|
imageUrl: PALM_TOKEN_IMAGE_URL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: POLYGON_CHAIN_ID,
|
||||||
|
nickname: `${POLYGON_DISPLAY_NAME} ${capitalize(MAINNET)}`,
|
||||||
|
rpcUrl: `https://polygon-mainnet.infura.io/v3/${infuraProjectId}`,
|
||||||
|
ticker: MATIC_SYMBOL,
|
||||||
|
rpcPrefs: {
|
||||||
|
blockExplorerUrl: 'https://polygonscan.com/',
|
||||||
|
imageUrl: MATIC_TOKEN_IMAGE_URL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
@ -1,168 +1,286 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { I18nContext } from '../../../contexts/i18n';
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
import Box from '../../ui/box';
|
import Box from '../../ui/box';
|
||||||
import Typography from '../../ui/typography';
|
import Typography from '../../ui/typography';
|
||||||
import {
|
import {
|
||||||
ALIGN_ITEMS,
|
ALIGN_ITEMS,
|
||||||
BLOCK_SIZES,
|
|
||||||
COLORS,
|
COLORS,
|
||||||
DISPLAY,
|
DISPLAY,
|
||||||
FLEX_DIRECTION,
|
FLEX_DIRECTION,
|
||||||
FONT_WEIGHT,
|
FONT_WEIGHT,
|
||||||
TYPOGRAPHY,
|
TYPOGRAPHY,
|
||||||
JUSTIFY_CONTENT,
|
JUSTIFY_CONTENT,
|
||||||
|
SIZES,
|
||||||
} from '../../../helpers/constants/design-system';
|
} from '../../../helpers/constants/design-system';
|
||||||
import Button from '../../ui/button';
|
import Button from '../../ui/button';
|
||||||
import IconCaretLeft from '../../ui/icon/icon-caret-left';
|
|
||||||
import Tooltip from '../../ui/tooltip';
|
import Tooltip from '../../ui/tooltip';
|
||||||
import IconWithFallback from '../../ui/icon-with-fallback';
|
import IconWithFallback from '../../ui/icon-with-fallback';
|
||||||
import IconBorder from '../../ui/icon-border';
|
import IconBorder from '../../ui/icon-border';
|
||||||
import { getTheme } from '../../../selectors';
|
import {
|
||||||
import { THEME_TYPE } from '../../../pages/settings/experimental-tab/experimental-tab.constant';
|
getFrequentRpcListDetail,
|
||||||
|
getUnapprovedConfirmations,
|
||||||
|
} from '../../../selectors';
|
||||||
|
|
||||||
const AddNetwork = ({
|
import {
|
||||||
onBackClick,
|
ENVIRONMENT_TYPE_FULLSCREEN,
|
||||||
onAddNetworkClick,
|
ENVIRONMENT_TYPE_POPUP,
|
||||||
onAddNetworkManuallyClick,
|
MESSAGE_TYPE,
|
||||||
featuredRPCS,
|
} from '../../../../shared/constants/app';
|
||||||
}) => {
|
import { requestUserApproval } from '../../../store/actions';
|
||||||
|
import Popover from '../../ui/popover';
|
||||||
|
import ConfirmationPage from '../../../pages/confirmation/confirmation';
|
||||||
|
import { FEATURED_RPCS } from '../../../../shared/constants/network';
|
||||||
|
import { ADD_NETWORK_ROUTE } from '../../../helpers/constants/routes';
|
||||||
|
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
|
||||||
|
|
||||||
|
const AddNetwork = () => {
|
||||||
const t = useContext(I18nContext);
|
const t = useContext(I18nContext);
|
||||||
const theme = useSelector(getTheme);
|
const dispatch = useDispatch();
|
||||||
|
const history = useHistory();
|
||||||
|
const frequentRpcList = useSelector(getFrequentRpcListDetail);
|
||||||
|
|
||||||
|
const frequentRpcListChainIds = Object.values(frequentRpcList).map(
|
||||||
|
(net) => net.chainId,
|
||||||
|
);
|
||||||
|
|
||||||
const infuraRegex = /infura.io/u;
|
const infuraRegex = /infura.io/u;
|
||||||
|
|
||||||
const nets = featuredRPCS
|
const nets = FEATURED_RPCS.sort((a, b) =>
|
||||||
.sort((a, b) => (a.ticker > b.ticker ? 1 : -1))
|
a.ticker > b.ticker ? 1 : -1,
|
||||||
.slice(0, 8);
|
).slice(0, FEATURED_RPCS.length);
|
||||||
|
|
||||||
|
const notFrequentRpcNetworks = nets.filter(
|
||||||
|
(net) => frequentRpcListChainIds.indexOf(net.chainId) === -1,
|
||||||
|
);
|
||||||
|
const unapprovedConfirmations = useSelector(getUnapprovedConfirmations);
|
||||||
|
const [showPopover, setShowPopover] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const anAddNetworkConfirmationFromMetaMaskExists = unapprovedConfirmations?.find(
|
||||||
|
(confirmation) => {
|
||||||
|
return (
|
||||||
|
confirmation.origin === 'metamask' &&
|
||||||
|
confirmation.type === MESSAGE_TYPE.ADD_ETHEREUM_CHAIN
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (!showPopover && anAddNetworkConfirmationFromMetaMaskExists) {
|
||||||
|
setShowPopover(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showPopover && !anAddNetworkConfirmationFromMetaMaskExists) {
|
||||||
|
setShowPopover(false);
|
||||||
|
}
|
||||||
|
}, [unapprovedConfirmations, showPopover]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<>
|
||||||
<Box
|
{Object.keys(notFrequentRpcNetworks).length === 0 ? (
|
||||||
height={BLOCK_SIZES.TWO_TWELFTHS}
|
<Box
|
||||||
padding={[4, 0, 4, 0]}
|
className="add-network__edge-case-box"
|
||||||
display={DISPLAY.FLEX}
|
borderRadius={SIZES.MD}
|
||||||
alignItems={ALIGN_ITEMS.CENTER}
|
padding={4}
|
||||||
flexDirection={FLEX_DIRECTION.ROW}
|
margin={[4, 6, 0, 6]}
|
||||||
className="add-network__header"
|
display={DISPLAY.FLEX}
|
||||||
>
|
flexDirection={FLEX_DIRECTION.ROW}
|
||||||
<IconCaretLeft
|
backgroundColor={COLORS.BACKGROUND_ALTERNATIVE}
|
||||||
aria-label={t('back')}
|
|
||||||
onClick={onBackClick}
|
|
||||||
className="add-network__header__back-icon"
|
|
||||||
/>
|
|
||||||
<Typography variant={TYPOGRAPHY.H3} color={COLORS.TEXT_DEFAULT}>
|
|
||||||
{t('addNetwork')}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
height={BLOCK_SIZES.FOUR_FIFTHS}
|
|
||||||
width={BLOCK_SIZES.TEN_TWELFTHS}
|
|
||||||
margin={[0, 6, 0, 6]}
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
variant={TYPOGRAPHY.H6}
|
|
||||||
color={COLORS.TEXT_ALTERNATIVE}
|
|
||||||
margin={[4, 0, 0, 0]}
|
|
||||||
>
|
>
|
||||||
{t('addFromAListOfPopularNetworks')}
|
<Box marginRight={4}>
|
||||||
</Typography>
|
<img src="images/info-fox.svg" />
|
||||||
<Typography
|
</Box>
|
||||||
variant={TYPOGRAPHY.H7}
|
<Box>
|
||||||
color={COLORS.TEXT_MUTED}
|
<Typography variant={TYPOGRAPHY.H7}>
|
||||||
margin={[4, 0, 3, 0]}
|
{t('youHaveAddedAll', [
|
||||||
>
|
<a
|
||||||
{t('popularCustomNetworks')}
|
key="link"
|
||||||
</Typography>
|
className="add-network__edge-case-box__link"
|
||||||
{nets.map((item, index) => (
|
href="https://chainlist.wtf/"
|
||||||
<Box
|
target="_blank"
|
||||||
key={index}
|
rel="noreferrer"
|
||||||
display={DISPLAY.FLEX}
|
>
|
||||||
alignItems={ALIGN_ITEMS.CENTER}
|
{t('here')}.
|
||||||
justifyContent={JUSTIFY_CONTENT.SPACE_BETWEEN}
|
</a>,
|
||||||
marginBottom={6}
|
<Button
|
||||||
>
|
key="button"
|
||||||
<Box display={DISPLAY.FLEX} alignItems={ALIGN_ITEMS.CENTER}>
|
type="inline"
|
||||||
<IconBorder size={24}>
|
onClick={(event) => {
|
||||||
<IconWithFallback
|
event.preventDefault();
|
||||||
icon={item.rpcPrefs.imageUrl}
|
getEnvironmentType() === ENVIRONMENT_TYPE_POPUP
|
||||||
name={item.nickname}
|
? global.platform.openExtensionInBrowser(
|
||||||
size={24}
|
ADD_NETWORK_ROUTE,
|
||||||
/>
|
)
|
||||||
</IconBorder>
|
: history.push(ADD_NETWORK_ROUTE);
|
||||||
<Typography
|
}}
|
||||||
variant={TYPOGRAPHY.H7}
|
>
|
||||||
color={COLORS.TEXT_DEFAULT}
|
<Typography
|
||||||
fontWeight={FONT_WEIGHT.BOLD}
|
variant={TYPOGRAPHY.H7}
|
||||||
boxProps={{ marginLeft: 2 }}
|
color={COLORS.INFO_DEFAULT}
|
||||||
>
|
>
|
||||||
{item.nickname}
|
{t('addMoreNetworks')}.
|
||||||
|
</Typography>
|
||||||
|
</Button>,
|
||||||
|
])}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<Box className="add-network__networks-container">
|
||||||
|
{getEnvironmentType() === ENVIRONMENT_TYPE_FULLSCREEN && (
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
alignItems={ALIGN_ITEMS.CENTER}
|
||||||
|
flexDirection={FLEX_DIRECTION.ROW}
|
||||||
|
marginTop={7}
|
||||||
|
marginBottom={4}
|
||||||
|
paddingBottom={2}
|
||||||
|
className="add-network__header"
|
||||||
|
>
|
||||||
|
<Typography variant={TYPOGRAPHY.H4} color={COLORS.TEXT_MUTED}>
|
||||||
|
{t('networks')}
|
||||||
|
</Typography>
|
||||||
|
<span className="add-network__header__subtitle">{' > '}</span>
|
||||||
|
<Typography variant={TYPOGRAPHY.H4} color={COLORS.TEXT_DEFAULT}>
|
||||||
|
{t('addANetwork')}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box display={DISPLAY.FLEX} alignItems={ALIGN_ITEMS.CENTER}>
|
)}
|
||||||
{
|
<Box
|
||||||
// Warning for the networks that doesn't use infura.io as the RPC
|
margin={
|
||||||
!infuraRegex.test(item.rpcUrl) && (
|
getEnvironmentType() === ENVIRONMENT_TYPE_POPUP
|
||||||
<Tooltip
|
? [0, 0, 1, 0]
|
||||||
className="add-network__warning-tooltip"
|
: [4, 0, 1, 0]
|
||||||
position="top"
|
}
|
||||||
interactive
|
className="add-network__main-container"
|
||||||
html={
|
>
|
||||||
<Box margin={3} className="add-network__warning-tooltip">
|
<Typography
|
||||||
{t('addNetworkTooltipWarning', [
|
variant={TYPOGRAPHY.H6}
|
||||||
<a
|
color={COLORS.TEXT_ALTERNATIVE}
|
||||||
key="zendesk_page_link"
|
margin={[4, 0, 0, 0]}
|
||||||
href="https://metamask.zendesk.com/hc/en-us/articles/4417500466971"
|
>
|
||||||
rel="noreferrer"
|
{t('addFromAListOfPopularNetworks')}
|
||||||
target="_blank"
|
</Typography>
|
||||||
>
|
<Typography
|
||||||
{t('learnMoreUpperCase')}
|
variant={TYPOGRAPHY.H7}
|
||||||
</a>,
|
color={COLORS.TEXT_MUTED}
|
||||||
])}
|
margin={[4, 0, 3, 0]}
|
||||||
</Box>
|
>
|
||||||
}
|
{t('popularCustomNetworks')}
|
||||||
trigger="mouseenter"
|
</Typography>
|
||||||
theme={theme === THEME_TYPE.DEFAULT ? 'light' : 'dark'}
|
{notFrequentRpcNetworks.map((item, index) => (
|
||||||
>
|
<Box
|
||||||
<i
|
key={index}
|
||||||
className="fa fa-exclamation-triangle add-network__warning-icon"
|
display={DISPLAY.FLEX}
|
||||||
title={t('warning')}
|
alignItems={ALIGN_ITEMS.CENTER}
|
||||||
/>
|
justifyContent={JUSTIFY_CONTENT.SPACE_BETWEEN}
|
||||||
</Tooltip>
|
marginBottom={6}
|
||||||
)
|
className="add-network__list-of-networks"
|
||||||
}
|
|
||||||
<Button
|
|
||||||
type="inline"
|
|
||||||
className="add-network__add-button"
|
|
||||||
onClick={onAddNetworkClick}
|
|
||||||
>
|
>
|
||||||
{t('add')}
|
<Box display={DISPLAY.FLEX} alignItems={ALIGN_ITEMS.CENTER}>
|
||||||
</Button>
|
<Box>
|
||||||
</Box>
|
<IconBorder size={24}>
|
||||||
|
<IconWithFallback
|
||||||
|
icon={item.rpcPrefs.imageUrl}
|
||||||
|
name={item.nickname}
|
||||||
|
size={24}
|
||||||
|
/>
|
||||||
|
</IconBorder>
|
||||||
|
</Box>
|
||||||
|
<Box marginLeft={2}>
|
||||||
|
<Typography
|
||||||
|
variant={TYPOGRAPHY.H7}
|
||||||
|
color={COLORS.TEXT_DEFAULT}
|
||||||
|
fontWeight={FONT_WEIGHT.BOLD}
|
||||||
|
>
|
||||||
|
{item.nickname}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
alignItems={ALIGN_ITEMS.CENTER}
|
||||||
|
marginLeft={1}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
// Warning for the networks that doesn't use infura.io as the RPC
|
||||||
|
!infuraRegex.test(item.rpcUrl) && (
|
||||||
|
<Tooltip
|
||||||
|
position="top"
|
||||||
|
interactive
|
||||||
|
html={
|
||||||
|
<Box
|
||||||
|
margin={3}
|
||||||
|
className="add-network__warning-tooltip"
|
||||||
|
>
|
||||||
|
{t('addNetworkTooltipWarning', [
|
||||||
|
<a
|
||||||
|
key="zendesk_page_link"
|
||||||
|
href="https://metamask.zendesk.com/hc/en-us/articles/4417500466971"
|
||||||
|
rel="noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{t('learnMoreUpperCase')}
|
||||||
|
</a>,
|
||||||
|
])}
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
trigger="mouseenter"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fa fa-exclamation-triangle add-network__warning-icon"
|
||||||
|
title={t('warning')}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<Button
|
||||||
|
type="inline"
|
||||||
|
className="add-network__add-button"
|
||||||
|
onClick={async () => {
|
||||||
|
await dispatch(requestUserApproval(item, true));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('add')}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
<Box
|
||||||
</Box>
|
padding={
|
||||||
<Box
|
getEnvironmentType() === ENVIRONMENT_TYPE_POPUP
|
||||||
height={BLOCK_SIZES.ONE_TWELFTH}
|
? [2, 0, 2, 6]
|
||||||
padding={[4, 4, 4, 4]}
|
: [2, 0, 2, 0]
|
||||||
className="add-network__footer"
|
}
|
||||||
>
|
className="add-network__footer"
|
||||||
<Button type="link" onClick={onAddNetworkManuallyClick}>
|
>
|
||||||
<Typography variant={TYPOGRAPHY.H6} color={COLORS.PRIMARY_DEFAULT}>
|
<Button
|
||||||
{t('addANetworkManually')}
|
type="link"
|
||||||
</Typography>
|
onClick={(event) => {
|
||||||
</Button>
|
event.preventDefault();
|
||||||
</Box>
|
getEnvironmentType() === ENVIRONMENT_TYPE_POPUP
|
||||||
</Box>
|
? global.platform.openExtensionInBrowser(ADD_NETWORK_ROUTE)
|
||||||
|
: history.push(ADD_NETWORK_ROUTE);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant={TYPOGRAPHY.H6}
|
||||||
|
color={COLORS.PRIMARY_DEFAULT}
|
||||||
|
>
|
||||||
|
{t('addANetworkManually')}
|
||||||
|
</Typography>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{showPopover && (
|
||||||
|
<Popover>
|
||||||
|
<ConfirmationPage />
|
||||||
|
</Popover>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
AddNetwork.propTypes = {
|
|
||||||
onBackClick: PropTypes.func,
|
|
||||||
onAddNetworkClick: PropTypes.func,
|
|
||||||
onAddNetworkManuallyClick: PropTypes.func,
|
|
||||||
featuredRPCS: PropTypes.array,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddNetwork;
|
export default AddNetwork;
|
||||||
|
60
ui/components/app/add-network/add-network.test.js
Normal file
60
ui/components/app/add-network/add-network.test.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { screen } from '@testing-library/react';
|
||||||
|
import { renderWithProvider } from '../../../../test/jest';
|
||||||
|
import configureStore from '../../../store/store';
|
||||||
|
import mockState from '../../../../test/data/mock-state.json';
|
||||||
|
import AddNetwork from './add-network';
|
||||||
|
|
||||||
|
jest.mock('../../../selectors', () => ({
|
||||||
|
getFrequentRpcListDetail: () => ({
|
||||||
|
frequentRpcList: [
|
||||||
|
{
|
||||||
|
chainId: '0x539',
|
||||||
|
nickname: 'Localhost 8545',
|
||||||
|
rpcPrefs: {},
|
||||||
|
rpcUrl: 'http://localhost:8545',
|
||||||
|
ticker: 'ETH',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: '0xA4B1',
|
||||||
|
nickname: 'Arbitrum One',
|
||||||
|
rpcPrefs: { blockExplorerUrl: 'https://explorer.arbitrum.io' },
|
||||||
|
rpcUrl:
|
||||||
|
'https://arbitrum-mainnet.infura.io/v3/7e127583378c4732a858df2550aff333',
|
||||||
|
ticker: 'AETH',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
getUnapprovedConfirmations: jest.fn(),
|
||||||
|
getTheme: () => 'light',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const render = () => {
|
||||||
|
const store = configureStore({
|
||||||
|
metamask: {
|
||||||
|
...mockState.metamask,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return renderWithProvider(<AddNetwork />, store);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('AddNetwork', () => {
|
||||||
|
it('should show Add from a list.. text', () => {
|
||||||
|
render();
|
||||||
|
expect(
|
||||||
|
screen.getByText(
|
||||||
|
'Add from a list of popular networks or add a network manually. Only interact with the entities you trust.',
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show Popular custom networks text', () => {
|
||||||
|
render();
|
||||||
|
expect(screen.getByText('Popular custom networks')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show Arbitrum One network nickname', () => {
|
||||||
|
render();
|
||||||
|
expect(screen.getByText('Arbitrum One')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
@ -1,10 +1,36 @@
|
|||||||
.add-network {
|
.add-network {
|
||||||
|
&__networks-container {
|
||||||
|
padding-inline-end: 24px;
|
||||||
|
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
border-bottom: 1px solid var(--color-border-default);
|
border-bottom: 1px solid var(--color-border-default);
|
||||||
|
|
||||||
&__back-icon {
|
@media screen and (max-width: 575px) {
|
||||||
margin-left: 24px;
|
padding-inline-start: 24px;
|
||||||
margin-right: 16px;
|
padding-inline-end: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtitle {
|
||||||
|
margin-inline-start: 10px;
|
||||||
|
margin-inline-end: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__main-container {
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
padding-inline-start: 24px;
|
||||||
|
padding-inline-end: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__list-of-networks {
|
||||||
|
@media screen and (min-width: $break-large) {
|
||||||
|
width: 75%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,19 +49,25 @@
|
|||||||
|
|
||||||
&__add-icon {
|
&__add-icon {
|
||||||
color: var(--color-text-alternative);
|
color: var(--color-text-alternative);
|
||||||
margin-left: auto;
|
margin-inline-start: auto;
|
||||||
margin-right: 0;
|
margin-inline-end: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__add-button.button {
|
&__add-button.button {
|
||||||
color: var(--color-primary-default);
|
color: var(--color-primary-default);
|
||||||
font-size: $font-size-h7;
|
font-size: $font-size-h7;
|
||||||
margin-left: 24px;
|
margin-inline-start: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__footer {
|
&__footer {
|
||||||
border-top: 1px solid var(--color-border-muted);
|
border-top: 1px solid var(--color-border-muted);
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
|
||||||
|
@media screen and (max-width: 575px) {
|
||||||
|
padding-inline-start: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
& .btn-link {
|
& .btn-link {
|
||||||
display: initial;
|
display: initial;
|
||||||
@ -51,6 +83,14 @@
|
|||||||
color: var(--color-text-alternative);
|
color: var(--color-text-alternative);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__edge-case-box {
|
||||||
|
border: 1px solid var(--color-border-muted);
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
color: var(--color-info-default);
|
||||||
|
display: inline;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app';
|
|||||||
import { EVENT } from '../../../../shared/constants/metametrics';
|
import { EVENT } from '../../../../shared/constants/metametrics';
|
||||||
import {
|
import {
|
||||||
ADD_NETWORK_ROUTE,
|
ADD_NETWORK_ROUTE,
|
||||||
|
ADD_POPULAR_CUSTOM_NETWORK,
|
||||||
ADVANCED_ROUTE,
|
ADVANCED_ROUTE,
|
||||||
} from '../../../helpers/constants/routes';
|
} from '../../../helpers/constants/routes';
|
||||||
import IconCheck from '../../ui/icon/icon-check';
|
import IconCheck from '../../ui/icon/icon-check';
|
||||||
@ -49,6 +50,7 @@ function mapStateToProps(state) {
|
|||||||
frequentRpcListDetail: state.metamask.frequentRpcListDetail || [],
|
frequentRpcListDetail: state.metamask.frequentRpcListDetail || [],
|
||||||
networkDropdownOpen: state.appState.networkDropdownOpen,
|
networkDropdownOpen: state.appState.networkDropdownOpen,
|
||||||
showTestnetMessageInDropdown: state.metamask.showTestnetMessageInDropdown,
|
showTestnetMessageInDropdown: state.metamask.showTestnetMessageInDropdown,
|
||||||
|
addPopularNetworkFeatureToggledOn: state.metamask.customNetworkListEnabled,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,6 +103,7 @@ class NetworkDropdown extends Component {
|
|||||||
showTestnetMessageInDropdown: PropTypes.bool.isRequired,
|
showTestnetMessageInDropdown: PropTypes.bool.isRequired,
|
||||||
hideTestNetMessage: PropTypes.func.isRequired,
|
hideTestNetMessage: PropTypes.func.isRequired,
|
||||||
history: PropTypes.object,
|
history: PropTypes.object,
|
||||||
|
addPopularNetworkFeatureToggledOn: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClick(newProviderType) {
|
handleClick(newProviderType) {
|
||||||
@ -129,10 +132,12 @@ class NetworkDropdown extends Component {
|
|||||||
<Button
|
<Button
|
||||||
type="secondary"
|
type="secondary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (getEnvironmentType() === ENVIRONMENT_TYPE_POPUP) {
|
if (this.props.addPopularNetworkFeatureToggledOn) {
|
||||||
global.platform.openExtensionInBrowser(ADD_NETWORK_ROUTE);
|
this.props.history.push(ADD_POPULAR_CUSTOM_NETWORK);
|
||||||
} else {
|
} else {
|
||||||
this.props.history.push(ADD_NETWORK_ROUTE);
|
getEnvironmentType() === ENVIRONMENT_TYPE_POPUP
|
||||||
|
? global.platform.openExtensionInBrowser(ADD_NETWORK_ROUTE)
|
||||||
|
: this.props.history.push(ADD_NETWORK_ROUTE);
|
||||||
}
|
}
|
||||||
this.props.hideNetworkDropdown();
|
this.props.hideNetworkDropdown();
|
||||||
}}
|
}}
|
||||||
|
@ -43,6 +43,9 @@ const MetaMaskTemplateRenderer = ({ sections }) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{sections.reduce((allChildren, child) => {
|
{sections.reduce((allChildren, child) => {
|
||||||
|
if (child?.hide === true) {
|
||||||
|
return allChildren;
|
||||||
|
}
|
||||||
if (typeof child === 'string') {
|
if (typeof child === 'string') {
|
||||||
// React can render strings directly, so push them into the accumulator
|
// React can render strings directly, so push them into the accumulator
|
||||||
allChildren.push(child);
|
allChildren.push(child);
|
||||||
|
@ -9,6 +9,8 @@ import MetaMaskTranslation from '../metamask-translation';
|
|||||||
import NetworkDisplay from '../network-display';
|
import NetworkDisplay from '../network-display';
|
||||||
import TextArea from '../../ui/textarea/textarea';
|
import TextArea from '../../ui/textarea/textarea';
|
||||||
import ConfirmationNetworkSwitch from '../../../pages/confirmation/components/confirmation-network-switch';
|
import ConfirmationNetworkSwitch from '../../../pages/confirmation/components/confirmation-network-switch';
|
||||||
|
import UrlIcon from '../../ui/url-icon';
|
||||||
|
import Tooltip from '../../ui/tooltip/tooltip';
|
||||||
|
|
||||||
export const safeComponentList = {
|
export const safeComponentList = {
|
||||||
MetaMaskTranslation,
|
MetaMaskTranslation,
|
||||||
@ -27,4 +29,7 @@ export const safeComponentList = {
|
|||||||
NetworkDisplay,
|
NetworkDisplay,
|
||||||
TextArea,
|
TextArea,
|
||||||
ConfirmationNetworkSwitch,
|
ConfirmationNetworkSwitch,
|
||||||
|
UrlIcon,
|
||||||
|
Tooltip,
|
||||||
|
i: 'i',
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
import Typography from '../typography';
|
import Typography from '../typography';
|
||||||
|
import UrlIcon from '../url-icon';
|
||||||
import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system';
|
import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
export default function Chip({
|
export default function Chip({
|
||||||
@ -14,9 +15,11 @@ export default function Chip({
|
|||||||
label,
|
label,
|
||||||
labelProps = {},
|
labelProps = {},
|
||||||
leftIcon,
|
leftIcon,
|
||||||
|
leftIconUrl = '',
|
||||||
rightIcon,
|
rightIcon,
|
||||||
onClick,
|
onClick,
|
||||||
maxContent = true,
|
maxContent = true,
|
||||||
|
displayInlineBlock = false,
|
||||||
}) {
|
}) {
|
||||||
const onKeyPress = (event) => {
|
const onKeyPress = (event) => {
|
||||||
if (event.key === 'Enter' && onClick) {
|
if (event.key === 'Enter' && onClick) {
|
||||||
@ -37,11 +40,17 @@ export default function Chip({
|
|||||||
[`chip--border-color-${borderColor}`]: true,
|
[`chip--border-color-${borderColor}`]: true,
|
||||||
[`chip--background-color-${backgroundColor}`]: true,
|
[`chip--background-color-${backgroundColor}`]: true,
|
||||||
'chip--max-content': maxContent,
|
'chip--max-content': maxContent,
|
||||||
|
'chip--display-inline-block': displayInlineBlock,
|
||||||
})}
|
})}
|
||||||
role={isInteractive ? 'button' : undefined}
|
role={isInteractive ? 'button' : undefined}
|
||||||
tabIndex={isInteractive ? 0 : undefined}
|
tabIndex={isInteractive ? 0 : undefined}
|
||||||
>
|
>
|
||||||
{leftIcon ? <div className="chip__left-icon">{leftIcon}</div> : null}
|
{leftIcon && !leftIconUrl ? (
|
||||||
|
<div className="chip__left-icon">{leftIcon}</div>
|
||||||
|
) : null}
|
||||||
|
{leftIconUrl ? (
|
||||||
|
<UrlIcon className="chip__left-url-icon" url={leftIconUrl} />
|
||||||
|
) : null}
|
||||||
{children ?? (
|
{children ?? (
|
||||||
<Typography
|
<Typography
|
||||||
className="chip__label"
|
className="chip__label"
|
||||||
@ -106,4 +115,12 @@ Chip.propTypes = {
|
|||||||
* max-content can overflow the parent's width and break designs
|
* max-content can overflow the parent's width and break designs
|
||||||
*/
|
*/
|
||||||
maxContent: PropTypes.bool,
|
maxContent: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Icon location
|
||||||
|
*/
|
||||||
|
leftIconUrl: PropTypes.string,
|
||||||
|
/**
|
||||||
|
* Display or not the inline block
|
||||||
|
*/
|
||||||
|
displayInlineBlock: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
@ -16,6 +16,10 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__left-url-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
};
|
||||||
|
|
||||||
@each $variant, $color in design-system.$color-map {
|
@each $variant, $color in design-system.$color-map {
|
||||||
&--border-color-#{$variant} {
|
&--border-color-#{$variant} {
|
||||||
border-color: var($color);
|
border-color: var($color);
|
||||||
@ -67,4 +71,8 @@
|
|||||||
&--max-content {
|
&--max-content {
|
||||||
width: max-content;
|
width: max-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--display-inline-block {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
SIZES,
|
SIZES,
|
||||||
TYPOGRAPHY,
|
TYPOGRAPHY,
|
||||||
FONT_WEIGHT,
|
FONT_WEIGHT,
|
||||||
|
OVERFLOW_WRAP,
|
||||||
} from '../../../helpers/constants/design-system';
|
} from '../../../helpers/constants/design-system';
|
||||||
import Tooltip from '../tooltip';
|
import Tooltip from '../tooltip';
|
||||||
|
|
||||||
@ -60,6 +61,7 @@ export default function DefinitionList({
|
|||||||
marginBottom: MARGIN_MAP[gapSize],
|
marginBottom: MARGIN_MAP[gapSize],
|
||||||
}}
|
}}
|
||||||
className="definition-list__definition"
|
className="definition-list__definition"
|
||||||
|
overflowWrap={OVERFLOW_WRAP.BREAK_WORD}
|
||||||
tag="dd"
|
tag="dd"
|
||||||
>
|
>
|
||||||
{definition}
|
{definition}
|
||||||
|
@ -60,6 +60,7 @@ export default function reduceApp(state = {}, action) {
|
|||||||
newCollectibleAddedMessage: '',
|
newCollectibleAddedMessage: '',
|
||||||
sendInputCurrencySwitched: false,
|
sendInputCurrencySwitched: false,
|
||||||
newTokensImported: '',
|
newTokensImported: '',
|
||||||
|
newCustomNetworkAdded: {},
|
||||||
...state,
|
...state,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -393,6 +394,11 @@ export default function reduceApp(state = {}, action) {
|
|||||||
...appState,
|
...appState,
|
||||||
sendInputCurrencySwitched: !appState.sendInputCurrencySwitched,
|
sendInputCurrencySwitched: !appState.sendInputCurrencySwitched,
|
||||||
};
|
};
|
||||||
|
case actionConstants.SET_NEW_CUSTOM_NETWORK_ADDED:
|
||||||
|
return {
|
||||||
|
...appState,
|
||||||
|
newCustomNetworkAdded: action.value,
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return appState;
|
return appState;
|
||||||
}
|
}
|
||||||
@ -444,3 +450,7 @@ export function getLedgerTransportStatus(state) {
|
|||||||
export function toggleCurrencySwitch() {
|
export function toggleCurrencySwitch() {
|
||||||
return { type: actionConstants.TOGGLE_CURRENCY_INPUT_SWITCH };
|
return { type: actionConstants.TOGGLE_CURRENCY_INPUT_SWITCH };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setNewCustomNetworkAdded(value) {
|
||||||
|
return { type: actionConstants.SET_NEW_CUSTOM_NETWORK_ADDED, value };
|
||||||
|
}
|
||||||
|
@ -12,6 +12,8 @@ const ALERTS_ROUTE = '/settings/alerts';
|
|||||||
const NETWORKS_ROUTE = '/settings/networks';
|
const NETWORKS_ROUTE = '/settings/networks';
|
||||||
const NETWORKS_FORM_ROUTE = '/settings/networks/form';
|
const NETWORKS_FORM_ROUTE = '/settings/networks/form';
|
||||||
const ADD_NETWORK_ROUTE = '/settings/networks/add-network';
|
const ADD_NETWORK_ROUTE = '/settings/networks/add-network';
|
||||||
|
const ADD_POPULAR_CUSTOM_NETWORK =
|
||||||
|
'/settings/networks/add-popular-custom-network';
|
||||||
const SNAPS_LIST_ROUTE = '/settings/snaps-list';
|
const SNAPS_LIST_ROUTE = '/settings/snaps-list';
|
||||||
const SNAPS_VIEW_ROUTE = '/settings/snaps-view';
|
const SNAPS_VIEW_ROUTE = '/settings/snaps-view';
|
||||||
const CONTACT_LIST_ROUTE = '/settings/contact-list';
|
const CONTACT_LIST_ROUTE = '/settings/contact-list';
|
||||||
@ -113,6 +115,8 @@ const PATH_NAME_MAP = {
|
|||||||
[NETWORKS_ROUTE]: 'Network Settings Page',
|
[NETWORKS_ROUTE]: 'Network Settings Page',
|
||||||
[NETWORKS_FORM_ROUTE]: 'Network Settings Page Form',
|
[NETWORKS_FORM_ROUTE]: 'Network Settings Page Form',
|
||||||
[ADD_NETWORK_ROUTE]: 'Add Network From Settings Page Form',
|
[ADD_NETWORK_ROUTE]: 'Add Network From Settings Page Form',
|
||||||
|
[ADD_POPULAR_CUSTOM_NETWORK]:
|
||||||
|
'Add Network From A List Of Popular Custom Networks',
|
||||||
[CONTACT_LIST_ROUTE]: 'Contact List Settings Page',
|
[CONTACT_LIST_ROUTE]: 'Contact List Settings Page',
|
||||||
[`${CONTACT_EDIT_ROUTE}/:address`]: 'Edit Contact Settings Page',
|
[`${CONTACT_EDIT_ROUTE}/:address`]: 'Edit Contact Settings Page',
|
||||||
[CONTACT_ADD_ROUTE]: 'Add Contact Settings Page',
|
[CONTACT_ADD_ROUTE]: 'Add Contact Settings Page',
|
||||||
@ -224,6 +228,7 @@ export {
|
|||||||
NETWORKS_ROUTE,
|
NETWORKS_ROUTE,
|
||||||
NETWORKS_FORM_ROUTE,
|
NETWORKS_FORM_ROUTE,
|
||||||
ADD_NETWORK_ROUTE,
|
ADD_NETWORK_ROUTE,
|
||||||
|
ADD_POPULAR_CUSTOM_NETWORK,
|
||||||
INITIALIZE_BACKUP_SEED_PHRASE_ROUTE,
|
INITIALIZE_BACKUP_SEED_PHRASE_ROUTE,
|
||||||
INITIALIZE_SEED_PHRASE_INTRO_ROUTE,
|
INITIALIZE_SEED_PHRASE_INTRO_ROUTE,
|
||||||
CONNECT_ROUTE,
|
CONNECT_ROUTE,
|
||||||
|
@ -346,4 +346,11 @@ export const SETTINGS_CONSTANTS = [
|
|||||||
icon: 'fa fa-flask',
|
icon: 'fa fa-flask',
|
||||||
featureFlag: 'COLLECTIBLES_V1',
|
featureFlag: 'COLLECTIBLES_V1',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
tabMessage: (t) => t('experimental'),
|
||||||
|
sectionMessage: (t) => t('showCustomNetworkList'),
|
||||||
|
descriptionMessage: (t) => t('showCustomNetworkListDescription'),
|
||||||
|
route: `${EXPERIMENTAL_ROUTE}#show-custom-network`,
|
||||||
|
icon: 'fa fa-flask',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
@ -195,7 +195,7 @@ describe('Settings Search Utils', () => {
|
|||||||
|
|
||||||
it('should get good experimental section number', () => {
|
it('should get good experimental section number', () => {
|
||||||
expect(getNumberOfSettingsInSection(t, t('experimental'))).toStrictEqual(
|
expect(getNumberOfSettingsInSection(t, t('experimental'))).toStrictEqual(
|
||||||
3,
|
4,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ import { getUnapprovedTemplatedConfirmations } from '../../selectors';
|
|||||||
import NetworkDisplay from '../../components/app/network-display/network-display';
|
import NetworkDisplay from '../../components/app/network-display/network-display';
|
||||||
import Callout from '../../components/ui/callout';
|
import Callout from '../../components/ui/callout';
|
||||||
import SiteOrigin from '../../components/ui/site-origin';
|
import SiteOrigin from '../../components/ui/site-origin';
|
||||||
|
import { addCustomNetwork } from '../../store/actions';
|
||||||
import ConfirmationFooter from './components/confirmation-footer';
|
import ConfirmationFooter from './components/confirmation-footer';
|
||||||
import { getTemplateValues, getTemplateAlerts } from './templates';
|
import { getTemplateValues, getTemplateAlerts } from './templates';
|
||||||
|
|
||||||
@ -130,6 +131,7 @@ export default function ConfirmationPage() {
|
|||||||
const pendingConfirmation = pendingConfirmations[currentPendingConfirmation];
|
const pendingConfirmation = pendingConfirmations[currentPendingConfirmation];
|
||||||
const originMetadata = useOriginMetadata(pendingConfirmation?.origin) || {};
|
const originMetadata = useOriginMetadata(pendingConfirmation?.origin) || {};
|
||||||
const [alertState, dismissAlert] = useAlertState(pendingConfirmation);
|
const [alertState, dismissAlert] = useAlertState(pendingConfirmation);
|
||||||
|
const [stayOnPage, setStayOnPage] = useState(false);
|
||||||
|
|
||||||
// Generating templatedValues is potentially expensive, and if done on every render
|
// Generating templatedValues is potentially expensive, and if done on every render
|
||||||
// will result in a new object. Avoiding calling this generation unnecessarily will
|
// will result in a new object. Avoiding calling this generation unnecessarily will
|
||||||
@ -146,11 +148,11 @@ export default function ConfirmationPage() {
|
|||||||
// confirmations reduces to a number that is less than the currently
|
// confirmations reduces to a number that is less than the currently
|
||||||
// viewed index, reset the index.
|
// viewed index, reset the index.
|
||||||
if (pendingConfirmations.length === 0) {
|
if (pendingConfirmations.length === 0) {
|
||||||
history.push(DEFAULT_ROUTE);
|
!stayOnPage && history.push(DEFAULT_ROUTE);
|
||||||
} else if (pendingConfirmations.length <= currentPendingConfirmation) {
|
} else if (pendingConfirmations.length <= currentPendingConfirmation) {
|
||||||
setCurrentPendingConfirmation(pendingConfirmations.length - 1);
|
setCurrentPendingConfirmation(pendingConfirmations.length - 1);
|
||||||
}
|
}
|
||||||
}, [pendingConfirmations, history, currentPendingConfirmation]);
|
}, [pendingConfirmations, history, currentPendingConfirmation, stayOnPage]);
|
||||||
if (!pendingConfirmation) {
|
if (!pendingConfirmation) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -197,23 +199,25 @@ export default function ConfirmationPage() {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
) : null}
|
) : null}
|
||||||
<Box
|
{pendingConfirmation.origin === 'metamask' ? null : (
|
||||||
alignItems="center"
|
<Box
|
||||||
marginTop={1}
|
alignItems="center"
|
||||||
padding={[1, 4, 4]}
|
marginTop={1}
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
padding={[1, 4, 4]}
|
||||||
>
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
<SiteIcon
|
>
|
||||||
icon={originMetadata.iconUrl}
|
<SiteIcon
|
||||||
name={originMetadata.hostname}
|
icon={originMetadata.iconUrl}
|
||||||
size={36}
|
name={originMetadata.hostname}
|
||||||
/>
|
size={36}
|
||||||
<SiteOrigin
|
/>
|
||||||
chip
|
<SiteOrigin
|
||||||
siteOrigin={stripHttpsScheme(originMetadata.origin)}
|
chip
|
||||||
title={stripHttpsScheme(originMetadata.origin)}
|
siteOrigin={stripHttpsScheme(originMetadata.origin)}
|
||||||
/>
|
title={stripHttpsScheme(originMetadata.origin)}
|
||||||
</Box>
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
<MetaMaskTemplateRenderer sections={templatedValues.content} />
|
<MetaMaskTemplateRenderer sections={templatedValues.content} />
|
||||||
</div>
|
</div>
|
||||||
<ConfirmationFooter
|
<ConfirmationFooter
|
||||||
@ -234,8 +238,15 @@ export default function ConfirmationPage() {
|
|||||||
</Callout>
|
</Callout>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
onApprove={templatedValues.onApprove}
|
onApprove={() => {
|
||||||
onCancel={templatedValues.onCancel}
|
templatedValues.onApprove.apply();
|
||||||
|
pendingConfirmation.origin === 'metamask' &&
|
||||||
|
dispatch(addCustomNetwork(pendingConfirmation.requestData));
|
||||||
|
}}
|
||||||
|
onCancel={() => {
|
||||||
|
templatedValues.onCancel.apply();
|
||||||
|
pendingConfirmation.origin === 'metamask' && setStayOnPage(true);
|
||||||
|
}}
|
||||||
approveText={templatedValues.approvalText}
|
approveText={templatedValues.approvalText}
|
||||||
cancelText={templatedValues.cancelText}
|
cancelText={templatedValues.cancelText}
|
||||||
/>
|
/>
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { ethErrors } from 'eth-rpc-errors';
|
import { ethErrors } from 'eth-rpc-errors';
|
||||||
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
SEVERITIES,
|
SEVERITIES,
|
||||||
TYPOGRAPHY,
|
TYPOGRAPHY,
|
||||||
|
TEXT_ALIGN,
|
||||||
|
JUSTIFY_CONTENT,
|
||||||
|
DISPLAY,
|
||||||
|
COLORS,
|
||||||
} from '../../../helpers/constants/design-system';
|
} from '../../../helpers/constants/design-system';
|
||||||
import fetchWithCache from '../../../helpers/utils/fetch-with-cache';
|
import fetchWithCache from '../../../helpers/utils/fetch-with-cache';
|
||||||
|
|
||||||
@ -79,6 +84,11 @@ async function getAlerts(pendingApproval) {
|
|||||||
);
|
);
|
||||||
let validated = Boolean(matchedChain);
|
let validated = Boolean(matchedChain);
|
||||||
|
|
||||||
|
const originIsMetaMask = pendingApproval.origin === 'metamask';
|
||||||
|
if (originIsMetaMask && validated) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
if (matchedChain) {
|
if (matchedChain) {
|
||||||
if (
|
if (
|
||||||
matchedChain.nativeCurrency?.decimals !== 18 ||
|
matchedChain.nativeCurrency?.decimals !== 18 ||
|
||||||
@ -104,12 +114,39 @@ async function getAlerts(pendingApproval) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getValues(pendingApproval, t, actions) {
|
function getValues(pendingApproval, t, actions) {
|
||||||
|
const originIsMetaMask = pendingApproval.origin === 'metamask';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
|
{
|
||||||
|
hide: !originIsMetaMask,
|
||||||
|
element: 'Box',
|
||||||
|
key: 'network-box',
|
||||||
|
props: {
|
||||||
|
textAlign: TEXT_ALIGN.CENTER,
|
||||||
|
display: DISPLAY.FLEX,
|
||||||
|
justifyContent: JUSTIFY_CONTENT.CENTER,
|
||||||
|
marginTop: 4,
|
||||||
|
marginBottom: 2,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
element: 'Chip',
|
||||||
|
key: 'network-chip',
|
||||||
|
props: {
|
||||||
|
label: pendingApproval.requestData.chainName,
|
||||||
|
backgroundColor: COLORS.BACKGROUND_ALTERNATIVE,
|
||||||
|
leftIconUrl: pendingApproval.requestData.imageUrl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
element: 'Typography',
|
element: 'Typography',
|
||||||
key: 'title',
|
key: 'title',
|
||||||
children: t('addEthereumChainConfirmationTitle'),
|
children: originIsMetaMask
|
||||||
|
? t('wantToAddThisNetwork')
|
||||||
|
: t('addEthereumChainConfirmationTitle'),
|
||||||
props: {
|
props: {
|
||||||
variant: TYPOGRAPHY.H3,
|
variant: TYPOGRAPHY.H3,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
@ -127,7 +164,7 @@ function getValues(pendingApproval, t, actions) {
|
|||||||
variant: TYPOGRAPHY.H7,
|
variant: TYPOGRAPHY.H7,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
boxProps: {
|
boxProps: {
|
||||||
margin: [0, 0, 4],
|
margin: originIsMetaMask ? [0, 8, 4] : [0, 0, 4],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -138,7 +175,55 @@ function getValues(pendingApproval, t, actions) {
|
|||||||
{
|
{
|
||||||
element: 'b',
|
element: 'b',
|
||||||
key: 'bolded-text',
|
key: 'bolded-text',
|
||||||
children: `${t('addEthereumChainConfirmationRisks')} `,
|
props: {
|
||||||
|
style: { display: originIsMetaMask && '-webkit-box' },
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
`${t('addEthereumChainConfirmationRisks')} `,
|
||||||
|
{
|
||||||
|
hide: !originIsMetaMask,
|
||||||
|
element: 'Tooltip',
|
||||||
|
key: 'tooltip-info',
|
||||||
|
props: {
|
||||||
|
position: 'bottom',
|
||||||
|
interactive: true,
|
||||||
|
trigger: 'mouseenter',
|
||||||
|
html: (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '180px',
|
||||||
|
margin: '16px',
|
||||||
|
textAlign: 'left',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('someNetworksMayPoseSecurity')}{' '}
|
||||||
|
<a
|
||||||
|
key="zendesk_page_link"
|
||||||
|
href="https://metamask.zendesk.com/hc/en-us/articles/4417500466971"
|
||||||
|
rel="noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
style={{ color: 'var(--color-primary-default)' }}
|
||||||
|
>
|
||||||
|
{t('learnMoreUpperCase')}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
element: 'i',
|
||||||
|
key: 'info-circle',
|
||||||
|
props: {
|
||||||
|
className: 'fas fa-info-circle',
|
||||||
|
style: {
|
||||||
|
marginLeft: '4px',
|
||||||
|
color: 'var(--color-icon-default)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
element: 'MetaMaskTranslation',
|
element: 'MetaMaskTranslation',
|
||||||
@ -164,7 +249,7 @@ function getValues(pendingApproval, t, actions) {
|
|||||||
variant: TYPOGRAPHY.H7,
|
variant: TYPOGRAPHY.H7,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
boxProps: {
|
boxProps: {
|
||||||
margin: 0,
|
margin: originIsMetaMask ? [0, 8] : 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -205,7 +290,7 @@ function getValues(pendingApproval, t, actions) {
|
|||||||
pendingApproval.id,
|
pendingApproval.id,
|
||||||
ethErrors.provider.userRejectedRequest().serialize(),
|
ethErrors.provider.userRejectedRequest().serialize(),
|
||||||
),
|
),
|
||||||
networkDisplay: true,
|
networkDisplay: !originIsMetaMask,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,9 +26,7 @@ import {
|
|||||||
TYPOGRAPHY,
|
TYPOGRAPHY,
|
||||||
FONT_WEIGHT,
|
FONT_WEIGHT,
|
||||||
DISPLAY,
|
DISPLAY,
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
|
||||||
COLORS,
|
COLORS,
|
||||||
///: END:ONLY_INCLUDE_IN
|
|
||||||
} from '../../helpers/constants/design-system';
|
} from '../../helpers/constants/design-system';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -143,6 +141,9 @@ export default class Home extends PureComponent {
|
|||||||
closeNotificationPopup: PropTypes.func.isRequired,
|
closeNotificationPopup: PropTypes.func.isRequired,
|
||||||
newTokensImported: PropTypes.string,
|
newTokensImported: PropTypes.string,
|
||||||
setNewTokensImported: PropTypes.func.isRequired,
|
setNewTokensImported: PropTypes.func.isRequired,
|
||||||
|
newCustomNetworkAdded: PropTypes.object,
|
||||||
|
setNewCustomNetworkAdded: PropTypes.func,
|
||||||
|
setRpcTarget: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -280,6 +281,9 @@ export default class Home extends PureComponent {
|
|||||||
setNewCollectibleAddedMessage,
|
setNewCollectibleAddedMessage,
|
||||||
newTokensImported,
|
newTokensImported,
|
||||||
setNewTokensImported,
|
setNewTokensImported,
|
||||||
|
newCustomNetworkAdded,
|
||||||
|
setNewCustomNetworkAdded,
|
||||||
|
setRpcTarget,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<MultipleNotifications>
|
<MultipleNotifications>
|
||||||
@ -479,6 +483,53 @@ export default class Home extends PureComponent {
|
|||||||
key="home-infuraBlockedNotification"
|
key="home-infuraBlockedNotification"
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
{Object.keys(newCustomNetworkAdded).length !== 0 && (
|
||||||
|
<Popover className="home__new-network-added">
|
||||||
|
<i className="fa fa-check-circle fa-2x home__new-network-added__check-circle" />
|
||||||
|
<Typography
|
||||||
|
variant={TYPOGRAPHY.H4}
|
||||||
|
margin={[5, 9, 0, 9]}
|
||||||
|
fontWeight={FONT_WEIGHT.BOLD}
|
||||||
|
>
|
||||||
|
{t('networkAddedSuccessfully')}
|
||||||
|
</Typography>
|
||||||
|
<Box margin={[8, 8, 5, 8]}>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
className="home__new-network-added__switch-to-button"
|
||||||
|
onClick={() => {
|
||||||
|
setRpcTarget(
|
||||||
|
newCustomNetworkAdded.rpcUrl,
|
||||||
|
newCustomNetworkAdded.chainId,
|
||||||
|
newCustomNetworkAdded.ticker,
|
||||||
|
newCustomNetworkAdded.chainName,
|
||||||
|
);
|
||||||
|
setNewCustomNetworkAdded();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant={TYPOGRAPHY.H6}
|
||||||
|
fontWeight={FONT_WEIGHT.NORMAL}
|
||||||
|
color={COLORS.PRIMARY_INVERSE}
|
||||||
|
>
|
||||||
|
{t('switchToNetwork', [newCustomNetworkAdded.chainName])}
|
||||||
|
</Typography>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="secondary"
|
||||||
|
onClick={() => setNewCustomNetworkAdded()}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant={TYPOGRAPHY.H6}
|
||||||
|
fontWeight={FONT_WEIGHT.NORMAL}
|
||||||
|
color={COLORS.PRIMARY_DEFAULT}
|
||||||
|
>
|
||||||
|
{t('dismiss')}
|
||||||
|
</Typography>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Popover>
|
||||||
|
)}
|
||||||
</MultipleNotifications>
|
</MultipleNotifications>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -37,11 +37,16 @@ import {
|
|||||||
setNewNetworkAdded,
|
setNewNetworkAdded,
|
||||||
setNewCollectibleAddedMessage,
|
setNewCollectibleAddedMessage,
|
||||||
setNewTokensImported,
|
setNewTokensImported,
|
||||||
|
setRpcTarget,
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||||
removeSnapError,
|
removeSnapError,
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
} from '../../store/actions';
|
} from '../../store/actions';
|
||||||
import { setThreeBoxLastUpdated, hideWhatsNewPopup } from '../../ducks/app/app';
|
import {
|
||||||
|
setThreeBoxLastUpdated,
|
||||||
|
hideWhatsNewPopup,
|
||||||
|
setNewCustomNetworkAdded,
|
||||||
|
} from '../../ducks/app/app';
|
||||||
import { getWeb3ShimUsageAlertEnabledness } from '../../ducks/metamask/metamask';
|
import { getWeb3ShimUsageAlertEnabledness } from '../../ducks/metamask/metamask';
|
||||||
import { getSwapsFeatureIsLive } from '../../ducks/swaps/swaps';
|
import { getSwapsFeatureIsLive } from '../../ducks/swaps/swaps';
|
||||||
import { getEnvironmentType } from '../../../app/scripts/lib/util';
|
import { getEnvironmentType } from '../../../app/scripts/lib/util';
|
||||||
@ -138,6 +143,7 @@ const mapStateToProps = (state) => {
|
|||||||
isSigningQRHardwareTransaction,
|
isSigningQRHardwareTransaction,
|
||||||
newCollectibleAddedMessage: getNewCollectibleAddedMessage(state),
|
newCollectibleAddedMessage: getNewCollectibleAddedMessage(state),
|
||||||
newTokensImported: getNewTokensImported(state),
|
newTokensImported: getNewTokensImported(state),
|
||||||
|
newCustomNetworkAdded: appState.newCustomNetworkAdded,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -180,6 +186,12 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
setNewTokensImported: (newTokens) => {
|
setNewTokensImported: (newTokens) => {
|
||||||
dispatch(setNewTokensImported(newTokens));
|
dispatch(setNewTokensImported(newTokens));
|
||||||
},
|
},
|
||||||
|
setNewCustomNetworkAdded: () => {
|
||||||
|
dispatch(setNewCustomNetworkAdded({}));
|
||||||
|
},
|
||||||
|
setRpcTarget: (rpcUrl, chainId, ticker, nickname) => {
|
||||||
|
dispatch(setRpcTarget(rpcUrl, chainId, ticker, nickname));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
|
@ -207,4 +207,18 @@
|
|||||||
margin-inline-start: 32px;
|
margin-inline-start: 32px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__new-network-added {
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&__check-circle {
|
||||||
|
color: var(--color-success-default);
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__switch-to-button {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ export default class ExperimentalTab extends PureComponent {
|
|||||||
setEIP1559V2Enabled: PropTypes.func,
|
setEIP1559V2Enabled: PropTypes.func,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
setTheme: PropTypes.func,
|
setTheme: PropTypes.func,
|
||||||
|
customNetworkListEnabled: PropTypes.bool,
|
||||||
|
setCustomNetworkListEnabled: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
settingsRefs = Array(
|
settingsRefs = Array(
|
||||||
@ -284,6 +286,45 @@ export default class ExperimentalTab extends PureComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderCustomNetworkListToggle() {
|
||||||
|
const { t } = this.context;
|
||||||
|
const {
|
||||||
|
customNetworkListEnabled,
|
||||||
|
setCustomNetworkListEnabled,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={this.settingsRefs[5]} className="settings-page__content-row">
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<span>{t('showCustomNetworkList')}</span>
|
||||||
|
<div className="settings-page__content-description">
|
||||||
|
{t('showCustomNetworkListDescription')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<div className="settings-page__content-item-col">
|
||||||
|
<ToggleButton
|
||||||
|
value={customNetworkListEnabled}
|
||||||
|
onToggle={(value) => {
|
||||||
|
this.context.trackEvent({
|
||||||
|
category: EVENT.CATEGORIES.SETTINGS,
|
||||||
|
event: 'Enabled/Disable CustomNetworkList',
|
||||||
|
properties: {
|
||||||
|
action: 'Enabled/Disable CustomNetworkList',
|
||||||
|
legacy_event: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setCustomNetworkListEnabled(!value);
|
||||||
|
}}
|
||||||
|
offLabel={t('off')}
|
||||||
|
onLabel={t('on')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="settings-page__body">
|
<div className="settings-page__body">
|
||||||
@ -295,6 +336,7 @@ export default class ExperimentalTab extends PureComponent {
|
|||||||
{this.renderCollectibleDetectionToggle()}
|
{this.renderCollectibleDetectionToggle()}
|
||||||
{this.renderEIP1559V2EnabledToggle()}
|
{this.renderEIP1559V2EnabledToggle()}
|
||||||
{this.renderTheme()}
|
{this.renderTheme()}
|
||||||
|
{this.renderCustomNetworkListToggle()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
setOpenSeaEnabled,
|
setOpenSeaEnabled,
|
||||||
setEIP1559V2Enabled,
|
setEIP1559V2Enabled,
|
||||||
setTheme,
|
setTheme,
|
||||||
|
setCustomNetworkListEnabled,
|
||||||
} from '../../../store/actions';
|
} from '../../../store/actions';
|
||||||
import {
|
import {
|
||||||
getUseTokenDetection,
|
getUseTokenDetection,
|
||||||
@ -14,6 +15,7 @@ import {
|
|||||||
getOpenSeaEnabled,
|
getOpenSeaEnabled,
|
||||||
getEIP1559V2Enabled,
|
getEIP1559V2Enabled,
|
||||||
getTheme,
|
getTheme,
|
||||||
|
getIsCustomNetworkListEnabled,
|
||||||
} from '../../../selectors';
|
} from '../../../selectors';
|
||||||
import ExperimentalTab from './experimental-tab.component';
|
import ExperimentalTab from './experimental-tab.component';
|
||||||
|
|
||||||
@ -26,6 +28,7 @@ const mapStateToProps = (state) => {
|
|||||||
openSeaEnabled: getOpenSeaEnabled(state),
|
openSeaEnabled: getOpenSeaEnabled(state),
|
||||||
eip1559V2Enabled: getEIP1559V2Enabled(state),
|
eip1559V2Enabled: getEIP1559V2Enabled(state),
|
||||||
theme: getTheme(state),
|
theme: getTheme(state),
|
||||||
|
customNetworkListEnabled: getIsCustomNetworkListEnabled(state),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,6 +43,8 @@ const mapDispatchToProps = (dispatch) => {
|
|||||||
setOpenSeaEnabled: (val) => dispatch(setOpenSeaEnabled(val)),
|
setOpenSeaEnabled: (val) => dispatch(setOpenSeaEnabled(val)),
|
||||||
setEIP1559V2Enabled: (val) => dispatch(setEIP1559V2Enabled(val)),
|
setEIP1559V2Enabled: (val) => dispatch(setEIP1559V2Enabled(val)),
|
||||||
setTheme: (val) => dispatch(setTheme(val)),
|
setTheme: (val) => dispatch(setTheme(val)),
|
||||||
|
setCustomNetworkListEnabled: (val) =>
|
||||||
|
dispatch(setCustomNetworkListEnabled(val)),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -522,7 +522,6 @@ const NetworksForm = ({
|
|||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
resetForm();
|
resetForm();
|
||||||
dispatch(setSelectedSettingsRpcUrl(''));
|
dispatch(setSelectedSettingsRpcUrl(''));
|
||||||
history.push(NETWORKS_ROUTE);
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -1,18 +1,31 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
||||||
import { ADD_NETWORK_ROUTE } from '../../../../helpers/constants/routes';
|
import {
|
||||||
|
ADD_NETWORK_ROUTE,
|
||||||
|
ADD_POPULAR_CUSTOM_NETWORK,
|
||||||
|
} from '../../../../helpers/constants/routes';
|
||||||
import Button from '../../../../components/ui/button';
|
import Button from '../../../../components/ui/button';
|
||||||
|
import { getIsCustomNetworkListEnabled } from '../../../../selectors';
|
||||||
|
|
||||||
const NetworksFormSubheader = ({ addNewNetwork }) => {
|
const NetworksFormSubheader = ({ addNewNetwork }) => {
|
||||||
const t = useI18nContext();
|
const t = useI18nContext();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const addPopularNetworkFeatureToggledOn = useSelector(
|
||||||
|
getIsCustomNetworkListEnabled,
|
||||||
|
);
|
||||||
|
|
||||||
return addNewNetwork ? (
|
return addNewNetwork ? (
|
||||||
<div className="networks-tab__subheader">
|
<div className="networks-tab__subheader">
|
||||||
<span className="networks-tab__sub-header-text">{t('networks')}</span>
|
<span className="networks-tab__sub-header-text">{t('networks')}</span>
|
||||||
|
<span className="networks-tab__sub-header-text">{' > '}</span>
|
||||||
|
<div className="networks-tab__sub-header-text">{t('addANetwork')}</div>
|
||||||
<span>{' > '}</span>
|
<span>{' > '}</span>
|
||||||
<div className="networks-tab__subheader--break">{t('addANetwork')}</div>
|
<div className="networks-tab__subheader--break">
|
||||||
|
{t('addANetworkManually')}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="settings-page__sub-header">
|
<div className="settings-page__sub-header">
|
||||||
@ -22,7 +35,9 @@ const NetworksFormSubheader = ({ addNewNetwork }) => {
|
|||||||
type="primary"
|
type="primary"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
history.push(ADD_NETWORK_ROUTE);
|
addPopularNetworkFeatureToggledOn
|
||||||
|
? history.push(ADD_POPULAR_CUSTOM_NETWORK)
|
||||||
|
: history.push(ADD_NETWORK_ROUTE);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('addANetwork')}
|
{t('addANetwork')}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import configureMockStore from 'redux-mock-store';
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
import { waitFor } from '@testing-library/react';
|
||||||
import { renderWithProvider } from '../../../../../test/jest/rendering';
|
import { renderWithProvider } from '../../../../../test/jest/rendering';
|
||||||
import NetworksTabSubheader from '.';
|
import NetworksTabSubheader from '.';
|
||||||
|
|
||||||
@ -36,11 +37,11 @@ describe('NetworksTabSubheader Component', () => {
|
|||||||
expect(getByRole('button', { text: 'Add a network' })).toBeDefined();
|
expect(getByRole('button', { text: 'Add a network' })).toBeDefined();
|
||||||
});
|
});
|
||||||
it('should render add network form subheader correctly', () => {
|
it('should render add network form subheader correctly', () => {
|
||||||
const { queryByText } = renderComponent({
|
const { queryByText, getAllByText } = renderComponent({
|
||||||
addNewNetwork: true,
|
addNewNetwork: true,
|
||||||
});
|
});
|
||||||
expect(queryByText('Networks')).toBeInTheDocument();
|
expect(queryByText('Networks')).toBeInTheDocument();
|
||||||
expect(queryByText('>')).toBeInTheDocument();
|
waitFor(() => expect(getAllByText('>')).toBeInTheDocument());
|
||||||
expect(queryByText('Add a network')).toBeInTheDocument();
|
expect(queryByText('Add a network')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation, useHistory } from 'react-router-dom';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||||
import {
|
import {
|
||||||
ADD_NETWORK_ROUTE,
|
ADD_NETWORK_ROUTE,
|
||||||
|
ADD_POPULAR_CUSTOM_NETWORK,
|
||||||
NETWORKS_FORM_ROUTE,
|
NETWORKS_FORM_ROUTE,
|
||||||
} from '../../../helpers/constants/routes';
|
} from '../../../helpers/constants/routes';
|
||||||
import { setSelectedSettingsRpcUrl } from '../../../store/actions';
|
import { setSelectedSettingsRpcUrl } from '../../../store/actions';
|
||||||
@ -14,6 +15,7 @@ import { getEnvironmentType } from '../../../../app/scripts/lib/util';
|
|||||||
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../shared/constants/app';
|
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../shared/constants/app';
|
||||||
import {
|
import {
|
||||||
getFrequentRpcListDetail,
|
getFrequentRpcListDetail,
|
||||||
|
getIsCustomNetworkListEnabled,
|
||||||
getNetworksTabSelectedRpcUrl,
|
getNetworksTabSelectedRpcUrl,
|
||||||
getProvider,
|
getProvider,
|
||||||
} from '../../../selectors';
|
} from '../../../selectors';
|
||||||
@ -36,6 +38,7 @@ const NetworksTab = ({ addNewNetwork }) => {
|
|||||||
const t = useI18nContext();
|
const t = useI18nContext();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
const environmentType = getEnvironmentType();
|
const environmentType = getEnvironmentType();
|
||||||
const isFullScreen = environmentType === ENVIRONMENT_TYPE_FULLSCREEN;
|
const isFullScreen = environmentType === ENVIRONMENT_TYPE_FULLSCREEN;
|
||||||
@ -45,6 +48,9 @@ const NetworksTab = ({ addNewNetwork }) => {
|
|||||||
const frequentRpcListDetail = useSelector(getFrequentRpcListDetail);
|
const frequentRpcListDetail = useSelector(getFrequentRpcListDetail);
|
||||||
const provider = useSelector(getProvider);
|
const provider = useSelector(getProvider);
|
||||||
const networksTabSelectedRpcUrl = useSelector(getNetworksTabSelectedRpcUrl);
|
const networksTabSelectedRpcUrl = useSelector(getNetworksTabSelectedRpcUrl);
|
||||||
|
const addPopularNetworkFeatureToggledOn = useSelector(
|
||||||
|
getIsCustomNetworkListEnabled,
|
||||||
|
);
|
||||||
|
|
||||||
const frequentRpcNetworkListDetails = frequentRpcListDetail.map((rpc) => {
|
const frequentRpcNetworkListDetails = frequentRpcListDetail.map((rpc) => {
|
||||||
return {
|
return {
|
||||||
@ -118,9 +124,16 @@ const NetworksTab = ({ addNewNetwork }) => {
|
|||||||
<div className="networks-tab__networks-list-popup-footer">
|
<div className="networks-tab__networks-list-popup-footer">
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={(event) => {
|
onClick={() => {
|
||||||
event.preventDefault();
|
if (addPopularNetworkFeatureToggledOn) {
|
||||||
global.platform.openExtensionInBrowser(ADD_NETWORK_ROUTE);
|
history.push(ADD_POPULAR_CUSTOM_NETWORK);
|
||||||
|
} else {
|
||||||
|
isFullScreen
|
||||||
|
? history.push(ADD_NETWORK_ROUTE)
|
||||||
|
: global.platform.openExtensionInBrowser(
|
||||||
|
ADD_NETWORK_ROUTE,
|
||||||
|
);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('addNetwork')}
|
{t('addNetwork')}
|
||||||
|
@ -23,9 +23,11 @@ import {
|
|||||||
CONTACT_VIEW_ROUTE,
|
CONTACT_VIEW_ROUTE,
|
||||||
EXPERIMENTAL_ROUTE,
|
EXPERIMENTAL_ROUTE,
|
||||||
ADD_NETWORK_ROUTE,
|
ADD_NETWORK_ROUTE,
|
||||||
|
ADD_POPULAR_CUSTOM_NETWORK,
|
||||||
} from '../../helpers/constants/routes';
|
} from '../../helpers/constants/routes';
|
||||||
|
|
||||||
import { getSettingsRoutes } from '../../helpers/utils/settings-search';
|
import { getSettingsRoutes } from '../../helpers/utils/settings-search';
|
||||||
|
import AddNetwork from '../../components/app/add-network/add-network';
|
||||||
import SettingsTab from './settings-tab';
|
import SettingsTab from './settings-tab';
|
||||||
import AlertsTab from './alerts-tab';
|
import AlertsTab from './alerts-tab';
|
||||||
import NetworksTab from './networks-tab';
|
import NetworksTab from './networks-tab';
|
||||||
@ -124,7 +126,6 @@ class SettingsPage extends PureComponent {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{this.renderTitle()}
|
{this.renderTitle()}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="settings-page__header__title-container__close-button"
|
className="settings-page__header__title-container__close-button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -343,9 +344,15 @@ class SettingsPage extends PureComponent {
|
|||||||
render={() => <NetworksTab addNewNetwork />}
|
render={() => <NetworksTab addNewNetwork />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
|
exact
|
||||||
path={NETWORKS_ROUTE}
|
path={NETWORKS_ROUTE}
|
||||||
render={() => <NetworksTab addNewNetwork={false} />}
|
render={() => <NetworksTab addNewNetwork={false} />}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path={ADD_POPULAR_CUSTOM_NETWORK}
|
||||||
|
render={() => <AddNetwork />}
|
||||||
|
/>
|
||||||
<Route exact path={SECURITY_ROUTE} component={SecurityTab} />
|
<Route exact path={SECURITY_ROUTE} component={SecurityTab} />
|
||||||
<Route exact path={EXPERIMENTAL_ROUTE} component={ExperimentalTab} />
|
<Route exact path={EXPERIMENTAL_ROUTE} component={ExperimentalTab} />
|
||||||
<Route exact path={CONTACT_LIST_ROUTE} component={ContactListTab} />
|
<Route exact path={CONTACT_LIST_ROUTE} component={ContactListTab} />
|
||||||
|
@ -27,6 +27,7 @@ import {
|
|||||||
ADD_NETWORK_ROUTE,
|
ADD_NETWORK_ROUTE,
|
||||||
SNAPS_LIST_ROUTE,
|
SNAPS_LIST_ROUTE,
|
||||||
SNAPS_VIEW_ROUTE,
|
SNAPS_VIEW_ROUTE,
|
||||||
|
ADD_POPULAR_CUSTOM_NETWORK,
|
||||||
} from '../../helpers/constants/routes';
|
} from '../../helpers/constants/routes';
|
||||||
import Settings from './settings.component';
|
import Settings from './settings.component';
|
||||||
|
|
||||||
@ -46,6 +47,7 @@ const ROUTES_TO_I18N_KEYS = {
|
|||||||
[ADD_NETWORK_ROUTE]: 'networks',
|
[ADD_NETWORK_ROUTE]: 'networks',
|
||||||
[SECURITY_ROUTE]: 'securityAndPrivacy',
|
[SECURITY_ROUTE]: 'securityAndPrivacy',
|
||||||
[EXPERIMENTAL_ROUTE]: 'experimental',
|
[EXPERIMENTAL_ROUTE]: 'experimental',
|
||||||
|
[ADD_POPULAR_CUSTOM_NETWORK]: 'addNetwork',
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state, ownProps) => {
|
||||||
@ -64,6 +66,9 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
Boolean(pathname.match(NETWORKS_FORM_ROUTE)) ||
|
Boolean(pathname.match(NETWORKS_FORM_ROUTE)) ||
|
||||||
Boolean(pathname.match(ADD_NETWORK_ROUTE));
|
Boolean(pathname.match(ADD_NETWORK_ROUTE));
|
||||||
const addNewNetwork = Boolean(pathname.match(ADD_NETWORK_ROUTE));
|
const addNewNetwork = Boolean(pathname.match(ADD_NETWORK_ROUTE));
|
||||||
|
const isAddPopularCustomNetwork = Boolean(
|
||||||
|
pathname.match(ADD_POPULAR_CUSTOM_NETWORK),
|
||||||
|
);
|
||||||
|
|
||||||
const isPopup = getEnvironmentType() === ENVIRONMENT_TYPE_POPUP;
|
const isPopup = getEnvironmentType() === ENVIRONMENT_TYPE_POPUP;
|
||||||
const pathnameI18nKey = ROUTES_TO_I18N_KEYS[pathname];
|
const pathnameI18nKey = ROUTES_TO_I18N_KEYS[pathname];
|
||||||
@ -77,6 +82,8 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
backRoute = NETWORKS_ROUTE;
|
backRoute = NETWORKS_ROUTE;
|
||||||
} else if (isSnapViewPage) {
|
} else if (isSnapViewPage) {
|
||||||
backRoute = SNAPS_LIST_ROUTE;
|
backRoute = SNAPS_LIST_ROUTE;
|
||||||
|
} else if (isAddPopularCustomNetwork) {
|
||||||
|
backRoute = NETWORKS_ROUTE;
|
||||||
}
|
}
|
||||||
|
|
||||||
let initialBreadCrumbRoute;
|
let initialBreadCrumbRoute;
|
||||||
|
@ -1062,3 +1062,13 @@ export function getDetectedTokensInCurrentNetwork(state) {
|
|||||||
export function getNewTokensImported(state) {
|
export function getNewTokensImported(state) {
|
||||||
return state.appState.newTokensImported;
|
return state.appState.newTokensImported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To get the `customNetworkListEnabled` value which determines whether we use the custom network list
|
||||||
|
*
|
||||||
|
* @param {*} state
|
||||||
|
* @returns Boolean
|
||||||
|
*/
|
||||||
|
export function getIsCustomNetworkListEnabled(state) {
|
||||||
|
return state.metamask.customNetworkListEnabled;
|
||||||
|
}
|
||||||
|
@ -92,6 +92,7 @@ export const SET_SELECTED_SETTINGS_RPC_URL = 'SET_SELECTED_SETTINGS_RPC_URL';
|
|||||||
export const SET_NEW_NETWORK_ADDED = 'SET_NEW_NETWORK_ADDED';
|
export const SET_NEW_NETWORK_ADDED = 'SET_NEW_NETWORK_ADDED';
|
||||||
export const SET_NEW_COLLECTIBLE_ADDED_MESSAGE =
|
export const SET_NEW_COLLECTIBLE_ADDED_MESSAGE =
|
||||||
'SET_NEW_COLLECTIBLE_ADDED_MESSAGE';
|
'SET_NEW_COLLECTIBLE_ADDED_MESSAGE';
|
||||||
|
export const SET_NEW_CUSTOM_NETWORK_ADDED = 'SET_NEW_CUSTOM_NETWORK_ADDED';
|
||||||
|
|
||||||
export const LOADING_METHOD_DATA_STARTED = 'LOADING_METHOD_DATA_STARTED';
|
export const LOADING_METHOD_DATA_STARTED = 'LOADING_METHOD_DATA_STARTED';
|
||||||
export const LOADING_METHOD_DATA_FINISHED = 'LOADING_METHOD_DATA_FINISHED';
|
export const LOADING_METHOD_DATA_FINISHED = 'LOADING_METHOD_DATA_FINISHED';
|
||||||
|
@ -42,6 +42,7 @@ import { isEqualCaseInsensitive } from '../../shared/modules/string-utils';
|
|||||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||||
import { NOTIFICATIONS_EXPIRATION_DELAY } from '../helpers/constants/notifications';
|
import { NOTIFICATIONS_EXPIRATION_DELAY } from '../helpers/constants/notifications';
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
import { setNewCustomNetworkAdded } from '../ducks/app/app';
|
||||||
import * as actionConstants from './actionConstants';
|
import * as actionConstants from './actionConstants';
|
||||||
|
|
||||||
let background = null;
|
let background = null;
|
||||||
@ -3728,6 +3729,18 @@ export function setEnableEIP1559V2NoticeDismissed() {
|
|||||||
return promisifiedBackground.setEnableEIP1559V2NoticeDismissed(true);
|
return promisifiedBackground.setEnableEIP1559V2NoticeDismissed(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setCustomNetworkListEnabled(customNetworkListEnabled) {
|
||||||
|
return async () => {
|
||||||
|
try {
|
||||||
|
await promisifiedBackground.setCustomNetworkListEnabled(
|
||||||
|
customNetworkListEnabled,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// QR Hardware Wallets
|
// QR Hardware Wallets
|
||||||
export async function submitQRHardwareCryptoHDKey(cbor) {
|
export async function submitQRHardwareCryptoHDKey(cbor) {
|
||||||
await promisifiedBackground.submitQRHardwareCryptoHDKey(cbor);
|
await promisifiedBackground.submitQRHardwareCryptoHDKey(cbor);
|
||||||
@ -3754,3 +3767,29 @@ export function cancelQRHardwareSignRequest() {
|
|||||||
await promisifiedBackground.cancelQRHardwareSignRequest();
|
await promisifiedBackground.cancelQRHardwareSignRequest();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addCustomNetwork(customRpc) {
|
||||||
|
return async (dispatch) => {
|
||||||
|
try {
|
||||||
|
dispatch(setNewCustomNetworkAdded(customRpc));
|
||||||
|
await promisifiedBackground.addCustomNetwork(customRpc);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(error);
|
||||||
|
dispatch(displayWarning('Had a problem changing networks!'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function requestUserApproval(customRpc, originIsMetaMask) {
|
||||||
|
return async (dispatch) => {
|
||||||
|
try {
|
||||||
|
await promisifiedBackground.requestUserApproval(
|
||||||
|
customRpc,
|
||||||
|
originIsMetaMask,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(error);
|
||||||
|
dispatch(displayWarning('Had a problem changing networks!'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user