MyCrypto/common/components/BalanceSidebar/TokenBalances/AddCustomTokenForm/BalanceField.tsx

125 lines
3.3 KiB
TypeScript

import React from 'react';
import { connect } from 'react-redux';
import { Result } from 'mycrypto-nano-result';
import ERC20 from 'libs/erc20';
import { shepherdProvider } from 'libs/nodes';
import { AppState } from 'features/reducers';
import { walletSelectors } from 'features/wallet';
import Spinner from 'components/ui/Spinner';
import { Input } from 'components/ui';
interface OwnProps {
address?: string;
}
interface StateProps {
walletInst: ReturnType<typeof walletSelectors.getWalletInst>;
}
interface State {
balance: Result<string>;
addressToLoad?: string;
loading: boolean;
}
type Props = OwnProps & StateProps;
class BalanceFieldClass extends React.Component<Props, State> {
public static getDerivedStateFromProps(
nextProps: OwnProps,
prevState: State
): Partial<State> | null {
if (nextProps.address && nextProps.address !== prevState.addressToLoad) {
return { loading: true, addressToLoad: nextProps.address };
}
return null;
}
public state: State = {
balance: Result.from({ res: '' }),
loading: false
};
private currentRequest: Promise<any> | null;
public componentDidUpdate() {
if (this.state.addressToLoad && this.state.loading) {
this.attemptToLoadBalance(this.state.addressToLoad);
}
}
public componentWillUnmount() {
if (this.currentRequest) {
this.currentRequest = null;
}
}
public render() {
const { balance, loading } = this.state;
return (
<label className="AddCustom-field form-group">
<div className="input-group-header">Balance</div>
{loading ? (
<Spinner />
) : (
<Input
isValid={balance.ok()}
className="input-group-input-small"
type="text"
name="Balance"
readOnly={true}
value={balance.ok() ? balance.unwrap() : '0'}
/>
)}
{balance.err() && <div className="AddCustom-field-error">{balance.err()}</div>}
</label>
);
}
private attemptToLoadBalance(address: string) {
// process request
this.currentRequest = this.loadBalance(address)
// set state on successful request e.g it was not cancelled
// and then also set our current request to null
.then(({ balance }) =>
this.setState({
balance,
loading: false
})
)
.catch(e => {
console.error(e);
// if the component is unmounted, then dont call set state
if (!this.currentRequest) {
return;
}
// otherwise it was a failed fetch call
this.setState({ loading: false });
})
.then(() => (this.currentRequest = null));
}
private loadBalance(address: string) {
if (!this.props.walletInst) {
return Promise.reject('No wallet found');
}
const owner = this.props.walletInst.getAddressString();
return shepherdProvider
.sendCallRequest({ data: ERC20.balanceOf.encodeInput({ _owner: owner }), to: address })
.then(ERC20.balanceOf.decodeOutput)
.then(({ balance }) => {
const result = Result.from({ res: balance });
return { balance: result };
});
}
}
function mapStateToProps(state: AppState): StateProps {
return { walletInst: walletSelectors.getWalletInst(state) };
}
export const BalanceField = connect(mapStateToProps)(BalanceFieldClass);