/ common / components / BalanceSidebar / TokenBalances / AddCustomTokenForm / AddCustomTokenForm.tsx
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 }