Performance Improvements (Pure components, debounced gas slider) (#899)
* PureComponent a ton of non-connected components. * Debounce gas price slider. Keep gas price in state to reflect changes immediately. * PureComponent balance sidebar and swap unconnected components. * Import correct component. * Move debouncing of gas slider to sagas via gasPriceInputIntent action. * Remove console log. * Remove leftover file from merge.
This commit is contained in:
parent
ae8b123cfd
commit
d1a2c885a2
|
@ -6,6 +6,7 @@ import {
|
|||
SetValueFieldAction,
|
||||
InputGasLimitAction,
|
||||
InputGasPriceAction,
|
||||
InputGasPriceIntentAction,
|
||||
InputDataAction,
|
||||
InputNonceAction,
|
||||
ResetAction,
|
||||
|
@ -25,6 +26,12 @@ const inputGasPrice = (payload: InputGasPriceAction['payload']) => ({
|
|||
payload
|
||||
});
|
||||
|
||||
type TInputGasPriceIntent = typeof inputGasPrice;
|
||||
const inputGasPriceIntent = (payload: InputGasPriceIntentAction['payload']) => ({
|
||||
type: TypeKeys.GAS_PRICE_INPUT_INTENT,
|
||||
payload
|
||||
});
|
||||
|
||||
type TInputNonce = typeof inputNonce;
|
||||
const inputNonce = (payload: InputNonceAction['payload']) => ({
|
||||
type: TypeKeys.NONCE_INPUT,
|
||||
|
@ -79,6 +86,7 @@ const reset = (): ResetAction => ({ type: TypeKeys.RESET });
|
|||
export {
|
||||
TInputGasLimit,
|
||||
TInputGasPrice,
|
||||
TInputGasPriceIntent,
|
||||
TInputNonce,
|
||||
TInputData,
|
||||
TSetGasLimitField,
|
||||
|
@ -90,6 +98,7 @@ export {
|
|||
TReset,
|
||||
inputGasLimit,
|
||||
inputGasPrice,
|
||||
inputGasPriceIntent,
|
||||
inputNonce,
|
||||
inputData,
|
||||
setGasLimitField,
|
||||
|
|
|
@ -10,6 +10,10 @@ interface InputGasPriceAction {
|
|||
type: TypeKeys.GAS_PRICE_INPUT;
|
||||
payload: string;
|
||||
}
|
||||
interface InputGasPriceIntentAction {
|
||||
type: TypeKeys.GAS_PRICE_INPUT_INTENT;
|
||||
payload: string;
|
||||
}
|
||||
interface InputDataAction {
|
||||
type: TypeKeys.DATA_FIELD_INPUT;
|
||||
payload: string;
|
||||
|
@ -84,6 +88,7 @@ type FieldAction =
|
|||
export {
|
||||
InputGasLimitAction,
|
||||
InputGasPriceAction,
|
||||
InputGasPriceIntentAction,
|
||||
InputDataAction,
|
||||
InputNonceAction,
|
||||
SetGasLimitFieldAction,
|
||||
|
|
|
@ -30,6 +30,7 @@ export enum TypeKeys {
|
|||
DATA_FIELD_INPUT = 'DATA_FIELD_INPUT',
|
||||
GAS_LIMIT_INPUT = 'GAS_LIMIT_INPUT',
|
||||
GAS_PRICE_INPUT = 'GAS_PRICE_INPUT',
|
||||
GAS_PRICE_INPUT_INTENT = 'GAS_PRICE_INPUT_INTENT',
|
||||
NONCE_INPUT = 'NONCE_INPUT',
|
||||
|
||||
DATA_FIELD_SET = 'DATA_FIELD_SET',
|
||||
|
|
|
@ -7,7 +7,7 @@ interface State {
|
|||
isFading: boolean;
|
||||
hasAcknowledged: boolean;
|
||||
}
|
||||
export default class AlphaAgreement extends React.Component<{}, State> {
|
||||
export default class AlphaAgreement extends React.PureComponent<{}, State> {
|
||||
public state = {
|
||||
hasAcknowledged: !!localStorage.getItem(LS_KEY),
|
||||
isFading: false
|
||||
|
|
|
@ -23,7 +23,7 @@ interface State {
|
|||
decimal: string;
|
||||
}
|
||||
|
||||
export default class AddCustomTokenForm extends React.Component<Props, State> {
|
||||
export default class AddCustomTokenForm extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
tokenSymbolLookup: {},
|
||||
address: '',
|
||||
|
|
|
@ -19,7 +19,7 @@ interface State {
|
|||
trackedTokens: { [symbol: string]: boolean };
|
||||
showCustomTokenForm: boolean;
|
||||
}
|
||||
export default class TokenBalances extends React.Component<Props, State> {
|
||||
export default class TokenBalances extends React.PureComponent<Props, State> {
|
||||
public state = {
|
||||
trackedTokens: {},
|
||||
showCustomTokenForm: false
|
||||
|
|
|
@ -19,7 +19,7 @@ interface State {
|
|||
showLongBalance: boolean;
|
||||
}
|
||||
|
||||
export default class TokenRow extends React.Component<Props, State> {
|
||||
export default class TokenRow extends React.PureComponent<Props, State> {
|
||||
public state = {
|
||||
showLongBalance: false
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import './PreFooter.scss';
|
||||
|
||||
const PreFooter = () => {
|
||||
const PreFooter: React.SFC<{}> = () => {
|
||||
return (
|
||||
<section className="pre-footer">
|
||||
<div className="container">
|
||||
|
|
|
@ -108,7 +108,7 @@ interface State {
|
|||
isOpen: boolean;
|
||||
}
|
||||
|
||||
export default class Footer extends React.Component<Props, State> {
|
||||
export default class Footer extends React.PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = { isOpen: false };
|
||||
|
|
|
@ -36,7 +36,7 @@ interface State {
|
|||
password: string;
|
||||
}
|
||||
|
||||
export default class CustomNodeModal extends React.Component<Props, State> {
|
||||
export default class CustomNodeModal extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
name: '',
|
||||
url: '',
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import NavigationLink from './NavigationLink';
|
||||
import { knowledgeBaseURL } from 'config';
|
||||
import './Navigation.scss';
|
||||
|
@ -58,7 +58,7 @@ interface BorderStyle {
|
|||
borderTopColor?: string;
|
||||
}
|
||||
|
||||
export default class Navigation extends Component<Props, State> {
|
||||
export default class Navigation extends PureComponent<Props, State> {
|
||||
public state = {
|
||||
showLeftArrow: false,
|
||||
showRightArrow: false
|
||||
|
|
|
@ -10,7 +10,7 @@ interface Props extends RouteComponentProps<{}> {
|
|||
isHomepage: boolean;
|
||||
}
|
||||
|
||||
class NavigationLink extends React.Component<Props, {}> {
|
||||
class NavigationLink extends React.PureComponent<Props, {}> {
|
||||
public render() {
|
||||
const { link, location, isHomepage } = this.props;
|
||||
const isExternalLink = link.to.includes('http');
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
} from 'actions/config';
|
||||
import logo from 'assets/images/logo-myetherwallet.svg';
|
||||
import { Dropdown, ColorDropdown } from 'components/ui';
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { TSetGasPriceField } from 'actions/transaction';
|
||||
|
@ -49,7 +49,7 @@ interface State {
|
|||
isAddingCustomNode: boolean;
|
||||
}
|
||||
|
||||
export default class Header extends Component<Props, State> {
|
||||
export default class Header extends PureComponent<Props, State> {
|
||||
public state = {
|
||||
isAddingCustomNode: false
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ interface Props {
|
|||
match: RouteComponentProps<{}>['match'];
|
||||
}
|
||||
|
||||
export default class SubTabs extends React.Component<Props> {
|
||||
export default class SubTabs extends React.PureComponent<Props> {
|
||||
public render() {
|
||||
const { tabs, match } = this.props;
|
||||
const currentPath = match.url;
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import React from 'react';
|
||||
import BN from 'bn.js';
|
||||
import { translateRaw } from 'translations';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
inputGasPrice,
|
||||
TInputGasPrice,
|
||||
inputGasPriceIntent,
|
||||
TInputGasPriceIntent,
|
||||
getNonceRequested,
|
||||
TGetNonceRequested,
|
||||
reset,
|
||||
|
@ -12,6 +15,7 @@ import {
|
|||
import { fetchCCRates, TFetchCCRates } from 'actions/rates';
|
||||
import { getNetworkConfig, getOffline } from 'selectors/config';
|
||||
import { AppState } from 'reducers';
|
||||
import { Units } from 'libs/units';
|
||||
import SimpleGas from './components/SimpleGas';
|
||||
import AdvancedGas, { AdvancedOptions } from './components/AdvancedGas';
|
||||
import './TXMetaDataPanel.scss';
|
||||
|
@ -27,6 +31,7 @@ interface StateProps {
|
|||
|
||||
interface DispatchProps {
|
||||
inputGasPrice: TInputGasPrice;
|
||||
inputGasPriceIntent: TInputGasPriceIntent;
|
||||
fetchCCRates: TFetchCCRates;
|
||||
getNonceRequested: TGetNonceRequested;
|
||||
reset: TReset;
|
||||
|
@ -47,6 +52,7 @@ interface OwnProps {
|
|||
type Props = DispatchProps & OwnProps & StateProps;
|
||||
|
||||
interface State {
|
||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||
sliderState: SliderStates;
|
||||
}
|
||||
|
||||
|
@ -56,6 +62,7 @@ class TXMetaDataPanel extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
public state: State = {
|
||||
gasPrice: this.props.gasPrice,
|
||||
sliderState: (this.props as DefaultProps).initialState
|
||||
};
|
||||
|
||||
|
@ -71,10 +78,14 @@ class TXMetaDataPanel extends React.Component<Props, State> {
|
|||
if (this.props.offline && !nextProps.offline) {
|
||||
this.props.fetchCCRates([this.props.network.unit]);
|
||||
}
|
||||
if (this.props.gasPrice !== nextProps.gasPrice) {
|
||||
this.setState({ gasPrice: nextProps.gasPrice });
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { offline, disableToggle, gasPrice, advancedGasOptions, className = '' } = this.props;
|
||||
const { offline, disableToggle, advancedGasOptions, className = '' } = this.props;
|
||||
const { gasPrice } = this.state;
|
||||
const showAdvanced = this.state.sliderState === 'advanced' || offline;
|
||||
return (
|
||||
<div className={`Gas col-md-12 ${className}`}>
|
||||
|
@ -85,7 +96,7 @@ class TXMetaDataPanel extends React.Component<Props, State> {
|
|||
options={advancedGasOptions}
|
||||
/>
|
||||
) : (
|
||||
<SimpleGas gasPrice={gasPrice} inputGasPrice={this.props.inputGasPrice} />
|
||||
<SimpleGas gasPrice={gasPrice} inputGasPrice={this.handleGasPriceInput} />
|
||||
)}
|
||||
|
||||
{!offline &&
|
||||
|
@ -107,6 +118,15 @@ class TXMetaDataPanel extends React.Component<Props, State> {
|
|||
private toggleAdvanced = () => {
|
||||
this.setState({ sliderState: this.state.sliderState === 'advanced' ? 'simple' : 'advanced' });
|
||||
};
|
||||
|
||||
private handleGasPriceInput = (raw: string) => {
|
||||
const gasBn = new BN(raw);
|
||||
const value = gasBn.mul(new BN(Units.gwei));
|
||||
this.setState({
|
||||
gasPrice: { raw, value }
|
||||
});
|
||||
this.props.inputGasPriceIntent(raw);
|
||||
};
|
||||
}
|
||||
|
||||
function mapStateToProps(state: AppState): StateProps {
|
||||
|
@ -119,6 +139,7 @@ function mapStateToProps(state: AppState): StateProps {
|
|||
|
||||
export default connect(mapStateToProps, {
|
||||
inputGasPrice,
|
||||
inputGasPriceIntent,
|
||||
fetchCCRates,
|
||||
getNonceRequested,
|
||||
reset
|
||||
|
|
|
@ -108,6 +108,7 @@ class AdvancedGas extends React.Component<Props, State> {
|
|||
{feeSummary && (
|
||||
<div className="AdvancedGas-fee-summary">
|
||||
<FeeSummary
|
||||
gasPrice={gasPrice}
|
||||
render={({ gasPriceWei, gasLimit, fee, usd }) => (
|
||||
<span>
|
||||
{gasPriceWei} * {gasLimit} = {fee} {usd && <span>~= ${usd} USD</span>}
|
||||
|
|
|
@ -14,17 +14,20 @@ interface RenderData {
|
|||
usd: React.ReactElement<string> | null;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
// Redux props
|
||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||
interface ReduxStateProps {
|
||||
gasLimit: AppState['transaction']['fields']['gasLimit'];
|
||||
rates: AppState['rates']['rates'];
|
||||
network: AppState['config']['network'];
|
||||
isOffline: AppState['config']['offline'];
|
||||
// Component props
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||
render(data: RenderData): React.ReactElement<string> | string;
|
||||
}
|
||||
|
||||
type Props = OwnProps & ReduxStateProps;
|
||||
|
||||
class FeeSummary extends React.Component<Props> {
|
||||
public render() {
|
||||
const { gasPrice, gasLimit, rates, network, isOffline } = this.props;
|
||||
|
@ -67,9 +70,8 @@ class FeeSummary extends React.Component<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state: AppState) {
|
||||
function mapStateToProps(state: AppState): ReduxStateProps {
|
||||
return {
|
||||
gasPrice: state.transaction.fields.gasPrice,
|
||||
gasLimit: state.transaction.fields.gasLimit,
|
||||
rates: state.rates.rates,
|
||||
network: getNetworkConfig(state),
|
||||
|
|
|
@ -3,7 +3,6 @@ import Slider from 'rc-slider';
|
|||
import translate, { translateRaw } from 'translations';
|
||||
import { gasPriceDefaults } from 'config';
|
||||
import FeeSummary from './FeeSummary';
|
||||
import { TInputGasPrice } from 'actions/transaction';
|
||||
import './SimpleGas.scss';
|
||||
import { AppState } from 'reducers';
|
||||
import { getGasLimitEstimationTimedOut } from 'selectors/transaction';
|
||||
|
@ -13,7 +12,7 @@ import { getIsWeb3Node } from 'selectors/config';
|
|||
|
||||
interface OwnProps {
|
||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||
inputGasPrice: TInputGasPrice;
|
||||
inputGasPrice(rawGas: string);
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
|
@ -41,7 +40,7 @@ class SimpleGas extends React.Component<Props> {
|
|||
<div className="prompt-toggle-gas-limit">
|
||||
<p className="small">
|
||||
{isWeb3Node
|
||||
? "Couldn't calculate gas limit, if you know what your doing, try setting manually in Advanced settings"
|
||||
? "Couldn't calculate gas limit, if you know what you're doing, try setting manually in Advanced settings"
|
||||
: "Couldn't calculate gas limit, try switching nodes"}
|
||||
</p>
|
||||
</div>
|
||||
|
@ -62,6 +61,7 @@ class SimpleGas extends React.Component<Props> {
|
|||
</div>
|
||||
</div>
|
||||
<FeeSummary
|
||||
gasPrice={gasPrice}
|
||||
render={({ fee, usd }) => (
|
||||
<span>
|
||||
{fee} {usd && <span>/ ${usd}</span>}
|
||||
|
|
|
@ -60,7 +60,7 @@ const customDPath: DPath = {
|
|||
value: 'custom'
|
||||
};
|
||||
|
||||
class DeterministicWalletsModalClass extends React.Component<Props, State> {
|
||||
class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
selectedAddress: '',
|
||||
selectedAddrIndex: 0,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
|
||||
export class DigitalBitboxDecrypt extends React.Component<{}, {}> {
|
||||
export class DigitalBitboxDecrypt extends React.PureComponent<{}, {}> {
|
||||
public render() {
|
||||
return <strong>Not yet implemented</strong>;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { isKeystorePassRequired } from 'libs/wallet';
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import Spinner from 'components/ui/Spinner';
|
||||
import { TShowNotification } from 'actions/notifications';
|
||||
|
@ -25,7 +25,7 @@ function isValidFile(rawFile: File): boolean {
|
|||
return fileType === '' || fileType === 'application/json';
|
||||
}
|
||||
|
||||
export class KeystoreDecrypt extends Component {
|
||||
export class KeystoreDecrypt extends PureComponent {
|
||||
public props: {
|
||||
value: KeystoreValue;
|
||||
isWalletPending: boolean;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import './LedgerNano.scss';
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import DeterministicWalletsModal from './DeterministicWalletsModal';
|
||||
import { LedgerWallet } from 'libs/wallet';
|
||||
|
@ -31,7 +31,7 @@ interface State {
|
|||
|
||||
type Props = OwnProps & StateProps;
|
||||
|
||||
class LedgerNanoSDecryptClass extends Component<Props, State> {
|
||||
class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
publicKey: '',
|
||||
chainCode: '',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { mnemonicToSeed, validateMnemonic } from 'bip39';
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import DeterministicWalletsModal from './DeterministicWalletsModal';
|
||||
import { formatMnemonic } from 'utils/formatters';
|
||||
|
@ -27,7 +27,7 @@ interface State {
|
|||
dPath: string;
|
||||
}
|
||||
|
||||
class MnemonicDecryptClass extends Component<Props & StateProps, State> {
|
||||
class MnemonicDecryptClass extends PureComponent<Props & StateProps, State> {
|
||||
public state: State = {
|
||||
phrase: '',
|
||||
formattedPhrase: '',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { isValidEncryptedPrivKey, isValidPrivKey } from 'libs/validators';
|
||||
import { stripHexPrefix } from 'libs/values';
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import { TogglablePassword } from 'components';
|
||||
|
||||
|
@ -45,7 +45,7 @@ interface Props {
|
|||
onUnlock(): void;
|
||||
}
|
||||
|
||||
export class PrivateKeyDecrypt extends Component<Props> {
|
||||
export class PrivateKeyDecrypt extends PureComponent<Props> {
|
||||
public render() {
|
||||
const { key, password } = this.props.value;
|
||||
const { isValidPkey, isPassRequired } = validatePkeyAndPass(key, password);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { TrezorWallet, TREZOR_MINIMUM_FIRMWARE } from 'libs/wallet';
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import TrezorConnect from 'vendor/trezor-connect';
|
||||
import DeterministicWalletsModal from './DeterministicWalletsModal';
|
||||
|
@ -32,7 +32,7 @@ interface State {
|
|||
|
||||
type Props = OwnProps & StateProps;
|
||||
|
||||
class TrezorDecryptClass extends Component<Props, State> {
|
||||
class TrezorDecryptClass extends PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
publicKey: '',
|
||||
chainCode: '',
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import translate from 'translations';
|
||||
import { donationAddressMap } from 'config';
|
||||
import { isValidETHAddress } from 'libs/validators';
|
||||
|
@ -12,7 +12,7 @@ interface State {
|
|||
address: string;
|
||||
}
|
||||
|
||||
export class ViewOnlyDecrypt extends Component<Props, State> {
|
||||
export class ViewOnlyDecrypt extends PureComponent<Props, State> {
|
||||
public state = {
|
||||
address: ''
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import translate from 'translations';
|
||||
import { NewTabLink } from 'components/ui';
|
||||
import './Web3.scss';
|
||||
|
@ -7,27 +7,20 @@ interface Props {
|
|||
onUnlock(): void;
|
||||
}
|
||||
|
||||
export class Web3Decrypt extends Component<Props> {
|
||||
public render() {
|
||||
return (
|
||||
<div className="Web3Decrypt">
|
||||
<div>
|
||||
<button
|
||||
className="Web3Decrypt-decrypt btn btn-primary btn-lg btn-block"
|
||||
onClick={this.props.onUnlock}
|
||||
>
|
||||
{translate('ADD_MetaMask')}
|
||||
</button>
|
||||
</div>
|
||||
export const Web3Decrypt: React.SFC<Props> = ({ onUnlock }) => (
|
||||
<div className="Web3Decrypt">
|
||||
<div>
|
||||
<button className="Web3Decrypt-decrypt btn btn-primary btn-lg btn-block" onClick={onUnlock}>
|
||||
{translate('ADD_MetaMask')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<NewTabLink
|
||||
className="Web3Decrypt-install btn btn-sm btn-default btn-block"
|
||||
content={translate('Download MetaMask')}
|
||||
href="https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn?hl=en"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
<div>
|
||||
<NewTabLink
|
||||
className="Web3Decrypt-install btn btn-sm btn-default btn-block"
|
||||
content={translate('Download MetaMask')}
|
||||
href="https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn?hl=en"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import DropdownShell from './DropdownShell';
|
||||
import removeIcon from 'assets/images/icon-remove.svg';
|
||||
|
@ -25,7 +25,7 @@ interface Props<T> {
|
|||
onChange(value: T): void;
|
||||
}
|
||||
|
||||
export default class ColorDropdown<T> extends Component<Props<T>, {}> {
|
||||
export default class ColorDropdown<T> extends PureComponent<Props<T>, {}> {
|
||||
private dropdownShell: DropdownShell | null;
|
||||
|
||||
public render() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import DropdownShell from './DropdownShell';
|
||||
|
||||
|
@ -19,7 +19,7 @@ interface State {
|
|||
search: string;
|
||||
}
|
||||
|
||||
export default class DropdownComponent<T> extends Component<Props<T>, State> {
|
||||
export default class DropdownComponent<T> extends PureComponent<Props<T>, State> {
|
||||
public state = {
|
||||
search: ''
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
interface Props {
|
||||
|
@ -14,7 +14,7 @@ interface State {
|
|||
expanded: boolean;
|
||||
}
|
||||
|
||||
export default class DropdownComponent extends Component<Props, State> {
|
||||
export default class DropdownComponent extends PureComponent<Props, State> {
|
||||
public static defaultProps = {
|
||||
color: 'default',
|
||||
size: 'sm'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
export type ExpandHandler = (ev: React.FormEvent<HTMLAnchorElement>) => void;
|
||||
|
||||
|
@ -13,7 +13,7 @@ interface State {
|
|||
|
||||
const initialState: State = { expanded: false };
|
||||
|
||||
export class Expandable extends Component<Props, State> {
|
||||
export class Expandable extends PureComponent<Props, State> {
|
||||
public state: State = initialState;
|
||||
|
||||
public render() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import closeIcon from 'assets/images/icon-x.svg';
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import './Modal.scss';
|
||||
|
||||
export interface IButton {
|
||||
|
@ -17,7 +17,7 @@ interface Props {
|
|||
handleClose?(): void;
|
||||
}
|
||||
|
||||
export default class Modal extends Component<Props, {}> {
|
||||
export default class Modal extends PureComponent<Props, {}> {
|
||||
private modalContent: HTMLElement | null = null;
|
||||
|
||||
public componentDidMount() {
|
||||
|
|
|
@ -13,7 +13,7 @@ interface State {
|
|||
qr?: string;
|
||||
}
|
||||
|
||||
export default class QRCode extends React.Component<Props, State> {
|
||||
export default class QRCode extends React.PureComponent<Props, State> {
|
||||
public state: State = {};
|
||||
public componentWillMount() {
|
||||
// Start generating QR codes immediately
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import Dropdown from './Dropdown';
|
||||
|
||||
interface Props {
|
||||
|
@ -8,7 +8,7 @@ interface Props {
|
|||
onChange(value: string): void;
|
||||
}
|
||||
|
||||
export default class SimpleDropdown extends Component<Props, void> {
|
||||
export default class SimpleDropdown extends PureComponent<Props, void> {
|
||||
public render() {
|
||||
const { options, value, onChange, ariaLabel } = this.props;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
interface Props {
|
||||
value?: string;
|
||||
|
@ -6,7 +6,7 @@ interface Props {
|
|||
onChange(event: React.FormEvent<HTMLSpanElement>): void;
|
||||
}
|
||||
|
||||
export default class SimpleSelect extends Component<Props, {}> {
|
||||
export default class SimpleSelect extends PureComponent<Props, {}> {
|
||||
public render() {
|
||||
return (
|
||||
<select
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import './SwapDropdown.scss';
|
||||
import classnames from 'classnames';
|
||||
|
||||
|
@ -16,7 +16,7 @@ interface Props<T> {
|
|||
onChange(value: T): void;
|
||||
}
|
||||
|
||||
class SwapDropdown<T> extends Component<Props<T>, {}> {
|
||||
class SwapDropdown<T> extends PureComponent<Props<T>, {}> {
|
||||
public state = {
|
||||
open: false
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@ interface State {
|
|||
isKeystoreModalOpen: boolean;
|
||||
}
|
||||
|
||||
export default class WalletInfo extends React.Component<Props, State> {
|
||||
export default class WalletInfo extends React.PureComponent<Props, State> {
|
||||
public state = {
|
||||
address: '',
|
||||
privateKey: '',
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import QRCode from 'qrcode.react';
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
interface Props {
|
||||
paymentAddress: string | null;
|
||||
destinationAmount: number;
|
||||
}
|
||||
|
||||
export default class BitcoinQR extends Component<Props, {}> {
|
||||
export default class BitcoinQR extends PureComponent<Props, {}> {
|
||||
public render() {
|
||||
const { paymentAddress, destinationAmount } = this.props;
|
||||
return (
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
} from 'reducers/swap/types';
|
||||
import SimpleButton from 'components/ui/SimpleButton';
|
||||
import { generateKindMax, generateKindMin, WhitelistedCoins, bityConfig } from 'config/bity';
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import translate from 'translations';
|
||||
import { combineAndUpper } from 'utils/formatters';
|
||||
import { SwapDropdown } from 'components/ui';
|
||||
|
@ -41,7 +41,7 @@ interface State {
|
|||
|
||||
type Props = StateProps & ActionProps;
|
||||
|
||||
export default class CurrencySwap extends Component<Props, State> {
|
||||
export default class CurrencySwap extends PureComponent<Props, State> {
|
||||
public state = {
|
||||
disabled: true,
|
||||
origin: {
|
||||
|
|
|
@ -7,7 +7,7 @@ import bityLogoWhite from 'assets/images/logo-bity-white.svg';
|
|||
import shapeshiftLogoWhite from 'assets/images/logo-shapeshift.svg';
|
||||
import Spinner from 'components/ui/Spinner';
|
||||
import { bityReferralURL, shapeshiftReferralURL } from 'config';
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import translate from 'translations';
|
||||
import './CurrentRates.scss';
|
||||
import { SHAPESHIFT_WHITELIST } from 'api/shapeshift';
|
||||
|
@ -22,7 +22,7 @@ interface Props {
|
|||
shapeshiftRates: NormalizedShapeshiftRates;
|
||||
}
|
||||
|
||||
export default class CurrentRates extends Component<Props> {
|
||||
export default class CurrentRates extends PureComponent<Props> {
|
||||
private shapeShiftRateCache = null;
|
||||
|
||||
public getRandomSSPairData = (
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
TStopPollShapeshiftOrderStatus
|
||||
} from 'actions/swap';
|
||||
import { SwapInput } from 'reducers/swap/types';
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import BitcoinQR from './BitcoinQR';
|
||||
import PaymentInfo from './PaymentInfo';
|
||||
import SwapProgress from './SwapProgress';
|
||||
|
@ -39,7 +39,7 @@ interface ReduxActionProps {
|
|||
showNotification: TShowNotification;
|
||||
}
|
||||
|
||||
export default class PartThree extends Component<ReduxActionProps & ReduxStateProps, {}> {
|
||||
export default class PartThree extends PureComponent<ReduxActionProps & ReduxStateProps, {}> {
|
||||
public componentDidMount() {
|
||||
const { provider } = this.props;
|
||||
if (provider === 'shapeshift') {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import translate from 'translations';
|
||||
import { SwapInput } from 'reducers/swap/types';
|
||||
import './PaymentInfo.scss';
|
||||
|
@ -8,7 +8,7 @@ export interface Props {
|
|||
paymentAddress: string | null;
|
||||
}
|
||||
|
||||
export default class PaymentInfo extends Component<Props, {}> {
|
||||
export default class PaymentInfo extends PureComponent<Props, {}> {
|
||||
public render() {
|
||||
const { origin } = this.props;
|
||||
return (
|
||||
|
|
|
@ -10,7 +10,7 @@ import classnames from 'classnames';
|
|||
import SimpleButton from 'components/ui/SimpleButton';
|
||||
import { donationAddressMap } from 'config';
|
||||
import { isValidBTCAddress, isValidETHAddress } from 'libs/validators';
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import translate from 'translations';
|
||||
import { combineAndUpper } from 'utils/formatters';
|
||||
import './ReceivingAddress.scss';
|
||||
|
@ -32,7 +32,7 @@ export interface ActionProps {
|
|||
shapeshiftOrderCreateRequestedSwap: TShapeshiftOrderCreateRequestedSwap;
|
||||
}
|
||||
|
||||
export default class ReceivingAddress extends Component<StateProps & ActionProps, {}> {
|
||||
export default class ReceivingAddress extends PureComponent<StateProps & ActionProps, {}> {
|
||||
public onChangeDestinationAddress = (event: React.FormEvent<HTMLInputElement>) => {
|
||||
const value = event.currentTarget.value;
|
||||
this.props.destinationAddressSwap(value);
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import './ShapeshiftBanner.scss';
|
||||
import shapeshiftSvg from 'assets/images/logo-shapeshift.svg';
|
||||
|
||||
export default () => (
|
||||
const ShapeshiftBanner: React.SFC<{}> = () => (
|
||||
<div className="ShapeshiftBanner">
|
||||
<div className="ShapeshiftBanner-banner">
|
||||
<p>
|
||||
|
@ -13,3 +13,5 @@ export default () => (
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default ShapeshiftBanner;
|
||||
|
|
|
@ -14,7 +14,7 @@ interface Props {
|
|||
bityRates: NormalizedBityRates;
|
||||
}
|
||||
|
||||
class SupportFooter extends React.Component<Props, {}> {
|
||||
class SupportFooter extends React.PureComponent<Props, {}> {
|
||||
public state = {
|
||||
open: false
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { RestartSwapAction } from 'actions/swap';
|
||||
import { SwapInput } from 'reducers/swap/types';
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import translate from 'translations';
|
||||
import classnames from 'classnames';
|
||||
import { toFixedIfLarger } from 'utils/formatters';
|
||||
|
@ -16,7 +16,7 @@ export interface SwapInfoHeaderProps {
|
|||
restartSwap(): RestartSwapAction;
|
||||
}
|
||||
|
||||
export default class SwapInfoHeader extends Component<SwapInfoHeaderProps, {}> {
|
||||
export default class SwapInfoHeader extends PureComponent<SwapInfoHeaderProps, {}> {
|
||||
public computedOriginDestinationRatio = () => {
|
||||
const { origin, destination } = this.props;
|
||||
if (!origin.amount || !destination.amount) {
|
||||
|
|
|
@ -2,7 +2,7 @@ import { RestartSwapAction } from 'actions/swap';
|
|||
import bityLogo from 'assets/images/logo-bity.svg';
|
||||
import shapeshiftLogo from 'assets/images/shapeshift-dark.svg';
|
||||
import { bityReferralURL } from 'config';
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import translate from 'translations';
|
||||
import './SwapInfoHeader.scss';
|
||||
|
||||
|
@ -11,7 +11,7 @@ export interface SwapInfoHeaderTitleProps {
|
|||
restartSwap(): RestartSwapAction;
|
||||
}
|
||||
|
||||
export default class SwapInfoHeaderTitle extends Component<SwapInfoHeaderTitleProps, {}> {
|
||||
export default class SwapInfoHeaderTitle extends PureComponent<SwapInfoHeaderTitleProps, {}> {
|
||||
public render() {
|
||||
const { provider } = this.props;
|
||||
const logoToRender = provider === 'shapeshift' ? shapeshiftLogo : bityLogo;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { TShowNotification } from 'actions/notifications';
|
||||
import { bityConfig } from 'config/bity';
|
||||
import React, { Component } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import './SwapProgress.scss';
|
||||
|
||||
|
@ -19,7 +19,7 @@ export interface Props {
|
|||
interface State {
|
||||
hasShownViewTx: boolean;
|
||||
}
|
||||
export default class SwapProgress extends Component<Props, State> {
|
||||
export default class SwapProgress extends PureComponent<Props, State> {
|
||||
public state = {
|
||||
hasShownViewTx: false
|
||||
};
|
||||
|
|
|
@ -11,7 +11,7 @@ type Data = Buffer;
|
|||
|
||||
export const ETH_DECIMAL = 18;
|
||||
|
||||
const Units = {
|
||||
export const Units = {
|
||||
wei: '1',
|
||||
kwei: '1000',
|
||||
ada: '1000',
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
import { State, RequestStatus } from './typings';
|
||||
import { TypeKeys as TK, ResetAction, NetworkAction } from 'actions/transaction';
|
||||
import {
|
||||
TypeKeys as TK,
|
||||
ResetAction,
|
||||
NetworkAction,
|
||||
InputGasPriceAction,
|
||||
InputGasPriceIntentAction
|
||||
} from 'actions/transaction';
|
||||
import { Action } from 'redux';
|
||||
|
||||
const INITIAL_STATE: State = {
|
||||
gasEstimationStatus: null,
|
||||
getFromStatus: null,
|
||||
getNonceStatus: null
|
||||
getNonceStatus: null,
|
||||
gasPriceStatus: null
|
||||
};
|
||||
|
||||
const getPostFix = (str: string) => {
|
||||
|
@ -18,9 +25,17 @@ const nextState = (field: keyof State) => (state: State, action: Action): State
|
|||
[field]: RequestStatus[getPostFix(action.type)]
|
||||
});
|
||||
|
||||
const setGasPriceStatus = (state: State, gasPriceStatus: RequestStatus) => ({
|
||||
...state,
|
||||
gasPriceStatus
|
||||
});
|
||||
|
||||
const reset = () => INITIAL_STATE;
|
||||
|
||||
export const network = (state: State = INITIAL_STATE, action: NetworkAction | ResetAction) => {
|
||||
export const network = (
|
||||
state: State = INITIAL_STATE,
|
||||
action: NetworkAction | ResetAction | InputGasPriceAction | InputGasPriceIntentAction
|
||||
) => {
|
||||
switch (action.type) {
|
||||
case TK.ESTIMATE_GAS_REQUESTED:
|
||||
return nextState('gasEstimationStatus')(state, action);
|
||||
|
@ -42,6 +57,14 @@ export const network = (state: State = INITIAL_STATE, action: NetworkAction | Re
|
|||
return nextState('getNonceStatus')(state, action);
|
||||
case TK.GET_NONCE_FAILED:
|
||||
return nextState('getNonceStatus')(state, action);
|
||||
|
||||
// Not exactly "network" requests, but we want to show pending while
|
||||
// gas price is subject to change
|
||||
case TK.GAS_PRICE_INPUT_INTENT:
|
||||
return setGasPriceStatus(state, RequestStatus.REQUESTED);
|
||||
case TK.GAS_PRICE_INPUT:
|
||||
return setGasPriceStatus(state, RequestStatus.SUCCEEDED);
|
||||
|
||||
case TK.RESET:
|
||||
return reset();
|
||||
default:
|
||||
|
|
|
@ -8,4 +8,5 @@ export interface State {
|
|||
gasEstimationStatus: RequestStatus | null;
|
||||
getFromStatus: RequestStatus | null;
|
||||
getNonceStatus: RequestStatus | null;
|
||||
gasPriceStatus: RequestStatus | null;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import BN from 'bn.js';
|
||||
import { call, put, takeEvery } from 'redux-saga/effects';
|
||||
import { SagaIterator } from 'redux-saga';
|
||||
import { call, put, takeEvery, takeLatest } from 'redux-saga/effects';
|
||||
import { SagaIterator, delay } from 'redux-saga';
|
||||
import {
|
||||
inputGasPrice,
|
||||
setDataField,
|
||||
setGasLimitField,
|
||||
setGasPriceField,
|
||||
|
@ -11,6 +12,7 @@ import {
|
|||
InputDataAction,
|
||||
InputGasLimitAction,
|
||||
InputGasPriceAction,
|
||||
InputGasPriceIntentAction,
|
||||
InputNonceAction,
|
||||
TypeKeys
|
||||
} from 'actions/transaction';
|
||||
|
@ -38,6 +40,13 @@ export function* handleGasPriceInput({ payload }: InputGasPriceAction): SagaIter
|
|||
);
|
||||
}
|
||||
|
||||
export function* handleGasPriceInputIntent({ payload }: InputGasPriceIntentAction): SagaIterator {
|
||||
yield call(delay, 300);
|
||||
// Important to put and not fork handleGasPriceInput, we want
|
||||
// action to go to reducers.
|
||||
yield put(inputGasPrice(payload));
|
||||
}
|
||||
|
||||
export function* handleNonceInput({ payload }: InputNonceAction): SagaIterator {
|
||||
const validNonce: boolean = yield call(isValidNonce, payload);
|
||||
yield put(setNonceField({ raw: payload, value: validNonce ? Nonce(payload) : null }));
|
||||
|
@ -47,5 +56,6 @@ export const fields = [
|
|||
takeEvery(TypeKeys.DATA_FIELD_INPUT, handleDataInput),
|
||||
takeEvery(TypeKeys.GAS_LIMIT_INPUT, handleGasLimitInput),
|
||||
takeEvery(TypeKeys.GAS_PRICE_INPUT, handleGasPriceInput),
|
||||
takeEvery(TypeKeys.NONCE_INPUT, handleNonceInput)
|
||||
takeEvery(TypeKeys.NONCE_INPUT, handleNonceInput),
|
||||
takeLatest(TypeKeys.GAS_PRICE_INPUT_INTENT, handleGasPriceInputIntent)
|
||||
];
|
||||
|
|
|
@ -10,10 +10,12 @@ const getGasLimit = (state: AppState) => getFields(state).gasLimit;
|
|||
const getGasPrice = (state: AppState) => getFields(state).gasPrice;
|
||||
const getValue = (state: AppState) => getFields(state).value;
|
||||
const getNonce = (state: AppState) => getFields(state).nonce;
|
||||
|
||||
const getDataExists = (state: AppState) => {
|
||||
const { value } = getData(state);
|
||||
return !!value && value.length > 0;
|
||||
};
|
||||
|
||||
const getValidGasCost = (state: AppState) => {
|
||||
const gasCost = getGasCost(state);
|
||||
const etherBalance = getEtherBalance(state);
|
||||
|
|
Loading…
Reference in New Issue