Improve Gas Price UX (Part 2) (#850)
* Remove gas dropdown & Add gas sliders * Update styles * Revert changes made to requestpayment.tsx * Update style & add custom labels to GasLimitField * Update styles * Update confirm transaction modal * Revert "Update confirm transaction modal" This reverts commit 743c9a505fe070feb55f7af550ad918a3d8899d1. * Add transaction fee to tx confirmation modal * Update styles * Remove old gasPriceDropdown files & use network units in tx fee * Add option to lock gaslimit data * fix tslint errors * Rename lockData to readOnly * Add option to check if validAmount before generating transaction * Add nonce field if gas slider is readonly * Automatically set nonce in <Send/> * Update snapshot * Move getNonceRequested to GasSlider component * Add optional to check value for isValidAmount selector * Add selector for transaction fee * Update GasSlider component & Rename to Gas * update snapshots * Fix subtabs className * Update styles * Remove dataField on contract interact * rename <Gas/> to <TXMetaDataPanel/>
This commit is contained in:
parent
22c107fe4c
commit
c631f45ab7
|
@ -1,4 +1,4 @@
|
||||||
import { GasPrice } from './components';
|
import { TransactionFee } from './components';
|
||||||
import { Amount } from '../../Amount';
|
import { Amount } from '../../Amount';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@ export const AmountAndGasPrice: React.SFC<{}> = () => (
|
||||||
<strong>
|
<strong>
|
||||||
<Amount />
|
<Amount />
|
||||||
</strong>{' '}
|
</strong>{' '}
|
||||||
with a gas price of{' '}
|
with a transaction fee of{' '}
|
||||||
<strong>
|
<strong>
|
||||||
<GasPrice />
|
<TransactionFee />
|
||||||
</strong>
|
</strong>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { getTransactionFee, makeTransaction } from 'libs/transaction';
|
||||||
|
import { SerializedTransaction } from 'components/renderCbs';
|
||||||
|
import { UnitDisplay } from 'components/ui';
|
||||||
|
import { AppState } from 'reducers';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { getNetworkConfig } from 'selectors/config';
|
||||||
|
import BN from 'bn.js';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
rates: AppState['rates']['rates'];
|
||||||
|
network: AppState['config']['network'];
|
||||||
|
isOffline: AppState['config']['offline'];
|
||||||
|
}
|
||||||
|
|
||||||
|
class TransactionFeeClass extends React.Component<Props> {
|
||||||
|
public render() {
|
||||||
|
const { rates, network, isOffline } = this.props;
|
||||||
|
return (
|
||||||
|
<SerializedTransaction
|
||||||
|
withSerializedTransaction={serializedTransaction => {
|
||||||
|
const transactionInstance = makeTransaction(serializedTransaction);
|
||||||
|
const fee = getTransactionFee(transactionInstance);
|
||||||
|
const usdFee = network.isTestnet ? new BN(0) : fee.muln(rates[network.unit].USD);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<UnitDisplay unit={'ether'} value={fee} symbol={network.unit} checkOffline={false} />{' '}
|
||||||
|
{!isOffline &&
|
||||||
|
rates[network.unit] && (
|
||||||
|
<span>
|
||||||
|
($
|
||||||
|
<UnitDisplay
|
||||||
|
value={usdFee}
|
||||||
|
unit="ether"
|
||||||
|
displayShortBalance={2}
|
||||||
|
displayTrailingZeroes={true}
|
||||||
|
checkOffline={true}
|
||||||
|
/>)
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: AppState) {
|
||||||
|
return {
|
||||||
|
rates: state.rates.rates,
|
||||||
|
network: getNetworkConfig(state),
|
||||||
|
isOffline: state.config.offline
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export const TransactionFee = connect(mapStateToProps)(TransactionFeeClass);
|
|
@ -1 +1,2 @@
|
||||||
export * from './GasPrice';
|
export * from './GasPrice';
|
||||||
|
export * from './TransactionFee';
|
||||||
|
|
|
@ -8,27 +8,46 @@ import { gasLimitValidator } from 'libs/validators';
|
||||||
interface Props {
|
interface Props {
|
||||||
includeLabel: boolean;
|
includeLabel: boolean;
|
||||||
onlyIncludeLoader: boolean;
|
onlyIncludeLoader: boolean;
|
||||||
|
customLabel?: string;
|
||||||
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GaslimitLoading: React.SFC<{ gasEstimationPending: boolean }> = ({
|
export const GaslimitLoading: React.SFC<{
|
||||||
gasEstimationPending
|
gasEstimationPending: boolean;
|
||||||
}) => (
|
onlyIncludeLoader?: boolean;
|
||||||
|
}> = ({ gasEstimationPending, onlyIncludeLoader }) => (
|
||||||
<CSSTransition in={gasEstimationPending} timeout={300} classNames="fade">
|
<CSSTransition in={gasEstimationPending} timeout={300} classNames="fade">
|
||||||
<div className={`SimpleGas-estimating small ${gasEstimationPending ? 'active' : ''}`}>
|
<div className={`Calculating-limit small ${gasEstimationPending ? 'active' : ''}`}>
|
||||||
Calculating gas limit
|
{!!onlyIncludeLoader ? 'Calculating gas limit' : 'Calculating'}
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
</CSSTransition>
|
</CSSTransition>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const GasLimitField: React.SFC<Props> = ({ includeLabel, onlyIncludeLoader }) => (
|
export const GasLimitField: React.SFC<Props> = ({
|
||||||
|
includeLabel,
|
||||||
|
onlyIncludeLoader,
|
||||||
|
customLabel,
|
||||||
|
disabled
|
||||||
|
}) => (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{includeLabel ? <label>{translate('TRANS_gas')} </label> : null}
|
|
||||||
|
|
||||||
<GasLimitFieldFactory
|
<GasLimitFieldFactory
|
||||||
withProps={({ gasLimit: { raw }, onChange, readOnly, gasEstimationPending }) => (
|
withProps={({ gasLimit: { raw }, onChange, readOnly, gasEstimationPending }) => (
|
||||||
<>
|
<React.Fragment>
|
||||||
<GaslimitLoading gasEstimationPending={gasEstimationPending} />
|
<div className="flex-wrapper">
|
||||||
|
{includeLabel ? (
|
||||||
|
customLabel ? (
|
||||||
|
<label>{customLabel} </label>
|
||||||
|
) : (
|
||||||
|
<label>{translate('TRANS_gas')} </label>
|
||||||
|
)
|
||||||
|
) : null}
|
||||||
|
<div className="flex-spacer" />
|
||||||
|
<GaslimitLoading
|
||||||
|
gasEstimationPending={gasEstimationPending}
|
||||||
|
onlyIncludeLoader={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
{onlyIncludeLoader ? null : (
|
{onlyIncludeLoader ? null : (
|
||||||
<input
|
<input
|
||||||
className={`form-control ${gasLimitValidator(raw) ? 'is-valid' : 'is-invalid'}`}
|
className={`form-control ${gasLimitValidator(raw) ? 'is-valid' : 'is-invalid'}`}
|
||||||
|
@ -37,9 +56,10 @@ export const GasLimitField: React.SFC<Props> = ({ includeLabel, onlyIncludeLoade
|
||||||
readOnly={!!readOnly}
|
readOnly={!!readOnly}
|
||||||
value={raw}
|
value={raw}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
@import 'common/sass/variables';
|
|
||||||
|
|
||||||
.GasSlider {
|
|
||||||
&-toggle {
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
margin-top: $space-sm;
|
|
||||||
left: -8px;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
.AdvancedGas {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
.checkbox {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
width: fit-content;
|
|
||||||
input[type='checkbox'] {
|
|
||||||
position: initial;
|
|
||||||
margin: 0;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
span {
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-gasLimit {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: baseline;
|
|
||||||
.flex-spacer {
|
|
||||||
flex-grow: 2;
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-nonce {
|
|
||||||
input {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
import translate from 'translations';
|
|
||||||
import FeeSummary from './FeeSummary';
|
|
||||||
import './AdvancedGas.scss';
|
|
||||||
import { TToggleAutoGasLimit, toggleAutoGasLimit } from 'actions/config';
|
|
||||||
import { AppState } from 'reducers';
|
|
||||||
import { TInputGasPrice } from 'actions/transaction';
|
|
||||||
import { NonceField, GasLimitField, DataField } from 'components';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { getAutoGasLimitEnabled } from 'selectors/config';
|
|
||||||
import { isValidGasPrice } from 'selectors/transaction';
|
|
||||||
import { sanitizeNumericalInput } from 'libs/values';
|
|
||||||
|
|
||||||
interface OwnProps {
|
|
||||||
inputGasPrice: TInputGasPrice;
|
|
||||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StateProps {
|
|
||||||
autoGasLimitEnabled: AppState['config']['autoGasLimit'];
|
|
||||||
validGasPrice: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DispatchProps {
|
|
||||||
toggleAutoGasLimit: TToggleAutoGasLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
type Props = OwnProps & StateProps & DispatchProps;
|
|
||||||
|
|
||||||
class AdvancedGas extends React.Component<Props> {
|
|
||||||
public render() {
|
|
||||||
const { autoGasLimitEnabled, gasPrice, validGasPrice } = this.props;
|
|
||||||
return (
|
|
||||||
<div className="AdvancedGas row form-group">
|
|
||||||
<div className="col-md-12">
|
|
||||||
<label className="checkbox">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
defaultChecked={autoGasLimitEnabled}
|
|
||||||
onChange={this.handleToggleAutoGasLimit}
|
|
||||||
/>
|
|
||||||
<span>Automatically Calculate Gas Limit</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-md-4 col-sm-6 col-xs-12">
|
|
||||||
<label>{translate('OFFLINE_Step2_Label_3')} (gwei)</label>
|
|
||||||
<input
|
|
||||||
className={classnames('form-control', { 'is-invalid': !validGasPrice })}
|
|
||||||
type="number"
|
|
||||||
placeholder="e.g. 40"
|
|
||||||
value={gasPrice.raw}
|
|
||||||
onChange={this.handleGasPriceChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-md-4 col-sm-6 col-xs-12 AdvancedGas-gasLimit">
|
|
||||||
<label>{translate('OFFLINE_Step2_Label_4')}</label>
|
|
||||||
<div className="SimpleGas-flex-spacer" />
|
|
||||||
<GasLimitField includeLabel={false} onlyIncludeLoader={false} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-md-4 col-sm-12 col-xs-12 AdvancedGas-nonce">
|
|
||||||
<NonceField alwaysDisplay={true} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-md-12 col-xs-12">
|
|
||||||
<DataField />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-sm-12 col-xs-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>) => {
|
|
||||||
const { value } = ev.currentTarget;
|
|
||||||
this.props.inputGasPrice(sanitizeNumericalInput(value));
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleToggleAutoGasLimit = (_: React.FormEvent<HTMLInputElement>) => {
|
|
||||||
this.props.toggleAutoGasLimit();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
(state: AppState) => ({
|
|
||||||
autoGasLimitEnabled: getAutoGasLimitEnabled(state),
|
|
||||||
validGasPrice: isValidGasPrice(state)
|
|
||||||
}),
|
|
||||||
{ toggleAutoGasLimit }
|
|
||||||
)(AdvancedGas);
|
|
|
@ -1,2 +0,0 @@
|
||||||
import GasSlider from './GasSlider';
|
|
||||||
export default GasSlider;
|
|
|
@ -1,38 +0,0 @@
|
||||||
@import "common/sass/variables";
|
|
||||||
|
|
||||||
.GasPrice {
|
|
||||||
|
|
||||||
&-dropdown-menu {
|
|
||||||
padding: 0.5rem !important;
|
|
||||||
min-width: 300px !important;
|
|
||||||
|
|
||||||
@media screen and (max-width: $screen-xs) {
|
|
||||||
left: 0;
|
|
||||||
right: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-header {
|
|
||||||
max-width: 26rem;
|
|
||||||
color: $text-color;
|
|
||||||
p {
|
|
||||||
font-weight: 400;
|
|
||||||
margin: $space-sm 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, a:hover, a:focus, a:visited {
|
|
||||||
color: $brand-primary !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-padding-reset {
|
|
||||||
padding-left: 0 !important;
|
|
||||||
padding-right: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-description {
|
|
||||||
white-space: normal;
|
|
||||||
font-weight: 300 !important;
|
|
||||||
margin: 2rem 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,7 +20,6 @@ import {
|
||||||
CustomNodeConfig,
|
CustomNodeConfig,
|
||||||
CustomNetworkConfig
|
CustomNetworkConfig
|
||||||
} from 'config';
|
} from 'config';
|
||||||
import GasPriceDropdown from './components/GasPriceDropdown';
|
|
||||||
import Navigation from './components/Navigation';
|
import Navigation from './components/Navigation';
|
||||||
import CustomNodeModal from './components/CustomNodeModal';
|
import CustomNodeModal from './components/CustomNodeModal';
|
||||||
import OnlineStatus from './components/OnlineStatus';
|
import OnlineStatus from './components/OnlineStatus';
|
||||||
|
@ -134,10 +133,6 @@ export default class Header extends Component<Props, State> {
|
||||||
<OnlineStatus isOffline={isOffline} />
|
<OnlineStatus isOffline={isOffline} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="Header-branding-right-dropdown">
|
|
||||||
<GasPriceDropdown onChange={this.props.setGasPriceField} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="Header-branding-right-dropdown">
|
<div className="Header-branding-right-dropdown">
|
||||||
<LanguageDropDown
|
<LanguageDropDown
|
||||||
ariaLabel={`change language. current language ${languages[selectedLanguage]}`}
|
ariaLabel={`change language. current language ${languages[selectedLanguage]}`}
|
||||||
|
|
|
@ -17,7 +17,7 @@ export const NonceField: React.SFC<Props> = ({ alwaysDisplay }) => (
|
||||||
<NonceFieldFactory
|
<NonceFieldFactory
|
||||||
withProps={({ nonce: { raw, value }, onChange, readOnly, shouldDisplay }) => {
|
withProps={({ nonce: { raw, value }, onChange, readOnly, shouldDisplay }) => {
|
||||||
const content = (
|
const content = (
|
||||||
<>
|
<div>
|
||||||
<label>Nonce</label>
|
<label>Nonce</label>
|
||||||
{nonceHelp}
|
{nonceHelp}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ export const NonceField: React.SFC<Props> = ({ alwaysDisplay }) => (
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
return alwaysDisplay || shouldDisplay ? content : null;
|
return alwaysDisplay || shouldDisplay ? content : null;
|
||||||
|
|
|
@ -21,7 +21,7 @@ export default class SubTabs extends React.Component<Props> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="SubTabs row">
|
<div className="SubTabs row">
|
||||||
<div className="SubTabs-tabs col-sm-8">
|
<div className="SubTabs-tabs col-sm-12">
|
||||||
{tabs.map((t, i) => (
|
{tabs.map((t, i) => (
|
||||||
// Same as normal Link, but knows when it's active, and applies activeClassName
|
// Same as normal Link, but knows when it's active, and applies activeClassName
|
||||||
<NavLink
|
<NavLink
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
@import 'common/sass/variables';
|
||||||
|
|
||||||
|
.Gas {
|
||||||
|
&-toggle {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
margin-top: $space-sm;
|
||||||
|
left: -8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Calculating-limit {
|
||||||
|
color: rgba(51, 51, 51, 0.7);
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
font-weight: 400;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.Spinner {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,24 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { translateRaw } from 'translations';
|
import { translateRaw } from 'translations';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { inputGasPrice, TInputGasPrice } from 'actions/transaction';
|
import {
|
||||||
|
inputGasPrice,
|
||||||
|
TInputGasPrice,
|
||||||
|
getNonceRequested,
|
||||||
|
TGetNonceRequested,
|
||||||
|
reset,
|
||||||
|
TReset
|
||||||
|
} from 'actions/transaction';
|
||||||
import { fetchCCRates, TFetchCCRates } from 'actions/rates';
|
import { fetchCCRates, TFetchCCRates } from 'actions/rates';
|
||||||
import { getNetworkConfig, getOffline } from 'selectors/config';
|
import { getNetworkConfig, getOffline } from 'selectors/config';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
import SimpleGas from './components/SimpleGas';
|
import SimpleGas from './components/SimpleGas';
|
||||||
import AdvancedGas from './components/AdvancedGas';
|
import AdvancedGas, { AdvancedOptions } from './components/AdvancedGas';
|
||||||
import './GasSlider.scss';
|
import './TXMetaDataPanel.scss';
|
||||||
import { getGasPrice } from 'selectors/transaction';
|
import { getGasPrice } from 'selectors/transaction';
|
||||||
|
|
||||||
|
type SliderStates = 'simple' | 'advanced';
|
||||||
|
|
||||||
interface StateProps {
|
interface StateProps {
|
||||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||||
offline: AppState['config']['offline'];
|
offline: AppState['config']['offline'];
|
||||||
|
@ -19,26 +28,42 @@ interface StateProps {
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
inputGasPrice: TInputGasPrice;
|
inputGasPrice: TInputGasPrice;
|
||||||
fetchCCRates: TFetchCCRates;
|
fetchCCRates: TFetchCCRates;
|
||||||
|
getNonceRequested: TGetNonceRequested;
|
||||||
|
reset: TReset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set default props for props that can't be truthy or falsy
|
||||||
|
interface DefaultProps {
|
||||||
|
initialState: SliderStates;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
disableAdvanced?: boolean;
|
initialState?: SliderStates;
|
||||||
|
disableToggle?: boolean;
|
||||||
|
advancedGasOptions?: AdvancedOptions;
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = DispatchProps & OwnProps & StateProps;
|
type Props = DispatchProps & OwnProps & StateProps;
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
showAdvanced: boolean;
|
sliderState: SliderStates;
|
||||||
}
|
}
|
||||||
|
|
||||||
class GasSlider extends React.Component<Props, State> {
|
class TXMetaDataPanel extends React.Component<Props, State> {
|
||||||
|
public static defaultProps: DefaultProps = {
|
||||||
|
initialState: 'simple'
|
||||||
|
};
|
||||||
|
|
||||||
public state: State = {
|
public state: State = {
|
||||||
showAdvanced: false
|
sliderState: (this.props as DefaultProps).initialState
|
||||||
};
|
};
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
if (!this.props.offline) {
|
if (!this.props.offline) {
|
||||||
|
this.props.reset();
|
||||||
this.props.fetchCCRates([this.props.network.unit]);
|
this.props.fetchCCRates([this.props.network.unit]);
|
||||||
|
this.props.getNonceRequested();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,21 +74,24 @@ class GasSlider extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { offline, disableAdvanced, gasPrice } = this.props;
|
const { offline, disableToggle, gasPrice, advancedGasOptions, className = '' } = this.props;
|
||||||
const showAdvanced = (this.state.showAdvanced || offline) && !disableAdvanced;
|
const showAdvanced = this.state.sliderState === 'advanced' || offline;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="GasSlider">
|
<div className={`Gas col-md-12 ${className}`}>
|
||||||
{showAdvanced ? (
|
{showAdvanced ? (
|
||||||
<AdvancedGas gasPrice={gasPrice} inputGasPrice={this.props.inputGasPrice} />
|
<AdvancedGas
|
||||||
|
gasPrice={gasPrice}
|
||||||
|
inputGasPrice={this.props.inputGasPrice}
|
||||||
|
options={advancedGasOptions}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<SimpleGas gasPrice={gasPrice} inputGasPrice={this.props.inputGasPrice} />
|
<SimpleGas gasPrice={gasPrice} inputGasPrice={this.props.inputGasPrice} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!offline &&
|
{!offline &&
|
||||||
!disableAdvanced && (
|
!disableToggle && (
|
||||||
<div className="help-block">
|
<div className="help-block">
|
||||||
<a className="GasSlider-toggle" onClick={this.toggleAdvanced}>
|
<a className="Gas-toggle" onClick={this.toggleAdvanced}>
|
||||||
<strong>
|
<strong>
|
||||||
{showAdvanced
|
{showAdvanced
|
||||||
? `- ${translateRaw('Back to simple')}`
|
? `- ${translateRaw('Back to simple')}`
|
||||||
|
@ -77,7 +105,7 @@ class GasSlider extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private toggleAdvanced = () => {
|
private toggleAdvanced = () => {
|
||||||
this.setState({ showAdvanced: !this.state.showAdvanced });
|
this.setState({ sliderState: this.state.sliderState === 'advanced' ? 'simple' : 'advanced' });
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,5 +119,7 @@ function mapStateToProps(state: AppState): StateProps {
|
||||||
|
|
||||||
export default connect(mapStateToProps, {
|
export default connect(mapStateToProps, {
|
||||||
inputGasPrice,
|
inputGasPrice,
|
||||||
fetchCCRates
|
fetchCCRates,
|
||||||
})(GasSlider);
|
getNonceRequested,
|
||||||
|
reset
|
||||||
|
})(TXMetaDataPanel);
|
|
@ -0,0 +1,49 @@
|
||||||
|
@import 'common/sass/variables';
|
||||||
|
|
||||||
|
.AdvancedGas {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
&-calculate-limit {
|
||||||
|
.checkbox {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: fit-content;
|
||||||
|
input[type='checkbox'] {
|
||||||
|
position: initial;
|
||||||
|
margin: 0;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-flex-wrapper {
|
||||||
|
margin: 0px -8px;
|
||||||
|
}
|
||||||
|
&-gas-price,
|
||||||
|
&-gas-limit,
|
||||||
|
&-nonce {
|
||||||
|
width: initial;
|
||||||
|
flex-grow: 1;
|
||||||
|
margin: 0px 8px;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: $screen-lg) {
|
||||||
|
&-flex-wrapper {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-nonce {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-data {
|
||||||
|
}
|
||||||
|
|
||||||
|
&-fee-summary {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
import React from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import translate, { translateRaw } from 'translations';
|
||||||
|
import FeeSummary from './FeeSummary';
|
||||||
|
import './AdvancedGas.scss';
|
||||||
|
import { TToggleAutoGasLimit, toggleAutoGasLimit } from 'actions/config';
|
||||||
|
import { AppState } from 'reducers';
|
||||||
|
import { TInputGasPrice } from 'actions/transaction';
|
||||||
|
import { NonceField, GasLimitField, DataField } from 'components';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { getAutoGasLimitEnabled } from 'selectors/config';
|
||||||
|
import { isValidGasPrice } from 'selectors/transaction';
|
||||||
|
import { sanitizeNumericalInput } from 'libs/values';
|
||||||
|
|
||||||
|
export interface AdvancedOptions {
|
||||||
|
gasPriceField?: boolean;
|
||||||
|
gasLimitField?: boolean;
|
||||||
|
nonceField?: boolean;
|
||||||
|
dataField?: boolean;
|
||||||
|
feeSummary?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OwnProps {
|
||||||
|
inputGasPrice: TInputGasPrice;
|
||||||
|
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||||
|
options?: AdvancedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StateProps {
|
||||||
|
autoGasLimitEnabled: AppState['config']['autoGasLimit'];
|
||||||
|
validGasPrice: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DispatchProps {
|
||||||
|
toggleAutoGasLimit: TToggleAutoGasLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
options: AdvancedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = OwnProps & StateProps & DispatchProps;
|
||||||
|
|
||||||
|
class AdvancedGas extends React.Component<Props, State> {
|
||||||
|
public state = {
|
||||||
|
options: {
|
||||||
|
gasPriceField: true,
|
||||||
|
gasLimitField: true,
|
||||||
|
nonceField: true,
|
||||||
|
dataField: true,
|
||||||
|
feeSummary: true,
|
||||||
|
...this.props.options
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { autoGasLimitEnabled, gasPrice, validGasPrice } = this.props;
|
||||||
|
const { gasPriceField, gasLimitField, nonceField, dataField, feeSummary } = this.state.options;
|
||||||
|
return (
|
||||||
|
<div className="AdvancedGas row form-group">
|
||||||
|
<div className="AdvancedGas-calculate-limit">
|
||||||
|
<label className="checkbox">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
defaultChecked={autoGasLimitEnabled}
|
||||||
|
onChange={this.handleToggleAutoGasLimit}
|
||||||
|
/>
|
||||||
|
<span>Automatically Calculate Gas Limit</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="AdvancedGas-flex-wrapper flex-wrapper">
|
||||||
|
{gasPriceField && (
|
||||||
|
<div className="AdvancedGas-gas-price">
|
||||||
|
<label>{translate('OFFLINE_Step2_Label_3')} (gwei)</label>
|
||||||
|
<input
|
||||||
|
className={classnames('form-control', { 'is-invalid': !validGasPrice })}
|
||||||
|
type="number"
|
||||||
|
placeholder="40"
|
||||||
|
value={gasPrice.raw}
|
||||||
|
onChange={this.handleGasPriceChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{gasLimitField && (
|
||||||
|
<div className="AdvancedGas-gas-limit">
|
||||||
|
<GasLimitField
|
||||||
|
includeLabel={true}
|
||||||
|
customLabel={translateRaw('OFFLINE_Step2_Label_4')}
|
||||||
|
onlyIncludeLoader={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{nonceField && (
|
||||||
|
<div className="AdvancedGas-nonce">
|
||||||
|
<NonceField alwaysDisplay={true} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{dataField && (
|
||||||
|
<div className="AdvancedGas-data">
|
||||||
|
<DataField />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{feeSummary && (
|
||||||
|
<div className="AdvancedGas-fee-summary">
|
||||||
|
<FeeSummary
|
||||||
|
render={({ gasPriceWei, gasLimit, fee, usd }) => (
|
||||||
|
<span>
|
||||||
|
{gasPriceWei} * {gasLimit} = {fee} {usd && <span>~= ${usd} USD</span>}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleGasPriceChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||||
|
const { value } = ev.currentTarget;
|
||||||
|
this.props.inputGasPrice(sanitizeNumericalInput(value));
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleToggleAutoGasLimit = (_: React.FormEvent<HTMLInputElement>) => {
|
||||||
|
this.props.toggleAutoGasLimit();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
(state: AppState) => ({
|
||||||
|
autoGasLimitEnabled: getAutoGasLimitEnabled(state),
|
||||||
|
validGasPrice: isValidGasPrice(state)
|
||||||
|
}),
|
||||||
|
{ toggleAutoGasLimit }
|
||||||
|
)(AdvancedGas);
|
|
@ -4,23 +4,26 @@
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
||||||
&-flex-spacer {
|
&-input-group {
|
||||||
flex-grow: 2;
|
|
||||||
}
|
|
||||||
&-title {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
> .SimpleGas-slider {
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-right: $input-padding-x;
|
||||||
}
|
}
|
||||||
&-estimating {
|
> .FeeSummary {
|
||||||
color: rgba(51, 51, 51, 0.7);
|
margin-left: $input-padding-x;
|
||||||
display: flex;
|
min-width: 224px;
|
||||||
align-items: baseline;
|
}
|
||||||
font-weight: 400;
|
@media screen and (max-width: $screen-md) {
|
||||||
opacity: 0;
|
flex-wrap: wrap;
|
||||||
&.active {
|
> .SimpleGas-slider {
|
||||||
opacity: 1;
|
width: 100%;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
> .FeeSummary {
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
.Spinner {
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Slider from 'rc-slider';
|
import Slider from 'rc-slider';
|
||||||
import translate from 'translations';
|
import translate, { translateRaw } from 'translations';
|
||||||
import { gasPriceDefaults } from 'config';
|
import { gasPriceDefaults } from 'config';
|
||||||
import FeeSummary from './FeeSummary';
|
import FeeSummary from './FeeSummary';
|
||||||
import { TInputGasPrice } from 'actions/transaction';
|
import { TInputGasPrice } from 'actions/transaction';
|
||||||
|
@ -29,14 +29,16 @@ class SimpleGas extends React.Component<Props> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="SimpleGas row form-group">
|
<div className="SimpleGas row form-group">
|
||||||
<div className="col-md-12 SimpleGas-title">
|
<div className="SimpleGas-title">
|
||||||
<label className="SimpleGas-label">{translate('Transaction Fee')}</label>
|
<GasLimitField
|
||||||
<div className="SimpleGas-flex-spacer" />
|
includeLabel={true}
|
||||||
<GasLimitField includeLabel={false} onlyIncludeLoader={true} />
|
customLabel={translateRaw('Transaction Fee')}
|
||||||
|
onlyIncludeLoader={true}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{gasLimitEstimationTimedOut && (
|
{gasLimitEstimationTimedOut && (
|
||||||
<div className="col-md-12 prompt-toggle-gas-limit">
|
<div className="prompt-toggle-gas-limit">
|
||||||
<p className="small">
|
<p className="small">
|
||||||
{isWeb3Node
|
{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 your doing, try setting manually in Advanced settings"
|
||||||
|
@ -45,7 +47,7 @@ class SimpleGas extends React.Component<Props> {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="col-md-8 col-sm-12">
|
<div className="SimpleGas-input-group">
|
||||||
<div className="SimpleGas-slider">
|
<div className="SimpleGas-slider">
|
||||||
<Slider
|
<Slider
|
||||||
onChange={this.handleSlider}
|
onChange={this.handleSlider}
|
||||||
|
@ -59,8 +61,6 @@ class SimpleGas extends React.Component<Props> {
|
||||||
<span>{translate('Fast')}</span>
|
<span>{translate('Fast')}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div className="col-md-4 col-sm-12">
|
|
||||||
<FeeSummary
|
<FeeSummary
|
||||||
render={({ fee, usd }) => (
|
render={({ fee, usd }) => (
|
||||||
<span>
|
<span>
|
|
@ -0,0 +1,2 @@
|
||||||
|
import TXMetaDataPanel from './TXMetaDataPanel';
|
||||||
|
export default TXMetaDataPanel;
|
|
@ -3,12 +3,12 @@
|
||||||
|
|
||||||
.DWModal {
|
.DWModal {
|
||||||
&-path {
|
&-path {
|
||||||
display: flex;
|
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
&-label {
|
&-label {
|
||||||
font-size: $font-size-medium;
|
font-size: $font-size-medium;
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
|
line-height: $input-height-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control {
|
.form-control {
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
&-addresses {
|
&-addresses {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
&-table {
|
&-table {
|
||||||
width: 695px;
|
width: 732px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
|
|
@ -122,8 +122,10 @@ class DeterministicWalletsModalClass extends React.Component<Props, State> {
|
||||||
handleClose={onCancel}
|
handleClose={onCancel}
|
||||||
>
|
>
|
||||||
<div className="DWModal">
|
<div className="DWModal">
|
||||||
{/* TODO: replace styles for flexbox with flexbox classes in https://github.com/MyEtherWallet/MyEtherWallet/pull/850/files#diff-2150778b9391533fec7b8afd060c7672 */}
|
<form
|
||||||
<form className="DWModal-path form-group-sm" onSubmit={this.handleSubmitCustomPath}>
|
className="DWModal-path form-group-sm flex-wrapper"
|
||||||
|
onSubmit={this.handleSubmitCustomPath}
|
||||||
|
>
|
||||||
<span className="DWModal-path-label">Addresses </span>
|
<span className="DWModal-path-label">Addresses </span>
|
||||||
<Select
|
<Select
|
||||||
name="fieldDPath"
|
name="fieldDPath"
|
||||||
|
|
|
@ -14,5 +14,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 TXMetaDataPanel } from './TXMetaDataPanel';
|
||||||
export { default as WalletDecrypt } from './WalletDecrypt';
|
export { default as WalletDecrypt } from './WalletDecrypt';
|
||||||
|
|
|
@ -7,10 +7,12 @@
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
padding: 0.4rem 1rem;
|
padding: 0.4rem 1rem;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
height: 2.5rem;
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
&:active, &:hover {
|
&:active,
|
||||||
|
&:hover {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
> li {
|
> li {
|
||||||
|
@ -46,10 +48,10 @@
|
||||||
text-align: left;
|
text-align: left;
|
||||||
z-index: 500;
|
z-index: 500;
|
||||||
background: white;
|
background: white;
|
||||||
box-shadow: 2px 1px 60px rgba(0,0,0,.175);
|
box-shadow: 2px 1px 60px rgba(0, 0, 0, 0.175);
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: "";
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -20px;
|
top: -20px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
@ -74,7 +76,7 @@
|
||||||
padding: 5px 20px;
|
padding: 5px 20px;
|
||||||
color: #163151;
|
color: #163151;
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: .8;
|
opacity: 0.8;
|
||||||
background-color: #163151;
|
background-color: #163151;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
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 { 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 WalletDecrypt, { DISABLE_WALLETS } from 'components/WalletDecrypt';
|
import WalletDecrypt, { DISABLE_WALLETS } from 'components/WalletDecrypt';
|
||||||
import { GenerateTransaction } from 'components/GenerateTransaction';
|
import { GenerateTransaction } from 'components/GenerateTransaction';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
@ -12,6 +10,7 @@ import { setToField, TSetToField } from 'actions/transaction';
|
||||||
import { resetWallet, TResetWallet } from 'actions/wallet';
|
import { resetWallet, TResetWallet } from 'actions/wallet';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { FullWalletOnly } from 'components/renderCbs';
|
import { FullWalletOnly } from 'components/renderCbs';
|
||||||
|
import { NonceField, TXMetaDataPanel } from 'components';
|
||||||
import './Deploy.scss';
|
import './Deploy.scss';
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
|
@ -46,27 +45,22 @@ class DeployClass extends Component<DispatchProps> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label className="Deploy-field form-group">
|
|
||||||
<h4 className="Deploy-field-label">Gas Limit</h4>
|
|
||||||
<GasLimitFieldFactory
|
|
||||||
withProps={({ gasLimit: { raw, value }, onChange, readOnly }) => (
|
|
||||||
<input
|
|
||||||
name="gasLimit"
|
|
||||||
value={raw}
|
|
||||||
disabled={readOnly}
|
|
||||||
onChange={onChange}
|
|
||||||
className={classnames('Deploy-field-input', 'form-control', {
|
|
||||||
'is-invalid': !value
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<div className="row form-group">
|
<div className="row form-group">
|
||||||
<div className="col-xs-11">
|
<div className="col-xs-12 clearfix">
|
||||||
<NonceField alwaysDisplay={false} />
|
<NonceField alwaysDisplay={false} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="row form-group">
|
||||||
|
<div className="col-xs-12 clearfix">
|
||||||
|
<TXMetaDataPanel
|
||||||
|
initialState="advanced"
|
||||||
|
disableToggle={true}
|
||||||
|
advancedGasOptions={{ dataField: false }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="row form-group">
|
<div className="row form-group">
|
||||||
<div className="col-xs-12 clearfix">
|
<div className="col-xs-12 clearfix">
|
||||||
<GenerateTransaction />
|
<GenerateTransaction />
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
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 { SendButton, SigningStatus, TXMetaDataPanel } from 'components';
|
||||||
import WalletDecrypt, { DISABLE_WALLETS } from 'components/WalletDecrypt';
|
import WalletDecrypt, { DISABLE_WALLETS } from 'components/WalletDecrypt';
|
||||||
import { FullWalletOnly } from 'components/renderCbs';
|
import { FullWalletOnly } from 'components/renderCbs';
|
||||||
|
|
||||||
|
@ -12,9 +11,13 @@ export class Fields extends Component<OwnProps> {
|
||||||
public render() {
|
public render() {
|
||||||
const makeContent = () => (
|
const makeContent = () => (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<GasLimitField />
|
|
||||||
<AmountField />
|
<AmountField />
|
||||||
<NonceField alwaysDisplay={false} />
|
<TXMetaDataPanel
|
||||||
|
className="form-group"
|
||||||
|
initialState="advanced"
|
||||||
|
disableToggle={true}
|
||||||
|
advancedGasOptions={{ dataField: false }}
|
||||||
|
/>
|
||||||
{this.props.button}
|
{this.props.button}
|
||||||
<SigningStatus />
|
<SigningStatus />
|
||||||
<SendButton />
|
<SendButton />
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { GasLimitFieldFactory } from 'components/GasLimitFieldFactory';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
|
|
||||||
export const GasLimitField: React.SFC<{}> = () => (
|
|
||||||
<label className="InteractExplorer-field form-group">
|
|
||||||
<h4 className="InteractExplorer-field-label">Gas Limit</h4>
|
|
||||||
<GasLimitFieldFactory
|
|
||||||
withProps={({ gasLimit: { raw, value }, onChange, readOnly }) => (
|
|
||||||
<input
|
|
||||||
name="gasLimit"
|
|
||||||
value={raw}
|
|
||||||
disabled={readOnly}
|
|
||||||
onChange={onChange}
|
|
||||||
className={classnames('InteractExplorer-field-input', 'form-control', {
|
|
||||||
'is-invalid': !value
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
);
|
|
|
@ -4,7 +4,7 @@ import { isAnyOfflineWithWeb3 } from 'selectors/derived';
|
||||||
import {
|
import {
|
||||||
AddressField,
|
AddressField,
|
||||||
AmountField,
|
AmountField,
|
||||||
GasSlider,
|
TXMetaDataPanel,
|
||||||
SendEverything,
|
SendEverything,
|
||||||
CurrentCustomMessage,
|
CurrentCustomMessage,
|
||||||
GenerateTransaction,
|
GenerateTransaction,
|
||||||
|
@ -29,7 +29,7 @@ const content = (
|
||||||
|
|
||||||
<div className="row form-group">
|
<div className="row form-group">
|
||||||
<div className="col-xs-12">
|
<div className="col-xs-12">
|
||||||
<GasSlider />
|
<TXMetaDataPanel />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import BN from 'bn.js';
|
||||||
import { NetworkConfig } from 'config';
|
import { NetworkConfig } from 'config';
|
||||||
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, GasLimitField } from 'components';
|
import { AddressField, AmountField, TXMetaDataPanel } 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,16 @@ 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">
|
||||||
<GasLimitField includeLabel={true} onlyIncludeLoader={false} />
|
<TXMetaDataPanel
|
||||||
|
initialState="advanced"
|
||||||
|
disableToggle={true}
|
||||||
|
advancedGasOptions={{
|
||||||
|
gasPriceField: false,
|
||||||
|
nonceField: false,
|
||||||
|
dataField: false,
|
||||||
|
feeSummary: false
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -34,13 +34,14 @@
|
||||||
}
|
}
|
||||||
&-dropdown {
|
&-dropdown {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0.5rem 0;
|
margin: 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-input {
|
&-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 10rem;
|
max-width: 10rem;
|
||||||
margin-right: $space-sm;
|
margin-right: $space-sm;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-divider {
|
&-divider {
|
||||||
|
@ -54,6 +55,3 @@
|
||||||
margin-top: $space * 2.5;
|
margin-top: $space * 2.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -329,7 +329,7 @@ export default class CurrencySwap extends Component<Props, State> {
|
||||||
<article className="CurrencySwap">
|
<article className="CurrencySwap">
|
||||||
<h1 className="CurrencySwap-title">{translate('SWAP_init_1')}</h1>
|
<h1 className="CurrencySwap-title">{translate('SWAP_init_1')}</h1>
|
||||||
{loaded || timeoutLoaded ? (
|
{loaded || timeoutLoaded ? (
|
||||||
<div className="form-inline CurrencySwap-inner-wrap">
|
<div className="CurrencySwap-inner-wrap">
|
||||||
<div className="CurrencySwap-input-group">
|
<div className="CurrencySwap-input-group">
|
||||||
{originErr && <span className="CurrencySwap-error-message">{originErr}</span>}
|
{originErr && <span className="CurrencySwap-error-message">{originErr}</span>}
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { AmountFieldFactory } from 'components/AmountFieldFactory';
|
||||||
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 { GenerateTransaction, SendButton, SigningStatus, GasSlider } from 'components';
|
import { GenerateTransaction, SendButton, SigningStatus, TXMetaDataPanel } 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';
|
||||||
|
@ -76,7 +76,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">
|
||||||
<GasSlider disableAdvanced={true} />
|
<TXMetaDataPanel initialState={'simple'} disableToggle={true} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SigningStatus />
|
<SigningStatus />
|
||||||
|
|
|
@ -30,6 +30,11 @@ const getTransactionFields = (t: Tx): IHexStrTransaction => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getTransactionFee = (t: Tx) => {
|
||||||
|
const { gasPrice, gasLimit } = getTransactionFields(t);
|
||||||
|
return Wei(gasPrice).mul(Wei(gasLimit));
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Return the minimum amount of ether needed
|
* @description Return the minimum amount of ether needed
|
||||||
* @param t
|
* @param t
|
||||||
|
@ -101,5 +106,6 @@ export {
|
||||||
validateTx,
|
validateTx,
|
||||||
makeTransaction,
|
makeTransaction,
|
||||||
getTransactionFields,
|
getTransactionFields,
|
||||||
|
getTransactionFee,
|
||||||
computeIndexingHash
|
computeIndexingHash
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,6 +20,7 @@ export {
|
||||||
validGasLimit,
|
validGasLimit,
|
||||||
makeTransaction,
|
makeTransaction,
|
||||||
getTransactionFields,
|
getTransactionFields,
|
||||||
|
getTransactionFee,
|
||||||
computeIndexingHash
|
computeIndexingHash
|
||||||
} from './ether';
|
} from './ether';
|
||||||
export * from './token';
|
export * from './token';
|
||||||
|
|
|
@ -33,4 +33,5 @@
|
||||||
@import './styles/overrides';
|
@import './styles/overrides';
|
||||||
@import './styles/scaffolding';
|
@import './styles/scaffolding';
|
||||||
@import './styles/tab';
|
@import './styles/tab';
|
||||||
|
@import './styles/flexbox';
|
||||||
@import './fonts';
|
@import './fonts';
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
.flex-wrapper {
|
||||||
|
display: flex;
|
||||||
|
&-wrap {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
&-nowrap {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
.flex-spacer {
|
||||||
|
flex-grow: 2;
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,6 @@ input[readonly] {
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control {
|
.form-control {
|
||||||
margin-top: $space-sm;
|
|
||||||
margin-bottom: $space-sm;
|
margin-bottom: $space-sm;
|
||||||
transition: $transition;
|
transition: $transition;
|
||||||
padding: $input-padding;
|
padding: $input-padding;
|
||||||
|
|
Loading…
Reference in New Issue