HenryNguyen5 01fc5f1a89 Move Nodes/Networks to Redux (#961)
* Start splitting networks into their own reducers

* Split out nodes and networks into their own reducers

* Cleanup file structure

* Make selectors for new state

* Change custom network typing

* re-type repo

* Fix up components to use selectors, work on fixing sagas

* Provide consistency in naming, fix more sagas

* Get non web3 node switching working

* Split config rehydration off into a different file for store

* Inline auth for custom nodes

* Include typing for app state

* moar selectors

* Get web3 working + cleanup sagas

* Cleanup tsc errors

* Use forof loop instead of foreach for clearing pruning custom networks

* Add reducer tests for new redux state

* Export needed variables

* Add console error

* Remove old comment

* Work on saga tests

* Get passing existing saga tests

* Fix more tests

* Remove irrlevant tests

* add console error

* Get rest of tests passing

* Fix merge errors

* Remove random text

* Fix store saving

* Fix selector lib only grabbing from static nodes

* Fix custom node removal crashing app

* Infer selected network via node

* Prune custom networks properly on node removal

* Infer network name from chainid from selecting state

* Cleanup tsc errors

* Remove MEW nodes for main and testnet
2018-02-12 14:43:07 -06:00

162 lines
4.4 KiB
TypeScript

import React from 'react';
import classnames from 'classnames';
import { HELP_ARTICLE } from 'config';
import { isPositiveIntegerOrZero, isValidETHAddress } from 'libs/validators';
import translate from 'translations';
import { HelpLink } from 'components/ui';
import './AddCustomTokenForm.scss';
import { Token } from 'types/network';
interface Props {
allTokens: Token[];
onSave(params: Token): void;
toggleForm(): void;
}
interface IGenerateSymbolLookup {
[tokenSymbol: string]: boolean;
}
interface State {
tokenSymbolLookup: IGenerateSymbolLookup;
address: string;
symbol: string;
decimal: string;
}
export default class AddCustomTokenForm extends React.PureComponent<Props, State> {
public state: State = {
tokenSymbolLookup: {},
address: '',
symbol: '',
decimal: ''
};
constructor(props: Props) {
super(props);
this.state = {
...this.state,
tokenSymbolLookup: this.generateSymbolLookup(props.allTokens)
};
}
public render() {
const { address, symbol, decimal } = this.state;
const inputClasses = 'AddCustom-field-input form-control input-sm';
const errors = this.getErrors();
const fields = [
{
name: 'symbol',
value: symbol,
label: translate('TOKEN_Symbol')
},
{
name: 'address',
value: address,
label: translate('TOKEN_Addr')
},
{
name: 'decimal',
value: decimal,
label: translate('TOKEN_Dec')
}
];
return (
<form className="AddCustom" onSubmit={this.onSave}>
{fields.map(field => {
return (
<label className="AddCustom-field form-group" key={field.name}>
<span className="AddCustom-field-label">{field.label}</span>
<input
className={classnames(
inputClasses,
errors[field.name] ? 'is-invalid' : field.value ? 'is-valid' : ''
)}
type="text"
name={field.name}
value={field.value}
onChange={this.onFieldChange}
/>
{typeof errors[field.name] === 'string' && (
<div className="AddCustom-field-error">{errors[field.name]}</div>
)}
</label>
);
})}
<div className="AddCustom-buttons">
<HelpLink article={HELP_ARTICLE.ADDING_NEW_TOKENS} className="AddCustom-buttons-help">
{translate('Need help? Learn how to add custom tokens.')}
</HelpLink>
<button
className="AddCustom-buttons-btn btn btn-primary btn-sm"
disabled={!this.isValid()}
>
{translate('x_Save')}
</button>
<button
className="AddCustom-buttons-btn btn btn-sm btn-default"
onClick={this.props.toggleForm}
>
{translate('x_Cancel')}
</button>
</div>
</form>
);
}
public getErrors() {
const { address, symbol, decimal } = this.state;
const errors: { [key: string]: boolean | string } = {};
// Formatting errors
if (decimal && !isPositiveIntegerOrZero(parseInt(decimal, 10))) {
errors.decimal = true;
}
if (address && !isValidETHAddress(address)) {
errors.address = true;
}
// Message errors
if (symbol && this.state.tokenSymbolLookup[symbol]) {
errors.symbol = 'A token with this symbol already exists';
}
return errors;
}
public isValid() {
const { address, symbol, decimal } = this.state;
return !Object.keys(this.getErrors()).length && address && symbol && decimal;
}
public onFieldChange = (e: React.FormEvent<HTMLInputElement>) => {
// TODO: typescript bug: https://github.com/Microsoft/TypeScript/issues/13948
const name: any = e.currentTarget.name;
const value = e.currentTarget.value;
this.setState({ [name]: value });
};
public onSave = (ev: React.FormEvent<HTMLFormElement>) => {
ev.preventDefault();
if (!this.isValid()) {
return;
}
const { address, symbol, decimal } = this.state;
this.props.onSave({ address, symbol, decimal: parseInt(decimal, 10) });
};
private generateSymbolLookup(tokens: Token[]) {
return tokens.reduce(
(prev, tk) => {
prev[tk.symbol] = true;
return prev;
},
{} as IGenerateSymbolLookup
);
}
}