import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; import { compose } from 'redux'; import * as actions from '../../../store/actions'; import { openAlert as displayInvalidCustomNetworkAlert } from '../../../ducks/alerts/invalid-custom-network'; import { ADD_NETWORK_ROUTE } from '../../../helpers/constants/routes'; import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../shared/constants/app'; import { NETWORK_TYPE_RPC } from '../../../../shared/constants/network'; import { isPrefixedFormattedHexString } from '../../../../shared/modules/network.utils'; import { getEnvironmentType } from '../../../../app/scripts/lib/util'; import ColorIndicator from '../../ui/color-indicator'; import { COLORS, SIZES } from '../../../helpers/constants/design-system'; import { Dropdown, DropdownMenuItem } from './dropdown'; // classes from nodes of the toggle element. const notToggleElementClassnames = [ 'menu-icon', 'network-name', 'network-indicator', 'network-caret', 'network-component', 'modal-container__footer-button', ]; const DROP_DOWN_MENU_ITEM_STYLE = { fontSize: '16px', lineHeight: '20px', padding: '12px 0', }; function mapStateToProps(state) { return { provider: state.metamask.provider, frequentRpcListDetail: state.metamask.frequentRpcListDetail || [], networkDropdownOpen: state.appState.networkDropdownOpen, }; } function mapDispatchToProps(dispatch) { return { setProviderType: (type) => { dispatch(actions.setProviderType(type)); }, setRpcTarget: (target, chainId, ticker, nickname) => { dispatch(actions.setRpcTarget(target, chainId, ticker, nickname)); }, hideNetworkDropdown: () => dispatch(actions.hideNetworkDropdown()), setSelectedSettingsRpcUrl: (url) => { dispatch(actions.setSelectedSettingsRpcUrl(url)); }, displayInvalidCustomNetworkAlert: (networkName) => { dispatch(displayInvalidCustomNetworkAlert(networkName)); }, showConfirmDeleteNetworkModal: ({ target, onConfirm }) => { return dispatch( actions.showModal({ name: 'CONFIRM_DELETE_NETWORK', target, onConfirm, }), ); }, }; } class NetworkDropdown extends Component { static contextTypes = { t: PropTypes.func, metricsEvent: PropTypes.func, }; static propTypes = { provider: PropTypes.shape({ nickname: PropTypes.string, rpcUrl: PropTypes.string, type: PropTypes.string, ticker: PropTypes.string, }).isRequired, setProviderType: PropTypes.func.isRequired, setRpcTarget: PropTypes.func.isRequired, hideNetworkDropdown: PropTypes.func.isRequired, setSelectedSettingsRpcUrl: PropTypes.func.isRequired, frequentRpcListDetail: PropTypes.array.isRequired, networkDropdownOpen: PropTypes.bool.isRequired, history: PropTypes.object.isRequired, displayInvalidCustomNetworkAlert: PropTypes.func.isRequired, showConfirmDeleteNetworkModal: PropTypes.func.isRequired, }; handleClick(newProviderType) { const { provider: { type: providerType }, setProviderType, } = this.props; const { metricsEvent } = this.context; metricsEvent({ eventOpts: { category: 'Navigation', action: 'Home', name: 'Switched Networks', }, customVariables: { fromNetwork: providerType, toNetwork: newProviderType, }, }); setProviderType(newProviderType); } renderCustomRpcList(rpcListDetail, provider) { const reversedRpcListDetail = rpcListDetail.slice().reverse(); return reversedRpcListDetail.map((entry) => { const { rpcUrl, chainId, ticker = 'ETH', nickname = '' } = entry; const isCurrentRpcTarget = provider.type === NETWORK_TYPE_RPC && rpcUrl === provider.rpcUrl; return ( this.props.hideNetworkDropdown()} onClick={() => { if (isPrefixedFormattedHexString(chainId)) { this.props.setRpcTarget(rpcUrl, chainId, ticker, nickname); } else { this.props.displayInvalidCustomNetworkAlert(nickname || rpcUrl); } }} style={{ fontSize: '16px', lineHeight: '20px', padding: '12px 0', }} > {isCurrentRpcTarget ? ( ) : (
)} {nickname || rpcUrl} {isCurrentRpcTarget ? null : ( { e.stopPropagation(); this.props.showConfirmDeleteNetworkModal({ target: rpcUrl, onConfirm: () => undefined, }); }} /> )}
); }); } getNetworkName() { const { provider } = this.props; const providerName = provider.type; let name; if (providerName === 'mainnet') { name = this.context.t('mainnet'); } else if (providerName === 'ropsten') { name = this.context.t('ropsten'); } else if (providerName === 'kovan') { name = this.context.t('kovan'); } else if (providerName === 'rinkeby') { name = this.context.t('rinkeby'); } else if (providerName === 'goerli') { name = this.context.t('goerli'); } else { name = provider.nickname || this.context.t('unknownNetwork'); } return name; } renderNetworkEntry(network) { const { provider: { type: providerType }, } = this.props; return ( this.handleClick(network)} style={DROP_DOWN_MENU_ITEM_STYLE} > {providerType === network ? ( ) : (
)} {this.context.t(network)}
); } render() { const { provider: { rpcUrl: activeNetwork }, setSelectedSettingsRpcUrl, history, } = this.props; const rpcListDetail = this.props.frequentRpcListDetail; const isOpen = this.props.networkDropdownOpen; return ( { const { classList } = event.target; const isInClassList = (className) => classList.contains(className); const notToggleElementIndex = notToggleElementClassnames.findIndex( isInClassList, ); if (notToggleElementIndex === -1) { event.stopPropagation(); this.props.hideNetworkDropdown(); } }} containerClassName="network-droppo" zIndex={55} style={{ position: 'absolute', top: '58px', width: '309px', zIndex: '55px', }} innerStyle={{ padding: '18px 8px', }} >
{this.context.t('networks')}
{this.context.t('defaultNetwork')}
{this.renderNetworkEntry('mainnet')} {this.renderNetworkEntry('ropsten')} {this.renderNetworkEntry('kovan')} {this.renderNetworkEntry('rinkeby')} {this.renderNetworkEntry('goerli')} {this.renderCustomRpcList(rpcListDetail, this.props.provider)} this.props.hideNetworkDropdown()} onClick={() => { if (getEnvironmentType() === ENVIRONMENT_TYPE_FULLSCREEN) { history.push(ADD_NETWORK_ROUTE); } else { global.platform.openExtensionInBrowser(ADD_NETWORK_ROUTE); } setSelectedSettingsRpcUrl(''); }} style={DROP_DOWN_MENU_ITEM_STYLE} > {activeNetwork === 'custom' ? ( ) : (
)} {this.context.t('customRPC')}
); } } export default compose( withRouter, connect(mapStateToProps, mapDispatchToProps), )(NetworkDropdown);