Update code blocks & generate / send tx buttons (#1333)
* Update account view routing * update styles * Fix WalletDecrypt types * Replace disabled textareas with code blocks * Fix broken animation * Update snapshot * Make contract interact dropdowns clearable & searchable * Update node-sass to v4.8.3 * Fix swap inputs incorrectly incorrectly displaying invalid * Refactor send tx & generate tx button * Update broadcast tx & add more transaction details in tx confirmation * Add signing prop to send button * Update lite send * Update codeblock styles * Update snapshot * Revert renaming Dropdown
This commit is contained in:
parent
221e9cf4a3
commit
910093b761
|
@ -8,5 +8,11 @@
|
|||
|
||||
.tx-modal-testnet-warn {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
background-color: $brand-warning;
|
||||
border-radius: 2px;
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-weight: 400;
|
||||
color: white;
|
||||
margin: auto;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
@import 'common/sass/variables';
|
||||
|
||||
.tx-modal-address {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
// Table is necessary here so the size of the div fits to it's content, and margins can be applied to it.
|
||||
// width: fit-content; margin: auto; isn't widely supported, so for now, this is the best option
|
||||
display: table;
|
||||
padding: 1rem 0;
|
||||
align-items: center;
|
||||
margin: auto;
|
||||
.Identicon {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
|
|
@ -33,12 +33,16 @@ class AddressesClass extends Component<StateProps> {
|
|||
<div className="tx-modal-address">
|
||||
<div className="tx-modal-address-from">
|
||||
{from && (
|
||||
<Identicon className="tx-modal-address-from-icon" size={size} address={from} />
|
||||
<React.Fragment>
|
||||
<Identicon className="tx-modal-address-from-icon" size={size} address={from} />
|
||||
<div className="tx-modal-address-from-content">
|
||||
<h5 className="tx-modal-address-from-title">
|
||||
{translate('CONFIRM_TX_FROM')}{' '}
|
||||
</h5>
|
||||
<h5 className="tx-modal-address-from-address small">{from}</h5>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)}
|
||||
<div className="tx-modal-address-from-content">
|
||||
<h5 className="tx-modal-address-from-title">{translate('CONFIRM_TX_FROM')} </h5>
|
||||
<h5 className="tx-modal-address-from-address small">{from}</h5>
|
||||
</div>
|
||||
</div>
|
||||
{isToken && (
|
||||
<div className="tx-modal-address-tkn-contract">
|
||||
|
@ -59,15 +63,19 @@ class AddressesClass extends Component<StateProps> {
|
|||
</div>
|
||||
)}
|
||||
<div className="tx-modal-address-to">
|
||||
<Identicon
|
||||
className="tx-modal-address-from-icon"
|
||||
size={size}
|
||||
address={toFormatted}
|
||||
/>
|
||||
<div className="tx-modal-address-to-content">
|
||||
<h5 className="tx-modal-address-to-title">{translate('CONFIRM_TX_TO')} </h5>
|
||||
<h5 className="small tx-modal-address-to-address">{toFormatted}</h5>
|
||||
</div>
|
||||
{to && (
|
||||
<React.Fragment>
|
||||
<Identicon
|
||||
className="tx-modal-address-from-icon"
|
||||
size={size}
|
||||
address={toFormatted}
|
||||
/>
|
||||
<div className="tx-modal-address-to-content">
|
||||
<h5 className="tx-modal-address-to-title">{translate('CONFIRM_TX_TO')} </h5>
|
||||
<h5 className="small tx-modal-address-to-address">{toFormatted}</h5>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import React, { Component } from 'react';
|
||||
import Code from 'components/ui/Code';
|
||||
import './Details.scss';
|
||||
import { SerializedTransaction } from 'components/renderCbs';
|
||||
import { AppState } from 'reducers';
|
||||
import { getNodeConfig } from 'selectors/config';
|
||||
import { connect } from 'react-redux';
|
||||
import { TokenValue } from 'libs/units';
|
||||
import { NodeConfig } from 'types/node';
|
||||
import translate from 'translations';
|
||||
import { CodeBlock, Input } from 'components/ui';
|
||||
import { addHexPrefix } from 'ethereumjs-util';
|
||||
|
||||
interface StateProps {
|
||||
node: NodeConfig;
|
||||
|
@ -17,21 +18,25 @@ class DetailsClass extends Component<StateProps> {
|
|||
const { node: { network, service } } = this.props;
|
||||
return (
|
||||
<div className="tx-modal-details">
|
||||
<p className="tx-modal-details-network-info">
|
||||
Interacting with the {network} network provided by {service}
|
||||
</p>
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">Network</div>
|
||||
<Input readOnly={true} value={`${network} network - provided by ${service}`} />
|
||||
</label>
|
||||
|
||||
<SerializedTransaction
|
||||
withSerializedTransaction={(_, fields) => {
|
||||
const { chainId, data, to, ...convertRestToBase10 } = fields;
|
||||
const base10Fields = Object.entries(convertRestToBase10).reduce(
|
||||
(convertedFields, [currName, currValue]) => ({
|
||||
...convertedFields,
|
||||
[currName]: TokenValue(currValue).toString()
|
||||
}),
|
||||
{} as typeof convertRestToBase10
|
||||
return (
|
||||
<React.Fragment>
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('SEND_RAW')}</div>
|
||||
<CodeBlock>{JSON.stringify(fields, null, 2)} </CodeBlock>
|
||||
</label>
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('SEND_SIGNED')}</div>
|
||||
<CodeBlock>{addHexPrefix(_)} </CodeBlock>
|
||||
</label>
|
||||
</React.Fragment>
|
||||
);
|
||||
return <Code>{JSON.stringify({ chainId, data, to, ...base10Fields }, null, 2)} </Code>;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
.GenerateTransaction {
|
||||
margin-bottom: 1rem;
|
||||
}
|
|
@ -1,13 +1,24 @@
|
|||
import { GenerateTransactionFactory } from './GenerateTransactionFactory';
|
||||
import React from 'react';
|
||||
import translate from 'translations';
|
||||
import { SigningStatus } from 'components';
|
||||
import './GenerateTransaction.scss';
|
||||
|
||||
export const GenerateTransaction: React.SFC<{}> = () => (
|
||||
<GenerateTransactionFactory
|
||||
withProps={({ disabled, isWeb3Wallet, onClick }) => (
|
||||
<button disabled={disabled} className="btn btn-info btn-block" onClick={onClick}>
|
||||
{isWeb3Wallet ? translate('SEND_GENERATE') : translate('DEP_SIGNTX')}
|
||||
</button>
|
||||
)}
|
||||
/>
|
||||
<React.Fragment>
|
||||
<GenerateTransactionFactory
|
||||
withProps={({ disabled, isWeb3Wallet, onClick }) => (
|
||||
<React.Fragment>
|
||||
<button
|
||||
disabled={disabled}
|
||||
className="btn btn-info btn-block GenerateTransaction"
|
||||
onClick={onClick}
|
||||
>
|
||||
{isWeb3Wallet ? translate('SEND_GENERATE') : translate('DEP_SIGNTX')}
|
||||
</button>
|
||||
</React.Fragment>
|
||||
)}
|
||||
/>
|
||||
<SigningStatus />
|
||||
</React.Fragment>
|
||||
);
|
||||
|
|
|
@ -7,9 +7,16 @@ import {
|
|||
getTransaction,
|
||||
isNetworkRequestPending,
|
||||
isValidGasPrice,
|
||||
isValidGasLimit
|
||||
isValidGasLimit,
|
||||
getSignedTx,
|
||||
getSerializedTransaction
|
||||
} from 'selectors/transaction';
|
||||
import { getWalletType } from 'selectors/wallet';
|
||||
import { getWalletType, IWalletType } from 'selectors/wallet';
|
||||
import { OfflineBroadcast } from 'components/SendButtonFactory/OfflineBroadcast';
|
||||
import { getTransactionFields, makeTransaction } from 'libs/transaction';
|
||||
import translate from 'translations';
|
||||
import { addHexPrefix } from 'ethereumjs-util';
|
||||
import { CodeBlock } from 'components/ui';
|
||||
|
||||
export interface CallbackProps {
|
||||
disabled: boolean;
|
||||
|
@ -19,11 +26,14 @@ export interface CallbackProps {
|
|||
|
||||
interface StateProps {
|
||||
transaction: EthTx;
|
||||
walletType: IWalletType;
|
||||
serializedTransaction: AppState['transaction']['sign']['local']['signedTransaction'];
|
||||
networkRequestPending: boolean;
|
||||
isFullTransaction: boolean;
|
||||
isWeb3Wallet: boolean;
|
||||
validGasPrice: boolean;
|
||||
validGasLimit: boolean;
|
||||
signedTx: boolean;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
|
@ -35,35 +45,68 @@ type Props = OwnProps & StateProps;
|
|||
class GenerateTransactionFactoryClass extends Component<Props> {
|
||||
public render() {
|
||||
const {
|
||||
walletType,
|
||||
serializedTransaction,
|
||||
isFullTransaction,
|
||||
isWeb3Wallet,
|
||||
networkRequestPending,
|
||||
validGasPrice,
|
||||
validGasLimit,
|
||||
transaction
|
||||
transaction,
|
||||
signedTx
|
||||
} = this.props;
|
||||
|
||||
const getStringifiedTx = (serializedTx: Buffer) =>
|
||||
JSON.stringify(getTransactionFields(makeTransaction(serializedTx)), null, 2);
|
||||
|
||||
const isButtonDisabled =
|
||||
!isFullTransaction || networkRequestPending || !validGasPrice || !validGasLimit;
|
||||
return (
|
||||
<WithSigner
|
||||
isWeb3={isWeb3Wallet}
|
||||
withSigner={signer =>
|
||||
this.props.withProps({
|
||||
disabled: isButtonDisabled,
|
||||
isWeb3Wallet,
|
||||
onClick: () => signer(transaction)
|
||||
})
|
||||
}
|
||||
/>
|
||||
<React.Fragment>
|
||||
<WithSigner
|
||||
isWeb3={isWeb3Wallet}
|
||||
withSigner={signer =>
|
||||
this.props.withProps({
|
||||
disabled: isButtonDisabled,
|
||||
isWeb3Wallet,
|
||||
onClick: () => signer(transaction)
|
||||
})
|
||||
}
|
||||
/>
|
||||
{signedTx && (
|
||||
<React.Fragment>
|
||||
{/* shows the json representation of the transaction */}
|
||||
<div className="col-xs-12">
|
||||
<label>
|
||||
{walletType.isWeb3Wallet ? 'Transaction Parameters' : translate('SEND_RAW')}
|
||||
</label>
|
||||
<CodeBlock>{getStringifiedTx(serializedTransaction as Buffer)}</CodeBlock>
|
||||
</div>
|
||||
{serializedTransaction && (
|
||||
<div className="col-xs-12">
|
||||
<label>
|
||||
{walletType.isWeb3Wallet
|
||||
? 'Serialized Transaction Parameters'
|
||||
: translate('SEND_SIGNED')}
|
||||
</label>
|
||||
<CodeBlock>{addHexPrefix(serializedTransaction.toString('hex'))}</CodeBlock>
|
||||
</div>
|
||||
)}
|
||||
<OfflineBroadcast />
|
||||
</React.Fragment>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const GenerateTransactionFactory = connect((state: AppState) => ({
|
||||
...getTransaction(state),
|
||||
walletType: getWalletType(state),
|
||||
serializedTransaction: getSerializedTransaction(state),
|
||||
networkRequestPending: isNetworkRequestPending(state),
|
||||
isWeb3Wallet: getWalletType(state).isWeb3Wallet,
|
||||
validGasPrice: isValidGasPrice(state),
|
||||
validGasLimit: isValidGasLimit(state)
|
||||
validGasLimit: isValidGasLimit(state),
|
||||
signedTx: !!getSignedTx(state)
|
||||
}))(GenerateTransactionFactoryClass);
|
||||
|
|
|
@ -12,8 +12,7 @@ import {
|
|||
getStaticNetworkConfigs
|
||||
} from 'selectors/config';
|
||||
import { CustomNode } from 'libs/nodes';
|
||||
import { Input } from 'components/ui';
|
||||
import Dropdown from 'components/ui/Dropdown';
|
||||
import { Input, Dropdown } from 'components/ui';
|
||||
import './CustomNodeModal.scss';
|
||||
|
||||
const CUSTOM = { label: 'Custom', value: 'custom' };
|
||||
|
@ -128,7 +127,6 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
<label className="col-sm-3 input-group">
|
||||
<div className="input-group-header">Network</div>
|
||||
<Dropdown
|
||||
className="input-group-dropdown"
|
||||
value={network}
|
||||
options={options}
|
||||
clearable={false}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
.SendButton {
|
||||
margin-bottom: 1rem;
|
||||
}
|
|
@ -2,24 +2,32 @@ import React from 'react';
|
|||
import { SendButtonFactory } from './SendButtonFactory';
|
||||
import translate from 'translations';
|
||||
import { ConfirmationModal } from 'components/ConfirmationModal';
|
||||
import { SigningStatus } from 'components';
|
||||
import './SendButton.scss';
|
||||
|
||||
export const SendButton: React.SFC<{
|
||||
onlyTransactionParameters?: boolean;
|
||||
toggleDisabled?: boolean;
|
||||
className?: string;
|
||||
signing?: boolean;
|
||||
customModal?: typeof ConfirmationModal;
|
||||
}> = ({ onlyTransactionParameters, toggleDisabled, customModal }) => (
|
||||
<SendButtonFactory
|
||||
onlyTransactionParameters={!!onlyTransactionParameters}
|
||||
toggleDisabled={toggleDisabled}
|
||||
Modal={customModal ? customModal : ConfirmationModal}
|
||||
withProps={({ disabled, onClick }: { disabled: boolean; onClick(): void }) => (
|
||||
<div className="row form-group">
|
||||
<div className="col-xs-12">
|
||||
<button disabled={disabled} className="btn btn-primary btn-block" onClick={onClick}>
|
||||
}> = ({ signing, customModal, className }) => (
|
||||
<React.Fragment>
|
||||
<SendButtonFactory
|
||||
signing={signing}
|
||||
Modal={customModal ? customModal : ConfirmationModal}
|
||||
withProps={({ disabled, openModal, signTx }) => (
|
||||
<React.Fragment>
|
||||
<button
|
||||
disabled={disabled}
|
||||
className={`SendButton btn btn-primary btn-block ${className}`}
|
||||
onClick={() => {
|
||||
!!signing ? (signTx(), openModal()) : openModal();
|
||||
}}
|
||||
>
|
||||
{translate('SEND_TRANS')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)}
|
||||
/>
|
||||
<SigningStatus />
|
||||
</React.Fragment>
|
||||
);
|
||||
|
|
|
@ -2,16 +2,29 @@ import React, { Component } from 'react';
|
|||
import { getOffline } from 'selectors/config';
|
||||
import { AppState } from 'reducers';
|
||||
import { connect } from 'react-redux';
|
||||
import { getCurrentTransactionStatus, currentTransactionBroadcasted } from 'selectors/transaction';
|
||||
import {
|
||||
getCurrentTransactionStatus,
|
||||
currentTransactionBroadcasted,
|
||||
signaturePending,
|
||||
getSignedTx,
|
||||
getWeb3Tx
|
||||
} from 'selectors/transaction';
|
||||
import { showNotification, TShowNotification } from 'actions/notifications';
|
||||
import { ITransactionStatus } from 'reducers/transaction/broadcast';
|
||||
import { reset, TReset } from 'actions/transaction';
|
||||
import {
|
||||
reset,
|
||||
TReset,
|
||||
TSignTransactionRequested,
|
||||
signTransactionRequested
|
||||
} from 'actions/transaction';
|
||||
import { ConfirmationModal } from 'components/ConfirmationModal';
|
||||
|
||||
interface StateProps {
|
||||
offline: boolean;
|
||||
currentTransaction: false | ITransactionStatus | null;
|
||||
transactionBroadcasted: boolean;
|
||||
signaturePending: boolean;
|
||||
signedTx: boolean;
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -21,11 +34,15 @@ interface State {
|
|||
interface DispatchProps {
|
||||
showNotification: TShowNotification;
|
||||
reset: TReset;
|
||||
signTransactionRequested: TSignTransactionRequested;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
Modal: typeof ConfirmationModal;
|
||||
withOnClick(onClick: { onClick(): void }): React.ReactElement<any> | null;
|
||||
withOnClick(onClick: {
|
||||
openModal(): void;
|
||||
signer(signer: any): void;
|
||||
}): React.ReactElement<any> | null;
|
||||
}
|
||||
|
||||
const INITIAL_STATE: State = {
|
||||
|
@ -38,12 +55,18 @@ class OnlineSendClass extends Component<Props, State> {
|
|||
public state: State = INITIAL_STATE;
|
||||
|
||||
public render() {
|
||||
return !this.props.offline ? (
|
||||
return (
|
||||
<React.Fragment>
|
||||
{this.props.withOnClick({ onClick: this.openModal })}
|
||||
<this.props.Modal isOpen={this.state.showModal} onClose={this.closeModal} />
|
||||
{this.props.withOnClick({
|
||||
openModal: this.openModal,
|
||||
signer: this.props.signTransactionRequested
|
||||
})}
|
||||
<this.props.Modal
|
||||
isOpen={!this.props.signaturePending && this.props.signedTx && this.state.showModal}
|
||||
onClose={this.closeModal}
|
||||
/>
|
||||
</React.Fragment>
|
||||
) : null;
|
||||
);
|
||||
}
|
||||
|
||||
public componentWillReceiveProps(nextProps: Props) {
|
||||
|
@ -73,7 +96,9 @@ export const OnlineSend = connect(
|
|||
(state: AppState) => ({
|
||||
offline: getOffline(state),
|
||||
currentTransaction: getCurrentTransactionStatus(state),
|
||||
transactionBroadcasted: currentTransactionBroadcasted(state)
|
||||
transactionBroadcasted: currentTransactionBroadcasted(state),
|
||||
signaturePending: signaturePending(state).isSignaturePending,
|
||||
signedTx: !!getSignedTx(state) || !!getWeb3Tx(state)
|
||||
}),
|
||||
{ showNotification, reset }
|
||||
{ showNotification, reset, signTransactionRequested }
|
||||
)(OnlineSendClass);
|
||||
|
|
|
@ -1,102 +1,89 @@
|
|||
import translate from 'translations';
|
||||
import { getTransactionFields, makeTransaction } from 'libs/transaction';
|
||||
import { OfflineBroadcast } from './OfflineBroadcast';
|
||||
import EthTx from 'ethereumjs-tx';
|
||||
import { OnlineSend } from './OnlineSend';
|
||||
import { addHexPrefix } from 'ethereumjs-util';
|
||||
import { getWalletType, IWalletType } from 'selectors/wallet';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
import { ConfirmationModal } from 'components/ConfirmationModal';
|
||||
import { TextArea } from 'components/ui';
|
||||
import { getSerializedTransaction } from 'selectors/transaction';
|
||||
import {
|
||||
getSerializedTransaction,
|
||||
getTransaction,
|
||||
isNetworkRequestPending,
|
||||
isValidGasPrice,
|
||||
isValidGasLimit,
|
||||
getSignedTx,
|
||||
getWeb3Tx
|
||||
} from 'selectors/transaction';
|
||||
|
||||
export interface CallbackProps {
|
||||
disabled: boolean;
|
||||
onClick(): void;
|
||||
signTx(): void;
|
||||
openModal(): void;
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
walletType: IWalletType;
|
||||
serializedTransaction: AppState['transaction']['sign']['local']['signedTransaction'];
|
||||
transaction: EthTx;
|
||||
isFullTransaction: boolean;
|
||||
networkRequestPending: boolean;
|
||||
validGasPrice: boolean;
|
||||
validGasLimit: boolean;
|
||||
signedTx: boolean;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
onlyTransactionParameters?: boolean;
|
||||
toggleDisabled?: boolean;
|
||||
signing?: boolean;
|
||||
Modal: typeof ConfirmationModal;
|
||||
withProps(props: CallbackProps): React.ReactElement<any> | null;
|
||||
}
|
||||
|
||||
const getStringifiedTx = (serializedTransaction: Buffer) =>
|
||||
JSON.stringify(getTransactionFields(makeTransaction(serializedTransaction)), null, 2);
|
||||
|
||||
type Props = StateProps & OwnProps;
|
||||
|
||||
class SendButtonFactoryClass extends Component<Props> {
|
||||
public render() {
|
||||
const {
|
||||
onlyTransactionParameters,
|
||||
signing,
|
||||
signedTx,
|
||||
transaction,
|
||||
isFullTransaction,
|
||||
serializedTransaction,
|
||||
toggleDisabled,
|
||||
walletType
|
||||
networkRequestPending,
|
||||
validGasPrice,
|
||||
validGasLimit
|
||||
} = this.props;
|
||||
const columnSize = onlyTransactionParameters ? 12 : 6;
|
||||
|
||||
/* Left and right transaction comparision boxes, only displayed when a serialized transaction
|
||||
exists in state */
|
||||
|
||||
// shows the json representation of the transaction
|
||||
const leftTxCompare = serializedTransaction && (
|
||||
<div className={`col-sm-${columnSize}`}>
|
||||
<label>{walletType.isWeb3Wallet ? 'Transaction Parameters' : translate('SEND_RAW')}</label>
|
||||
<TextArea value={getStringifiedTx(serializedTransaction)} rows={4} readOnly={true} />
|
||||
</div>
|
||||
);
|
||||
|
||||
// shows the serialized representation of the transaction
|
||||
// "onlyTransactionParameters" used in broadcast tx so the same serialized tx isnt redundantly
|
||||
// displayed
|
||||
const rightTxCompare = serializedTransaction &&
|
||||
!onlyTransactionParameters && (
|
||||
<div className="col-sm-6">
|
||||
<label>
|
||||
{walletType.isWeb3Wallet
|
||||
? 'Serialized Transaction Parameters'
|
||||
: translate('SEND_SIGNED')}
|
||||
</label>
|
||||
<TextArea
|
||||
value={addHexPrefix(serializedTransaction.toString('hex'))}
|
||||
rows={4}
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const shouldDisplayOnlineSend = toggleDisabled || serializedTransaction;
|
||||
|
||||
// return signing ? true : signedTx ? true : false
|
||||
return (
|
||||
<>
|
||||
{leftTxCompare}
|
||||
{rightTxCompare}
|
||||
<OfflineBroadcast />
|
||||
{shouldDisplayOnlineSend && (
|
||||
<OnlineSend
|
||||
withOnClick={({ onClick }) =>
|
||||
this.props.withProps({
|
||||
disabled: !!(toggleDisabled && !serializedTransaction),
|
||||
onClick
|
||||
})
|
||||
}
|
||||
Modal={this.props.Modal}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
(signing || (!signing && signedTx)) && (
|
||||
<OnlineSend
|
||||
withOnClick={({ openModal, signer }) =>
|
||||
this.props.withProps({
|
||||
disabled: signing
|
||||
? !isFullTransaction || networkRequestPending || !validGasPrice || !validGasLimit
|
||||
: !!(signing && !serializedTransaction),
|
||||
signTx: () => signer(transaction),
|
||||
openModal
|
||||
})
|
||||
}
|
||||
Modal={this.props.Modal}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const SendButtonFactory = connect((state: AppState) => ({
|
||||
walletType: getWalletType(state),
|
||||
serializedTransaction: getSerializedTransaction(state)
|
||||
}))(SendButtonFactoryClass);
|
||||
const mapStateToProps = (state: AppState) => {
|
||||
return {
|
||||
walletType: getWalletType(state),
|
||||
serializedTransaction: getSerializedTransaction(state),
|
||||
...getTransaction(state),
|
||||
networkRequestPending: isNetworkRequestPending(state),
|
||||
validGasPrice: isValidGasPrice(state),
|
||||
validGasLimit: isValidGasLimit(state),
|
||||
signedTx: !!getSignedTx(state) || !!getWeb3Tx(state)
|
||||
};
|
||||
};
|
||||
|
||||
export const SendButtonFactory = connect(mapStateToProps)(SendButtonFactoryClass);
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
.SigningStatus {
|
||||
margin-bottom: 1rem;
|
||||
}
|
|
@ -3,7 +3,8 @@ import { connect } from 'react-redux';
|
|||
import { AppState } from 'reducers';
|
||||
import { signaturePending } from 'selectors/transaction';
|
||||
import { Spinner } from 'components/ui';
|
||||
import translate from 'translations';
|
||||
import './SigningStatus.scss';
|
||||
import { translate } from 'translations';
|
||||
interface StateProps {
|
||||
isSignaturePending: boolean;
|
||||
isHardwareWallet: boolean;
|
||||
|
@ -13,7 +14,7 @@ class SigningStatusClass extends Component<StateProps> {
|
|||
public render() {
|
||||
const { isHardwareWallet, isSignaturePending } = this.props;
|
||||
|
||||
const HWWalletPrompt: React.SFC<{}> = _ =>
|
||||
const HWWalletPrompt: React.SFC<{}> = () =>
|
||||
isHardwareWallet ? (
|
||||
<p>
|
||||
<b>{translate('CONFIRM_HARDWARE_WALLET_TRANSACTION')}</b>
|
||||
|
@ -21,11 +22,9 @@ class SigningStatusClass extends Component<StateProps> {
|
|||
) : null;
|
||||
|
||||
return isSignaturePending ? (
|
||||
<div className="container">
|
||||
<div className="row form-group text-center">
|
||||
<HWWalletPrompt />
|
||||
<Spinner size="x2" />
|
||||
</div>
|
||||
<div className="SigningStatus text-center">
|
||||
<HWWalletPrompt />
|
||||
<Spinner size="x2" />
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import translate from 'translations';
|
||||
import { Identicon, UnitDisplay, NewTabLink, TextArea, Address } from 'components/ui';
|
||||
import { Identicon, UnitDisplay, NewTabLink, Address, CodeBlock } from 'components/ui';
|
||||
import { TransactionData, TransactionReceipt } from 'types/transactions';
|
||||
import { NetworkConfig } from 'types/network';
|
||||
import './TransactionDataTable.scss';
|
||||
|
@ -152,7 +152,7 @@ const TransactionDataTable: React.SFC<Props> = ({ data, receipt, network }) => {
|
|||
},
|
||||
{
|
||||
label: translate('TRANS_DATA'),
|
||||
data: hasInputData ? <TextArea value={data.input} disabled={true} /> : null
|
||||
data: hasInputData ? <CodeBlock>{data.input}</CodeBlock> : null
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React, { Component } from 'react';
|
||||
import { setUnitMeta, TSetUnitMeta } from 'actions/transaction';
|
||||
import Dropdown from 'components/ui/Dropdown';
|
||||
import { TokenBalance, MergedToken, getShownTokenBalances, getTokens } from 'selectors/wallet';
|
||||
import { Query } from 'components/renderCbs';
|
||||
import { connect } from 'react-redux';
|
||||
|
@ -8,6 +7,7 @@ import { AppState } from 'reducers';
|
|||
import { getUnit } from 'selectors/transaction';
|
||||
import { getNetworkUnit } from 'selectors/config';
|
||||
import { Option } from 'react-select';
|
||||
import { Dropdown } from 'components/ui';
|
||||
|
||||
interface DispatchProps {
|
||||
setUnitMeta: TSetUnitMeta;
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
pre {
|
||||
color: #333;
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #ececec;
|
||||
padding: 0.5rem 1rem;
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
border-radius: 5px;
|
||||
code {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import React from 'react';
|
||||
import './Code.scss';
|
||||
|
||||
const Code = ({ children }: React.Props<{}>) => (
|
||||
<pre>
|
||||
<code>{children}</code>
|
||||
</pre>
|
||||
);
|
||||
|
||||
export default Code;
|
|
@ -0,0 +1,32 @@
|
|||
@import 'common/sass/variables';
|
||||
@import 'common/sass/mixins';
|
||||
|
||||
.CodeBlock {
|
||||
font-weight: 400;
|
||||
font-size: 1rem;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
background-color: $gray-lightest;
|
||||
border: 1px solid $border-disabled;
|
||||
box-shadow: inset 0 1px 0 0 rgba(63, 63, 68, 0.05);
|
||||
padding: 0.75rem 1rem;
|
||||
margin: 0;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1rem;
|
||||
border-radius: 2px;
|
||||
overflow: auto;
|
||||
& > code {
|
||||
display: block;
|
||||
text-align: left;
|
||||
max-height: 320px;
|
||||
border: none;
|
||||
background-color: inherit;
|
||||
font-size: 14px;
|
||||
white-space: pre;
|
||||
@include mono;
|
||||
}
|
||||
&.wrap {
|
||||
& > code {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import React from 'react';
|
||||
import './CodeBlock.scss';
|
||||
|
||||
interface Props {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const CodeBlock = ({ children, className }: Props) => (
|
||||
<pre className={`${className} CodeBlock`}>
|
||||
<code>{children}</code>
|
||||
</pre>
|
||||
);
|
||||
|
||||
export default CodeBlock;
|
|
@ -12,6 +12,11 @@
|
|||
> .TogglablePassword {
|
||||
width: 100%;
|
||||
}
|
||||
& > pre,
|
||||
& > .Select,
|
||||
&-input {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
&-inline {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@ -45,12 +50,9 @@
|
|||
color: rgba(0, 0, 0, 0.54);
|
||||
}
|
||||
}
|
||||
&-dropdown {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
&-input {
|
||||
width: 100%;
|
||||
border: 1px solid #e5ecf3;
|
||||
border: 1px solid $border-idle;
|
||||
border-radius: 2px;
|
||||
height: $input-height-base;
|
||||
padding: 0.75rem 1rem;
|
||||
|
|
|
@ -13,10 +13,10 @@ interface Props {
|
|||
}
|
||||
|
||||
export default class ModalBody extends React.Component<Props> {
|
||||
public firstTabStop: HTMLElement;
|
||||
private modal: HTMLElement;
|
||||
private modalContent: HTMLElement;
|
||||
private focusedElementBeforeModal: HTMLElement;
|
||||
private firstTabStop: HTMLElement;
|
||||
private lastTabStop: HTMLElement;
|
||||
|
||||
public componentDidMount() {
|
||||
|
@ -32,9 +32,6 @@ export default class ModalBody extends React.Component<Props> {
|
|||
this.firstTabStop = focusableElements[0];
|
||||
this.lastTabStop = focusableElements[focusableElements.length - 1];
|
||||
|
||||
// Focus first child
|
||||
this.firstTabStop.focus();
|
||||
|
||||
this.modal.addEventListener('keydown', this.keyDownListener);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { PureComponent } from 'react';
|
||||
import { CSSTransition, TransitionGroup } from 'react-transition-group';
|
||||
import ModalBody from './ModalBody';
|
||||
import { TransitionGroup, CSSTransition } from 'react-transition-group';
|
||||
import './index.scss';
|
||||
|
||||
export interface IButton {
|
||||
|
@ -23,10 +23,8 @@ interface ModalStyle {
|
|||
maxWidth?: string;
|
||||
}
|
||||
|
||||
const Fade = ({ children, ...props }: any) => (
|
||||
<CSSTransition {...props} timeout={300} classNames="animate-modal">
|
||||
{children}
|
||||
</CSSTransition>
|
||||
const Fade = ({ ...props }: any) => (
|
||||
<CSSTransition {...props} timeout={300} classNames="animate-modal" />
|
||||
);
|
||||
|
||||
export default class Modal extends PureComponent<Props, {}> {
|
||||
|
@ -57,7 +55,8 @@ export default class Modal extends PureComponent<Props, {}> {
|
|||
return (
|
||||
<TransitionGroup>
|
||||
{isOpen && (
|
||||
<Fade>
|
||||
// Trap focus in modal by focusing the first element after the animation is complete
|
||||
<Fade onEntered={() => this.modalBody.firstTabStop.focus()}>
|
||||
<div>
|
||||
<div className="Modal-overlay" onClick={handleClose} />
|
||||
<ModalBody {...modalBodyProps} ref={div => (this.modalBody = div as ModalBody)} />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export { default as Dropdown } from './Dropdown';
|
||||
export { default as ColorDropdown } from './ColorDropdown';
|
||||
export { default as OldDropDown } from './OldDropdown';
|
||||
export { default as DropDown } from './Dropdown';
|
||||
export { default as DropdownShell } from './DropdownShell';
|
||||
export { default as Identicon } from './Identicon';
|
||||
export { default as Modal } from './Modal';
|
||||
|
@ -16,6 +16,7 @@ export { default as HelpLink } from './HelpLink';
|
|||
export { default as Input } from './Input';
|
||||
export { default as TextArea } from './TextArea';
|
||||
export { default as Address } from './Address';
|
||||
export { default as CodeBlock } from './CodeBlock';
|
||||
export * from './ConditionalInput';
|
||||
export * from './Expandable';
|
||||
export * from './InlineSpinner';
|
||||
|
|
|
@ -8,8 +8,8 @@ import {
|
|||
signTransactionFailed,
|
||||
TSignTransactionFailed
|
||||
} from 'actions/transaction';
|
||||
import { computeIndexingHash } from 'libs/transaction';
|
||||
import { QRCode, TextArea } from 'components/ui';
|
||||
import { computeIndexingHash, getTransactionFields, makeTransaction } from 'libs/transaction';
|
||||
import { QRCode, Input, CodeBlock } from 'components/ui';
|
||||
import EthTx from 'ethereumjs-tx';
|
||||
import { SendButton } from 'components/SendButton';
|
||||
import { toBuffer, bufferToHex } from 'ethereumjs-util';
|
||||
|
@ -33,6 +33,9 @@ const INITIAL_STATE: State = { userInput: '' };
|
|||
|
||||
type Props = DispatchProps & StateProps & RouteComponentProps<{}>;
|
||||
|
||||
const getStringifiedTx = (serializedTx: Buffer) =>
|
||||
JSON.stringify(getTransactionFields(makeTransaction(serializedTx)), null, 2);
|
||||
|
||||
class BroadcastTx extends Component<Props> {
|
||||
public state: State = INITIAL_STATE;
|
||||
|
||||
|
@ -42,29 +45,41 @@ class BroadcastTx extends Component<Props> {
|
|||
const currentPath = this.props.match.url;
|
||||
return (
|
||||
<TabSection isUnavailableOffline={true}>
|
||||
<div className="Tab-content-pane row block text-center">
|
||||
<div className="Tab-content-pane row block">
|
||||
<Switch>
|
||||
<Route
|
||||
exact={true}
|
||||
path={currentPath}
|
||||
render={() => (
|
||||
<div className="BroadcastTx">
|
||||
<h1 className="BroadcastTx-title">{translate('BROADCAST_TX_TITLE')}</h1>
|
||||
<p className="BroadcastTx-help">{translate('BROADCAST_TX_DESCRIPTION')}</p>
|
||||
<h1 className="BroadcastTx-title text-center">
|
||||
{translate('BROADCAST_TX_TITLE')}
|
||||
</h1>
|
||||
<p className="BroadcastTx-help text-center">
|
||||
{translate('BROADCAST_TX_DESCRIPTION')}
|
||||
</p>
|
||||
|
||||
<div className="input-group-wrapper InteractForm-interface">
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('SEND_SIGNED')}</div>
|
||||
<TextArea
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="0xf86b0284ee6b2800825208944bbeeb066ed09b7aed07bf39eee0460dfa26152088016345785d8a00008029a03ba7a0cc6d1756cd771f2119cf688b6d4dc9d37096089f0331fe0de0d1cc1254a02f7bcd19854c8d46f8de09e457aec25b127ab4328e1c0d24bfbff8702ee1f474"
|
||||
className={stateTransaction ? '' : 'invalid'}
|
||||
rows={7}
|
||||
value={userInput}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<SendButton toggleDisabled={true} onlyTransactionParameters={true} />
|
||||
{stateTransaction && (
|
||||
<React.Fragment>
|
||||
<label>{translate('SEND_RAW')}</label>
|
||||
<CodeBlock>{getStringifiedTx(stateTransaction)}</CodeBlock>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
<SendButton className="form-group" />
|
||||
|
||||
<div className="BroadcastTx-qr">
|
||||
{stateTransaction && <QRCode data={bufferToHex(stateTransaction)} />}
|
||||
|
@ -79,7 +94,7 @@ class BroadcastTx extends Component<Props> {
|
|||
);
|
||||
}
|
||||
|
||||
protected handleChange = ({ currentTarget }: React.FormEvent<HTMLTextAreaElement>) => {
|
||||
protected handleChange = ({ currentTarget }: React.FormEvent<HTMLInputElement>) => {
|
||||
const { value } = currentTarget;
|
||||
this.setState({ userInput: value });
|
||||
try {
|
||||
|
|
|
@ -11,4 +11,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
&-submit {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,13 @@ import translate from 'translations';
|
|||
import classnames from 'classnames';
|
||||
import { DataFieldFactory } from 'components/DataFieldFactory';
|
||||
import { SendButtonFactory } from 'components/SendButtonFactory';
|
||||
import { SigningStatus } from 'components/SigningStatus';
|
||||
import WalletDecrypt, { DISABLE_WALLETS } from 'components/WalletDecrypt';
|
||||
import { GenerateTransaction } from 'components/GenerateTransaction';
|
||||
import React, { Component } from 'react';
|
||||
import { setToField, TSetToField } from 'actions/transaction';
|
||||
import { resetWallet, TResetWallet } from 'actions/wallet';
|
||||
import { connect } from 'react-redux';
|
||||
import { FullWalletOnly } from 'components/renderCbs';
|
||||
import { NonceField, TXMetaDataPanel } from 'components';
|
||||
import { NonceField, TXMetaDataPanel, SigningStatus } from 'components';
|
||||
import './Deploy.scss';
|
||||
import { ConfirmationModal } from 'components/ConfirmationModal';
|
||||
import { TextArea } from 'components/ui';
|
||||
|
@ -40,7 +38,7 @@ class DeployClass extends Component<DispatchProps> {
|
|||
rows={6}
|
||||
onChange={onChange}
|
||||
disabled={readOnly}
|
||||
className={classnames('Deploy-field-input', 'form-control', {
|
||||
className={classnames('Deploy-field-input', {
|
||||
'is-valid': value && value.length > 0
|
||||
})}
|
||||
value={raw}
|
||||
|
@ -66,20 +64,23 @@ class DeployClass extends Component<DispatchProps> {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row form-group">
|
||||
<div className="col-xs-12 clearfix">
|
||||
<GenerateTransaction />
|
||||
</div>
|
||||
</div>
|
||||
<SigningStatus />
|
||||
<SendButtonFactory
|
||||
signing={true}
|
||||
Modal={ConfirmationModal}
|
||||
withProps={({ onClick }) => (
|
||||
<button className="Deploy-submit btn btn-primary" onClick={onClick}>
|
||||
withProps={({ disabled, signTx, openModal }) => (
|
||||
<button
|
||||
disabled={disabled}
|
||||
className="Deploy-submit btn btn-primary btn-block"
|
||||
onClick={() => {
|
||||
signTx();
|
||||
openModal();
|
||||
}}
|
||||
>
|
||||
{translate('NAV_DEPLOYCONTRACT')}
|
||||
</button>
|
||||
)}
|
||||
/>
|
||||
<SigningStatus />
|
||||
</main>
|
||||
);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { AmountField } from './AmountField';
|
||||
import React, { Component } from 'react';
|
||||
import { SendButton, SigningStatus, TXMetaDataPanel } from 'components';
|
||||
import { SendButton, TXMetaDataPanel } from 'components';
|
||||
import WalletDecrypt, { DISABLE_WALLETS } from 'components/WalletDecrypt';
|
||||
import { FullWalletOnly } from 'components/renderCbs';
|
||||
|
||||
|
@ -21,7 +21,6 @@ export class Fields extends Component<OwnProps> {
|
|||
resetIncludeExcludeProperties={{ exclude: { fields: ['to'] }, include: {} }}
|
||||
/>
|
||||
{this.props.button}
|
||||
<SigningStatus />
|
||||
<SendButton />
|
||||
</React.Fragment>
|
||||
);
|
||||
|
|
|
@ -12,8 +12,7 @@ import { setDataField, TSetDataField } from 'actions/transaction';
|
|||
import { Data } from 'libs/units';
|
||||
import { Web3Node } from 'libs/nodes';
|
||||
import RpcNode from 'libs/nodes/rpc';
|
||||
import { Input } from 'components/ui';
|
||||
import Dropdown from 'components/ui/Dropdown';
|
||||
import { Input, Dropdown } from 'components/ui';
|
||||
|
||||
interface StateProps {
|
||||
nodeLib: RpcNode | Web3Node;
|
||||
|
@ -97,7 +96,8 @@ class InteractExplorerClass extends Component<Props, State> {
|
|||
placeholder={translate('SELECT_A_THING', { $thing: 'function' })}
|
||||
onChange={this.handleFunctionSelect}
|
||||
options={contractFunctionsOptions}
|
||||
clearable={false}
|
||||
clearable={true}
|
||||
searchable={true}
|
||||
labelKey="name"
|
||||
valueKey="contract"
|
||||
/>
|
||||
|
|
|
@ -7,8 +7,7 @@ import { isValidETHAddress, isValidAbiJson } from 'libs/validators';
|
|||
import classnames from 'classnames';
|
||||
import { NetworkContract } from 'types/network';
|
||||
import { donationAddressMap } from 'config';
|
||||
import { Input, TextArea } from 'components/ui';
|
||||
import Dropdown from 'components/ui/Dropdown';
|
||||
import { Input, TextArea, CodeBlock, Dropdown } from 'components/ui';
|
||||
|
||||
interface ContractOption {
|
||||
name: string;
|
||||
|
@ -68,22 +67,39 @@ class InteractForm extends Component<Props, State> {
|
|||
const validEthAddress = isValidETHAddress(address);
|
||||
const validAbiJson = isValidAbiJson(abiJson);
|
||||
const showContractAccessButton = validEthAddress && validAbiJson;
|
||||
let contractOptions: ContractOption[] = [];
|
||||
let options: ContractOption[] = [];
|
||||
|
||||
if (this.isContractsValid()) {
|
||||
contractOptions = contracts.map(con => {
|
||||
const contractOptions = contracts.map(con => {
|
||||
const addr = con.address ? `(${con.address.substr(0, 10)}...)` : '';
|
||||
return {
|
||||
name: `${con.name} ${addr}`,
|
||||
value: this.makeContractValue(con)
|
||||
};
|
||||
});
|
||||
options = [{ name: 'Custom', value: '' }, ...contractOptions];
|
||||
}
|
||||
|
||||
// TODO: Use common components for address, abi json
|
||||
return (
|
||||
<div className="InteractForm">
|
||||
<div className="InteractForm-address row">
|
||||
<div className="input-group-wrapper InteractForm-address-field col-sm-6">
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('CONTRACT_TITLE_2')}</div>
|
||||
<Dropdown
|
||||
className={`${!contract ? 'invalid' : ''}`}
|
||||
value={contract as any}
|
||||
placeholder={this.state.contractPlaceholder}
|
||||
onChange={this.handleSelectContract}
|
||||
options={options}
|
||||
searchable={true}
|
||||
clearable={true}
|
||||
labelKey="name"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="input-group-wrapper InteractForm-address-field col-sm-6">
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('CONTRACT_TITLE')}</div>
|
||||
|
@ -99,26 +115,23 @@ class InteractForm extends Component<Props, State> {
|
|||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="input-group-wrapper InteractForm-address-field col-sm-6">
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('CONTRACT_TITLE_2')}</div>
|
||||
<Dropdown
|
||||
className={`${!contract ? 'invalid' : ''}`}
|
||||
value={contract as any}
|
||||
placeholder={this.state.contractPlaceholder}
|
||||
onChange={this.handleSelectContract}
|
||||
options={contractOptions}
|
||||
clearable={false}
|
||||
labelKey="name"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="input-group-wrapper InteractForm-interface">
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('CONTRACT_JSON')}</div>
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('CONTRACT_JSON')}</div>
|
||||
{!!contract ? (
|
||||
contract.name === 'Custom' ? (
|
||||
<TextArea
|
||||
placeholder={this.abiJsonPlaceholder}
|
||||
className={`InteractForm-interface-field-input ${validAbiJson ? '' : 'invalid'}`}
|
||||
onChange={this.handleInput('abiJson')}
|
||||
value={abiJson}
|
||||
rows={6}
|
||||
/>
|
||||
) : (
|
||||
<CodeBlock className="wrap">{abiJson}</CodeBlock>
|
||||
)
|
||||
) : (
|
||||
<TextArea
|
||||
placeholder={this.abiJsonPlaceholder}
|
||||
className={`InteractForm-interface-field-input ${validAbiJson ? '' : 'invalid'}`}
|
||||
|
@ -126,8 +139,8 @@ class InteractForm extends Component<Props, State> {
|
|||
value={abiJson}
|
||||
rows={6}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</label>
|
||||
|
||||
<button
|
||||
className="InteractForm-submit btn btn-primary"
|
||||
|
@ -150,7 +163,7 @@ class InteractForm extends Component<Props, State> {
|
|||
private handleSelectContract = (contract: ContractOption) => {
|
||||
this.props.resetState();
|
||||
const fullContract = this.props.contracts.find(currContract => {
|
||||
return this.makeContractValue(currContract) === contract.value;
|
||||
return contract && this.makeContractValue(currContract) === contract.value;
|
||||
});
|
||||
|
||||
this.setState({
|
||||
|
|
|
@ -7,45 +7,14 @@ import {
|
|||
TXMetaDataPanel,
|
||||
CurrentCustomMessage,
|
||||
GenerateTransaction,
|
||||
SendButton,
|
||||
SigningStatus
|
||||
SendButton
|
||||
} from 'components';
|
||||
import { OnlyUnlocked, WhenQueryExists } from 'components/renderCbs';
|
||||
import translate from 'translations';
|
||||
|
||||
import { AppState } from 'reducers';
|
||||
import { NonStandardTransaction } from './components';
|
||||
|
||||
const content = (
|
||||
<div className="Tab-content-pane">
|
||||
<AddressField />
|
||||
|
||||
<div className="row form-group">
|
||||
<div className="col-xs-12">
|
||||
<AmountField hasUnitDropdown={true} hasSendEverything={true} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row form-group">
|
||||
<div className="col-xs-12">
|
||||
<TXMetaDataPanel />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CurrentCustomMessage />
|
||||
<NonStandardTransaction />
|
||||
|
||||
<div className="row form-group">
|
||||
<div className="col-xs-12 clearfix">
|
||||
<GenerateTransaction />
|
||||
</div>
|
||||
</div>
|
||||
<SigningStatus />
|
||||
<div className="row form-group">
|
||||
<SendButton />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
import { getOffline } from 'selectors/config';
|
||||
|
||||
const QueryWarning: React.SFC<{}> = () => (
|
||||
<WhenQueryExists
|
||||
|
@ -59,17 +28,38 @@ const QueryWarning: React.SFC<{}> = () => (
|
|||
|
||||
interface StateProps {
|
||||
shouldDisplay: boolean;
|
||||
offline: boolean;
|
||||
}
|
||||
|
||||
class FieldsClass extends Component<StateProps> {
|
||||
public render() {
|
||||
const { shouldDisplay } = this.props;
|
||||
const { shouldDisplay, offline } = this.props;
|
||||
return (
|
||||
<OnlyUnlocked
|
||||
whenUnlocked={
|
||||
<React.Fragment>
|
||||
<QueryWarning />
|
||||
{shouldDisplay ? content : null}
|
||||
{shouldDisplay && (
|
||||
<div className="Tab-content-pane">
|
||||
<AddressField />
|
||||
<div className="row form-group">
|
||||
<div className="col-xs-12">
|
||||
<AmountField hasUnitDropdown={true} hasSendEverything={true} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row form-group">
|
||||
<div className="col-xs-12">
|
||||
<TXMetaDataPanel />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CurrentCustomMessage />
|
||||
<NonStandardTransaction />
|
||||
|
||||
{offline ? <GenerateTransaction /> : <SendButton signing={true} />}
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
}
|
||||
/>
|
||||
|
@ -78,5 +68,6 @@ class FieldsClass extends Component<StateProps> {
|
|||
}
|
||||
|
||||
export const Fields = connect((state: AppState) => ({
|
||||
shouldDisplay: !isAnyOfflineWithWeb3(state)
|
||||
shouldDisplay: !isAnyOfflineWithWeb3(state),
|
||||
offline: getOffline(state)
|
||||
}))(FieldsClass);
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
&-qr {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&-codeBox {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { connect } from 'react-redux';
|
|||
import { AppState } from 'reducers';
|
||||
import translate from 'translations';
|
||||
import { IWallet } from 'libs/wallet';
|
||||
import { QRCode, TextArea } from 'components/ui';
|
||||
import { QRCode, CodeBlock } from 'components/ui';
|
||||
import { getUnit, getDecimal } from 'selectors/transaction/meta';
|
||||
import {
|
||||
getCurrentTo,
|
||||
|
@ -132,7 +132,7 @@ class RequestPayment extends React.Component<Props, {}> {
|
|||
</div>
|
||||
</div>
|
||||
<div className="col-xs-6 RequestPayment-codeContainer">
|
||||
<TextArea className="RequestPayment-codeBox" value={eip681String} disabled={true} />
|
||||
<CodeBlock className="wrap">{eip681String}</CodeBlock>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -10,7 +10,7 @@ import { AppState } from 'reducers';
|
|||
import SignButton from './SignButton';
|
||||
import { isWalletFullyUnlocked } from 'selectors/wallet';
|
||||
import './index.scss';
|
||||
import { TextArea } from 'components/ui';
|
||||
import { TextArea, CodeBlock } from 'components/ui';
|
||||
|
||||
interface Props {
|
||||
wallet: IFullWallet;
|
||||
|
@ -78,12 +78,9 @@ export class SignMessage extends Component<Props, State> {
|
|||
<div className="input-group-wrapper SignMessage-inputBox">
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('MSG_SIGNATURE')}</div>
|
||||
<TextArea
|
||||
className="SignMessage-inputBox"
|
||||
value={JSON.stringify(signedMessage, null, 2)}
|
||||
disabled={true}
|
||||
onChange={this.handleMessageChange}
|
||||
/>
|
||||
<CodeBlock className="SignMessage-inputBox">
|
||||
{JSON.stringify(signedMessage, null, 2)}
|
||||
</CodeBlock>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -317,7 +317,7 @@ export default class CurrencySwap extends PureComponent<Props, State> {
|
|||
<Input
|
||||
id="origin-swap-input"
|
||||
className={`input-group-input ${
|
||||
!origin.amount &&
|
||||
!!origin.amount &&
|
||||
this.isMinMaxValid(origin.amount, origin.label, destination.label)
|
||||
? ''
|
||||
: 'invalid'
|
||||
|
@ -342,7 +342,7 @@ export default class CurrencySwap extends PureComponent<Props, State> {
|
|||
<Input
|
||||
id="destination-swap-input"
|
||||
className={`${
|
||||
!destination.amount &&
|
||||
!!destination.amount &&
|
||||
this.isMinMaxValid(origin.amount, origin.label, destination.label)
|
||||
? ''
|
||||
: 'invalid'
|
||||
|
|
|
@ -3,7 +3,7 @@ import { AmountFieldFactory } from 'components/AmountFieldFactory';
|
|||
import { AddressFieldFactory } from 'components/AddressFieldFactory';
|
||||
import { connect } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
import { GenerateTransaction, SendButton, SigningStatus, TXMetaDataPanel } from 'components';
|
||||
import { SendButton, TXMetaDataPanel } from 'components';
|
||||
import { resetWallet, TResetWallet } from 'actions/wallet';
|
||||
import translate from 'translations';
|
||||
import { getUnit } from 'selectors/transaction';
|
||||
|
@ -76,7 +76,6 @@ class FieldsClass extends Component<Props> {
|
|||
<TXMetaDataPanel initialState={'simple'} disableToggle={true} />
|
||||
</div>
|
||||
</div>
|
||||
<SigningStatus />
|
||||
<div className="row form-group">
|
||||
<div className="col-xs-12 clearfix">
|
||||
{currentBalance === null ? (
|
||||
|
@ -84,13 +83,10 @@ class FieldsClass extends Component<Props> {
|
|||
<Spinner />
|
||||
</div>
|
||||
) : (
|
||||
<GenerateTransaction />
|
||||
<SendButton signing={true} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row form-group">
|
||||
<SendButton />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
@import './styles/tab';
|
||||
@import './styles/flexbox';
|
||||
@import './styles/helpers';
|
||||
@import './styles/code';
|
||||
@import './fonts';
|
||||
|
||||
[data-whatintent='mouse'] *:focus {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
code {
|
||||
color: #333;
|
||||
background-color: #eff5fe;
|
||||
font-size: 14px;
|
||||
line-height: inherit;
|
||||
border-radius: 2px;
|
||||
padding: 2px 0.25rem;
|
||||
border: 1px solid $border-idle;
|
||||
}
|
|
@ -25,7 +25,7 @@ input[type='checkbox'] {
|
|||
}
|
||||
|
||||
input[readonly] {
|
||||
background-color: #fafafa;
|
||||
background-color: $gray-lightest;
|
||||
cursor: text !important;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
border-color: inherit;
|
||||
}
|
||||
&-menu {
|
||||
max-height: 8.625rem;
|
||||
max-height: 10.0625rem;
|
||||
}
|
||||
&.invalid.has-blurred {
|
||||
border-color: $brand-danger;
|
||||
|
|
|
@ -9,6 +9,9 @@ $gray-light: #9a9a9a;
|
|||
$gray-lighter: #ececec;
|
||||
$gray-lightest: #fafafa;
|
||||
|
||||
$border-idle: #e5ecf3;
|
||||
$border-disabled: #e6e6e6;
|
||||
|
||||
$brand-primary: #007896;
|
||||
$brand-success: #5dba5a;
|
||||
$brand-info: $ether-navy;
|
||||
|
|
24
package.json
24
package.json
|
@ -145,10 +145,14 @@
|
|||
"prebuild": "check-node-version --package",
|
||||
"build:downloadable": "webpack --config webpack_config/webpack.html.js",
|
||||
"prebuild:downloadable": "check-node-version --package",
|
||||
"build:electron": "webpack --config webpack_config/webpack.electron-prod.js && node webpack_config/buildElectron.js",
|
||||
"build:electron:osx": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=osx node webpack_config/buildElectron.js",
|
||||
"build:electron:windows": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=windows node webpack_config/buildElectron.js",
|
||||
"build:electron:linux": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=linux node webpack_config/buildElectron.js",
|
||||
"build:electron":
|
||||
"webpack --config webpack_config/webpack.electron-prod.js && node webpack_config/buildElectron.js",
|
||||
"build:electron:osx":
|
||||
"webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=osx node webpack_config/buildElectron.js",
|
||||
"build:electron:windows":
|
||||
"webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=windows node webpack_config/buildElectron.js",
|
||||
"build:electron:linux":
|
||||
"webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=linux node webpack_config/buildElectron.js",
|
||||
"prebuild:electron": "check-node-version --package",
|
||||
"jenkins:build:linux": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=JENKINS_LINUX node webpack_config/buildElectron.js",
|
||||
"jenkins:build:mac": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=JENKINS_MAC node webpack_config/buildElectron.js",
|
||||
|
@ -163,14 +167,18 @@
|
|||
"predev": "check-node-version --package",
|
||||
"dev:https": "HTTPS=true node webpack_config/devServer.js",
|
||||
"predev:https": "check-node-version --package",
|
||||
"dev:electron": "concurrently --kill-others --names 'webpack,electron' 'BUILD_ELECTRON=true node webpack_config/devServer.js' 'webpack --config webpack_config/webpack.electron-dev.js && electron dist/electron-js/main.js'",
|
||||
"dev:electron:https": "concurrently --kill-others --names 'webpack,electron' 'BUILD_ELECTRON=true HTTPS=true node webpack_config/devServer.js' 'HTTPS=true webpack --config webpack_config/webpack.electron-dev.js && electron dist/electron-js/main.js'",
|
||||
"dev:electron":
|
||||
"concurrently --kill-others --names 'webpack,electron' 'BUILD_ELECTRON=true node webpack_config/devServer.js' 'webpack --config webpack_config/webpack.electron-dev.js && electron dist/electron-js/main.js'",
|
||||
"dev:electron:https":
|
||||
"concurrently --kill-others --names 'webpack,electron' 'BUILD_ELECTRON=true HTTPS=true node webpack_config/devServer.js' 'HTTPS=true webpack --config webpack_config/webpack.electron-dev.js && electron dist/electron-js/main.js'",
|
||||
"tslint": "tslint --project . --exclude common/vendor/**/*",
|
||||
"tscheck": "tsc --noEmit",
|
||||
"start": "npm run dev",
|
||||
"precommit": "lint-staged",
|
||||
"formatAll": "find ./common/ -name '*.ts*' | xargs prettier --write --config ./.prettierrc --config-precedence file-override",
|
||||
"prettier:diff": "prettier --write --config ./.prettierrc --list-different \"common/**/*.ts\" \"common/**/*.tsx\"",
|
||||
"formatAll":
|
||||
"find ./common/ -name '*.ts*' | xargs prettier --write --config ./.prettierrc --config-precedence file-override",
|
||||
"prettier:diff":
|
||||
"prettier --write --config ./.prettierrc --list-different \"common/**/*.ts\" \"common/**/*.tsx\"",
|
||||
"prepush": "npm run tslint && npm run tscheck"
|
||||
},
|
||||
"lint-staged": {
|
||||
|
|
Loading…
Reference in New Issue