AddressInputFactory.tsx
1 import React, { Component } from 'react'; 2 import { connect } from 'react-redux'; 3 import { addHexPrefix } from 'ethereumjs-util'; 4 5 import translate, { translateRaw } from 'translations'; 6 import { isValidENSAddress } from 'libs/validators'; 7 import { Address } from 'libs/units'; 8 import { ICurrentTo } from 'features/types'; 9 import { AppState } from 'features/reducers'; 10 import * as selectors from 'features/selectors'; 11 import { walletSelectors } from 'features/wallet'; 12 import { ensSelectors } from 'features/ens'; 13 import { Identicon, Spinner } from 'components/ui'; 14 import { Query } from 'components/renderCbs'; 15 import { CallbackProps } from 'components/AddressFieldFactory'; 16 import AddressFieldDropdown from './AddressFieldDropdown'; 17 import './AddressInputFactory.scss'; 18 19 interface StateProps { 20 currentTo: ICurrentTo; 21 label: string | null; 22 isValid: boolean; 23 isLabelEntry: boolean; 24 isResolving: boolean; 25 } 26 27 interface OwnProps { 28 isSelfAddress?: boolean; 29 showLabelMatch?: boolean; 30 showIdenticon?: boolean; 31 isFocused?: boolean; 32 className?: string; 33 value?: string; 34 dropdownThreshold?: number; 35 onChange(ev: React.FormEvent<HTMLInputElement>): void; 36 onChangeOverride?(ev: React.FormEvent<HTMLInputElement>): void; 37 onFocus(ev: React.FormEvent<HTMLInputElement>): void; 38 onBlur(ev: React.FormEvent<HTMLInputElement>): void; 39 withProps(props: CallbackProps): React.ReactElement<any> | null; 40 } 41 42 const ENSStatus: React.SFC<{ isLoading: boolean; ensAddress: string; rawAddress: string }> = ({ 43 isLoading, 44 ensAddress, 45 rawAddress 46 }) => { 47 const isENS = isValidENSAddress(ensAddress); 48 const text = translate('LOADING_ENS_ADDRESS'); 49 50 if (isLoading) { 51 return ( 52 <React.Fragment> 53 <Spinner /> {text} 54 </React.Fragment> 55 ); 56 } else { 57 return isENS ? <React.Fragment>{`Resolved Address: ${rawAddress}`}</React.Fragment> : null; 58 } 59 }; 60 61 type Props = OwnProps & StateProps; 62 63 class AddressInputFactoryClass extends Component<Props> { 64 public render() { 65 const { 66 label, 67 currentTo, 68 onChange, 69 onFocus, 70 onBlur, 71 isValid, 72 isLabelEntry, 73 withProps, 74 showLabelMatch, 75 isSelfAddress, 76 isResolving, 77 isFocused, 78 showIdenticon = true, 79 onChangeOverride, 80 value, 81 dropdownThreshold 82 } = this.props; 83 const inputClassName = `AddressInput-input ${label ? 'AddressInput-input-with-label' : ''}`; 84 const sendingTo = label 85 ? translateRaw('SENDING_TO', { 86 $label: label 87 }) 88 : ''; 89 const isENSAddress = currentTo.raw.includes('.eth'); 90 91 /** 92 * @desc Initially set the address to the passed value. 93 * If there wasn't a value passed, use the value from the redux store. 94 */ 95 let addr = value; 96 97 if (addr == null) { 98 addr = addHexPrefix(currentTo.value ? currentTo.value.toString('hex') : '0'); 99 } 100 101 /** 102 * @desc If passed a value and an onChangeOverride function, 103 * infer that the dropdown should be uncontrolled. 104 */ 105 const controlled = value == null && !onChangeOverride; 106 107 return ( 108 <div className="AddressInput form-group"> 109 <div className={inputClassName}> 110 <Query 111 params={['readOnly']} 112 withQuery={({ readOnly }) => 113 withProps({ 114 currentTo, 115 isValid, 116 isLabelEntry, 117 onChange, 118 onFocus, 119 onBlur, 120 readOnly: !!(readOnly || this.props.isResolving || isSelfAddress) 121 }) 122 } 123 /> 124 <ENSStatus ensAddress={currentTo.raw} isLoading={isResolving} rawAddress={addr} /> 125 {isFocused && 126 !isENSAddress && ( 127 <AddressFieldDropdown 128 controlled={controlled} 129 value={value} 130 onChangeOverride={onChangeOverride} 131 dropdownThreshold={dropdownThreshold} 132 /> 133 )} 134 {showLabelMatch && 135 label && ( 136 <div title={sendingTo} className="AddressInput-input-label"> 137 <i className="fa fa-check" /> {sendingTo} 138 </div> 139 )} 140 </div> 141 {showIdenticon && ( 142 <div className="AddressInput-identicon"> 143 <Identicon address={addr} /> 144 </div> 145 )} 146 </div> 147 ); 148 } 149 } 150 151 export const AddressInputFactory = connect((state: AppState, ownProps: OwnProps) => { 152 let currentTo: ICurrentTo; 153 if (ownProps.isSelfAddress) { 154 const wallet = walletSelectors.getWalletInst(state); 155 const addr = wallet ? wallet.getAddressString() : ''; 156 currentTo = { 157 raw: addr, 158 value: Address(addr) 159 }; 160 } else { 161 currentTo = selectors.getCurrentTo(state); 162 } 163 164 return { 165 currentTo, 166 label: selectors.getCurrentToLabel(state), 167 isResolving: ensSelectors.getResolvingDomain(state), 168 isValid: selectors.isValidCurrentTo(state), 169 isLabelEntry: selectors.isCurrentToLabelEntry(state) 170 }; 171 })(AddressInputFactoryClass);