New settings custom rpc form (#6490)
* Add networks tab to settings, with header. * Adds network list to settings network tab. * Adds form to settings networks tab and connects it to network list. * Network tab: form adding and editing working * Settings network form properly handles input errors * Add translations for settings network form * Clean up styles of settings network tab. * Add popup-view styles and behaviour to settings network tab. * Fix save button on settings network form * Adds 'Add Network' button and addMode to settings networks tab * Lint fix for settings networks tab addition * Fix navigation in settings networks tab. * Editing an rpcurl in networks tab does not create new network, just changes rpc of old * Fix layout of settings tabs other than network * Networks dropdown 'Custom Rpc' item links to networks tab in settings. * Update settings sidebar networks subheader. * Make networks tab buttons width consistent with input widths in extension view. * Fix settings screen subheader height in popup view * Fix height of add networks button in popup view * Add optional label to chainId and symbol form labels in networks setting tab * Style fixes for networks tab headers * Add ability to customize block explorer used by custom rpc * Stylistic improvements+fixes to custom rpc form. * Hide cancel button. * Highlight and show network form of provider by default. * Standardize network subheader name to 'Networks' * Update e2e tests for new settings network form * Update unit tests for new rpcPrefs prop * Extract blockexplorer url construction into method. * Fix broken styles on non-network tabs in popup mode * Fix block explorer url links for cases when provider in state has not been updated. * Fix vertical spacing of network form * Don't allow click of save button on network form if nothing has changed * Ensure add network button is shown in popup view * Lint fix for networks tab * Fix block explorer url preference setting. * Fix e2e tests for custom blockexplorer in account details modal changes. * Update integration test states to include frequentRpcList property * Fix some capitalizations in en/messages.json * Remove some console.logs added during custom rpc form work * Fix external account link text and url for modal and dropdown. * Documentation, url validation, proptype required additions and lint fixes on network tab and form.feature/default_network_editable
parent
094e4cf555
commit
13be683701
@ -0,0 +1 @@ |
||||
export { default } from './networks-tab.container' |
@ -0,0 +1,200 @@ |
||||
.networks-tab { |
||||
&__content { |
||||
margin-top: 24px; |
||||
display: flex; |
||||
height: 100%; |
||||
max-width: 739px; |
||||
justify-content: space-between; |
||||
|
||||
@media screen and (max-width: 575px) { |
||||
margin-top: 0px; |
||||
} |
||||
} |
||||
|
||||
&__body { |
||||
padding: 12px 24px; |
||||
height: 100%; |
||||
display: flex; |
||||
flex-direction: column; |
||||
|
||||
@media screen and (max-width: 575px) { |
||||
padding: 0; |
||||
} |
||||
} |
||||
|
||||
&__back-button { |
||||
display: none; |
||||
|
||||
@media screen and (max-width: 575px) { |
||||
display: block; |
||||
background-image: url('/images/caret-left-black.svg'); |
||||
width: 18px; |
||||
height: 18px; |
||||
opacity: .5; |
||||
background-size: contain; |
||||
background-repeat: no-repeat; |
||||
background-position: center; |
||||
margin-right: 16px; |
||||
cursor: pointer; |
||||
position: absolute; |
||||
margin-left: 10px; |
||||
} |
||||
} |
||||
|
||||
&__network-form { |
||||
flex: 0.5 0 auto; |
||||
max-width: 343px; |
||||
max-height: 465px; |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: space-between; |
||||
|
||||
.page-container__footer { |
||||
border-top: none; |
||||
|
||||
@media screen and (max-width: 575px) { |
||||
width: 93%; |
||||
} |
||||
|
||||
header { |
||||
padding: 10px 0px; |
||||
} |
||||
} |
||||
|
||||
@media screen and (max-width: 575px) { |
||||
display: flex; |
||||
flex: auto; |
||||
max-width: 100%; |
||||
max-height: 100%; |
||||
align-items: center; |
||||
width: 100%; |
||||
margin-top: 10px; |
||||
} |
||||
} |
||||
|
||||
&__network-form-row { |
||||
@media screen and (max-width: 575px) { |
||||
display: flex; |
||||
flex-direction: column; |
||||
width: 93%; |
||||
} |
||||
} |
||||
|
||||
&__network-form-label { |
||||
font-family: Roboto; |
||||
font-style: normal; |
||||
font-weight: normal; |
||||
font-size: 14px; |
||||
line-height: 20px; |
||||
color: #000000; |
||||
} |
||||
|
||||
&__networks-list { |
||||
flex: 0.5 0 auto; |
||||
max-width: 343px; |
||||
|
||||
@media screen and (max-width: 575px) { |
||||
max-width: 100vw; |
||||
width: 100vw; |
||||
overflow-y: scroll; |
||||
} |
||||
} |
||||
|
||||
&__add-network-button-wrapper { |
||||
display: none; |
||||
|
||||
@media screen and (max-width: 575px) { |
||||
display: flex; |
||||
padding-top: 19px; |
||||
padding-bottom: 23px; |
||||
justify-content: center; |
||||
align-items: center; |
||||
border-top: 1px solid #D8D8D8; |
||||
|
||||
.button { |
||||
width: 178px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
&__add-network-header-button-wrapper { |
||||
padding-top: 15px; |
||||
padding-bottom: 21px; |
||||
justify-content: center; |
||||
|
||||
.button { |
||||
width: 178px; |
||||
} |
||||
|
||||
@media screen and (max-width: 575px) { |
||||
display: none; |
||||
} |
||||
} |
||||
|
||||
&__networks-list--selection { |
||||
@media screen and (max-width: 575px) { |
||||
display: none; |
||||
} |
||||
} |
||||
|
||||
&__networks-list-item { |
||||
display: flex; |
||||
padding: 13px 0px 13px 17px; |
||||
position: relative; |
||||
|
||||
.menu-icon-circle { |
||||
&:hover { |
||||
cursor: pointer; |
||||
} |
||||
} |
||||
|
||||
@media screen and (max-width: 575px) { |
||||
padding: 20px 23px 21px 17px; |
||||
border-bottom: 1px solid #D8D8D8; |
||||
} |
||||
} |
||||
|
||||
&__networks-list-item:last-of-type { |
||||
@media screen and (max-width: 575px) { |
||||
border-bottom: none; |
||||
} |
||||
} |
||||
|
||||
&__networks-list-name { |
||||
margin-left: 11px; |
||||
font-family: Roboto; |
||||
font-style: normal; |
||||
font-weight: normal; |
||||
font-size: 16px; |
||||
line-height: 23px; |
||||
color: #6A737D; |
||||
|
||||
&:hover { |
||||
cursor: pointer; |
||||
} |
||||
} |
||||
|
||||
&__networks-list-arrow { |
||||
display: none; |
||||
|
||||
@media screen and (max-width: 575px) { |
||||
display: block; |
||||
background-image: url('/images/caret-right.svg'); |
||||
width: 24px; |
||||
height: 24px; |
||||
background-size: contain; |
||||
background-repeat: no-repeat; |
||||
background-position: center; |
||||
right: 10px; |
||||
cursor: pointer; |
||||
position: absolute; |
||||
width: 24px; |
||||
height: 24px; |
||||
} |
||||
} |
||||
|
||||
&__networks-list-name--selected { |
||||
font-weight: bold; |
||||
color: #000000; |
||||
} |
||||
} |
@ -0,0 +1 @@ |
||||
export { default } from './network-form.component' |
@ -0,0 +1,225 @@ |
||||
import React, { PureComponent } from 'react' |
||||
import PropTypes from 'prop-types' |
||||
import validUrl from 'valid-url' |
||||
import PageContainerFooter from '../../../../components/ui/page-container/page-container-footer' |
||||
import TextField from '../../../../components/ui/text-field' |
||||
|
||||
export default class NetworksTab extends PureComponent { |
||||
static contextTypes = { |
||||
t: PropTypes.func.isRequired, |
||||
metricsEvent: PropTypes.func.isRequired, |
||||
} |
||||
|
||||
static propTypes = { |
||||
editRpc: PropTypes.func.isRequired, |
||||
rpcUrl: PropTypes.string, |
||||
chainId: PropTypes.string, |
||||
ticker: PropTypes.string, |
||||
viewOnly: PropTypes.bool, |
||||
networkName: PropTypes.string, |
||||
onClear: PropTypes.func.isRequired, |
||||
setRpcTarget: PropTypes.func.isRequired, |
||||
networksTabIsInAddMode: PropTypes.bool, |
||||
blockExplorerUrl: PropTypes.string, |
||||
rpcPrefs: PropTypes.object, |
||||
} |
||||
|
||||
state = { |
||||
rpcUrl: this.props.rpcUrl, |
||||
chainId: this.props.chainId, |
||||
ticker: this.props.ticker, |
||||
networkName: this.props.networkName, |
||||
blockExplorerUrl: this.props.blockExplorerUrl, |
||||
errors: {}, |
||||
} |
||||
|
||||
componentDidUpdate (prevProps) { |
||||
const { rpcUrl: prevRpcUrl, networksTabIsInAddMode: prevAddMode } = prevProps |
||||
const { |
||||
rpcUrl, |
||||
chainId, |
||||
ticker, |
||||
networkName, |
||||
networksTabIsInAddMode, |
||||
blockExplorerUrl, |
||||
} = this.props |
||||
|
||||
if (!prevAddMode && networksTabIsInAddMode) { |
||||
this.setState({ |
||||
rpcUrl: '', |
||||
chainId: '', |
||||
ticker: '', |
||||
networkName: '', |
||||
blockExplorerUrl: '', |
||||
errors: {}, |
||||
}) |
||||
} else if (prevRpcUrl !== rpcUrl) { |
||||
this.setState({ rpcUrl, chainId, ticker, networkName, blockExplorerUrl, errors: {} }) |
||||
} |
||||
} |
||||
|
||||
componentWillUnmount () { |
||||
this.props.onClear() |
||||
this.setState({ |
||||
rpcUrl: '', |
||||
chainId: '', |
||||
ticker: '', |
||||
networkName: '', |
||||
blockExplorerUrl: '', |
||||
errors: {}, |
||||
}) |
||||
} |
||||
|
||||
stateIsUnchanged () { |
||||
const { |
||||
rpcUrl, |
||||
chainId, |
||||
ticker, |
||||
networkName, |
||||
blockExplorerUrl, |
||||
} = this.props |
||||
|
||||
const { |
||||
rpcUrl: stateRpcUrl, |
||||
chainId: stateChainId, |
||||
ticker: stateTicker, |
||||
networkName: stateNetworkName, |
||||
blockExplorerUrl: stateBlockExplorerUrl, |
||||
} = this.state |
||||
|
||||
return ( |
||||
stateRpcUrl === rpcUrl && |
||||
stateChainId === chainId && |
||||
stateTicker === ticker && |
||||
stateNetworkName === networkName && |
||||
stateBlockExplorerUrl === blockExplorerUrl |
||||
) |
||||
} |
||||
|
||||
renderFormTextField (fieldKey, textFieldId, onChange, value, optionalTextFieldKey) { |
||||
const { errors } = this.state |
||||
const { viewOnly } = this.props |
||||
|
||||
return ( |
||||
<div className="networks-tab__network-form-row"> |
||||
<div className="networks-tab__network-form-label">{this.context.t(optionalTextFieldKey || fieldKey)}</div> |
||||
<TextField |
||||
type="text" |
||||
id={textFieldId} |
||||
onChange={onChange} |
||||
fullWidth |
||||
margin="dense" |
||||
value={value} |
||||
disabled={viewOnly} |
||||
error={errors[fieldKey]} |
||||
/> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
setStateWithValue = (stateKey, validator) => { |
||||
return (e) => { |
||||
validator && validator(e.target.value, stateKey) |
||||
this.setState({ [stateKey]: e.target.value }) |
||||
} |
||||
} |
||||
|
||||
setErrorTo = (errorKey, errorVal) => { |
||||
this.setState({ |
||||
errors: { |
||||
...this.state.errors, |
||||
[errorKey]: errorVal, |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
validateChainId = (chainId) => { |
||||
this.setErrorTo('chainId', !!chainId && Number.isNaN(parseInt(chainId)) |
||||
? `${this.context.t('invalidInput')} chainId` |
||||
: '' |
||||
) |
||||
} |
||||
|
||||
validateUrl = (url, stateKey) => { |
||||
if (validUrl.isWebUri(url)) { |
||||
this.setErrorTo(stateKey, '') |
||||
} else { |
||||
const appendedRpc = `http://${url}` |
||||
const validWhenAppended = validUrl.isWebUri(appendedRpc) && !url.match(/^https?:\/\/$/) |
||||
|
||||
this.setErrorTo(stateKey, this.context.t(validWhenAppended ? 'uriErrorMsg' : 'invalidRPC')) |
||||
} |
||||
} |
||||
|
||||
render () { |
||||
const { setRpcTarget, viewOnly, rpcUrl: propsRpcUrl, editRpc, rpcPrefs = {} } = this.props |
||||
const { |
||||
networkName, |
||||
rpcUrl, |
||||
chainId, |
||||
ticker, |
||||
blockExplorerUrl, |
||||
errors, |
||||
} = this.state |
||||
|
||||
|
||||
return ( |
||||
<div className="networks-tab__network-form"> |
||||
{this.renderFormTextField( |
||||
'networkName', |
||||
'network-name', |
||||
this.setStateWithValue('networkName'), |
||||
networkName, |
||||
)} |
||||
{this.renderFormTextField( |
||||
'rpcUrl', |
||||
'rpc-url', |
||||
this.setStateWithValue('rpcUrl', this.validateUrl), |
||||
rpcUrl, |
||||
)} |
||||
{this.renderFormTextField( |
||||
'chainId', |
||||
'chainId', |
||||
this.setStateWithValue('chainId', this.validateChainId), |
||||
chainId, |
||||
'optionalChainId', |
||||
)} |
||||
{this.renderFormTextField( |
||||
'symbol', |
||||
'network-ticker', |
||||
this.setStateWithValue('ticker'), |
||||
ticker, |
||||
'optionalSymbol', |
||||
)} |
||||
{this.renderFormTextField( |
||||
'blockExplorerUrl', |
||||
'block-explorer-url', |
||||
this.setStateWithValue('blockExplorerUrl', this.validateUrl), |
||||
blockExplorerUrl, |
||||
'optionalBlockExplorerUrl', |
||||
)} |
||||
<PageContainerFooter |
||||
cancelText={this.context.t('cancel')} |
||||
hideCancel={true} |
||||
onSubmit={() => { |
||||
if (propsRpcUrl && rpcUrl !== propsRpcUrl) { |
||||
editRpc(propsRpcUrl, rpcUrl, chainId, ticker, networkName, { |
||||
blockExplorerUrl: blockExplorerUrl || rpcPrefs.blockExplorerUrl, |
||||
...rpcPrefs, |
||||
}) |
||||
} else { |
||||
setRpcTarget(rpcUrl, chainId, ticker, networkName, { |
||||
blockExplorerUrl: blockExplorerUrl || rpcPrefs.blockExplorerUrl, |
||||
...rpcPrefs, |
||||
}) |
||||
} |
||||
}} |
||||
submitText={this.context.t('save')} |
||||
submitButtonType={'confirm'} |
||||
disabled={viewOnly || this.stateIsUnchanged() || Object.values(errors).some(x => x) || !rpcUrl} |
||||
/> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
} |
@ -0,0 +1,214 @@ |
||||
import React, { PureComponent } from 'react' |
||||
import PropTypes from 'prop-types' |
||||
import { SETTINGS_ROUTE } from '../../../helpers/constants/routes' |
||||
import { ENVIRONMENT_TYPE_POPUP } from '../../../../../app/scripts/lib/enums' |
||||
import { getEnvironmentType } from '../../../../../app/scripts/lib/util' |
||||
import classnames from 'classnames' |
||||
import Button from '../../../components/ui/button' |
||||
import NetworkForm from './network-form' |
||||
import NetworkDropdownIcon from '../../../components/app/dropdowns/components/network-dropdown-icon' |
||||
|
||||
export default class NetworksTab extends PureComponent { |
||||
static contextTypes = { |
||||
t: PropTypes.func.isRequired, |
||||
metricsEvent: PropTypes.func.isRequired, |
||||
} |
||||
|
||||
static propTypes = { |
||||
editRpc: PropTypes.func.isRequired, |
||||
history: PropTypes.object.isRequired, |
||||
location: PropTypes.object.isRequired, |
||||
networkIsSelected: PropTypes.bool, |
||||
networksTabIsInAddMode: PropTypes.bool, |
||||
networksToRender: PropTypes.array.isRequired, |
||||
selectedNetwork: PropTypes.object, |
||||
setNetworksTabAddMode: PropTypes.func.isRequired, |
||||
setRpcTarget: PropTypes.func.isRequired, |
||||
setSelectedSettingsRpcUrl: PropTypes.func.isRequired, |
||||
providerUrl: PropTypes.string, |
||||
providerType: PropTypes.string, |
||||
networkDefaultedToProvider: PropTypes.bool, |
||||
} |
||||
|
||||
componentWillMount () { |
||||
this.props.setSelectedSettingsRpcUrl(null) |
||||
} |
||||
|
||||
isCurrentPath (pathname) { |
||||
return this.props.location.pathname === pathname |
||||
} |
||||
|
||||
renderSubHeader () { |
||||
const { |
||||
networkIsSelected, |
||||
setSelectedSettingsRpcUrl, |
||||
setNetworksTabAddMode, |
||||
networksTabIsInAddMode, |
||||
networkDefaultedToProvider, |
||||
} = this.props |
||||
|
||||
return ( |
||||
<div className="settings-page__sub-header"> |
||||
<div |
||||
className="networks-tab__back-button" |
||||
onClick={(networkIsSelected && !networkDefaultedToProvider) || networksTabIsInAddMode |
||||
? () => { |
||||
setNetworksTabAddMode(false) |
||||
setSelectedSettingsRpcUrl(null) |
||||
} |
||||
: () => this.props.history.push(SETTINGS_ROUTE) |
||||
} |
||||
/> |
||||
<span className="settings-page__sub-header-text">{ this.context.t('networks') }</span> |
||||
<div className="networks-tab__add-network-header-button-wrapper"> |
||||
<Button |
||||
type="primary" |
||||
onClick={event => { |
||||
event.preventDefault() |
||||
setSelectedSettingsRpcUrl(null) |
||||
setNetworksTabAddMode(true) |
||||
}} |
||||
> |
||||
{ this.context.t('addNetwork') } |
||||
</Button> |
||||
</div> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
renderNetworkListItem (network, selectRpcUrl) { |
||||
const { |
||||
setSelectedSettingsRpcUrl, |
||||
setNetworksTabAddMode, |
||||
networkIsSelected, |
||||
providerUrl, |
||||
providerType, |
||||
networksTabIsInAddMode, |
||||
} = this.props |
||||
const { |
||||
border, |
||||
iconColor, |
||||
label, |
||||
labelKey, |
||||
rpcUrl, |
||||
providerType: currentProviderType, |
||||
} = network |
||||
|
||||
const listItemNetworkIsSelected = selectRpcUrl && selectRpcUrl === rpcUrl |
||||
const listItemUrlIsProviderUrl = rpcUrl === providerUrl |
||||
const listItemTypeIsProviderNonRpcType = providerType !== 'rpc' && currentProviderType === providerType |
||||
const listItemNetworkIsCurrentProvider = !networkIsSelected && !networksTabIsInAddMode && (listItemUrlIsProviderUrl || listItemTypeIsProviderNonRpcType) |
||||
const displayNetworkListItemAsSelected = listItemNetworkIsSelected || listItemNetworkIsCurrentProvider |
||||
|
||||
return ( |
||||
<div |
||||
key={'settings-network-list-item:' + rpcUrl} |
||||
className="networks-tab__networks-list-item" |
||||
onClick={ () => { |
||||
setNetworksTabAddMode(false) |
||||
setSelectedSettingsRpcUrl(rpcUrl) |
||||
}} |
||||
> |
||||
<NetworkDropdownIcon |
||||
backgroundColor={iconColor || 'white'} |
||||
innerBorder={border} |
||||
/> |
||||
<div className={ classnames('networks-tab__networks-list-name', { |
||||
'networks-tab__networks-list-name--selected': displayNetworkListItemAsSelected, |
||||
}) }> |
||||
{ label || this.context.t(labelKey) } |
||||
</div> |
||||
<div className="networks-tab__networks-list-arrow" /> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
renderNetworksList () { |
||||
const { networksToRender, selectedNetwork, networkIsSelected, networksTabIsInAddMode, networkDefaultedToProvider } = this.props |
||||
|
||||
return ( |
||||
<div className={classnames('networks-tab__networks-list', { |
||||
'networks-tab__networks-list--selection': (networkIsSelected && !networkDefaultedToProvider) || networksTabIsInAddMode, |
||||
})}> |
||||
{ networksToRender.map(network => this.renderNetworkListItem(network, selectedNetwork.rpcUrl)) } |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
renderNetworksTabContent () { |
||||
const { |
||||
setRpcTarget, |
||||
setSelectedSettingsRpcUrl, |
||||
setNetworksTabAddMode, |
||||
selectedNetwork: { |
||||
labelKey, |
||||
label, |
||||
rpcUrl, |
||||
chainId, |
||||
ticker, |
||||
viewOnly, |
||||
rpcPrefs, |
||||
blockExplorerUrl, |
||||
}, |
||||
networksTabIsInAddMode, |
||||
editRpc, |
||||
networkDefaultedToProvider, |
||||
} = this.props |
||||
const envIsPopup = getEnvironmentType() === ENVIRONMENT_TYPE_POPUP |
||||
|
||||
return ( |
||||
<div className="networks-tab__content"> |
||||
{this.renderNetworksList()} |
||||
{networksTabIsInAddMode || !envIsPopup || (envIsPopup && !networkDefaultedToProvider) |
||||
? <NetworkForm |
||||
setRpcTarget={setRpcTarget} |
||||
editRpc={editRpc} |
||||
networkName={label || labelKey && this.context.t(labelKey) || ''} |
||||
rpcUrl={rpcUrl} |
||||
chainId={chainId} |
||||
ticker={ticker} |
||||
onClear={() => { |
||||
setNetworksTabAddMode(false) |
||||
setSelectedSettingsRpcUrl(null) |
||||
}} |
||||
viewOnly={viewOnly} |
||||
networksTabIsInAddMode={networksTabIsInAddMode} |
||||
rpcPrefs={rpcPrefs} |
||||
blockExplorerUrl={blockExplorerUrl} |
||||
/> |
||||
: null |
||||
} |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
renderContent () { |
||||
const { setNetworksTabAddMode, setSelectedSettingsRpcUrl, networkIsSelected, networksTabIsInAddMode } = this.props |
||||
|
||||
return ( |
||||
<div className="networks-tab__body"> |
||||
{this.renderSubHeader()} |
||||
{this.renderNetworksTabContent()} |
||||
{!networkIsSelected && !networksTabIsInAddMode |
||||
? <div className="networks-tab__add-network-button-wrapper"> |
||||
<Button |
||||
type="primary" |
||||
onClick={event => { |
||||
event.preventDefault() |
||||
setSelectedSettingsRpcUrl(null) |
||||
setNetworksTabAddMode(true) |
||||
}} |
||||
> |
||||
{ this.context.t('addNetwork') } |
||||
</Button> |
||||
</div> |
||||
: null |
||||
} |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
render () { |
||||
return this.renderContent() |
||||
} |
||||
} |
@ -0,0 +1,50 @@ |
||||
const defaultNetworksData = [ |
||||
{ |
||||
labelKey: 'mainnet', |
||||
iconColor: '#29B6AF', |
||||
providerType: 'mainnet', |
||||
rpcUrl: 'https://api.infura.io/v1/jsonrpc/mainnet', |
||||
chainId: '1', |
||||
ticker: 'ETH', |
||||
blockExplorerUrl: 'https://etherscan.io', |
||||
}, |
||||
{ |
||||
labelKey: 'ropsten', |
||||
iconColor: '#FF4A8D', |
||||
providerType: 'ropsten', |
||||
rpcUrl: 'https://api.infura.io/v1/jsonrpc/ropsten', |
||||
chainId: '3', |
||||
ticker: 'ETH', |
||||
blockExplorerUrl: 'https://ropsten.etherscan.io', |
||||
}, |
||||
{ |
||||
labelKey: 'kovan', |
||||
iconColor: '#9064FF', |
||||
providerType: 'kovan', |
||||
rpcUrl: 'https://api.infura.io/v1/jsonrpc/kovan', |
||||
chainId: '4', |
||||
ticker: 'ETH', |
||||
blockExplorerUrl: 'https://etherscan.io', |
||||
}, |
||||
{ |
||||
labelKey: 'rinkeby', |
||||
iconColor: '#F6C343', |
||||
providerType: 'rinkeby', |
||||
rpcUrl: 'https://api.infura.io/v1/jsonrpc/rinkeby', |
||||
chainId: '42', |
||||
ticker: 'ETH', |
||||
blockExplorerUrl: 'https://rinkeby.etherscan.io', |
||||
}, |
||||
{ |
||||
labelKey: 'localhost', |
||||
iconColor: 'white', |
||||
border: '1px solid #6A737D', |
||||
providerType: 'localhost', |
||||
rpcUrl: 'http://localhost:8545/', |
||||
blockExplorerUrl: 'https://etherscan.io', |
||||
}, |
||||
] |
||||
|
||||
export { |
||||
defaultNetworksData, |
||||
} |
@ -0,0 +1,77 @@ |
||||
import NetworksTab from './networks-tab.component' |
||||
import { compose } from 'recompose' |
||||
import { connect } from 'react-redux' |
||||
import { withRouter } from 'react-router-dom' |
||||
import { |
||||
setSelectedSettingsRpcUrl, |
||||
updateAndSetCustomRpc, |
||||
displayWarning, |
||||
setNetworksTabAddMode, |
||||
editRpc, |
||||
} from '../../../store/actions' |
||||
import { defaultNetworksData } from './networks-tab.constants' |
||||
const defaultNetworks = defaultNetworksData.map(network => ({ ...network, viewOnly: true })) |
||||
|
||||
const mapStateToProps = state => { |
||||
const { |
||||
frequentRpcListDetail, |
||||
provider, |
||||
} = state.metamask |
||||
const { |
||||
networksTabSelectedRpcUrl, |
||||
networksTabIsInAddMode, |
||||
} = state.appState |
||||
|
||||
const frequentRpcNetworkListDetails = frequentRpcListDetail.map(rpc => { |
||||
return { |
||||
label: rpc.nickname, |
||||
iconColor: '#6A737D', |
||||
providerType: 'rpc', |
||||
rpcUrl: rpc.rpcUrl, |
||||
chainId: rpc.chainId, |
||||
ticker: rpc.ticker, |
||||
blockExplorerUrl: rpc.rpcPrefs && rpc.rpcPrefs.blockExplorerUrl || '', |
||||
} |
||||
}) |
||||
|
||||
const networksToRender = [ ...defaultNetworks, ...frequentRpcNetworkListDetails ] |
||||
let selectedNetwork = networksToRender.find(network => network.rpcUrl === networksTabSelectedRpcUrl) || {} |
||||
const networkIsSelected = Boolean(selectedNetwork.rpcUrl) |
||||
|
||||
let networkDefaultedToProvider = false |
||||
if (!networkIsSelected && !networksTabIsInAddMode) { |
||||
selectedNetwork = networksToRender.find(network => { |
||||
return network.rpcUrl === provider.rpcTarget || network.providerType !== 'rpc' && network.providerType === provider.type |
||||
}) || {} |
||||
networkDefaultedToProvider = true |
||||
} |
||||
|
||||
return { |
||||
selectedNetwork, |
||||
networksToRender, |
||||
networkIsSelected, |
||||
networksTabIsInAddMode, |
||||
providerType: provider.type, |
||||
providerUrl: provider.rpcTarget, |
||||
networkDefaultedToProvider, |
||||
} |
||||
} |
||||
|
||||
const mapDispatchToProps = dispatch => { |
||||
return { |
||||
setSelectedSettingsRpcUrl: newRpcUrl => dispatch(setSelectedSettingsRpcUrl(newRpcUrl)), |
||||
setRpcTarget: (newRpc, chainId, ticker, nickname, rpcPrefs) => { |
||||
dispatch(updateAndSetCustomRpc(newRpc, chainId, ticker, nickname, rpcPrefs)) |
||||
}, |
||||
displayWarning: warning => dispatch(displayWarning(warning)), |
||||
setNetworksTabAddMode: isInAddMode => dispatch(setNetworksTabAddMode(isInAddMode)), |
||||
editRpc: (oldRpc, newRpc, chainId, ticker, nickname, rpcPrefs) => { |
||||
dispatch(editRpc(oldRpc, newRpc, chainId, ticker, nickname, rpcPrefs)) |
||||
}, |
||||
} |
||||
} |
||||
|
||||
export default compose( |
||||
withRouter, |
||||
connect(mapStateToProps, mapDispatchToProps) |
||||
)(NetworksTab) |
Loading…
Reference in new issue