Fix up components to use selectors, work on fixing sagas
This commit is contained in:
parent
139cf405e7
commit
7fbe1966de
|
@ -24,10 +24,12 @@ export function changeLanguage(sign: string): interfaces.ChangeLanguageAction {
|
|||
}
|
||||
|
||||
export type TChangeNode = typeof changeNode;
|
||||
export function changeNode(networkName: string, nodeName: string): interfaces.ChangeNodeAction {
|
||||
export function changeNode(
|
||||
payload: interfaces.ChangeNodeAction['payload']
|
||||
): interfaces.ChangeNodeAction {
|
||||
return {
|
||||
type: TypeKeys.CONFIG_NODE_CHANGE,
|
||||
payload: { networkName, nodeName }
|
||||
payload
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,11 +2,19 @@ import React from 'react';
|
|||
import classnames from 'classnames';
|
||||
import Modal, { IButton } from 'components/ui/Modal';
|
||||
import translate from 'translations';
|
||||
import { NETWORKS, CustomNodeConfig, CustomNetworkConfig } from 'config';
|
||||
import { makeCustomNodeId } from 'utils/node';
|
||||
import { makeCustomNetworkId } from 'utils/network';
|
||||
import { CustomNetworkConfig } from 'types/network';
|
||||
import { CustomNodeConfig } from 'types/node';
|
||||
import { TAddCustomNetwork, addCustomNetwork, AddCustomNodeAction } from 'actions/config';
|
||||
import { connect, Omit } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
import {
|
||||
getCustomNetworkConfigs,
|
||||
getCustomNodeConfigs,
|
||||
getStaticNetworkConfigs
|
||||
} from 'selectors/config';
|
||||
import { CustomNode } from 'libs/nodes';
|
||||
|
||||
const NETWORK_KEYS = Object.keys(NETWORKS);
|
||||
const CUSTOM = 'custom';
|
||||
|
||||
interface Input {
|
||||
|
@ -15,14 +23,21 @@ interface Input {
|
|||
type?: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
customNodes: CustomNodeConfig[];
|
||||
customNetworks: CustomNetworkConfig[];
|
||||
handleAddCustomNode(node: CustomNodeConfig): void;
|
||||
handleAddCustomNetwork(node: CustomNetworkConfig): void;
|
||||
interface OwnProps {
|
||||
addCustomNode(payload: AddCustomNodeAction['payload']): void;
|
||||
handleClose(): void;
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
addCustomNetwork: TAddCustomNetwork;
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
customNodes: AppState['config']['nodes']['customNodes'];
|
||||
customNetworks: AppState['config']['networks']['customNetworks'];
|
||||
staticNetworks: AppState['config']['networks']['staticNetworks'];
|
||||
}
|
||||
|
||||
interface State {
|
||||
name: string;
|
||||
url: string;
|
||||
|
@ -36,12 +51,14 @@ interface State {
|
|||
password: string;
|
||||
}
|
||||
|
||||
export default class CustomNodeModal extends React.Component<Props, State> {
|
||||
type Props = OwnProps & StateProps & DispatchProps;
|
||||
|
||||
class CustomNodeModal extends React.Component<Props, State> {
|
||||
public state: State = {
|
||||
name: '',
|
||||
url: '',
|
||||
port: '',
|
||||
network: NETWORK_KEYS[0],
|
||||
network: Object.keys(this.props.staticNetworks)[0],
|
||||
customNetworkName: '',
|
||||
customNetworkUnit: '',
|
||||
customNetworkChainId: '',
|
||||
|
@ -51,7 +68,7 @@ export default class CustomNodeModal extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
public render() {
|
||||
const { customNetworks, handleClose } = this.props;
|
||||
const { customNetworks, handleClose, staticNetworks } = this.props;
|
||||
const { network } = this.state;
|
||||
const isHttps = window.location.protocol.includes('https');
|
||||
const invalids = this.getInvalids();
|
||||
|
@ -109,12 +126,12 @@ export default class CustomNodeModal extends React.Component<Props, State> {
|
|||
value={network}
|
||||
onChange={this.handleChange}
|
||||
>
|
||||
{NETWORK_KEYS.map(net => (
|
||||
{Object.keys(staticNetworks).map(net => (
|
||||
<option key={net} value={net}>
|
||||
{net}
|
||||
</option>
|
||||
))}
|
||||
{customNetworks.map(net => {
|
||||
{Object.values(customNetworks).map(net => {
|
||||
const id = makeCustomNetworkId(net);
|
||||
return (
|
||||
<option key={id} value={id}>
|
||||
|
@ -173,7 +190,7 @@ export default class CustomNodeModal extends React.Component<Props, State> {
|
|||
placeholder: 'http://127.0.0.1/'
|
||||
},
|
||||
invalids
|
||||
)}
|
||||
)}node
|
||||
</div>
|
||||
|
||||
<div className="col-sm-3">
|
||||
|
@ -303,12 +320,13 @@ export default class CustomNodeModal extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
private makeCustomNetworkConfigFromState(): CustomNetworkConfig {
|
||||
const similarNetworkConfig = Object.values(NETWORKS).find(
|
||||
const similarNetworkConfig = Object.values(this.props.staticNetworks).find(
|
||||
n => n.chainId === +this.state.customNetworkChainId
|
||||
);
|
||||
const dPathFormats = similarNetworkConfig ? similarNetworkConfig.dPathFormats : null;
|
||||
|
||||
return {
|
||||
isCustom: true,
|
||||
name: this.state.customNetworkName,
|
||||
unit: this.state.customNetworkUnit,
|
||||
chainId: this.state.customNetworkChainId ? parseInt(this.state.customNetworkChainId, 10) : 0,
|
||||
|
@ -318,14 +336,22 @@ export default class CustomNodeModal extends React.Component<Props, State> {
|
|||
|
||||
private makeCustomNodeConfigFromState(): CustomNodeConfig {
|
||||
const { network } = this.state;
|
||||
const node: CustomNodeConfig = {
|
||||
const networkId =
|
||||
network === CUSTOM ? makeCustomNetworkId(this.makeCustomNetworkConfigFromState()) : network;
|
||||
|
||||
const port = parseInt(this.state.port, 10);
|
||||
const url = this.state.url.trim();
|
||||
const node: Omit<CustomNodeConfig, 'lib'> = {
|
||||
isCustom: true,
|
||||
service: 'your custom node',
|
||||
id: `${url}:${port}`,
|
||||
name: this.state.name.trim(),
|
||||
url: this.state.url.trim(),
|
||||
port: parseInt(this.state.port, 10),
|
||||
network:
|
||||
network === CUSTOM ? makeCustomNetworkId(this.makeCustomNetworkConfigFromState()) : network
|
||||
url,
|
||||
port,
|
||||
network: networkId
|
||||
};
|
||||
|
||||
const lib = new CustomNode(node);
|
||||
if (this.state.hasAuth) {
|
||||
node.auth = {
|
||||
username: this.state.username,
|
||||
|
@ -333,14 +359,14 @@ export default class CustomNodeModal extends React.Component<Props, State> {
|
|||
};
|
||||
}
|
||||
|
||||
return node;
|
||||
return { ...node, lib };
|
||||
}
|
||||
|
||||
private getConflictedNode(): CustomNodeConfig | undefined {
|
||||
const { customNodes } = this.props;
|
||||
const config = this.makeCustomNodeConfigFromState();
|
||||
const thisId = makeCustomNodeId(config);
|
||||
return customNodes.find(conf => makeCustomNodeId(conf) === thisId);
|
||||
|
||||
return customNodes[config.id];
|
||||
}
|
||||
|
||||
private handleChange = (ev: React.FormEvent<HTMLInputElement | HTMLSelectElement>) => {
|
||||
|
@ -359,9 +385,21 @@ export default class CustomNodeModal extends React.Component<Props, State> {
|
|||
if (this.state.network === CUSTOM) {
|
||||
const network = this.makeCustomNetworkConfigFromState();
|
||||
|
||||
this.props.handleAddCustomNetwork(network);
|
||||
this.props.addCustomNetwork({ config: network, id: node.network });
|
||||
}
|
||||
|
||||
this.props.handleAddCustomNode(node);
|
||||
this.props.addCustomNode({ config: node, id: node.id });
|
||||
};
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: AppState): StateProps => ({
|
||||
customNetworks: getCustomNetworkConfigs(state),
|
||||
customNodes: getCustomNodeConfigs(state),
|
||||
staticNetworks: getStaticNetworkConfigs(state)
|
||||
});
|
||||
|
||||
const mapDispatchToProps: DispatchProps = {
|
||||
addCustomNetwork
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(CustomNodeModal);
|
||||
|
|
|
@ -46,7 +46,7 @@ const tabs: TabLink[] = [
|
|||
];
|
||||
|
||||
interface Props {
|
||||
color?: string;
|
||||
color?: string | false;
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
|
|
@ -3,40 +3,44 @@ import {
|
|||
TChangeNodeIntent,
|
||||
TAddCustomNode,
|
||||
TRemoveCustomNode,
|
||||
TAddCustomNetwork
|
||||
TAddCustomNetwork,
|
||||
AddCustomNodeAction,
|
||||
changeLanguage,
|
||||
changeNodeIntent,
|
||||
addCustomNode,
|
||||
removeCustomNode,
|
||||
addCustomNetwork
|
||||
} from 'actions/config';
|
||||
import logo from 'assets/images/logo-myetherwallet.svg';
|
||||
import { Dropdown, ColorDropdown } from 'components/ui';
|
||||
import React, { Component } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { TSetGasPriceField } from 'actions/transaction';
|
||||
import {
|
||||
ANNOUNCEMENT_MESSAGE,
|
||||
ANNOUNCEMENT_TYPE,
|
||||
languages,
|
||||
NODES,
|
||||
NodeConfig,
|
||||
CustomNodeConfig,
|
||||
CustomNetworkConfig
|
||||
} from 'config';
|
||||
import { TSetGasPriceField, setGasPriceField } from 'actions/transaction';
|
||||
import { ANNOUNCEMENT_MESSAGE, ANNOUNCEMENT_TYPE, languages } from 'config';
|
||||
import Navigation from './components/Navigation';
|
||||
import CustomNodeModal from './components/CustomNodeModal';
|
||||
import OnlineStatus from './components/OnlineStatus';
|
||||
import Version from './components/Version';
|
||||
import { getKeyByValue } from 'utils/helpers';
|
||||
import { makeCustomNodeId } from 'utils/node';
|
||||
import { getNetworkConfigFromId } from 'utils/network';
|
||||
import { NodeConfig } from 'types/node';
|
||||
import './index.scss';
|
||||
import { AppState } from 'reducers';
|
||||
import {
|
||||
getOffline,
|
||||
isNodeChanging,
|
||||
getLanguageSelection,
|
||||
getNodeName,
|
||||
getNodeConfig,
|
||||
CustomNodeOption,
|
||||
NodeOption,
|
||||
getNodeOptions,
|
||||
getNetworkConfig
|
||||
} from 'selectors/config';
|
||||
import { NetworkConfig } from 'types/network';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
interface Props {
|
||||
languageSelection: string;
|
||||
node: NodeConfig;
|
||||
nodeSelection: string;
|
||||
isChangingNode: boolean;
|
||||
isOffline: boolean;
|
||||
customNodes: CustomNodeConfig[];
|
||||
customNetworks: CustomNetworkConfig[];
|
||||
interface DispatchProps {
|
||||
changeLanguage: TChangeLanguage;
|
||||
changeNodeIntent: TChangeNodeIntent;
|
||||
setGasPriceField: TSetGasPriceField;
|
||||
|
@ -45,11 +49,42 @@ interface Props {
|
|||
addCustomNetwork: TAddCustomNetwork;
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
network: NetworkConfig;
|
||||
languageSelection: AppState['config']['meta']['languageSelection'];
|
||||
node: NodeConfig;
|
||||
nodeSelection: AppState['config']['nodes']['selectedNode']['nodeName'];
|
||||
isChangingNode: AppState['config']['nodes']['selectedNode']['pending'];
|
||||
isOffline: AppState['config']['meta']['offline'];
|
||||
nodeOptions: (CustomNodeOption | NodeOption)[];
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: AppState): StateProps => ({
|
||||
isOffline: getOffline(state),
|
||||
isChangingNode: isNodeChanging(state),
|
||||
languageSelection: getLanguageSelection(state),
|
||||
nodeSelection: getNodeName(state),
|
||||
node: getNodeConfig(state),
|
||||
nodeOptions: getNodeOptions(state),
|
||||
network: getNetworkConfig(state)
|
||||
});
|
||||
|
||||
const mapDispatchToProps: DispatchProps = {
|
||||
setGasPriceField,
|
||||
changeLanguage,
|
||||
changeNodeIntent,
|
||||
addCustomNode,
|
||||
removeCustomNode,
|
||||
addCustomNetwork
|
||||
};
|
||||
|
||||
interface State {
|
||||
isAddingCustomNode: boolean;
|
||||
}
|
||||
|
||||
export default class Header extends Component<Props, State> {
|
||||
type Props = StateProps & DispatchProps;
|
||||
|
||||
class Header extends Component<Props, State> {
|
||||
public state = {
|
||||
isAddingCustomNode: false
|
||||
};
|
||||
|
@ -57,50 +92,40 @@ export default class Header extends Component<Props, State> {
|
|||
public render() {
|
||||
const {
|
||||
languageSelection,
|
||||
changeNodeIntent,
|
||||
node,
|
||||
nodeSelection,
|
||||
isChangingNode,
|
||||
isOffline,
|
||||
customNodes,
|
||||
customNetworks
|
||||
nodeOptions,
|
||||
network
|
||||
} = this.props;
|
||||
const { isAddingCustomNode } = this.state;
|
||||
const selectedLanguage = languageSelection;
|
||||
const selectedNetwork = getNetworkConfigFromId(node.network, customNetworks);
|
||||
const LanguageDropDown = Dropdown as new () => Dropdown<typeof selectedLanguage>;
|
||||
|
||||
const nodeOptions = Object.keys(NODES)
|
||||
.map(key => {
|
||||
const n = NODES[key];
|
||||
const network = getNetworkConfigFromId(n.network, customNetworks);
|
||||
const options = nodeOptions.map(n => {
|
||||
if (n.isCustom) {
|
||||
const { name: { networkName, nodeName }, isCustom, id, ...rest } = n;
|
||||
return {
|
||||
value: key,
|
||||
...rest,
|
||||
name: (
|
||||
<span>
|
||||
{network && network.name} <small>({n.service})</small>
|
||||
{networkName} - {nodeName} <small>(custom)</small>
|
||||
</span>
|
||||
),
|
||||
color: network && network.color,
|
||||
hidden: n.hidden
|
||||
onRemove: () => this.props.removeCustomNode({ id })
|
||||
};
|
||||
})
|
||||
.concat(
|
||||
customNodes.map(cn => {
|
||||
const network = getNetworkConfigFromId(cn.network, customNetworks);
|
||||
return {
|
||||
value: makeCustomNodeId(cn),
|
||||
name: (
|
||||
<span>
|
||||
{network && network.name} - {cn.name} <small>(custom)</small>
|
||||
</span>
|
||||
),
|
||||
color: network && network.color,
|
||||
hidden: false,
|
||||
onRemove: () => this.props.removeCustomNode(cn)
|
||||
};
|
||||
})
|
||||
);
|
||||
} else {
|
||||
const { name: { networkName, service }, isCustom, ...rest } = n;
|
||||
return {
|
||||
...rest,
|
||||
name: (
|
||||
<span>
|
||||
{networkName} <small>({service})</small>
|
||||
</span>
|
||||
)
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="Header">
|
||||
|
@ -162,8 +187,8 @@ export default class Header extends Component<Props, State> {
|
|||
change node. current node is on the ${node.network} network
|
||||
provided by ${node.service}
|
||||
`}
|
||||
options={nodeOptions}
|
||||
value={nodeSelection}
|
||||
options={options}
|
||||
value={nodeSelection || ''}
|
||||
extra={
|
||||
<li>
|
||||
<a onClick={this.openCustomNodeModal}>Add Custom Node</a>
|
||||
|
@ -180,14 +205,11 @@ export default class Header extends Component<Props, State> {
|
|||
</section>
|
||||
</section>
|
||||
|
||||
<Navigation color={selectedNetwork && selectedNetwork.color} />
|
||||
<Navigation color={!network.isCustom && network.color} />
|
||||
|
||||
{isAddingCustomNode && (
|
||||
<CustomNodeModal
|
||||
customNodes={customNodes}
|
||||
customNetworks={customNetworks}
|
||||
handleAddCustomNode={this.addCustomNode}
|
||||
handleAddCustomNetwork={this.props.addCustomNetwork}
|
||||
addCustomNode={this.addCustomNode}
|
||||
handleClose={this.closeCustomNodeModal}
|
||||
/>
|
||||
)}
|
||||
|
@ -210,8 +232,10 @@ export default class Header extends Component<Props, State> {
|
|||
this.setState({ isAddingCustomNode: false });
|
||||
};
|
||||
|
||||
private addCustomNode = (node: CustomNodeConfig) => {
|
||||
private addCustomNode = (payload: AddCustomNodeAction['payload']) => {
|
||||
this.setState({ isAddingCustomNode: false });
|
||||
this.props.addCustomNode(node);
|
||||
this.props.addCustomNode(payload);
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Header);
|
||||
|
|
|
@ -8,7 +8,7 @@ interface Option<T> {
|
|||
name: any;
|
||||
value: T;
|
||||
color?: string;
|
||||
hidden: boolean | undefined;
|
||||
hidden?: boolean | undefined;
|
||||
onRemove?(): void;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,51 +1,31 @@
|
|||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
changeLanguage as dChangeLanguage,
|
||||
changeNodeIntent as dChangeNodeIntent,
|
||||
addCustomNode as dAddCustomNode,
|
||||
removeCustomNode as dRemoveCustomNode,
|
||||
addCustomNetwork as dAddCustomNetwork,
|
||||
TChangeLanguage,
|
||||
TChangeNodeIntent,
|
||||
TAddCustomNode,
|
||||
TRemoveCustomNode,
|
||||
TAddCustomNetwork
|
||||
} from 'actions/config';
|
||||
import { TSetGasPriceField, setGasPriceField as dSetGasPriceField } from 'actions/transaction';
|
||||
import { AlphaAgreement, Footer, Header } from 'components';
|
||||
import { AppState } from 'reducers';
|
||||
import Notifications from './Notifications';
|
||||
import OfflineTab from './OfflineTab';
|
||||
import {
|
||||
getOffline,
|
||||
getLanguageSelection,
|
||||
getCustomNodeConfigs,
|
||||
getCustomNetworkConfigs,
|
||||
getLatestBlock,
|
||||
isNodeChanging
|
||||
} from 'selectors/config';
|
||||
import { NodeConfig, CustomNodeConfig } from 'types/node';
|
||||
import { CustomNetworkConfig } from 'types/network';
|
||||
import { getOffline, getLatestBlock } from 'selectors/config';
|
||||
|
||||
interface Props {
|
||||
interface StateProps {
|
||||
isOffline: AppState['config']['meta']['offline'];
|
||||
latestBlock: AppState['config']['meta']['latestBlock'];
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
isUnavailableOffline?: boolean;
|
||||
children: string | React.ReactElement<string> | React.ReactElement<string>[];
|
||||
}
|
||||
|
||||
type Props = OwnProps & StateProps;
|
||||
|
||||
class TabSection extends Component<Props, {}> {
|
||||
public render() {
|
||||
const {
|
||||
isUnavailableOffline,
|
||||
children,
|
||||
// APP
|
||||
isOffline
|
||||
} = this.props;
|
||||
const { isUnavailableOffline, children, isOffline, latestBlock } = this.props;
|
||||
|
||||
return (
|
||||
<div className="page-layout">
|
||||
<main>
|
||||
<Header {...headerProps} />
|
||||
<Header />
|
||||
<div className="Tab container">
|
||||
{isUnavailableOffline && isOffline ? <OfflineTab /> : children}
|
||||
</div>
|
||||
|
@ -58,15 +38,9 @@ class TabSection extends Component<Props, {}> {
|
|||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state: AppState): ReduxProps {
|
||||
function mapStateToProps(state: AppState): StateProps {
|
||||
return {
|
||||
node: state.config.node,
|
||||
nodeSelection: state.config.nodeSelection,
|
||||
isChangingNode: isNodeChanging(state),
|
||||
isOffline: getOffline(state),
|
||||
languageSelection: getLanguageSelection(state),
|
||||
customNodes: getCustomNodeConfigs(state),
|
||||
customNetworks: getCustomNetworkConfigs(state),
|
||||
latestBlock: getLatestBlock(state)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import RPCNode from '../rpc';
|
||||
import RPCClient from '../rpc/client';
|
||||
import { CustomNodeConfig } from 'types/node';
|
||||
import { Omit } from 'react-router';
|
||||
|
||||
export default class CustomNode extends RPCNode {
|
||||
constructor(config: CustomNodeConfig) {
|
||||
const endpoint = `${config.url}:${config.port}`;
|
||||
super(endpoint);
|
||||
constructor(config: Omit<CustomNodeConfig, 'lib'>) {
|
||||
super(config.id);
|
||||
|
||||
const headers: { [key: string]: string } = {};
|
||||
if (config.auth) {
|
||||
|
@ -13,6 +13,6 @@ export default class CustomNode extends RPCNode {
|
|||
headers.Authorization = `Basic ${btoa(`${username}:${password}`)}`;
|
||||
}
|
||||
|
||||
this.client = new RPCClient(endpoint, headers);
|
||||
this.client = new RPCClient(config.id, headers);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ interface NodeLoaded {
|
|||
|
||||
interface NodeChangePending {
|
||||
pending: true;
|
||||
nodeName: undefined;
|
||||
nodeName: string;
|
||||
}
|
||||
|
||||
export type State = NodeLoaded | NodeChangePending;
|
||||
|
@ -22,8 +22,8 @@ const changeNode = (_: State, { payload }: ChangeNodeAction): State => ({
|
|||
pending: false
|
||||
});
|
||||
|
||||
const changeNodeIntent = (_: State, _2: ChangeNodeIntentAction): State => ({
|
||||
nodeName: undefined,
|
||||
const changeNodeIntent = (state: State, _: ChangeNodeIntentAction): State => ({
|
||||
...state,
|
||||
pending: true
|
||||
});
|
||||
|
||||
|
|
|
@ -7,72 +7,84 @@ export type State = NonWeb3NodeConfigs & Web3NodeConfig;
|
|||
export const INITIAL_STATE: State = {
|
||||
eth_mew: {
|
||||
network: 'ETH',
|
||||
isCustom: false,
|
||||
lib: new RPCNode('https://api.myetherapi.com/eth'),
|
||||
service: 'MyEtherWallet',
|
||||
estimateGas: true
|
||||
},
|
||||
eth_mycrypto: {
|
||||
network: 'ETH',
|
||||
isCustom: false,
|
||||
lib: new RPCNode('https://api.mycryptoapi.com/eth'),
|
||||
service: 'MyCrypto',
|
||||
estimateGas: true
|
||||
},
|
||||
eth_ethscan: {
|
||||
network: 'ETH',
|
||||
isCustom: false,
|
||||
service: 'Etherscan.io',
|
||||
lib: new EtherscanNode('https://api.etherscan.io/api'),
|
||||
estimateGas: false
|
||||
},
|
||||
eth_infura: {
|
||||
network: 'ETH',
|
||||
isCustom: false,
|
||||
service: 'infura.io',
|
||||
lib: new InfuraNode('https://mainnet.infura.io/mew'),
|
||||
estimateGas: false
|
||||
},
|
||||
rop_mew: {
|
||||
network: 'Ropsten',
|
||||
isCustom: false,
|
||||
service: 'MyEtherWallet',
|
||||
lib: new RPCNode('https://api.myetherapi.com/rop'),
|
||||
estimateGas: false
|
||||
},
|
||||
rop_infura: {
|
||||
network: 'Ropsten',
|
||||
isCustom: false,
|
||||
service: 'infura.io',
|
||||
lib: new InfuraNode('https://ropsten.infura.io/mew'),
|
||||
estimateGas: false
|
||||
},
|
||||
kov_ethscan: {
|
||||
network: 'Kovan',
|
||||
isCustom: false,
|
||||
service: 'Etherscan.io',
|
||||
lib: new EtherscanNode('https://kovan.etherscan.io/api'),
|
||||
estimateGas: false
|
||||
},
|
||||
rin_ethscan: {
|
||||
network: 'Rinkeby',
|
||||
isCustom: false,
|
||||
service: 'Etherscan.io',
|
||||
lib: new EtherscanNode('https://rinkeby.etherscan.io/api'),
|
||||
estimateGas: false
|
||||
},
|
||||
rin_infura: {
|
||||
network: 'Rinkeby',
|
||||
isCustom: false,
|
||||
service: 'infura.io',
|
||||
lib: new InfuraNode('https://rinkeby.infura.io/mew'),
|
||||
estimateGas: false
|
||||
},
|
||||
etc_epool: {
|
||||
network: 'ETC',
|
||||
isCustom: false,
|
||||
service: 'Epool.io',
|
||||
lib: new RPCNode('https://mewapi.epool.io'),
|
||||
estimateGas: false
|
||||
},
|
||||
ubq: {
|
||||
network: 'UBQ',
|
||||
isCustom: false,
|
||||
service: 'ubiqscan.io',
|
||||
lib: new RPCNode('https://pyrus2.ubiqscan.io'),
|
||||
estimateGas: true
|
||||
},
|
||||
exp_tech: {
|
||||
network: 'EXP',
|
||||
isCustom: false,
|
||||
service: 'Expanse.tech',
|
||||
lib: new RPCNode('https://node.expanse.tech/'),
|
||||
estimateGas: true
|
||||
|
|
|
@ -8,20 +8,22 @@ import {
|
|||
takeLatest,
|
||||
takeEvery,
|
||||
select,
|
||||
race
|
||||
race,
|
||||
apply
|
||||
} from 'redux-saga/effects';
|
||||
import {
|
||||
makeCustomNodeId,
|
||||
getCustomNodeConfigFromId,
|
||||
makeNodeConfigFromCustomConfig
|
||||
} from 'utils/node';
|
||||
import { makeCustomNetworkId, getNetworkConfigFromId } from 'utils/network';
|
||||
import { makeCustomNetworkId } from 'utils/network';
|
||||
import {
|
||||
getNodeName,
|
||||
getNodeConfig,
|
||||
getCustomNodeConfigs,
|
||||
getCustomNetworkConfigs,
|
||||
getOffline
|
||||
getOffline,
|
||||
getNetworkConfig,
|
||||
isStaticNodeName,
|
||||
getCustomNodeFromId,
|
||||
getStaticNodeFromId,
|
||||
getNetworkConfigById,
|
||||
getStaticAltNodeToWeb3
|
||||
} from 'selectors/config';
|
||||
import { AppState } from 'reducers';
|
||||
import { TypeKeys } from 'actions/config/constants';
|
||||
|
@ -38,9 +40,10 @@ import { showNotification } from 'actions/notifications';
|
|||
import { translateRaw } from 'translations';
|
||||
import { Web3Wallet } from 'libs/wallet';
|
||||
import { TypeKeys as WalletTypeKeys } from 'actions/wallet/constants';
|
||||
import { State as ConfigState, INITIAL_STATE as configInitialState } from 'reducers/config';
|
||||
import { StaticNodeConfig, CustomNodeConfig } from 'types/node';
|
||||
import { CustomNetworkConfig } from 'types/network';
|
||||
import { State as ConfigState } from 'reducers/config';
|
||||
import { StaticNodeConfig, CustomNodeConfig, NodeConfig } from 'types/node';
|
||||
import { CustomNetworkConfig, StaticNetworkConfig } from 'types/network';
|
||||
import { Web3Service } from 'reducers/config/nodes/typings';
|
||||
|
||||
export const getConfig = (state: AppState): ConfigState => state.config;
|
||||
|
||||
|
@ -113,41 +116,45 @@ export function* reload(): SagaIterator {
|
|||
setTimeout(() => location.reload(), 1150);
|
||||
}
|
||||
|
||||
export function* handleNodeChangeIntent(action: ChangeNodeIntentAction): SagaIterator {
|
||||
const currentNode: string = yield select(getNodeName);
|
||||
const currentConfig: StaticNodeConfig = yield select(getNodeConfig);
|
||||
const customNets: CustomNetworkConfig[] = yield select(getCustomNetworkConfigs);
|
||||
const currentNetwork =
|
||||
getNetworkConfigFromId(currentConfig.network, customNets) || NETWORKS[currentConfig.network];
|
||||
export function* handleNodeChangeIntent({
|
||||
payload: nodeIdToSwitchTo
|
||||
}: ChangeNodeIntentAction): SagaIterator {
|
||||
const isStaticNode: boolean = yield select(isStaticNodeName, nodeIdToSwitchTo);
|
||||
const currentConfig: NodeConfig = yield select(getNodeConfig);
|
||||
|
||||
function* bailOut(message: string) {
|
||||
const currentNodeName: string = yield select(getNodeName);
|
||||
yield put(showNotification('danger', message, 5000));
|
||||
yield put(changeNode(currentNode, currentConfig, currentNetwork));
|
||||
yield put(changeNode({ networkName: currentConfig.network, nodeName: currentNodeName }));
|
||||
}
|
||||
|
||||
let actionConfig = NODES[action.payload];
|
||||
if (!actionConfig) {
|
||||
const customConfigs: CustomNodeConfig[] = yield select(getCustomNodeConfigs);
|
||||
const config = getCustomNodeConfigFromId(action.payload, customConfigs);
|
||||
let nextNodeConfig: CustomNodeConfig | StaticNodeConfig;
|
||||
|
||||
if (!isStaticNode) {
|
||||
const config: CustomNodeConfig | undefined = yield select(
|
||||
getCustomNodeFromId,
|
||||
nodeIdToSwitchTo
|
||||
);
|
||||
|
||||
if (config) {
|
||||
actionConfig = makeNodeConfigFromCustomConfig(config);
|
||||
nextNodeConfig = config;
|
||||
} else {
|
||||
return yield* bailOut(`Attempted to switch to unknown node '${nodeIdToSwitchTo}'`);
|
||||
}
|
||||
} else {
|
||||
nextNodeConfig = yield select(getStaticNodeFromId, nodeIdToSwitchTo);
|
||||
}
|
||||
|
||||
if (!actionConfig) {
|
||||
return yield* bailOut(`Attempted to switch to unknown node '${action.payload}'`);
|
||||
}
|
||||
|
||||
// Grab latest block from the node, before switching, to confirm it's online
|
||||
// Grab current block from the node, before switching, to confirm it's online
|
||||
// Give it 5 seconds before we call it offline
|
||||
let latestBlock;
|
||||
let currentBlock;
|
||||
let timeout;
|
||||
try {
|
||||
const { lb, to } = yield race({
|
||||
lb: call(actionConfig.lib.getCurrentBlock.bind(actionConfig.lib)),
|
||||
lb: apply(nextNodeConfig, nextNodeConfig.lib.getCurrentBlock),
|
||||
to: call(delay, 5000)
|
||||
});
|
||||
latestBlock = lb;
|
||||
currentBlock = lb;
|
||||
timeout = to;
|
||||
} catch (err) {
|
||||
// Whether it times out or errors, same message
|
||||
|
@ -158,16 +165,19 @@ export function* handleNodeChangeIntent(action: ChangeNodeIntentAction): SagaIte
|
|||
return yield* bailOut(translateRaw('ERROR_32'));
|
||||
}
|
||||
|
||||
const actionNetwork = getNetworkConfigFromId(actionConfig.network, customNets);
|
||||
const nextNetwork: StaticNetworkConfig | CustomNetworkConfig = yield select(
|
||||
getNetworkConfigById,
|
||||
nextNodeConfig.network
|
||||
);
|
||||
|
||||
if (!actionNetwork) {
|
||||
if (!nextNetwork) {
|
||||
return yield* bailOut(
|
||||
`Unknown custom network for your node '${action.payload}', try re-adding it`
|
||||
`Unknown custom network for your node '${nodeIdToSwitchTo}', try re-adding it`
|
||||
);
|
||||
}
|
||||
|
||||
yield put(setLatestBlock(latestBlock));
|
||||
yield put(changeNode(action.payload, actionConfig, actionNetwork));
|
||||
yield put(setLatestBlock(currentBlock));
|
||||
yield put(changeNode({ networkName: nextNodeConfig.network, nodeName: nodeIdToSwitchTo }));
|
||||
|
||||
// TODO - re-enable once DeterministicWallet state is fixed to flush properly.
|
||||
// DeterministicWallet keeps path related state we need to flush before we can stop reloading
|
||||
|
@ -176,8 +186,8 @@ export function* handleNodeChangeIntent(action: ChangeNodeIntentAction): SagaIte
|
|||
// if there's no wallet, do not reload as there's no component state to resync
|
||||
// if (currentWallet && currentConfig.network !== actionConfig.network) {
|
||||
|
||||
const isNewNetwork = currentConfig.network !== actionConfig.network;
|
||||
const newIsWeb3 = actionConfig.service === Web3Service;
|
||||
const isNewNetwork = currentConfig.network !== nextNodeConfig.network;
|
||||
const newIsWeb3 = nextNodeConfig.service === Web3Service;
|
||||
// don't reload when web3 is selected; node will automatically re-set and state is not an issue here
|
||||
if (isNewNetwork && !newIsWeb3) {
|
||||
yield call(reload);
|
||||
|
@ -185,8 +195,7 @@ export function* handleNodeChangeIntent(action: ChangeNodeIntentAction): SagaIte
|
|||
}
|
||||
|
||||
export function* switchToNewNode(action: AddCustomNodeAction): SagaIterator {
|
||||
const nodeId = makeCustomNodeId(action.payload);
|
||||
yield put(changeNodeIntent(nodeId));
|
||||
yield put(changeNodeIntent(action.payload.id));
|
||||
}
|
||||
|
||||
// If there are any orphaned custom networks, purge them
|
||||
|
@ -208,7 +217,6 @@ export function* cleanCustomNetworks(): SagaIterator {
|
|||
// unset web3 as the selected node if a non-web3 wallet has been selected
|
||||
export function* unsetWeb3NodeOnWalletEvent(action): SagaIterator {
|
||||
const node = yield select(getNodeName);
|
||||
const nodeConfig = yield select(getNodeConfig);
|
||||
const newWallet = action.payload;
|
||||
const isWeb3Wallet = newWallet instanceof Web3Wallet;
|
||||
|
||||
|
@ -216,8 +224,9 @@ export function* unsetWeb3NodeOnWalletEvent(action): SagaIterator {
|
|||
return;
|
||||
}
|
||||
|
||||
const altNode = yield select(getStaticAltNodeToWeb3);
|
||||
// switch back to a node with the same network as MetaMask/Mist
|
||||
yield put(changeNodeIntent(equivalentNodeOrDefault(nodeConfig)));
|
||||
yield put(changeNodeIntent(altNode));
|
||||
}
|
||||
|
||||
export function* unsetWeb3Node(): SagaIterator {
|
||||
|
@ -227,30 +236,11 @@ export function* unsetWeb3Node(): SagaIterator {
|
|||
return;
|
||||
}
|
||||
|
||||
const nodeConfig: StaticNodeConfig = yield select(getNodeConfig);
|
||||
const newNode = equivalentNodeOrDefault(nodeConfig);
|
||||
|
||||
yield put(changeNodeIntent(newNode));
|
||||
const altNode = yield select(getStaticAltNodeToWeb3);
|
||||
// switch back to a node with the same network as MetaMask/Mist
|
||||
yield put(changeNodeIntent(altNode));
|
||||
}
|
||||
|
||||
export const equivalentNodeOrDefault = (nodeConfig: StaticNodeConfig) => {
|
||||
const node = Object.keys(NODES)
|
||||
.filter(key => key !== 'web3')
|
||||
.reduce((found, key) => {
|
||||
const config = NODES[key];
|
||||
if (found.length) {
|
||||
return found;
|
||||
}
|
||||
if (nodeConfig.network === config.network) {
|
||||
return (found = key);
|
||||
}
|
||||
return found;
|
||||
}, '');
|
||||
|
||||
// if no equivalent node was found, use the app default
|
||||
return node.length ? node : configInitialState.nodeSelection;
|
||||
};
|
||||
|
||||
export default function* configSaga(): SagaIterator {
|
||||
yield takeLatest(TypeKeys.CONFIG_POLL_OFFLINE_STATUS, handlePollOfflineStatus);
|
||||
yield takeEvery(TypeKeys.CONFIG_NODE_CHANGE_INTENT, handleNodeChangeIntent);
|
||||
|
|
|
@ -38,7 +38,7 @@ import {
|
|||
} from 'libs/wallet';
|
||||
import { SagaIterator, delay, Task } from 'redux-saga';
|
||||
import { apply, call, fork, put, select, takeEvery, take, cancel } from 'redux-saga/effects';
|
||||
import { getNodeLib, getAllTokens, getOffline } from 'selectors/config';
|
||||
import { getNodeLib, getAllTokens, getOffline, getWeb3Node } from 'selectors/config';
|
||||
import {
|
||||
getTokens,
|
||||
getWalletInst,
|
||||
|
@ -51,6 +51,7 @@ import Web3Node, { isWeb3Node } from 'libs/nodes/web3';
|
|||
import { loadWalletConfig, saveWalletConfig } from 'utils/localStorage';
|
||||
import { getTokenBalances, filterScannedTokenBalances } from './helpers';
|
||||
import { Token } from 'types/network';
|
||||
import { Web3NodeConfig } from '../../../shared/types/node';
|
||||
|
||||
export interface TokenBalanceLookup {
|
||||
[symbol: string]: TokenBalance;
|
||||
|
@ -265,11 +266,12 @@ export function* unlockWeb3(): SagaIterator {
|
|||
action.type === ConfigTypeKeys.CONFIG_NODE_CHANGE && action.payload.nodeSelection === 'web3'
|
||||
);
|
||||
|
||||
if (!NODES.web3) {
|
||||
const web3Node: Web3NodeConfig | null = yield select(getWeb3Node);
|
||||
if (!web3Node) {
|
||||
throw Error('Web3 node config not found!');
|
||||
}
|
||||
const network = NODES.web3.network;
|
||||
const nodeLib: INode | Web3Node = yield select(getNodeLib);
|
||||
const network = web3Node.network;
|
||||
const nodeLib: Web3Node = web3Node.lib;
|
||||
|
||||
if (!isWeb3Node(nodeLib)) {
|
||||
throw new Error('Cannot use Web3 wallet without a Web3 node.');
|
||||
|
|
|
@ -9,14 +9,24 @@ import {
|
|||
|
||||
export const getNetworks = (state: AppState) => getConfig(state).networks;
|
||||
|
||||
export const getNetworkConfigById = (state: AppState, networkId: string) =>
|
||||
isStaticNetworkName(state, networkId)
|
||||
? getStaticNetworkConfigs(state)[networkId]
|
||||
: getCustomNetworkConfigs(state)[networkId];
|
||||
|
||||
export const getStaticNetworkNames = (state: AppState): StaticNetworkNames[] =>
|
||||
Object.keys(getNetworks(state).staticNetworks) as StaticNetworkNames[];
|
||||
|
||||
export const isStaticNetworkName = (
|
||||
state: AppState,
|
||||
networkName: string
|
||||
): networkName is StaticNetworkNames =>
|
||||
Object.keys(getStaticNetworkConfigs(state)).includes(networkName);
|
||||
|
||||
export const getStaticNetworkConfig = (state: AppState): StaticNetworkConfig | undefined => {
|
||||
const { staticNetworks, selectedNetwork } = getNetworks(state);
|
||||
const isDefaultNetworkName = (networkName: string): networkName is StaticNetworkNames =>
|
||||
Object.keys(staticNetworks).includes(networkName);
|
||||
const defaultNetwork = isDefaultNetworkName(selectedNetwork)
|
||||
|
||||
const defaultNetwork = isStaticNetworkName(state, selectedNetwork)
|
||||
? staticNetworks[selectedNetwork]
|
||||
: undefined;
|
||||
return defaultNetwork;
|
||||
|
|
|
@ -1,29 +1,70 @@
|
|||
import { AppState } from 'reducers';
|
||||
import { getConfig, getStaticNetworkConfigs } from 'selectors/config';
|
||||
import { CustomNodeConfig, StaticNodeConfig, StaticNodeName } from 'types/node';
|
||||
import {
|
||||
getConfig,
|
||||
getStaticNetworkConfigs,
|
||||
getCustomNetworkConfigs,
|
||||
isStaticNetworkName
|
||||
} from 'selectors/config';
|
||||
import { CustomNodeConfig, StaticNodeConfig, StaticNodeName, Web3NodeConfig } from 'types/node';
|
||||
import { INITIAL_STATE as SELECTED_NODE_INITIAL_STATE } from 'reducers/config/nodes/selectedNode';
|
||||
|
||||
export const getNodes = (state: AppState) => getConfig(state).nodes;
|
||||
|
||||
export function isNodeCustom(state: AppState, nodeName: string): CustomNodeConfig | undefined {
|
||||
return getCustomNodeConfigs(state)[nodeName];
|
||||
}
|
||||
|
||||
export const getCustomNodeFromId = (
|
||||
state: AppState,
|
||||
nodeName: string
|
||||
): CustomNodeConfig | undefined => getCustomNodeConfigs(state)[nodeName];
|
||||
|
||||
export const getStaticAltNodeToWeb3 = (state: AppState) => {
|
||||
const { web3, ...configs } = getStaticNodeConfigs(state);
|
||||
if (!web3) {
|
||||
return SELECTED_NODE_INITIAL_STATE.nodeName;
|
||||
}
|
||||
const res = Object.entries(configs).find(
|
||||
([_, config]: [StaticNodeName, StaticNodeConfig]) => web3.network === config.network
|
||||
);
|
||||
if (res) {
|
||||
return res[0];
|
||||
}
|
||||
return SELECTED_NODE_INITIAL_STATE.nodeName;
|
||||
};
|
||||
|
||||
export const getStaticNodeFromId = (state: AppState, nodeName: StaticNodeName) =>
|
||||
getStaticNodeConfigs(state)[nodeName];
|
||||
|
||||
export const isStaticNodeName = (state: AppState, nodeName: string): nodeName is StaticNodeName =>
|
||||
Object.keys(getStaticNodeConfigs(state)).includes(nodeName);
|
||||
|
||||
const getStaticNodeConfigs = (state: AppState) => getNodes(state).staticNodes;
|
||||
|
||||
export const getStaticNodeConfig = (state: AppState): StaticNodeConfig | undefined => {
|
||||
const { staticNodes, selectedNode: { nodeName } } = getNodes(state);
|
||||
if (nodeName === undefined) {
|
||||
return nodeName;
|
||||
}
|
||||
|
||||
const isStaticNodeName = (networkName: string): networkName is StaticNodeName =>
|
||||
Object.keys(staticNodes).includes(networkName);
|
||||
|
||||
const defaultNetwork = isStaticNodeName(nodeName) ? staticNodes[nodeName] : undefined;
|
||||
const defaultNetwork = isStaticNodeName(state, nodeName) ? staticNodes[nodeName] : undefined;
|
||||
return defaultNetwork;
|
||||
};
|
||||
|
||||
export const getWeb3Node = (state: AppState): Web3NodeConfig | null => {
|
||||
const currNode = getStaticNodeConfig(state);
|
||||
const currNodeName = getNodeName(state);
|
||||
if (
|
||||
currNode &&
|
||||
currNodeName &&
|
||||
isStaticNodeName(state, currNodeName) &&
|
||||
currNodeName === 'web3'
|
||||
) {
|
||||
return currNode;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const getCustomNodeConfig = (state: AppState): CustomNodeConfig | undefined => {
|
||||
const { customNodes, selectedNode: { nodeName } } = getNodes(state);
|
||||
|
||||
if (nodeName === undefined) {
|
||||
return nodeName;
|
||||
}
|
||||
|
||||
const customNode = customNodes[nodeName];
|
||||
return customNode;
|
||||
};
|
||||
|
@ -40,7 +81,7 @@ export function isNodeChanging(state): boolean {
|
|||
return getNodes(state).selectedNode.pending;
|
||||
}
|
||||
|
||||
export function getNodeName(state: AppState): string | undefined {
|
||||
export function getNodeName(state: AppState): string {
|
||||
return getNodes(state).selectedNode.nodeName;
|
||||
}
|
||||
|
||||
|
@ -48,15 +89,15 @@ export function getIsWeb3Node(state: AppState): boolean {
|
|||
return getNodeName(state) === 'web3';
|
||||
}
|
||||
|
||||
export function getNodeConfig(state: AppState): StaticNodeConfig | CustomNodeConfig | undefined {
|
||||
export function getNodeConfig(state: AppState): StaticNodeConfig | CustomNodeConfig {
|
||||
const config = getStaticNodeConfig(state) || getCustomNodeConfig(state);
|
||||
/*
|
||||
|
||||
if (!config) {
|
||||
const { selectedNode } = getNodes(state);
|
||||
throw Error(
|
||||
`No node config found for ${selectedNode.nodeName} in either static or custom nodes`
|
||||
);
|
||||
}*/
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
|
@ -68,25 +109,63 @@ export function getNodeLib(state: AppState) {
|
|||
return config.lib;
|
||||
}
|
||||
|
||||
interface NodeOption {
|
||||
export interface NodeOption {
|
||||
isCustom: false;
|
||||
value: string;
|
||||
name: { networkName?: string; service: string };
|
||||
color?: string;
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
export function getStaticNodeOptions(state: AppState) {
|
||||
export function getStaticNodeOptions(state: AppState): NodeOption[] {
|
||||
const staticNetworkConfigs = getStaticNetworkConfigs(state);
|
||||
Object.entries(getStaticNodes(state)).map(
|
||||
([nodeName, nodeConfig]: [string, StaticNodeConfig]) => {
|
||||
const networkName = nodeConfig.network;
|
||||
return Object.entries(getStaticNodes(state)).map(
|
||||
([nodeName, node]: [string, StaticNodeConfig]) => {
|
||||
const networkName = node.network;
|
||||
const associatedNetwork = staticNetworkConfigs[networkName];
|
||||
return {
|
||||
const opt: NodeOption = {
|
||||
isCustom: node.isCustom,
|
||||
value: nodeName,
|
||||
name: { networkName, service: nodeConfig.service },
|
||||
name: { networkName, service: node.service },
|
||||
color: associatedNetwork.color,
|
||||
hidden: nodeConfig.hidden
|
||||
hidden: node.hidden
|
||||
};
|
||||
return opt;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export interface CustomNodeOption {
|
||||
isCustom: true;
|
||||
id: string;
|
||||
value: string;
|
||||
name: { networkName?: string; nodeName: string };
|
||||
color?: string;
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
export function getCustomNodeOptions(state: AppState): CustomNodeOption[] {
|
||||
const staticNetworkConfigs = getStaticNetworkConfigs(state);
|
||||
const customNetworkConfigs = getCustomNetworkConfigs(state);
|
||||
return Object.entries(getCustomNodeConfigs(state)).map(
|
||||
([nodeName, node]: [string, CustomNodeConfig]) => {
|
||||
const networkName = node.network;
|
||||
const associatedNetwork = isStaticNetworkName(state, networkName)
|
||||
? staticNetworkConfigs[networkName]
|
||||
: customNetworkConfigs[networkName];
|
||||
const opt: CustomNodeOption = {
|
||||
isCustom: node.isCustom,
|
||||
value: node.id,
|
||||
name: { networkName, nodeName },
|
||||
color: associatedNetwork.isCustom ? undefined : associatedNetwork.color,
|
||||
hidden: false,
|
||||
id: node.id
|
||||
};
|
||||
return opt;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getNodeOptions(state: AppState) {
|
||||
return [...getStaticNodeOptions(state), ...getCustomNodeOptions(state)];
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ const configureStore = () => {
|
|||
|
||||
// If they have a saved node, make sure we assign that too. The node selected
|
||||
// isn't serializable, so we have to assign it here.
|
||||
if (savedConfigState && savedConfigState.nodeSelection) {
|
||||
if (savedConfigState && savedConfigState.nodes.selectedNode.nodeName) {
|
||||
const savedNode = getNodeConfigFromId(
|
||||
savedConfigState.nodeSelection,
|
||||
savedConfigState.customNodes
|
||||
|
|
|
@ -39,20 +39,6 @@ export function makeNetworkConfigFromCustomConfig(
|
|||
return customConfig;
|
||||
}
|
||||
|
||||
export function getNetworkConfigFromId(
|
||||
id: string,
|
||||
configs: CustomNetworkConfig[]
|
||||
): StaticNetworkConfig | undefined {
|
||||
if (NETWORKS[id]) {
|
||||
return NETWORKS[id];
|
||||
}
|
||||
|
||||
const customConfig = configs.find(conf => makeCustomNetworkId(conf) === id);
|
||||
if (customConfig) {
|
||||
return makeNetworkConfigFromCustomConfig(customConfig);
|
||||
}
|
||||
}
|
||||
|
||||
type PathType = keyof DPathFormats;
|
||||
|
||||
type DPathFormat =
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
import { CustomNode } from 'libs/nodes';
|
||||
import { CustomNodeConfig, StaticNodeConfig } from 'types/node';
|
||||
|
||||
export function makeCustomNodeId(config: CustomNodeConfig): string {
|
||||
return `${config.url}:${config.port}`;
|
||||
}
|
||||
|
||||
export function getCustomNodeConfigFromId(
|
||||
id: string,
|
||||
configs: CustomNodeConfig[]
|
||||
): CustomNodeConfig | undefined {
|
||||
return configs.find(node => makeCustomNodeId(node) === id);
|
||||
}
|
||||
|
||||
export function getNodeConfigFromId(
|
||||
id: string,
|
||||
configs: CustomNodeConfig[]
|
||||
): StaticNodeConfig | undefined {
|
||||
if (NODES[id]) {
|
||||
return NODES[id];
|
||||
}
|
||||
|
||||
const config = getCustomNodeConfigFromId(id, configs);
|
||||
if (config) {
|
||||
return makeNodeConfigFromCustomConfig(config);
|
||||
}
|
||||
}
|
||||
|
||||
export function makeNodeConfigFromCustomConfig(config: CustomNodeConfig): StaticNodeConfig {
|
||||
interface Override extends StaticNodeConfig {
|
||||
network: any;
|
||||
}
|
||||
|
||||
const customConfig: Override = {
|
||||
network: config.network,
|
||||
lib: new CustomNode(config),
|
||||
service: 'your custom node',
|
||||
estimateGas: true
|
||||
};
|
||||
|
||||
return customConfig;
|
||||
}
|
|
@ -28,7 +28,6 @@ interface DPathFormats {
|
|||
}
|
||||
|
||||
interface StaticNetworkConfig {
|
||||
// TODO really try not to allow strings due to custom networks
|
||||
isCustom: false; // used for type guards
|
||||
name: StaticNetworkNames;
|
||||
unit: string;
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
import { RPCNode, Web3Node } from 'libs/nodes';
|
||||
import { StaticNetworkNames } from './network';
|
||||
import { StaticNodesState, CustomNodesState } from 'reducers/config/nodes';
|
||||
import CustomNode from 'libs/nodes/custom';
|
||||
|
||||
interface CustomNodeConfig {
|
||||
id: string;
|
||||
isCustom: true;
|
||||
name: string;
|
||||
lib: CustomNode;
|
||||
service: 'your custom node';
|
||||
url: string;
|
||||
port: number;
|
||||
network: string;
|
||||
|
@ -14,6 +19,7 @@ interface CustomNodeConfig {
|
|||
}
|
||||
|
||||
interface StaticNodeConfig {
|
||||
isCustom: false;
|
||||
network: StaticNetworkNames;
|
||||
lib: RPCNode | Web3Node;
|
||||
service: string;
|
||||
|
@ -21,6 +27,10 @@ interface StaticNodeConfig {
|
|||
hidden?: boolean;
|
||||
}
|
||||
|
||||
interface Web3NodeConfig extends StaticNodeConfig {
|
||||
lib: Web3Node;
|
||||
}
|
||||
|
||||
declare enum StaticNodeName {
|
||||
ETH_MEW = 'eth_mew',
|
||||
ETH_MYCRYPTO = 'eth_mycrypto',
|
||||
|
@ -39,7 +49,7 @@ declare enum StaticNodeName {
|
|||
type NonWeb3NodeConfigs = { [key in StaticNodeName]: StaticNodeConfig };
|
||||
|
||||
interface Web3NodeConfig {
|
||||
web3?: StaticNodeConfig;
|
||||
web3?: Web3NodeConfig;
|
||||
}
|
||||
|
||||
type NodeConfig = StaticNodesState[StaticNodeName] | CustomNodesState[string];
|
||||
|
|
Loading…
Reference in New Issue