Improved Gas UX (Pt. 1 - Gas Slider on Send) (#728)
* Initial crack at simple only gas slider component. * Work on advanced component. Refactor redux and components to specify gas limit vs price. * Convert fee summary to a render cbesque thing. * Rework responsive columns. * Remove force offline button. * Tweak styles. * Fix tscheck issues, remove unneeded prop. * Fix references to GasField * Gas slider in lite send. * Make gas slider network-aware for symbol and price calculation.
This commit is contained in:
parent
98afc22537
commit
edda9f71ea
|
@ -5,6 +5,7 @@ import {
|
||||||
SetNonceFieldAction,
|
SetNonceFieldAction,
|
||||||
SetValueFieldAction,
|
SetValueFieldAction,
|
||||||
InputGasLimitAction,
|
InputGasLimitAction,
|
||||||
|
InputGasPriceAction,
|
||||||
InputDataAction,
|
InputDataAction,
|
||||||
InputNonceAction,
|
InputNonceAction,
|
||||||
ResetAction,
|
ResetAction,
|
||||||
|
@ -18,6 +19,12 @@ const inputGasLimit = (payload: InputGasLimitAction['payload']) => ({
|
||||||
payload
|
payload
|
||||||
});
|
});
|
||||||
|
|
||||||
|
type TInputGasPrice = typeof inputGasPrice;
|
||||||
|
const inputGasPrice = (payload: InputGasPriceAction['payload']) => ({
|
||||||
|
type: TypeKeys.GAS_PRICE_INPUT,
|
||||||
|
payload
|
||||||
|
});
|
||||||
|
|
||||||
type TInputNonce = typeof inputNonce;
|
type TInputNonce = typeof inputNonce;
|
||||||
const inputNonce = (payload: InputNonceAction['payload']) => ({
|
const inputNonce = (payload: InputNonceAction['payload']) => ({
|
||||||
type: TypeKeys.NONCE_INPUT,
|
type: TypeKeys.NONCE_INPUT,
|
||||||
|
@ -71,6 +78,7 @@ const reset = (): ResetAction => ({ type: TypeKeys.RESET });
|
||||||
|
|
||||||
export {
|
export {
|
||||||
TInputGasLimit,
|
TInputGasLimit,
|
||||||
|
TInputGasPrice,
|
||||||
TInputNonce,
|
TInputNonce,
|
||||||
TInputData,
|
TInputData,
|
||||||
TSetGasLimitField,
|
TSetGasLimitField,
|
||||||
|
@ -81,6 +89,7 @@ export {
|
||||||
TSetGasPriceField,
|
TSetGasPriceField,
|
||||||
TReset,
|
TReset,
|
||||||
inputGasLimit,
|
inputGasLimit,
|
||||||
|
inputGasPrice,
|
||||||
inputNonce,
|
inputNonce,
|
||||||
inputData,
|
inputData,
|
||||||
setGasLimitField,
|
setGasLimitField,
|
||||||
|
|
|
@ -6,6 +6,10 @@ interface InputGasLimitAction {
|
||||||
type: TypeKeys.GAS_LIMIT_INPUT;
|
type: TypeKeys.GAS_LIMIT_INPUT;
|
||||||
payload: string;
|
payload: string;
|
||||||
}
|
}
|
||||||
|
interface InputGasPriceAction {
|
||||||
|
type: TypeKeys.GAS_PRICE_INPUT;
|
||||||
|
payload: string;
|
||||||
|
}
|
||||||
interface InputDataAction {
|
interface InputDataAction {
|
||||||
type: TypeKeys.DATA_FIELD_INPUT;
|
type: TypeKeys.DATA_FIELD_INPUT;
|
||||||
payload: string;
|
payload: string;
|
||||||
|
@ -79,6 +83,7 @@ type FieldAction =
|
||||||
|
|
||||||
export {
|
export {
|
||||||
InputGasLimitAction,
|
InputGasLimitAction,
|
||||||
|
InputGasPriceAction,
|
||||||
InputDataAction,
|
InputDataAction,
|
||||||
InputNonceAction,
|
InputNonceAction,
|
||||||
SetGasLimitFieldAction,
|
SetGasLimitFieldAction,
|
||||||
|
|
|
@ -28,6 +28,7 @@ export enum TypeKeys {
|
||||||
|
|
||||||
DATA_FIELD_INPUT = 'DATA_FIELD_INPUT',
|
DATA_FIELD_INPUT = 'DATA_FIELD_INPUT',
|
||||||
GAS_LIMIT_INPUT = 'GAS_LIMIT_INPUT',
|
GAS_LIMIT_INPUT = 'GAS_LIMIT_INPUT',
|
||||||
|
GAS_PRICE_INPUT = 'GAS_PRICE_INPUT',
|
||||||
NONCE_INPUT = 'NONCE_INPUT',
|
NONCE_INPUT = 'NONCE_INPUT',
|
||||||
|
|
||||||
DATA_FIELD_SET = 'DATA_FIELD_SET',
|
DATA_FIELD_SET = 'DATA_FIELD_SET',
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { forceOfflineConfig as dForceOfflineConfig, TForceOfflineConfig } from 'actions/config';
|
|
||||||
import OfflineSymbol from 'components/ui/OfflineSymbol';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { AppState } from 'reducers';
|
|
||||||
|
|
||||||
type sizeType = 'small' | 'medium' | 'large';
|
|
||||||
|
|
||||||
interface OfflineToggleProps {
|
|
||||||
offline: boolean;
|
|
||||||
forceOffline: boolean;
|
|
||||||
forceOfflineConfig: TForceOfflineConfig;
|
|
||||||
size?: sizeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
class OfflineToggle extends React.Component<OfflineToggleProps, {}> {
|
|
||||||
public render() {
|
|
||||||
const { forceOfflineConfig, offline, forceOffline, size } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{!offline ? (
|
|
||||||
<div className="row text-center">
|
|
||||||
<div className="col-xs-3">
|
|
||||||
<OfflineSymbol offline={offline || forceOffline} size={size} />
|
|
||||||
</div>
|
|
||||||
<div className="col-xs-6">
|
|
||||||
<button className="btn-xs btn-info" onClick={forceOfflineConfig}>
|
|
||||||
{forceOffline ? 'Go Online' : 'Go Offline'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="text-center">
|
|
||||||
<h5>You are currently offline.</h5>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapStateToProps(state: AppState) {
|
|
||||||
return {
|
|
||||||
offline: state.config.offline,
|
|
||||||
forceOffline: state.config.forceOffline
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, {
|
|
||||||
forceOfflineConfig: dForceOfflineConfig
|
|
||||||
})(OfflineToggle);
|
|
|
@ -10,7 +10,6 @@ import AccountInfo from './AccountInfo';
|
||||||
import EquivalentValues from './EquivalentValues';
|
import EquivalentValues from './EquivalentValues';
|
||||||
import Promos from './Promos';
|
import Promos from './Promos';
|
||||||
import TokenBalances from './TokenBalances';
|
import TokenBalances from './TokenBalances';
|
||||||
import OfflineToggle from './OfflineToggle';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
wallet: IWallet;
|
wallet: IWallet;
|
||||||
|
@ -37,10 +36,6 @@ export class BalanceSidebar extends React.Component<Props, {}> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const blocks: Block[] = [
|
const blocks: Block[] = [
|
||||||
{
|
|
||||||
name: 'Go Offline',
|
|
||||||
content: <OfflineToggle />
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'Account Info',
|
name: 'Account Info',
|
||||||
content: <AccountInfo wallet={wallet} balance={balance} network={network} />
|
content: <AccountInfo wallet={wallet} balance={balance} network={network} />
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
export * from './GasFieldFactory';
|
|
|
@ -1,12 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { GasFieldFactory } from './GasFieldFactory';
|
import { GasLimitFieldFactory } from './GasLimitFieldFactory';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import { Aux } from 'components/ui';
|
import { Aux } from 'components/ui';
|
||||||
|
|
||||||
export const GasField: React.SFC<{}> = () => (
|
export const GasLimitField: React.SFC<{}> = () => (
|
||||||
<Aux>
|
<Aux>
|
||||||
<label>{translate('TRANS_gas')} </label>
|
<label>{translate('TRANS_gas')} </label>
|
||||||
<GasFieldFactory
|
<GasLimitFieldFactory
|
||||||
withProps={({ gasLimit: { raw, value }, onChange, readOnly }) => (
|
withProps={({ gasLimit: { raw, value }, onChange, readOnly }) => (
|
||||||
<input
|
<input
|
||||||
className={`form-control ${!!value ? 'is-valid' : 'is-invalid'}`}
|
className={`form-control ${!!value ? 'is-valid' : 'is-invalid'}`}
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { GasQuery } from 'components/renderCbs';
|
import { GasQuery } from 'components/renderCbs';
|
||||||
import { GasInput } from './GasInputFactory';
|
import { GasLimitInput } from './GasLimitInputFactory';
|
||||||
import { inputGasLimit, TInputGasLimit } from 'actions/transaction';
|
import { inputGasLimit, TInputGasLimit } from 'actions/transaction';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
|
@ -34,7 +34,7 @@ class GasLimitFieldClass extends Component<Props, {}> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
return <GasInput onChange={this.setGas} withProps={this.props.withProps} />;
|
return <GasLimitInput onChange={this.setGas} withProps={this.props.withProps} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
private setGas = (ev: React.FormEvent<HTMLInputElement>) => {
|
private setGas = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||||
|
@ -45,13 +45,13 @@ class GasLimitFieldClass extends Component<Props, {}> {
|
||||||
|
|
||||||
const GasLimitField = connect(null, { inputGasLimit })(GasLimitFieldClass);
|
const GasLimitField = connect(null, { inputGasLimit })(GasLimitFieldClass);
|
||||||
|
|
||||||
interface DefaultGasFieldProps {
|
interface DefaultGasLimitFieldProps {
|
||||||
withProps(props: CallBackProps): React.ReactElement<any> | null;
|
withProps(props: CallBackProps): React.ReactElement<any> | null;
|
||||||
}
|
}
|
||||||
const DefaultGasField: React.SFC<DefaultGasFieldProps> = ({ withProps }) => (
|
const DefaultGasLimitField: React.SFC<DefaultGasLimitFieldProps> = ({ withProps }) => (
|
||||||
<GasQuery
|
<GasQuery
|
||||||
withQuery={({ gasLimit }) => <GasLimitField gasLimit={gasLimit} withProps={withProps} />}
|
withQuery={({ gasLimit }) => <GasLimitField gasLimit={gasLimit} withProps={withProps} />}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
export { DefaultGasField as GasFieldFactory };
|
export { DefaultGasLimitField as GasLimitFieldFactory };
|
|
@ -3,7 +3,7 @@ import { Query } from 'components/renderCbs';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import { getGasLimit } from 'selectors/transaction';
|
import { getGasLimit } from 'selectors/transaction';
|
||||||
import { CallBackProps } from 'components/GasFieldFactory';
|
import { CallBackProps } from 'components/GasLimitFieldFactory';
|
||||||
|
|
||||||
interface StateProps {
|
interface StateProps {
|
||||||
gasLimit: AppState['transaction']['fields']['gasLimit'];
|
gasLimit: AppState['transaction']['fields']['gasLimit'];
|
||||||
|
@ -15,7 +15,7 @@ interface OwnProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = StateProps & OwnProps;
|
type Props = StateProps & OwnProps;
|
||||||
class GasInputClass extends Component<Props> {
|
class GasLimitInputClass extends Component<Props> {
|
||||||
public render() {
|
public render() {
|
||||||
const { gasLimit, onChange } = this.props;
|
const { gasLimit, onChange } = this.props;
|
||||||
return (
|
return (
|
||||||
|
@ -29,6 +29,6 @@ class GasInputClass extends Component<Props> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GasInput = connect((state: AppState) => ({ gasLimit: getGasLimit(state) }))(
|
export const GasLimitInput = connect((state: AppState) => ({ gasLimit: getGasLimit(state) }))(
|
||||||
GasInputClass
|
GasLimitInputClass
|
||||||
);
|
);
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './GasLimitFieldFactory';
|
|
@ -0,0 +1,10 @@
|
||||||
|
@import 'common/sass/variables';
|
||||||
|
|
||||||
|
.GasSlider {
|
||||||
|
&-toggle {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
margin-top: $space-sm;
|
||||||
|
left: -8px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { translateRaw } from 'translations';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
inputGasPrice,
|
||||||
|
TInputGasPrice,
|
||||||
|
inputGasLimit,
|
||||||
|
TInputGasLimit,
|
||||||
|
inputNonce,
|
||||||
|
TInputNonce
|
||||||
|
} from 'actions/transaction';
|
||||||
|
import { fetchCCRates, TFetchCCRates } from 'actions/rates';
|
||||||
|
import { getNetworkConfig } from 'selectors/config';
|
||||||
|
import { AppState } from 'reducers';
|
||||||
|
import SimpleGas from './components/SimpleGas';
|
||||||
|
import AdvancedGas from './components/AdvancedGas';
|
||||||
|
import './GasSlider.scss';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
// Component configuration
|
||||||
|
disableAdvanced?: boolean;
|
||||||
|
// Data
|
||||||
|
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||||
|
gasLimit: AppState['transaction']['fields']['gasLimit'];
|
||||||
|
offline: AppState['config']['offline'];
|
||||||
|
network: AppState['config']['network'];
|
||||||
|
// Actions
|
||||||
|
inputGasPrice: TInputGasPrice;
|
||||||
|
inputGasLimit: TInputGasLimit;
|
||||||
|
inputNonce: TInputNonce;
|
||||||
|
fetchCCRates: TFetchCCRates;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
showAdvanced: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GasSlider extends React.Component<Props, State> {
|
||||||
|
public state: State = {
|
||||||
|
showAdvanced: false
|
||||||
|
};
|
||||||
|
|
||||||
|
public componentDidMount() {
|
||||||
|
this.props.fetchCCRates([this.props.network.unit]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { gasPrice, gasLimit, offline, disableAdvanced } = this.props;
|
||||||
|
const showAdvanced = (this.state.showAdvanced || offline) && !disableAdvanced;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="GasSlider">
|
||||||
|
{showAdvanced ? (
|
||||||
|
<AdvancedGas
|
||||||
|
gasPrice={gasPrice.raw}
|
||||||
|
gasLimit={gasLimit.raw}
|
||||||
|
changeGasPrice={this.props.inputGasPrice}
|
||||||
|
changeGasLimit={this.props.inputGasLimit}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<SimpleGas gasPrice={gasPrice.raw} changeGasPrice={this.props.inputGasPrice} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!offline &&
|
||||||
|
!disableAdvanced && (
|
||||||
|
<div className="help-block">
|
||||||
|
<a className="GasSlider-toggle" onClick={this.toggleAdvanced}>
|
||||||
|
<strong>
|
||||||
|
{showAdvanced
|
||||||
|
? `- ${translateRaw('Back to simple')}`
|
||||||
|
: `+ ${translateRaw('Advanced: Data, Gas Price, Gas Limit')}`}
|
||||||
|
</strong>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private toggleAdvanced = () => {
|
||||||
|
this.setState({ showAdvanced: !this.state.showAdvanced });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: AppState) {
|
||||||
|
return {
|
||||||
|
gasPrice: state.transaction.fields.gasPrice,
|
||||||
|
gasLimit: state.transaction.fields.gasLimit,
|
||||||
|
offline: state.config.offline,
|
||||||
|
network: getNetworkConfig(state)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, {
|
||||||
|
inputGasPrice,
|
||||||
|
inputGasLimit,
|
||||||
|
inputNonce,
|
||||||
|
fetchCCRates
|
||||||
|
})(GasSlider);
|
|
@ -0,0 +1,4 @@
|
||||||
|
.AdvancedGas {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
import React from 'react';
|
||||||
|
import translate from 'translations';
|
||||||
|
import { DataFieldFactory } from 'components/DataFieldFactory';
|
||||||
|
import FeeSummary from './FeeSummary';
|
||||||
|
import './AdvancedGas.scss';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
gasPrice: string;
|
||||||
|
gasLimit: string;
|
||||||
|
changeGasPrice(gwei: string): void;
|
||||||
|
changeGasLimit(wei: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class AdvancedGas extends React.Component<Props> {
|
||||||
|
public render() {
|
||||||
|
return (
|
||||||
|
<div className="AdvancedGas row form-group">
|
||||||
|
<div className="col-md-3 col-sm-6 col-xs-12">
|
||||||
|
<label>{translate('OFFLINE_Step2_Label_3')} (gwei)</label>
|
||||||
|
<input
|
||||||
|
className="form-control"
|
||||||
|
value={this.props.gasPrice}
|
||||||
|
onChange={this.handleGasPriceChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-md-3 col-sm-6 col-xs-12">
|
||||||
|
<label>{translate('OFFLINE_Step2_Label_4')}</label>
|
||||||
|
<input
|
||||||
|
className="form-control"
|
||||||
|
value={this.props.gasLimit}
|
||||||
|
onChange={this.handleGasLimitChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-md-6 col-sm-12">
|
||||||
|
<label>{translate('OFFLINE_Step2_Label_6')}</label>
|
||||||
|
<DataFieldFactory
|
||||||
|
withProps={({ data, onChange }) => (
|
||||||
|
<input
|
||||||
|
className="form-control"
|
||||||
|
value={data.raw}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="0x7cB57B5A..."
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-sm-12">
|
||||||
|
<FeeSummary
|
||||||
|
render={({ gasPriceWei, gasLimit, fee, usd }) => (
|
||||||
|
<span>
|
||||||
|
{gasPriceWei} * {gasLimit} = {fee} {usd && <span>~= ${usd} USD</span>}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleGasPriceChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||||
|
this.props.changeGasPrice(ev.currentTarget.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleGasLimitChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||||
|
this.props.changeGasLimit(ev.currentTarget.value);
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
@import 'common/sass/variables';
|
||||||
|
|
||||||
|
.FeeSummary {
|
||||||
|
background: $gray-lighter;
|
||||||
|
height: 42px;
|
||||||
|
line-height: 42px;
|
||||||
|
padding: 0 12px;
|
||||||
|
font-family: $font-family-monospace;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
import React from 'react';
|
||||||
|
import BN from 'bn.js';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { AppState } from 'reducers';
|
||||||
|
import { getNetworkConfig } from 'selectors/config';
|
||||||
|
import { UnitDisplay } from 'components/ui';
|
||||||
|
import './FeeSummary.scss';
|
||||||
|
|
||||||
|
interface RenderData {
|
||||||
|
gasPriceWei: string;
|
||||||
|
gasPriceGwei: string;
|
||||||
|
gasLimit: string;
|
||||||
|
fee: React.ReactElement<string>;
|
||||||
|
usd: React.ReactElement<string> | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
// Redux props
|
||||||
|
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||||
|
gasLimit: AppState['transaction']['fields']['gasLimit'];
|
||||||
|
rates: AppState['rates']['rates'];
|
||||||
|
network: AppState['config']['network'];
|
||||||
|
// Component props
|
||||||
|
render(data: RenderData): React.ReactElement<string> | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class FeeSummary extends React.Component<Props> {
|
||||||
|
public render() {
|
||||||
|
const { gasPrice, gasLimit, rates, network } = this.props;
|
||||||
|
|
||||||
|
const feeBig = gasPrice.value && gasLimit.value && gasPrice.value.mul(gasLimit.value);
|
||||||
|
const fee = (
|
||||||
|
<UnitDisplay
|
||||||
|
value={feeBig}
|
||||||
|
unit="ether"
|
||||||
|
symbol={network.unit}
|
||||||
|
displayShortBalance={6}
|
||||||
|
checkOffline={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const usdBig = network.isTestnet
|
||||||
|
? new BN(0)
|
||||||
|
: feeBig && rates[network.unit] && feeBig.muln(rates[network.unit].USD);
|
||||||
|
const usd = (
|
||||||
|
<UnitDisplay
|
||||||
|
value={usdBig}
|
||||||
|
unit="ether"
|
||||||
|
displayShortBalance={2}
|
||||||
|
displayTrailingZeroes={true}
|
||||||
|
checkOffline={true}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="FeeSummary">
|
||||||
|
{this.props.render({
|
||||||
|
gasPriceWei: gasPrice.value.toString(),
|
||||||
|
gasPriceGwei: gasPrice.raw,
|
||||||
|
fee,
|
||||||
|
usd,
|
||||||
|
gasLimit: gasLimit.raw
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: AppState) {
|
||||||
|
return {
|
||||||
|
gasPrice: state.transaction.fields.gasPrice,
|
||||||
|
gasLimit: state.transaction.fields.gasLimit,
|
||||||
|
rates: state.rates.rates,
|
||||||
|
network: getNetworkConfig(state)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(FeeSummary);
|
|
@ -0,0 +1,36 @@
|
||||||
|
@import 'common/sass/variables';
|
||||||
|
|
||||||
|
.SimpleGas {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-slider {
|
||||||
|
padding-top: 8px;
|
||||||
|
margin-bottom: $space-xs;
|
||||||
|
|
||||||
|
&-labels {
|
||||||
|
margin-top: 4px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0 $space-xs;
|
||||||
|
text-align: center;
|
||||||
|
color: $gray-light;
|
||||||
|
font-size: $font-size-xs;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Slider from 'rc-slider';
|
||||||
|
import translate from 'translations';
|
||||||
|
import { gasPriceDefaults } from 'config/data';
|
||||||
|
import FeeSummary from './FeeSummary';
|
||||||
|
import './SimpleGas.scss';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
gasPrice: string;
|
||||||
|
changeGasPrice(gwei: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SimpleGas extends React.Component<Props> {
|
||||||
|
public render() {
|
||||||
|
const { gasPrice } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="SimpleGas row form-group">
|
||||||
|
<div className="col-md-12">
|
||||||
|
<label className="SimpleGas-label">{translate('Transaction Fee')}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-md-8 col-sm-12">
|
||||||
|
<div className="SimpleGas-slider">
|
||||||
|
<Slider
|
||||||
|
onChange={this.handleSlider}
|
||||||
|
min={gasPriceDefaults.gasPriceMinGwei}
|
||||||
|
max={gasPriceDefaults.gasPriceMaxGwei}
|
||||||
|
value={gasPrice}
|
||||||
|
/>
|
||||||
|
<div className="SimpleGas-slider-labels">
|
||||||
|
<span>{translate('Cheap')}</span>
|
||||||
|
<span>{translate('Balanced')}</span>
|
||||||
|
<span>{translate('Fast')}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-4 col-sm-12">
|
||||||
|
<FeeSummary
|
||||||
|
render={({ fee, usd }) => (
|
||||||
|
<span>
|
||||||
|
{fee} {usd && <span>/ ${usd}</span>}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleSlider = (gasGwei: number) => {
|
||||||
|
this.props.changeGasPrice(gasGwei.toString());
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
import GasSlider from './GasSlider';
|
||||||
|
export default GasSlider;
|
|
@ -27,8 +27,8 @@ class NonceInputClass extends Component<Props> {
|
||||||
const { nonce: { raw, value }, onChange, shouldDisplay } = this.props;
|
const { nonce: { raw, value }, onChange, shouldDisplay } = this.props;
|
||||||
const content = (
|
const content = (
|
||||||
<Aux>
|
<Aux>
|
||||||
{nonceHelp}
|
|
||||||
<label>Nonce</label>
|
<label>Nonce</label>
|
||||||
|
{nonceHelp}
|
||||||
|
|
||||||
<Query
|
<Query
|
||||||
params={['readOnly']}
|
params={['readOnly']}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export * from './AddressField';
|
export * from './AddressField';
|
||||||
export * from './DataField';
|
export * from './DataField';
|
||||||
export * from './GasField';
|
export * from './GasLimitField';
|
||||||
export * from './NonceField';
|
export * from './NonceField';
|
||||||
export * from './AmountField';
|
export * from './AmountField';
|
||||||
export * from './SendEverything';
|
export * from './SendEverything';
|
||||||
|
@ -15,4 +15,5 @@ export { default as Footer } from './Footer';
|
||||||
export { default as BalanceSidebar } from './BalanceSidebar';
|
export { default as BalanceSidebar } from './BalanceSidebar';
|
||||||
export { default as PaperWallet } from './PaperWallet';
|
export { default as PaperWallet } from './PaperWallet';
|
||||||
export { default as AlphaAgreement } from './AlphaAgreement';
|
export { default as AlphaAgreement } from './AlphaAgreement';
|
||||||
|
export { default as GasSlider } from './GasSlider';
|
||||||
export { default as WalletDecrypt } from './WalletDecrypt';
|
export { default as WalletDecrypt } from './WalletDecrypt';
|
||||||
|
|
|
@ -25,7 +25,8 @@ interface Props {
|
||||||
* @memberof Props
|
* @memberof Props
|
||||||
*/
|
*/
|
||||||
displayShortBalance?: boolean | number;
|
displayShortBalance?: boolean | number;
|
||||||
checkOffline: boolean;
|
displayTrailingZeroes?: boolean;
|
||||||
|
checkOffline?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EthProps extends Props {
|
interface EthProps extends Props {
|
||||||
|
@ -39,7 +40,7 @@ const isEthereumUnit = (param: EthProps | TokenProps): param is EthProps =>
|
||||||
!!(param as EthProps).unit;
|
!!(param as EthProps).unit;
|
||||||
|
|
||||||
const UnitDisplay: React.SFC<EthProps | TokenProps> = params => {
|
const UnitDisplay: React.SFC<EthProps | TokenProps> = params => {
|
||||||
const { value, symbol, displayShortBalance, checkOffline } = params;
|
const { value, symbol, displayShortBalance, displayTrailingZeroes, checkOffline } = params;
|
||||||
let element;
|
let element;
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
|
@ -58,6 +59,9 @@ const UnitDisplay: React.SFC<EthProps | TokenProps> = params => {
|
||||||
if (parseFloat(formattedValue) === 0 && parseFloat(convertedValue) !== 0) {
|
if (parseFloat(formattedValue) === 0 && parseFloat(convertedValue) !== 0) {
|
||||||
const padding = digits !== 0 ? `.${'0'.repeat(digits - 1)}1` : '';
|
const padding = digits !== 0 ? `.${'0'.repeat(digits - 1)}1` : '';
|
||||||
formattedValue = `< 0${padding}`;
|
formattedValue = `< 0${padding}`;
|
||||||
|
} else if (displayTrailingZeroes) {
|
||||||
|
const [whole, deci] = formattedValue.split('.');
|
||||||
|
formattedValue = `${whole}.${(deci || '').padEnd(digits, '0')}`;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
formattedValue = convertedValue;
|
formattedValue = convertedValue;
|
||||||
|
|
|
@ -83,6 +83,7 @@ export interface NetworkConfig {
|
||||||
chainId: number;
|
chainId: number;
|
||||||
tokens: Token[];
|
tokens: Token[];
|
||||||
contracts: NetworkContract[] | null;
|
contracts: NetworkContract[] | null;
|
||||||
|
isTestnet?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CustomNetworkConfig {
|
export interface CustomNetworkConfig {
|
||||||
|
@ -150,7 +151,8 @@ export const NETWORKS: { [key: string]: NetworkConfig } = {
|
||||||
color: '#adc101',
|
color: '#adc101',
|
||||||
blockExplorer: makeExplorer('https://ropsten.etherscan.io'),
|
blockExplorer: makeExplorer('https://ropsten.etherscan.io'),
|
||||||
tokens: require('./tokens/ropsten.json'),
|
tokens: require('./tokens/ropsten.json'),
|
||||||
contracts: require('./contracts/ropsten.json')
|
contracts: require('./contracts/ropsten.json'),
|
||||||
|
isTestnet: true
|
||||||
},
|
},
|
||||||
Kovan: {
|
Kovan: {
|
||||||
name: 'Kovan',
|
name: 'Kovan',
|
||||||
|
@ -159,7 +161,8 @@ export const NETWORKS: { [key: string]: NetworkConfig } = {
|
||||||
color: '#adc101',
|
color: '#adc101',
|
||||||
blockExplorer: makeExplorer('https://kovan.etherscan.io'),
|
blockExplorer: makeExplorer('https://kovan.etherscan.io'),
|
||||||
tokens: require('./tokens/ropsten.json'),
|
tokens: require('./tokens/ropsten.json'),
|
||||||
contracts: require('./contracts/ropsten.json')
|
contracts: require('./contracts/ropsten.json'),
|
||||||
|
isTestnet: true
|
||||||
},
|
},
|
||||||
Rinkeby: {
|
Rinkeby: {
|
||||||
name: 'Rinkeby',
|
name: 'Rinkeby',
|
||||||
|
@ -168,7 +171,8 @@ export const NETWORKS: { [key: string]: NetworkConfig } = {
|
||||||
color: '#adc101',
|
color: '#adc101',
|
||||||
blockExplorer: makeExplorer('https://rinkeby.etherscan.io'),
|
blockExplorer: makeExplorer('https://rinkeby.etherscan.io'),
|
||||||
tokens: require('./tokens/rinkeby.json'),
|
tokens: require('./tokens/rinkeby.json'),
|
||||||
contracts: require('./contracts/rinkeby.json')
|
contracts: require('./contracts/rinkeby.json'),
|
||||||
|
isTestnet: true
|
||||||
},
|
},
|
||||||
RSK: {
|
RSK: {
|
||||||
name: 'RSK',
|
name: 'RSK',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { DataFieldFactory } from 'components/DataFieldFactory';
|
import { DataFieldFactory } from 'components/DataFieldFactory';
|
||||||
import { GasFieldFactory } from 'components/GasFieldFactory';
|
import { GasLimitFieldFactory } from 'components/GasLimitFieldFactory';
|
||||||
import { SendButtonFactory } from 'components/SendButtonFactory';
|
import { SendButtonFactory } from 'components/SendButtonFactory';
|
||||||
import { SigningStatus } from 'components/SigningStatus';
|
import { SigningStatus } from 'components/SigningStatus';
|
||||||
import { NonceField } from 'components/NonceField';
|
import { NonceField } from 'components/NonceField';
|
||||||
|
@ -48,7 +48,7 @@ class DeployClass extends Component<DispatchProps> {
|
||||||
|
|
||||||
<label className="Deploy-field form-group">
|
<label className="Deploy-field form-group">
|
||||||
<h4 className="Deploy-field-label">Gas Limit</h4>
|
<h4 className="Deploy-field-label">Gas Limit</h4>
|
||||||
<GasFieldFactory
|
<GasLimitFieldFactory
|
||||||
withProps={({ gasLimit: { raw, value }, onChange, readOnly }) => (
|
withProps={({ gasLimit: { raw, value }, onChange, readOnly }) => (
|
||||||
<input
|
<input
|
||||||
name="gasLimit"
|
name="gasLimit"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { GasField } from './GasField';
|
import { GasLimitField } from './GasLimitField';
|
||||||
import { AmountField } from './AmountField';
|
import { AmountField } from './AmountField';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { NonceField, SendButton, SigningStatus } from 'components';
|
import { NonceField, SendButton, SigningStatus } from 'components';
|
||||||
|
@ -13,7 +13,7 @@ export class Fields extends Component<OwnProps> {
|
||||||
public render() {
|
public render() {
|
||||||
const makeContent = () => (
|
const makeContent = () => (
|
||||||
<Aux>
|
<Aux>
|
||||||
<GasField />
|
<GasLimitField />
|
||||||
<AmountField />
|
<AmountField />
|
||||||
<NonceField />
|
<NonceField />
|
||||||
{this.props.button}
|
{this.props.button}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { GasFieldFactory } from 'components/GasFieldFactory';
|
import { GasLimitFieldFactory } from 'components/GasLimitFieldFactory';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
export const GasField: React.SFC<{}> = () => (
|
export const GasLimitField: React.SFC<{}> = () => (
|
||||||
<label className="InteractExplorer-field form-group">
|
<label className="InteractExplorer-field form-group">
|
||||||
<h4 className="InteractExplorer-field-label">Gas Limit</h4>
|
<h4 className="InteractExplorer-field-label">Gas Limit</h4>
|
||||||
<GasFieldFactory
|
<GasLimitFieldFactory
|
||||||
withProps={({ gasLimit: { raw, value }, onChange, readOnly }) => (
|
withProps={({ gasLimit: { raw, value }, onChange, readOnly }) => (
|
||||||
<input
|
<input
|
||||||
name="gasLimit"
|
name="gasLimit"
|
|
@ -5,8 +5,7 @@ import {
|
||||||
NonceField,
|
NonceField,
|
||||||
AddressField,
|
AddressField,
|
||||||
AmountField,
|
AmountField,
|
||||||
DataField,
|
GasSlider,
|
||||||
GasField,
|
|
||||||
SendEverything,
|
SendEverything,
|
||||||
CurrentCustomMessage,
|
CurrentCustomMessage,
|
||||||
GenerateTransaction,
|
GenerateTransaction,
|
||||||
|
@ -23,30 +22,26 @@ const content = (
|
||||||
<div className="Tab-content-pane">
|
<div className="Tab-content-pane">
|
||||||
<AddressField />
|
<AddressField />
|
||||||
<div className="row form-group">
|
<div className="row form-group">
|
||||||
<div className="col-xs-11">
|
<div className="col-xs-12">
|
||||||
<AmountField hasUnitDropdown={true} />
|
<AmountField hasUnitDropdown={true} />
|
||||||
<SendEverything />
|
<SendEverything />
|
||||||
</div>
|
</div>
|
||||||
<div className="col-xs-1" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="row form-group">
|
<div className="row form-group">
|
||||||
<div className="col-xs-11">
|
<div className="col-xs-12">
|
||||||
<GasField />
|
<GasSlider />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="row form-group">
|
<div className="row form-group">
|
||||||
<div className="col-xs-11">
|
<div className="col-xs-12">
|
||||||
<NonceField />
|
<NonceField />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="row form-group">
|
|
||||||
<div className="col-xs-11">
|
|
||||||
<DataField />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<CurrentCustomMessage />
|
<CurrentCustomMessage />
|
||||||
<NonStandardTransaction />
|
<NonStandardTransaction />
|
||||||
|
|
||||||
<div className="row form-group">
|
<div className="row form-group">
|
||||||
<div className="col-xs-12 clearfix">
|
<div className="col-xs-12 clearfix">
|
||||||
<GenerateTransaction />
|
<GenerateTransaction />
|
||||||
|
|
|
@ -15,7 +15,7 @@ import BN from 'bn.js';
|
||||||
import { NetworkConfig } from 'config/data';
|
import { NetworkConfig } from 'config/data';
|
||||||
import { validNumber, validDecimal } from 'libs/validators';
|
import { validNumber, validDecimal } from 'libs/validators';
|
||||||
import { getGasLimit } from 'selectors/transaction';
|
import { getGasLimit } from 'selectors/transaction';
|
||||||
import { AddressField, AmountField, GasField } from 'components';
|
import { AddressField, AmountField, GasLimitField } from 'components';
|
||||||
import { SetGasLimitFieldAction } from 'actions/transaction/actionTypes/fields';
|
import { SetGasLimitFieldAction } from 'actions/transaction/actionTypes/fields';
|
||||||
import { buildEIP681EtherRequest, buildEIP681TokenRequest } from 'libs/values';
|
import { buildEIP681EtherRequest, buildEIP681TokenRequest } from 'libs/values';
|
||||||
import { getNetworkConfig, getSelectedTokenContractAddress } from 'selectors/config';
|
import { getNetworkConfig, getSelectedTokenContractAddress } from 'selectors/config';
|
||||||
|
@ -106,7 +106,7 @@ class RequestPayment extends React.Component<Props, {}> {
|
||||||
|
|
||||||
<div className="row form-group">
|
<div className="row form-group">
|
||||||
<div className="col-xs-11">
|
<div className="col-xs-11">
|
||||||
<GasField />
|
<GasLimitField />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { AmountFieldFactory } from 'components/AmountFieldFactory';
|
import { AmountFieldFactory } from 'components/AmountFieldFactory';
|
||||||
import { GasFieldFactory } from 'components/GasFieldFactory';
|
|
||||||
import { AddressFieldFactory } from 'components/AddressFieldFactory';
|
import { AddressFieldFactory } from 'components/AddressFieldFactory';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
|
|
||||||
import { Aux } from 'components/ui';
|
import { Aux } from 'components/ui';
|
||||||
import { GenerateTransaction, SendButton, SigningStatus } from 'components';
|
import { GenerateTransaction, SendButton, SigningStatus, GasSlider } from 'components';
|
||||||
import { resetWallet, TResetWallet } from 'actions/wallet';
|
import { resetWallet, TResetWallet } from 'actions/wallet';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import { getUnit } from 'selectors/transaction';
|
import { getUnit } from 'selectors/transaction';
|
||||||
|
@ -69,13 +68,7 @@ class FieldsClass extends Component<Props> {
|
||||||
</div>
|
</div>
|
||||||
<div className="row form-group">
|
<div className="row form-group">
|
||||||
<div className="col-xs-12">
|
<div className="col-xs-12">
|
||||||
<label>{translate('TRANS_gas')} </label>
|
<GasSlider disableAdvanced={true} />
|
||||||
|
|
||||||
<GasFieldFactory
|
|
||||||
withProps={({ gasLimit }) => (
|
|
||||||
<input className="form-control" type="text" value={gasLimit.raw} readOnly={true} />
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SigningStatus />
|
<SigningStatus />
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import BN from 'bn.js';
|
||||||
import {
|
import {
|
||||||
FieldAction,
|
FieldAction,
|
||||||
TypeKeys as TK,
|
TypeKeys as TK,
|
||||||
|
@ -16,7 +17,7 @@ const INITIAL_STATE: State = {
|
||||||
data: { raw: '', value: null },
|
data: { raw: '', value: null },
|
||||||
nonce: { raw: '', value: null },
|
nonce: { raw: '', value: null },
|
||||||
value: { raw: '', value: null },
|
value: { raw: '', value: null },
|
||||||
gasLimit: { raw: '', value: null },
|
gasLimit: { raw: '21000', value: new BN(21000) },
|
||||||
gasPrice: { raw: '21', value: gasPricetoBase(21) }
|
gasPrice: { raw: '21', value: gasPricetoBase(21) }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
|
import BN from 'bn.js';
|
||||||
import { call, put, takeEvery } from 'redux-saga/effects';
|
import { call, put, takeEvery } from 'redux-saga/effects';
|
||||||
import { SagaIterator } from 'redux-saga';
|
import { SagaIterator } from 'redux-saga';
|
||||||
import { setDataField, setGasLimitField, setNonceField } from 'actions/transaction/actionCreators';
|
import {
|
||||||
|
setDataField,
|
||||||
|
setGasLimitField,
|
||||||
|
setGasPriceField,
|
||||||
|
setNonceField
|
||||||
|
} from 'actions/transaction/actionCreators';
|
||||||
import {
|
import {
|
||||||
InputDataAction,
|
InputDataAction,
|
||||||
InputGasLimitAction,
|
InputGasLimitAction,
|
||||||
|
InputGasPriceAction,
|
||||||
InputNonceAction,
|
InputNonceAction,
|
||||||
TypeKeys
|
TypeKeys
|
||||||
} from 'actions/transaction';
|
} from 'actions/transaction';
|
||||||
import { isValidHex, isValidNonce, validNumber } from 'libs/validators';
|
import { isValidHex, isValidNonce, validNumber } from 'libs/validators';
|
||||||
import { Data, Wei, Nonce } from 'libs/units';
|
import { Data, Wei, Nonce, gasPricetoBase } from 'libs/units';
|
||||||
|
|
||||||
export function* handleDataInput({ payload }: InputDataAction): SagaIterator {
|
export function* handleDataInput({ payload }: InputDataAction): SagaIterator {
|
||||||
const validData: boolean = yield call(isValidHex, payload);
|
const validData: boolean = yield call(isValidHex, payload);
|
||||||
|
@ -21,6 +28,17 @@ export function* handleGasLimitInput({ payload }: InputGasLimitAction): SagaIter
|
||||||
yield put(setGasLimitField({ raw: payload, value: validGasLimit ? Wei(payload) : null }));
|
yield put(setGasLimitField({ raw: payload, value: validGasLimit ? Wei(payload) : null }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function* handleGasPriceInput({ payload }: InputGasPriceAction): SagaIterator {
|
||||||
|
const priceFloat = parseFloat(payload);
|
||||||
|
const validGasPrice = validNumber(priceFloat) && isFinite(priceFloat) && priceFloat > 0;
|
||||||
|
yield put(
|
||||||
|
setGasPriceField({
|
||||||
|
raw: payload,
|
||||||
|
value: validGasPrice ? gasPricetoBase(priceFloat) : new BN(0)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function* handleNonceInput({ payload }: InputNonceAction): SagaIterator {
|
export function* handleNonceInput({ payload }: InputNonceAction): SagaIterator {
|
||||||
const validNonce: boolean = yield call(isValidNonce, payload);
|
const validNonce: boolean = yield call(isValidNonce, payload);
|
||||||
yield put(setNonceField({ raw: payload, value: validNonce ? Nonce(payload) : null }));
|
yield put(setNonceField({ raw: payload, value: validNonce ? Nonce(payload) : null }));
|
||||||
|
@ -29,5 +47,6 @@ export function* handleNonceInput({ payload }: InputNonceAction): SagaIterator {
|
||||||
export const fields = [
|
export const fields = [
|
||||||
takeEvery(TypeKeys.DATA_FIELD_INPUT, handleDataInput),
|
takeEvery(TypeKeys.DATA_FIELD_INPUT, handleDataInput),
|
||||||
takeEvery(TypeKeys.GAS_LIMIT_INPUT, handleGasLimitInput),
|
takeEvery(TypeKeys.GAS_LIMIT_INPUT, handleGasLimitInput),
|
||||||
|
takeEvery(TypeKeys.GAS_PRICE_INPUT, handleGasPriceInput),
|
||||||
takeEvery(TypeKeys.NONCE_INPUT, handleNonceInput)
|
takeEvery(TypeKeys.NONCE_INPUT, handleNonceInput)
|
||||||
];
|
];
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/utilities";
|
@import "~bootstrap-sass/assets/stylesheets/bootstrap/utilities";
|
||||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/responsive-utilities";
|
@import "~bootstrap-sass/assets/stylesheets/bootstrap/responsive-utilities";
|
||||||
|
|
||||||
|
// --- RC SLIDER ---
|
||||||
|
@import "~rc-slider/assets/index.css";
|
||||||
|
|
||||||
// --- CUSTOM ---
|
// --- CUSTOM ---
|
||||||
@import "./styles/badbrowser";
|
@import "./styles/badbrowser";
|
||||||
@import "./styles/noscript";
|
@import "./styles/noscript";
|
||||||
|
|
|
@ -8,3 +8,6 @@
|
||||||
@import "./overrides/grid";
|
@import "./overrides/grid";
|
||||||
@import "./overrides/input-groups";
|
@import "./overrides/input-groups";
|
||||||
@import "./overrides/type";
|
@import "./overrides/type";
|
||||||
|
|
||||||
|
// And an override for rc-slider
|
||||||
|
@import "./overrides/rc-slider";
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
@import 'common/sass/variables';
|
||||||
|
|
||||||
|
$rail-height: 4px;
|
||||||
|
$handle-size: 22px;
|
||||||
|
$speed: 70ms;
|
||||||
|
|
||||||
|
.rc-slider {
|
||||||
|
&-rail {
|
||||||
|
background: $gray-lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-track {
|
||||||
|
background: $brand-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-handle {
|
||||||
|
top: 50%;
|
||||||
|
width: $handle-size;
|
||||||
|
height: $handle-size;
|
||||||
|
background: $brand-primary;
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,7 @@
|
||||||
"qrcode": "1.2.0",
|
"qrcode": "1.2.0",
|
||||||
"qrcode.react": "0.7.2",
|
"qrcode.react": "0.7.2",
|
||||||
"query-string": "5.0.1",
|
"query-string": "5.0.1",
|
||||||
|
"rc-slider": "^8.5.0",
|
||||||
"react": "16.2.0",
|
"react": "16.2.0",
|
||||||
"react-dom": "16.2.0",
|
"react-dom": "16.2.0",
|
||||||
"react-markdown": "2.5.1",
|
"react-markdown": "2.5.1",
|
||||||
|
|
Loading…
Reference in New Issue