AddCustomTokenForm.tsx
  1  import React from 'react';
  2  import { Result } from 'mycrypto-nano-result';
  3  
  4  import { HELP_ARTICLE } from 'config';
  5  import translate from 'translations';
  6  import { Token } from 'types/network';
  7  import { HelpLink } from 'components/ui';
  8  import { AddressField } from './AddressField';
  9  import { DecimalField } from './DecimalField';
 10  import { SymbolField } from './SymbolField';
 11  import { BalanceField } from './BalanceField';
 12  import './AddCustomTokenForm.scss';
 13  
 14  interface Props {
 15    allTokens: Token[];
 16    onSave(params: Token): void;
 17    toggleForm(): void;
 18  }
 19  
 20  export interface IGenerateSymbolLookup {
 21    [tokenSymbol: string]: boolean;
 22  }
 23  
 24  export interface IGenerateAddressLookup {
 25    [address: string]: boolean;
 26  }
 27  
 28  interface State {
 29    address: Result<string>;
 30    symbol: Result<string>;
 31    decimal: Result<string>;
 32  }
 33  
 34  export class AddCustomTokenForm extends React.PureComponent<Props, State> {
 35    public state: State = {
 36      address: Result.from({ err: 'This field is empty' }),
 37      symbol: Result.from({ err: 'This field is empty' }),
 38      decimal: Result.from({ err: 'This field is empty' })
 39    };
 40  
 41    private tokenSymbolLookup = this.generateSymbolLookup();
 42    private tokenAddressLookup = this.generateAddressMap();
 43  
 44    public render() {
 45      const address = this.state.address.toVal().res;
 46  
 47      return (
 48        <form className="AddCustom" onSubmit={this.onSave}>
 49          <AddressField
 50            addressLookup={this.tokenAddressLookup}
 51            onChange={this.handleFieldChange('address')}
 52          />
 53          <DecimalField address={address} onChange={this.handleFieldChange('decimal')} />
 54          <SymbolField
 55            address={address}
 56            symbolLookup={this.tokenSymbolLookup}
 57            onChange={this.handleFieldChange('symbol')}
 58          />
 59          <BalanceField address={address} />
 60          <HelpLink article={HELP_ARTICLE.ADDING_NEW_TOKENS} className="AddCustom-buttons-help">
 61            {translate('ADD_CUSTOM_TKN_HELP')}
 62          </HelpLink>
 63          <div className="AddCustom-buttons">
 64            <button
 65              className="AddCustom-buttons-btn btn btn-sm btn-default"
 66              onClick={this.props.toggleForm}
 67            >
 68              {translate('ACTION_2')}
 69            </button>
 70            <button
 71              className="AddCustom-buttons-btn btn btn-primary btn-sm"
 72              disabled={!this.isValid()}
 73            >
 74              {translate('X_SAVE')}
 75            </button>
 76          </div>
 77        </form>
 78      );
 79    }
 80  
 81    public onSave = (ev: React.FormEvent<HTMLFormElement>) => {
 82      ev.preventDefault();
 83      if (!this.isValid()) {
 84        return;
 85      }
 86  
 87      const { address, symbol, decimal } = this.state;
 88      this.props.onSave({
 89        address: address.unwrap(),
 90        symbol: symbol.unwrap(),
 91        decimal: parseInt(decimal.unwrap(), 10)
 92      });
 93    };
 94  
 95    private handleFieldChange = (fieldName: keyof State) => (res: Result<string>) => {
 96      this.setState({ [fieldName as any]: res });
 97    };
 98  
 99    private isValid() {
100      const { address, decimal, symbol } = this.state;
101      const valid = address.ok() && decimal.ok() && symbol.ok();
102      return valid;
103    }
104  
105    private generateSymbolLookup() {
106      return this.tokenArrayToMap('symbol');
107    }
108  
109    private generateAddressMap() {
110      return this.tokenArrayToMap('address');
111    }
112  
113    private tokenArrayToMap(key: Exclude<keyof Token, 'error'>) {
114      const tokens = this.props.allTokens;
115      return tokens.reduce<{ [k: string]: boolean }>((prev, tk) => {
116        prev[tk[key]] = true;
117        return prev;
118      }, {});
119    }
120  }