mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
New network info popup (#13319)
This commit is contained in:
parent
4dab986ad2
commit
365bf11fdd
19
app/_locales/en/messages.json
generated
19
app/_locales/en/messages.json
generated
@ -369,6 +369,9 @@
|
|||||||
"assets": {
|
"assets": {
|
||||||
"message": "Assets"
|
"message": "Assets"
|
||||||
},
|
},
|
||||||
|
"attemptSendingAssets": {
|
||||||
|
"message": "If you attempt to send assets directly from one network to another, this may result in permanent asset loss. Make sure to use a bridge."
|
||||||
|
},
|
||||||
"attemptToCancel": {
|
"attemptToCancel": {
|
||||||
"message": "Attempt to cancel?"
|
"message": "Attempt to cancel?"
|
||||||
},
|
},
|
||||||
@ -583,6 +586,9 @@
|
|||||||
"message": "Click here to connect your Ledger via WebHID",
|
"message": "Click here to connect your Ledger via WebHID",
|
||||||
"description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid"
|
"description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid"
|
||||||
},
|
},
|
||||||
|
"clickToManuallyAdd": {
|
||||||
|
"message": "Click here to manually add the tokens."
|
||||||
|
},
|
||||||
"clickToRevealSeed": {
|
"clickToRevealSeed": {
|
||||||
"message": "Click here to reveal secret words"
|
"message": "Click here to reveal secret words"
|
||||||
},
|
},
|
||||||
@ -2041,6 +2047,10 @@
|
|||||||
"name": {
|
"name": {
|
||||||
"message": "Name"
|
"message": "Name"
|
||||||
},
|
},
|
||||||
|
"nativeToken": {
|
||||||
|
"message": "The native token on this network is $1. It is the token used for gas fees.",
|
||||||
|
"description": "$1 represents the name of the native token on the current network"
|
||||||
|
},
|
||||||
"needCryptoInWallet": {
|
"needCryptoInWallet": {
|
||||||
"message": "To interact with decentralized applications using MetaMask, you’ll need $1 in your wallet.",
|
"message": "To interact with decentralized applications using MetaMask, you’ll need $1 in your wallet.",
|
||||||
"description": "$1 represents the cypto symbol to be purchased"
|
"description": "$1 represents the cypto symbol to be purchased"
|
||||||
@ -3762,6 +3772,9 @@
|
|||||||
"switchToThisAccount": {
|
"switchToThisAccount": {
|
||||||
"message": "Switch to this account"
|
"message": "Switch to this account"
|
||||||
},
|
},
|
||||||
|
"switchedTo": {
|
||||||
|
"message": "You have switched to"
|
||||||
|
},
|
||||||
"switchingNetworksCancelsPendingConfirmations": {
|
"switchingNetworksCancelsPendingConfirmations": {
|
||||||
"message": "Switching networks will cancel all pending confirmations"
|
"message": "Switching networks will cancel all pending confirmations"
|
||||||
},
|
},
|
||||||
@ -3828,6 +3841,9 @@
|
|||||||
"themeDescription": {
|
"themeDescription": {
|
||||||
"message": "Choose your preferred MetaMask theme."
|
"message": "Choose your preferred MetaMask theme."
|
||||||
},
|
},
|
||||||
|
"thingsToKeep": {
|
||||||
|
"message": "Things to keep in mind:"
|
||||||
|
},
|
||||||
"thisWillCreate": {
|
"thisWillCreate": {
|
||||||
"message": "This will create a new wallet and Secret Recovery Phrase"
|
"message": "This will create a new wallet and Secret Recovery Phrase"
|
||||||
},
|
},
|
||||||
@ -3890,6 +3906,9 @@
|
|||||||
"tokenScamSecurityRisk": {
|
"tokenScamSecurityRisk": {
|
||||||
"message": "token scams and security risks"
|
"message": "token scams and security risks"
|
||||||
},
|
},
|
||||||
|
"tokenShowUp": {
|
||||||
|
"message": "Your tokens may not automatically show up in your wallet."
|
||||||
|
},
|
||||||
"tokenSymbol": {
|
"tokenSymbol": {
|
||||||
"message": "Token symbol"
|
"message": "Token symbol"
|
||||||
},
|
},
|
||||||
|
@ -37,6 +37,14 @@ export default class AppStateController extends EventEmitter {
|
|||||||
...initState,
|
...initState,
|
||||||
qrHardware: {},
|
qrHardware: {},
|
||||||
collectiblesDropdownState: {},
|
collectiblesDropdownState: {},
|
||||||
|
usedNetworks: {
|
||||||
|
'0x1': true,
|
||||||
|
'0x2a': true,
|
||||||
|
'0x3': true,
|
||||||
|
'0x4': true,
|
||||||
|
'0x5': true,
|
||||||
|
'0x539': true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
this.timer = null;
|
this.timer = null;
|
||||||
|
|
||||||
@ -294,4 +302,18 @@ export default class AppStateController extends EventEmitter {
|
|||||||
collectiblesDropdownState,
|
collectiblesDropdownState,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the array of the first time used networks
|
||||||
|
*
|
||||||
|
* @param chainId
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
setFirstTimeUsedNetwork(chainId) {
|
||||||
|
const currentState = this.store.getState();
|
||||||
|
const { usedNetworks } = currentState;
|
||||||
|
usedNetworks[chainId] = true;
|
||||||
|
|
||||||
|
this.store.updateState({ usedNetworks });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1739,6 +1739,8 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
appStateController.updateCollectibleDropDownState.bind(
|
appStateController.updateCollectibleDropDownState.bind(
|
||||||
appStateController,
|
appStateController,
|
||||||
),
|
),
|
||||||
|
setFirstTimeUsedNetwork:
|
||||||
|
appStateController.setFirstTimeUsedNetwork.bind(appStateController),
|
||||||
// EnsController
|
// EnsController
|
||||||
tryReverseResolveAddress:
|
tryReverseResolveAddress:
|
||||||
ensController.reverseResolveAddress.bind(ensController),
|
ensController.reverseResolveAddress.bind(ensController),
|
||||||
|
@ -36,3 +36,6 @@ export const STATIC_MAINNET_TOKEN_LIST = Object.keys(contractMap).reduce(
|
|||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const TOKEN_API_METASWAP_CODEFI_URL =
|
||||||
|
'https://token-api.metaswap.codefi.network/tokens/';
|
||||||
|
1
ui/components/ui/new-network-info/index.js
Normal file
1
ui/components/ui/new-network-info/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './new-network-info';
|
48
ui/components/ui/new-network-info/index.scss
Normal file
48
ui/components/ui/new-network-info/index.scss
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
.new-network-info {
|
||||||
|
&__wrapper {
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.214);
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
.popover-footer {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-header {
|
||||||
|
padding-bottom: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-question-circle,
|
||||||
|
.popover-header__button {
|
||||||
|
color: var(--color-icon-default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__token-box {
|
||||||
|
align-self: center;
|
||||||
|
margin-top: 8px;
|
||||||
|
max-width: 245px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__bullet-paragraph {
|
||||||
|
border-bottom: 1px solid var(--color-border-default);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__token-show-up {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
display: initial;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__manually-add-tokens {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip--with-left-icon {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
225
ui/components/ui/new-network-info/new-network-info.js
Normal file
225
ui/components/ui/new-network-info/new-network-info.js
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
|
import Popover from '../popover';
|
||||||
|
import Button from '../button';
|
||||||
|
import Identicon from '../identicon/identicon.component';
|
||||||
|
import { NETWORK_TYPE_RPC } from '../../../../shared/constants/network';
|
||||||
|
import Box from '../box';
|
||||||
|
import {
|
||||||
|
ALIGN_ITEMS,
|
||||||
|
COLORS,
|
||||||
|
DISPLAY,
|
||||||
|
FONT_WEIGHT,
|
||||||
|
TEXT_ALIGN,
|
||||||
|
TYPOGRAPHY,
|
||||||
|
} from '../../../helpers/constants/design-system';
|
||||||
|
import Typography from '../typography';
|
||||||
|
import { TOKEN_API_METASWAP_CODEFI_URL } from '../../../../shared/constants/tokens';
|
||||||
|
import fetchWithCache from '../../../helpers/utils/fetch-with-cache';
|
||||||
|
import {
|
||||||
|
getNativeCurrencyImage,
|
||||||
|
getProvider,
|
||||||
|
getUseTokenDetection,
|
||||||
|
} from '../../../selectors';
|
||||||
|
import { IMPORT_TOKEN_ROUTE } from '../../../helpers/constants/routes';
|
||||||
|
import Chip from '../chip/chip';
|
||||||
|
import { setFirstTimeUsedNetwork } from '../../../store/actions';
|
||||||
|
|
||||||
|
const NewNetworkInfo = () => {
|
||||||
|
const t = useContext(I18nContext);
|
||||||
|
const history = useHistory();
|
||||||
|
const [tokenDetectionSupported, setTokenDetectionSupported] = useState(false);
|
||||||
|
const [showPopup, setShowPopup] = useState(true);
|
||||||
|
const autoDetectToken = useSelector(getUseTokenDetection);
|
||||||
|
const primaryTokenImage = useSelector(getNativeCurrencyImage);
|
||||||
|
const currentProvider = useSelector(getProvider);
|
||||||
|
|
||||||
|
const onCloseClick = () => {
|
||||||
|
setShowPopup(false);
|
||||||
|
setFirstTimeUsedNetwork(currentProvider.chainId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addTokenManually = () => {
|
||||||
|
history.push(IMPORT_TOKEN_ROUTE);
|
||||||
|
setShowPopup(false);
|
||||||
|
setFirstTimeUsedNetwork(currentProvider.chainId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getIsTokenDetectionSupported = async () => {
|
||||||
|
const fetchedTokenData = await fetchWithCache(
|
||||||
|
`${TOKEN_API_METASWAP_CODEFI_URL}${currentProvider.chainId}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return !fetchedTokenData.error;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkTokenDetection = async () => {
|
||||||
|
const fetchedData = await getIsTokenDetectionSupported();
|
||||||
|
|
||||||
|
setTokenDetectionSupported(fetchedData);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
checkTokenDetection();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!showPopup) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
onClose={onCloseClick}
|
||||||
|
className="new-network-info__wrapper"
|
||||||
|
footer={
|
||||||
|
<Button type="primary" onClick={onCloseClick}>
|
||||||
|
{t('recoveryPhraseReminderConfirm')}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant={TYPOGRAPHY.H4}
|
||||||
|
color={COLORS.TEXT_DEFAULT}
|
||||||
|
fontWeight={FONT_WEIGHT.BOLD}
|
||||||
|
align={TEXT_ALIGN.CENTER}
|
||||||
|
>
|
||||||
|
{t('switchedTo')}
|
||||||
|
</Typography>
|
||||||
|
<Chip
|
||||||
|
className="new-network-info__token-box"
|
||||||
|
backgroundColor={COLORS.BACKGROUND_ALTERNATIVE}
|
||||||
|
maxContent={false}
|
||||||
|
label={
|
||||||
|
currentProvider.type === NETWORK_TYPE_RPC
|
||||||
|
? currentProvider.nickname ?? t('privateNetwork')
|
||||||
|
: t(currentProvider.type)
|
||||||
|
}
|
||||||
|
labelProps={{
|
||||||
|
color: COLORS.TEXT_DEFAULT,
|
||||||
|
}}
|
||||||
|
leftIcon={
|
||||||
|
primaryTokenImage ? (
|
||||||
|
<Identicon image={primaryTokenImage} diameter={14} />
|
||||||
|
) : (
|
||||||
|
<i className="fa fa-question-circle" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Typography
|
||||||
|
variant={TYPOGRAPHY.H7}
|
||||||
|
color={COLORS.TEXT_DEFAULT}
|
||||||
|
fontWeight={FONT_WEIGHT.BOLD}
|
||||||
|
align={TEXT_ALIGN.CENTER}
|
||||||
|
margin={[8, 0, 0, 0]}
|
||||||
|
>
|
||||||
|
{t('thingsToKeep')}
|
||||||
|
</Typography>
|
||||||
|
<Box marginRight={4} marginLeft={5} marginTop={6}>
|
||||||
|
{currentProvider.ticker ? (
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
alignItems={ALIGN_ITEMS.CENTER}
|
||||||
|
marginBottom={2}
|
||||||
|
paddingBottom={2}
|
||||||
|
className="new-network-info__bullet-paragraph"
|
||||||
|
>
|
||||||
|
<Box marginRight={4} color={COLORS.TEXT_DEFAULT}>
|
||||||
|
•
|
||||||
|
</Box>
|
||||||
|
<Typography
|
||||||
|
variant={TYPOGRAPHY.H7}
|
||||||
|
color={COLORS.TEXT_DEFAULT}
|
||||||
|
boxProps={{ display: DISPLAY.INLINE_BLOCK }}
|
||||||
|
key="nativeTokenInfo"
|
||||||
|
>
|
||||||
|
{t('nativeToken', [
|
||||||
|
<Typography
|
||||||
|
variant={TYPOGRAPHY.H7}
|
||||||
|
boxProps={{ display: DISPLAY.INLINE_BLOCK }}
|
||||||
|
fontWeight={FONT_WEIGHT.BOLD}
|
||||||
|
key="ticker"
|
||||||
|
>
|
||||||
|
{currentProvider.ticker}
|
||||||
|
</Typography>,
|
||||||
|
])}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
) : null}
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
alignItems={ALIGN_ITEMS.CENTER}
|
||||||
|
marginBottom={2}
|
||||||
|
paddingBottom={2}
|
||||||
|
className={
|
||||||
|
!autoDetectToken || !tokenDetectionSupported
|
||||||
|
? 'new-network-info__bullet-paragraph'
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Box marginRight={4} color={COLORS.TEXT_DEFAULT}>
|
||||||
|
•
|
||||||
|
</Box>
|
||||||
|
<Typography
|
||||||
|
variant={TYPOGRAPHY.H7}
|
||||||
|
color={COLORS.TEXT_DEFAULT}
|
||||||
|
boxProps={{ display: DISPLAY.INLINE_BLOCK }}
|
||||||
|
className="new-network-info__bullet-paragraph__text"
|
||||||
|
>
|
||||||
|
{t('attemptSendingAssets')}{' '}
|
||||||
|
<a
|
||||||
|
href="https://metamask.zendesk.com/hc/en-us/articles/4404424659995"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant={TYPOGRAPHY.H7}
|
||||||
|
color={COLORS.INFO_DEFAULT}
|
||||||
|
boxProps={{ display: DISPLAY.INLINE_BLOCK }}
|
||||||
|
>
|
||||||
|
{t('learnMoreUpperCase')}
|
||||||
|
</Typography>
|
||||||
|
</a>
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
{!autoDetectToken || !tokenDetectionSupported ? (
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
alignItems={ALIGN_ITEMS.CENTER}
|
||||||
|
marginBottom={2}
|
||||||
|
paddingBottom={2}
|
||||||
|
>
|
||||||
|
<Box marginRight={4} color={COLORS.TEXT_DEFAULT}>
|
||||||
|
•
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Typography
|
||||||
|
variant={TYPOGRAPHY.H7}
|
||||||
|
color={COLORS.TEXT_DEFAULT}
|
||||||
|
className="new-network-info__token-show-up"
|
||||||
|
>
|
||||||
|
{t('tokenShowUp')}{' '}
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
onClick={addTokenManually}
|
||||||
|
className="new-network-info__button"
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant={TYPOGRAPHY.H7}
|
||||||
|
color={COLORS.INFO_DEFAULT}
|
||||||
|
className="new-network-info__manually-add-tokens"
|
||||||
|
>
|
||||||
|
{t('clickToManuallyAdd')}
|
||||||
|
</Typography>
|
||||||
|
</Button>
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
) : null}
|
||||||
|
</Box>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NewNetworkInfo;
|
171
ui/components/ui/new-network-info/new-network-info.test.js
Normal file
171
ui/components/ui/new-network-info/new-network-info.test.js
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
import nock from 'nock';
|
||||||
|
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||||
|
import NewNetworkInfo from './new-network-info';
|
||||||
|
|
||||||
|
const fetchWithCache =
|
||||||
|
require('../../../helpers/utils/fetch-with-cache').default;
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
metamask: {
|
||||||
|
provider: {
|
||||||
|
ticker: 'ETH',
|
||||||
|
nickname: '',
|
||||||
|
chainId: '0x1',
|
||||||
|
type: 'mainnet',
|
||||||
|
},
|
||||||
|
useTokenDetection: false,
|
||||||
|
nativeCurrency: 'ETH',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('NewNetworkInfo', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
nock.cleanAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render title', async () => {
|
||||||
|
nock('https://token-api.metaswap.codefi.network')
|
||||||
|
.get('/tokens/0x1')
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
'[{"address":"0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f","symbol":"SNX","decimals":18,"name":"Synthetix Network Token","iconUrl":"https://assets.coingecko.com/coins/images/3406/large/SNX.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","synthetix","zapper","zerion","zeroEx"],"occurrences":12},{"address":"0x1f9840a85d5af5bf1d1762f925bdaddc4201f984","symbol":"UNI","decimals":18,"name":"Uniswap","iconUrl":"https://images.prismic.io/token-price-prod/d0352dd9-5de8-4633-839d-bc3422c44d9c_UNI%404x.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","zapper","zerion","zeroEx"],"occurrences":11}]',
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateTokenDetectionSupportStatus = await fetchWithCache(
|
||||||
|
'https://token-api.metaswap.codefi.network/tokens/0x1',
|
||||||
|
);
|
||||||
|
|
||||||
|
const store = configureMockStore()(
|
||||||
|
state,
|
||||||
|
updateTokenDetectionSupportStatus,
|
||||||
|
);
|
||||||
|
const { getByText } = renderWithProvider(<NewNetworkInfo />, store);
|
||||||
|
|
||||||
|
expect(getByText('You have switched to')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render a question mark icon image', async () => {
|
||||||
|
nock('https://token-api.metaswap.codefi.network')
|
||||||
|
.get('/tokens/0x1')
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
'[{"address":"0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f","symbol":"SNX","decimals":18,"name":"Synthetix Network Token","iconUrl":"https://assets.coingecko.com/coins/images/3406/large/SNX.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","synthetix","zapper","zerion","zeroEx"],"occurrences":12},{"address":"0x1f9840a85d5af5bf1d1762f925bdaddc4201f984","symbol":"UNI","decimals":18,"name":"Uniswap","iconUrl":"https://images.prismic.io/token-price-prod/d0352dd9-5de8-4633-839d-bc3422c44d9c_UNI%404x.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","zapper","zerion","zeroEx"],"occurrences":11}]',
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateTokenDetectionSupportStatus = await fetchWithCache(
|
||||||
|
'https://token-api.metaswap.codefi.network/tokens/0x1',
|
||||||
|
);
|
||||||
|
|
||||||
|
state.metamask.nativeCurrency = '';
|
||||||
|
|
||||||
|
const store = configureMockStore()(
|
||||||
|
state,
|
||||||
|
updateTokenDetectionSupportStatus,
|
||||||
|
);
|
||||||
|
const { container } = renderWithProvider(<NewNetworkInfo />, store);
|
||||||
|
const questionMark = container.querySelector('.fa fa-question-circle');
|
||||||
|
|
||||||
|
expect(questionMark).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render Ethereum Mainnet caption', async () => {
|
||||||
|
nock('https://token-api.metaswap.codefi.network')
|
||||||
|
.get('/tokens/0x1')
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
'[{"address":"0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f","symbol":"SNX","decimals":18,"name":"Synthetix Network Token","iconUrl":"https://assets.coingecko.com/coins/images/3406/large/SNX.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","synthetix","zapper","zerion","zeroEx"],"occurrences":12},{"address":"0x1f9840a85d5af5bf1d1762f925bdaddc4201f984","symbol":"UNI","decimals":18,"name":"Uniswap","iconUrl":"https://images.prismic.io/token-price-prod/d0352dd9-5de8-4633-839d-bc3422c44d9c_UNI%404x.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","zapper","zerion","zeroEx"],"occurrences":11}]',
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateTokenDetectionSupportStatus = await fetchWithCache(
|
||||||
|
'https://token-api.metaswap.codefi.network/tokens/0x1',
|
||||||
|
);
|
||||||
|
|
||||||
|
const store = configureMockStore()(
|
||||||
|
state,
|
||||||
|
updateTokenDetectionSupportStatus,
|
||||||
|
);
|
||||||
|
const { getByText } = renderWithProvider(<NewNetworkInfo />, store);
|
||||||
|
|
||||||
|
expect(getByText('Ethereum Mainnet')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render things to keep in mind text', async () => {
|
||||||
|
nock('https://token-api.metaswap.codefi.network')
|
||||||
|
.get('/tokens/0x1')
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
'[{"address":"0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f","symbol":"SNX","decimals":18,"name":"Synthetix Network Token","iconUrl":"https://assets.coingecko.com/coins/images/3406/large/SNX.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","synthetix","zapper","zerion","zeroEx"],"occurrences":12},{"address":"0x1f9840a85d5af5bf1d1762f925bdaddc4201f984","symbol":"UNI","decimals":18,"name":"Uniswap","iconUrl":"https://images.prismic.io/token-price-prod/d0352dd9-5de8-4633-839d-bc3422c44d9c_UNI%404x.png","aggregators":["aave","bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","zapper","zerion","zeroEx"],"occurrences":11}]',
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateTokenDetectionSupportStatus = await fetchWithCache(
|
||||||
|
'https://token-api.metaswap.codefi.network/tokens/0x1',
|
||||||
|
);
|
||||||
|
|
||||||
|
const store = configureMockStore()(
|
||||||
|
state,
|
||||||
|
updateTokenDetectionSupportStatus,
|
||||||
|
);
|
||||||
|
const { getByText } = renderWithProvider(<NewNetworkInfo />, store);
|
||||||
|
|
||||||
|
expect(getByText('Things to keep in mind:')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render things to keep in mind text when token detection support is not available', async () => {
|
||||||
|
nock('https://token-api.metaswap.codefi.network')
|
||||||
|
.get('/tokens/0x3')
|
||||||
|
.reply(200, '{"error":"ChainId 0x3 is not supported"}');
|
||||||
|
|
||||||
|
const updateTokenDetectionSupportStatus = await fetchWithCache(
|
||||||
|
'https://token-api.metaswap.codefi.network/tokens/0x3',
|
||||||
|
);
|
||||||
|
|
||||||
|
const store = configureMockStore()(
|
||||||
|
state,
|
||||||
|
updateTokenDetectionSupportStatus,
|
||||||
|
);
|
||||||
|
const { getByText } = renderWithProvider(<NewNetworkInfo />, store);
|
||||||
|
|
||||||
|
expect(getByText('Things to keep in mind:')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render first bullet when provider ticker is null', async () => {
|
||||||
|
nock('https://token-api.metaswap.codefi.network')
|
||||||
|
.get('/tokens/0x3')
|
||||||
|
.reply(200, '{"error":"ChainId 0x3 is not supported"}');
|
||||||
|
|
||||||
|
const updateTokenDetectionSupportStatus = await fetchWithCache(
|
||||||
|
'https://token-api.metaswap.codefi.network/tokens/0x3',
|
||||||
|
);
|
||||||
|
|
||||||
|
state.metamask.provider.ticker = null;
|
||||||
|
|
||||||
|
const store = configureMockStore()(
|
||||||
|
state,
|
||||||
|
updateTokenDetectionSupportStatus,
|
||||||
|
);
|
||||||
|
const { container } = renderWithProvider(<NewNetworkInfo />, store);
|
||||||
|
const firstBox = container.querySelector('new-network-info__content-box-1');
|
||||||
|
|
||||||
|
expect(firstBox).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render click to manually add link', async () => {
|
||||||
|
nock('https://token-api.metaswap.codefi.network')
|
||||||
|
.get('/tokens/0x3')
|
||||||
|
.reply(200, '{"error":"ChainId 0x3 is not supported"}');
|
||||||
|
|
||||||
|
const updateTokenDetectionSupportStatus = await fetchWithCache(
|
||||||
|
'https://token-api.metaswap.codefi.network/tokens/0x3',
|
||||||
|
);
|
||||||
|
|
||||||
|
const store = configureMockStore()(
|
||||||
|
state,
|
||||||
|
updateTokenDetectionSupportStatus,
|
||||||
|
);
|
||||||
|
const { getByText } = renderWithProvider(<NewNetworkInfo />, store);
|
||||||
|
|
||||||
|
expect(getByText('Click here to manually add the tokens.')).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
@ -33,6 +33,7 @@
|
|||||||
@import 'logo/logo-coinbasepay.scss';
|
@import 'logo/logo-coinbasepay.scss';
|
||||||
@import 'loading-screen/index';
|
@import 'loading-screen/index';
|
||||||
@import 'menu/menu';
|
@import 'menu/menu';
|
||||||
|
@import 'new-network-info/index';
|
||||||
@import 'numeric-input/numeric-input';
|
@import 'numeric-input/numeric-input';
|
||||||
@import 'nickname-popover/index';
|
@import 'nickname-popover/index';
|
||||||
@import 'form-field/index';
|
@import 'form-field/index';
|
||||||
|
@ -77,6 +77,7 @@ import OnboardingFlow from '../onboarding-flow/onboarding-flow';
|
|||||||
import QRHardwarePopover from '../../components/app/qr-hardware-popover';
|
import QRHardwarePopover from '../../components/app/qr-hardware-popover';
|
||||||
import { SEND_STAGES } from '../../ducks/send';
|
import { SEND_STAGES } from '../../ducks/send';
|
||||||
import { THEME_TYPE } from '../settings/experimental-tab/experimental-tab.constant';
|
import { THEME_TYPE } from '../settings/experimental-tab/experimental-tab.constant';
|
||||||
|
import NewNetworkInfo from '../../components/ui/new-network-info/new-network-info';
|
||||||
|
|
||||||
export default class Routes extends Component {
|
export default class Routes extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -104,6 +105,8 @@ export default class Routes extends Component {
|
|||||||
browserEnvironmentBrowser: PropTypes.string,
|
browserEnvironmentBrowser: PropTypes.string,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
sendStage: PropTypes.string,
|
sendStage: PropTypes.string,
|
||||||
|
isNetworkUsed: PropTypes.bool,
|
||||||
|
hasAnAccountWithNoFundsOnNetwork: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
@ -358,11 +361,17 @@ export default class Routes extends Component {
|
|||||||
isMouseUser,
|
isMouseUser,
|
||||||
browserEnvironmentOs: os,
|
browserEnvironmentOs: os,
|
||||||
browserEnvironmentBrowser: browser,
|
browserEnvironmentBrowser: browser,
|
||||||
|
isNetworkUsed,
|
||||||
|
hasAnAccountWithNoFundsOnNetwork,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const loadMessage =
|
const loadMessage =
|
||||||
loadingMessage || isNetworkLoading
|
loadingMessage || isNetworkLoading
|
||||||
? this.getConnectingLabel(loadingMessage)
|
? this.getConnectingLabel(loadingMessage)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
const shouldShowNetworkInfo =
|
||||||
|
isUnlocked && !isNetworkUsed && hasAnAccountWithNoFundsOnNetwork;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames('app', {
|
className={classnames('app', {
|
||||||
@ -378,6 +387,7 @@ export default class Routes extends Component {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{shouldShowNetworkInfo && <NewNetworkInfo />}
|
||||||
<QRHardwarePopover />
|
<QRHardwarePopover />
|
||||||
<Modal />
|
<Modal />
|
||||||
<Alert visible={this.props.alertOpen} msg={alertMessage} />
|
<Alert visible={this.props.alertOpen} msg={alertMessage} />
|
||||||
|
@ -2,6 +2,8 @@ import { connect } from 'react-redux';
|
|||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import { compose } from 'redux';
|
import { compose } from 'redux';
|
||||||
import {
|
import {
|
||||||
|
getHasAnyAccountWithNoFundsOnNetwork,
|
||||||
|
getIsNetworkUsed,
|
||||||
getNetworkIdentifier,
|
getNetworkIdentifier,
|
||||||
getPreferences,
|
getPreferences,
|
||||||
isNetworkLoading,
|
isNetworkLoading,
|
||||||
@ -40,6 +42,9 @@ function mapStateToProps(state) {
|
|||||||
providerType: state.metamask.provider?.type,
|
providerType: state.metamask.provider?.type,
|
||||||
theme: getTheme(state),
|
theme: getTheme(state),
|
||||||
sendStage: getSendStage(state),
|
sendStage: getSendStage(state),
|
||||||
|
isNetworkUsed: getIsNetworkUsed(state),
|
||||||
|
hasAnAccountWithNoFundsOnNetwork:
|
||||||
|
getHasAnyAccountWithNoFundsOnNetwork(state),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1170,3 +1170,18 @@ export function getBlockExplorerLinkText(
|
|||||||
|
|
||||||
return blockExplorerLinkText;
|
return blockExplorerLinkText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getIsNetworkUsed(state) {
|
||||||
|
const chainId = getCurrentChainId(state);
|
||||||
|
const { usedNetworks } = state.metamask;
|
||||||
|
|
||||||
|
return Boolean(usedNetworks[chainId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getHasAnyAccountWithNoFundsOnNetwork(state) {
|
||||||
|
const balances = getMetaMaskCachedBalances(state) ?? {};
|
||||||
|
const hasAnAccountWithNoFundsOnNetwork =
|
||||||
|
Object.values(balances).indexOf('0x0');
|
||||||
|
|
||||||
|
return hasAnAccountWithNoFundsOnNetwork !== -1;
|
||||||
|
}
|
||||||
|
@ -3785,6 +3785,10 @@ export function setCustomNetworkListEnabled(customNetworkListEnabled) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setFirstTimeUsedNetwork(chainId) {
|
||||||
|
return promisifiedBackground.setFirstTimeUsedNetwork(chainId);
|
||||||
|
}
|
||||||
|
|
||||||
// QR Hardware Wallets
|
// QR Hardware Wallets
|
||||||
export async function submitQRHardwareCryptoHDKey(cbor) {
|
export async function submitQRHardwareCryptoHDKey(cbor) {
|
||||||
await promisifiedBackground.submitQRHardwareCryptoHDKey(cbor);
|
await promisifiedBackground.submitQRHardwareCryptoHDKey(cbor);
|
||||||
|
Loading…
Reference in New Issue
Block a user