Logout Prompt on Navigation (#540)
* Update TODO comments & Remove old TODO comments * Add NavigationPrompts to WalletDecrypt * Remove commented code * Move NavigationPrompt & Remove formatting diffs * Remove formating diff * Bind WalletDecrypt action creators & Add new selector for readonly wallet
This commit is contained in:
parent
72e30643a9
commit
e6a958d6c1
|
@ -116,7 +116,6 @@ export default class Header extends Component<Props, State> {
|
|||
<section className="Header-branding">
|
||||
<section className="Header-branding-inner container">
|
||||
<Link to="/" className="Header-branding-title" aria-label="Go to homepage">
|
||||
{/* TODO - don't hardcode image path*/}
|
||||
<img
|
||||
className="Header-branding-title-logo"
|
||||
src={logo}
|
||||
|
|
|
@ -35,10 +35,7 @@ class NavigationPrompt extends React.Component<Props, State> {
|
|||
|
||||
public setupUnblock() {
|
||||
this.unblock = this.injected.history.block(nextLocation => {
|
||||
if (
|
||||
this.props.when &&
|
||||
nextLocation.pathname !== this.injected.location.pathname
|
||||
) {
|
||||
if (this.props.when && nextLocation.pathname !== this.injected.location.pathname) {
|
||||
this.setState({
|
||||
openModal: true,
|
||||
nextLocation
|
||||
|
@ -92,9 +89,7 @@ class NavigationPrompt extends React.Component<Props, State> {
|
|||
handleClose={this.onCancel}
|
||||
buttons={buttons}
|
||||
>
|
||||
<p>
|
||||
Leaving this page will log you out. Are you sure you want to continue?
|
||||
</p>
|
||||
<p>Leaving this page will log you out. Are you sure you want to continue?</p>
|
||||
</Modal>
|
||||
);
|
||||
}
|
|
@ -1,18 +1,21 @@
|
|||
import {
|
||||
setWallet,
|
||||
TSetWallet,
|
||||
unlockKeystore,
|
||||
UnlockKeystoreAction,
|
||||
TUnlockKeystore,
|
||||
unlockMnemonic,
|
||||
UnlockMnemonicAction,
|
||||
TUnlockMnemonic,
|
||||
unlockPrivateKey,
|
||||
UnlockPrivateKeyAction,
|
||||
unlockWeb3
|
||||
TUnlockPrivateKey,
|
||||
unlockWeb3,
|
||||
TUnlockWeb3,
|
||||
resetWallet,
|
||||
TResetWallet
|
||||
} from 'actions/wallet';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import map from 'lodash/map';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Dispatch } from 'redux';
|
||||
import translate from 'translations';
|
||||
import KeystoreDecrypt from './Keystore';
|
||||
import LedgerNanoSDecrypt from './LedgerNano';
|
||||
|
@ -24,77 +27,20 @@ import { AppState } from 'reducers';
|
|||
import Web3Decrypt from './Web3';
|
||||
import Help from 'components/ui/Help';
|
||||
import { knowledgeBaseURL } from 'config/data';
|
||||
|
||||
const WALLETS = {
|
||||
web3: {
|
||||
lid: 'x_MetaMask',
|
||||
component: Web3Decrypt,
|
||||
initialParams: {},
|
||||
unlock: unlockWeb3,
|
||||
helpLink: `${knowledgeBaseURL}/migration/moving-from-private-key-to-metamask`
|
||||
},
|
||||
'ledger-nano-s': {
|
||||
lid: 'x_Ledger',
|
||||
component: LedgerNanoSDecrypt,
|
||||
initialParams: {},
|
||||
unlock: setWallet,
|
||||
helpLink:
|
||||
'https://ledger.zendesk.com/hc/en-us/articles/115005200009-How-to-use-MyEtherWallet-with-Ledger'
|
||||
},
|
||||
trezor: {
|
||||
lid: 'x_Trezor',
|
||||
component: TrezorDecrypt,
|
||||
initialParams: {},
|
||||
unlock: setWallet,
|
||||
helpLink: 'https://doc.satoshilabs.com/trezor-apps/mew.html'
|
||||
},
|
||||
'keystore-file': {
|
||||
lid: 'x_Keystore2',
|
||||
component: KeystoreDecrypt,
|
||||
initialParams: {
|
||||
file: '',
|
||||
password: ''
|
||||
},
|
||||
unlock: unlockKeystore,
|
||||
helpLink: `${
|
||||
knowledgeBaseURL
|
||||
}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
|
||||
},
|
||||
'mnemonic-phrase': {
|
||||
lid: 'x_Mnemonic',
|
||||
component: MnemonicDecrypt,
|
||||
initialParams: {},
|
||||
unlock: unlockMnemonic,
|
||||
helpLink: `${
|
||||
knowledgeBaseURL
|
||||
}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
|
||||
},
|
||||
'private-key': {
|
||||
lid: 'x_PrivKey2',
|
||||
component: PrivateKeyDecrypt,
|
||||
initialParams: {
|
||||
key: '',
|
||||
password: ''
|
||||
},
|
||||
unlock: unlockPrivateKey,
|
||||
helpLink: `${
|
||||
knowledgeBaseURL
|
||||
}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
|
||||
},
|
||||
'view-only': {
|
||||
lid: 'View with Address Only',
|
||||
component: ViewOnlyDecrypt,
|
||||
initialParams: {},
|
||||
unlock: setWallet,
|
||||
helpLink: ''
|
||||
}
|
||||
};
|
||||
import NavigationPrompt from './NavigationPrompt';
|
||||
import { IWallet } from 'libs/wallet';
|
||||
|
||||
type UnlockParams = {} | PrivateKeyValue;
|
||||
|
||||
interface Props {
|
||||
// FIXME
|
||||
dispatch: Dispatch<UnlockKeystoreAction | UnlockMnemonicAction | UnlockPrivateKeyAction>;
|
||||
unlockKeystore: TUnlockKeystore;
|
||||
unlockMnemonic: TUnlockMnemonic;
|
||||
unlockPrivateKey: TUnlockPrivateKey;
|
||||
setWallet: TSetWallet;
|
||||
unlockWeb3: TUnlockWeb3;
|
||||
resetWallet: TResetWallet;
|
||||
wallet: IWallet;
|
||||
hidden?: boolean;
|
||||
offline: boolean;
|
||||
allowReadOnly?: boolean;
|
||||
}
|
||||
|
@ -105,19 +51,82 @@ interface State {
|
|||
}
|
||||
|
||||
export class WalletDecrypt extends Component<Props, State> {
|
||||
public WALLETS = {
|
||||
web3: {
|
||||
lid: 'x_MetaMask',
|
||||
component: Web3Decrypt,
|
||||
initialParams: {},
|
||||
unlock: this.props.unlockWeb3,
|
||||
helpLink: `${knowledgeBaseURL}/migration/moving-from-private-key-to-metamask`
|
||||
},
|
||||
'ledger-nano-s': {
|
||||
lid: 'x_Ledger',
|
||||
component: LedgerNanoSDecrypt,
|
||||
initialParams: {},
|
||||
unlock: this.props.setWallet,
|
||||
helpLink:
|
||||
'https://ledger.zendesk.com/hc/en-us/articles/115005200009-How-to-use-MyEtherWallet-with-Ledger'
|
||||
},
|
||||
trezor: {
|
||||
lid: 'x_Trezor',
|
||||
component: TrezorDecrypt,
|
||||
initialParams: {},
|
||||
unlock: this.props.setWallet,
|
||||
helpLink: 'https://doc.satoshilabs.com/trezor-apps/mew.html'
|
||||
},
|
||||
'keystore-file': {
|
||||
lid: 'x_Keystore2',
|
||||
component: KeystoreDecrypt,
|
||||
initialParams: {
|
||||
file: '',
|
||||
password: ''
|
||||
},
|
||||
unlock: this.props.unlockKeystore,
|
||||
helpLink: `${
|
||||
knowledgeBaseURL
|
||||
}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
|
||||
},
|
||||
'mnemonic-phrase': {
|
||||
lid: 'x_Mnemonic',
|
||||
component: MnemonicDecrypt,
|
||||
initialParams: {},
|
||||
unlock: this.props.unlockMnemonic,
|
||||
helpLink: `${
|
||||
knowledgeBaseURL
|
||||
}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
|
||||
},
|
||||
'private-key': {
|
||||
lid: 'x_PrivKey2',
|
||||
component: PrivateKeyDecrypt,
|
||||
initialParams: {
|
||||
key: '',
|
||||
password: ''
|
||||
},
|
||||
unlock: this.props.unlockPrivateKey,
|
||||
helpLink: `${
|
||||
knowledgeBaseURL
|
||||
}/private-keys-passwords/difference-beween-private-key-and-keystore-file.html`
|
||||
},
|
||||
'view-only': {
|
||||
lid: 'View with Address Only',
|
||||
component: ViewOnlyDecrypt,
|
||||
initialParams: {},
|
||||
unlock: this.props.setWallet,
|
||||
helpLink: ''
|
||||
}
|
||||
};
|
||||
public state: State = {
|
||||
selectedWalletKey: 'keystore-file',
|
||||
value: WALLETS['keystore-file'].initialParams
|
||||
value: this.WALLETS['keystore-file'].initialParams
|
||||
};
|
||||
|
||||
public getDecryptionComponent() {
|
||||
const { selectedWalletKey, value } = this.state;
|
||||
const selectedWallet = WALLETS[selectedWalletKey];
|
||||
const selectedWallet = this.WALLETS[selectedWalletKey];
|
||||
|
||||
if (!selectedWallet) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<selectedWallet.component value={value} onChange={this.onChange} onUnlock={this.onUnlock} />
|
||||
);
|
||||
|
@ -129,7 +138,7 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||
}
|
||||
|
||||
public buildWalletOptions() {
|
||||
return map(WALLETS, (wallet, key) => {
|
||||
return map(this.WALLETS, (wallet, key) => {
|
||||
const { helpLink } = wallet;
|
||||
const isSelected = this.state.selectedWalletKey === key;
|
||||
const isDisabled =
|
||||
|
@ -156,7 +165,7 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||
}
|
||||
|
||||
public handleDecryptionChoiceChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
const wallet = WALLETS[(event.target as HTMLInputElement).value];
|
||||
const wallet = this.WALLETS[(event.target as HTMLInputElement).value];
|
||||
|
||||
if (!wallet) {
|
||||
return;
|
||||
|
@ -169,33 +178,37 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||
};
|
||||
|
||||
public render() {
|
||||
const { wallet, hidden } = this.props;
|
||||
const decryptionComponent = this.getDecryptionComponent();
|
||||
|
||||
const unlocked = !!wallet;
|
||||
return (
|
||||
<article className="Tab-content-pane row">
|
||||
<section className="col-md-4 col-sm-6">
|
||||
<h4>{translate('decrypt_Access')}</h4>
|
||||
|
||||
{this.buildWalletOptions()}
|
||||
</section>
|
||||
|
||||
{decryptionComponent}
|
||||
{!!(this.state.value as PrivateKeyValue).valid && (
|
||||
<div>
|
||||
<NavigationPrompt when={unlocked} onConfirm={this.props.resetWallet} />
|
||||
<article hidden={hidden} className="Tab-content-pane row">
|
||||
<section className="col-md-4 col-sm-6">
|
||||
<h4 id="uploadbtntxt-wallet">{translate('ADD_Label_6')}</h4>
|
||||
<div className="form-group">
|
||||
<a
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
className="btn btn-primary btn-block"
|
||||
onClick={this.onUnlock}
|
||||
>
|
||||
{translate('ADD_Label_6_short')}
|
||||
</a>
|
||||
</div>
|
||||
<h4>{translate('decrypt_Access')}</h4>
|
||||
|
||||
{this.buildWalletOptions()}
|
||||
</section>
|
||||
)}
|
||||
</article>
|
||||
|
||||
{decryptionComponent}
|
||||
{!!(this.state.value as PrivateKeyValue).valid && (
|
||||
<section className="col-md-4 col-sm-6">
|
||||
<h4 id="uploadbtntxt-wallet">{translate('ADD_Label_6')}</h4>
|
||||
<div className="form-group">
|
||||
<a
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
className="btn btn-primary btn-block"
|
||||
onClick={this.onUnlock}
|
||||
>
|
||||
{translate('ADD_Label_6_short')}
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</article>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -207,14 +220,22 @@ export class WalletDecrypt extends Component<Props, State> {
|
|||
// some components (TrezorDecrypt) don't take an onChange prop, and thus this.state.value will remain unpopulated.
|
||||
// in this case, we can expect the payload to contain the unlocked wallet info.
|
||||
const unlockValue = this.state.value && !isEmpty(this.state.value) ? this.state.value : payload;
|
||||
this.props.dispatch(WALLETS[this.state.selectedWalletKey].unlock(unlockValue));
|
||||
this.WALLETS[this.state.selectedWalletKey].unlock(unlockValue);
|
||||
};
|
||||
}
|
||||
|
||||
function mapStateToProps(state: AppState) {
|
||||
return {
|
||||
offline: state.config.offline
|
||||
offline: state.config.offline,
|
||||
wallet: state.wallet.inst
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(WalletDecrypt);
|
||||
export default connect(mapStateToProps, {
|
||||
unlockKeystore,
|
||||
unlockMnemonic,
|
||||
unlockPrivateKey,
|
||||
unlockWeb3,
|
||||
setWallet,
|
||||
resetWallet
|
||||
})(WalletDecrypt);
|
||||
|
|
|
@ -4,13 +4,7 @@ import Spinner from './Spinner';
|
|||
const DEFAULT_BUTTON_TYPE = 'primary';
|
||||
const DEFAULT_BUTTON_SIZE = 'lg';
|
||||
|
||||
type ButtonType =
|
||||
| 'default'
|
||||
| 'primary'
|
||||
| 'success'
|
||||
| 'info'
|
||||
| 'warning'
|
||||
| 'danger';
|
||||
type ButtonType = 'default' | 'primary' | 'success' | 'info' | 'warning' | 'danger';
|
||||
type ButtonSize = 'lg' | 'sm' | 'xs';
|
||||
|
||||
interface Props {
|
||||
|
@ -25,19 +19,15 @@ interface Props {
|
|||
|
||||
export default class SimpleButton extends Component<Props, {}> {
|
||||
public computedClass = () => {
|
||||
return `btn btn-${this.props.size || DEFAULT_BUTTON_TYPE} btn-${this.props
|
||||
.type || DEFAULT_BUTTON_SIZE}`;
|
||||
return `btn btn-${this.props.size || DEFAULT_BUTTON_TYPE} btn-${this.props.type ||
|
||||
DEFAULT_BUTTON_SIZE}`;
|
||||
};
|
||||
|
||||
public render() {
|
||||
const { loading, disabled, loadingText, text, onClick } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
onClick={onClick}
|
||||
disabled={loading || disabled}
|
||||
className={this.computedClass()}
|
||||
>
|
||||
<button onClick={onClick} disabled={loading || disabled} className={this.computedClass()}>
|
||||
{loading ? (
|
||||
<div>
|
||||
<Spinner /> {loadingText || text}
|
||||
|
|
|
@ -14,17 +14,12 @@ interface State {
|
|||
}
|
||||
export class UnlockHeader extends React.Component<Props, State> {
|
||||
public state = {
|
||||
expanded: !this.props.wallet
|
||||
expanded: !!this.props.wallet
|
||||
};
|
||||
|
||||
public componentDidUpdate(prevProps: Props) {
|
||||
if (this.props.wallet && this.props.wallet !== prevProps.wallet) {
|
||||
this.setState({ expanded: false });
|
||||
}
|
||||
|
||||
// not sure if could happen
|
||||
if (!this.props.wallet && this.props.wallet !== prevProps.wallet) {
|
||||
this.setState({ expanded: true });
|
||||
this.setState({ expanded: !this.state.expanded });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,8 +33,7 @@ export class UnlockHeader extends React.Component<Props, State> {
|
|||
</a>
|
||||
<h1>{title}</h1>
|
||||
</div>
|
||||
{this.state.expanded && <WalletDecrypt allowReadOnly={allowReadOnly} />}
|
||||
{this.state.expanded && <hr />}
|
||||
<WalletDecrypt hidden={this.state.expanded} allowReadOnly={allowReadOnly} />
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -14,9 +14,7 @@ export interface Props {
|
|||
txCompare: React.ReactElement<TTxCompare> | null;
|
||||
displayModal: boolean;
|
||||
deployModal: React.ReactElement<TTxModal> | null;
|
||||
handleInput(
|
||||
input: string
|
||||
): (ev: React.FormEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
|
||||
handleInput(input: string): (ev: React.FormEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
|
||||
handleSignTx(): Promise<void>;
|
||||
handleDeploy(): void;
|
||||
}
|
||||
|
@ -40,9 +38,7 @@ const Deploy = (props: Props) => {
|
|||
<div className="Deploy">
|
||||
<section>
|
||||
<label className="Deploy-field form-group">
|
||||
<h4 className="Deploy-field-label">
|
||||
{translate('CONTRACT_ByteCode')}
|
||||
</h4>
|
||||
<h4 className="Deploy-field-label">{translate('CONTRACT_ByteCode')}</h4>
|
||||
<textarea
|
||||
name="byteCode"
|
||||
placeholder="0x8f87a973e..."
|
||||
|
@ -67,7 +63,7 @@ const Deploy = (props: Props) => {
|
|||
/>
|
||||
</label>
|
||||
|
||||
{walletExists ? (
|
||||
{walletExists && (
|
||||
<button
|
||||
className="Sign-submit btn btn-primary"
|
||||
disabled={!showSignTxButton}
|
||||
|
@ -75,17 +71,13 @@ const Deploy = (props: Props) => {
|
|||
>
|
||||
{translate('DEP_signtx')}
|
||||
</button>
|
||||
) : (
|
||||
<WalletDecrypt />
|
||||
)}
|
||||
<WalletDecrypt hidden={walletExists} />
|
||||
|
||||
{txCompare ? (
|
||||
<section>
|
||||
{txCompare}
|
||||
<button
|
||||
className="Deploy-submit btn btn-primary"
|
||||
onClick={handleDeploy}
|
||||
>
|
||||
<button className="Deploy-submit btn btn-primary" onClick={handleDeploy}>
|
||||
{translate('NAV_DeployContract')}
|
||||
</button>
|
||||
</section>
|
||||
|
|
|
@ -49,12 +49,7 @@ export default class InteractExplorer extends Component<Props, State> {
|
|||
};
|
||||
|
||||
public render() {
|
||||
const {
|
||||
inputs,
|
||||
outputs,
|
||||
selectedFunction,
|
||||
selectedFunctionName
|
||||
} = this.state;
|
||||
const { inputs, outputs, selectedFunction, selectedFunctionName } = this.state;
|
||||
|
||||
const {
|
||||
address,
|
||||
|
@ -96,15 +91,10 @@ export default class InteractExplorer extends Component<Props, State> {
|
|||
const { type, name } = input;
|
||||
|
||||
return (
|
||||
<label
|
||||
key={name}
|
||||
className="InteractExplorer-func-in form-group"
|
||||
>
|
||||
<label key={name} className="InteractExplorer-func-in form-group">
|
||||
<h4 className="InteractExplorer-func-in-label">
|
||||
{name}
|
||||
<span className="InteractExplorer-func-in-label-type">
|
||||
{type}
|
||||
</span>
|
||||
<span className="InteractExplorer-func-in-label-type">{type}</span>
|
||||
</h4>
|
||||
<input
|
||||
className="InteractExplorer-func-in-input form-control"
|
||||
|
@ -120,15 +110,10 @@ export default class InteractExplorer extends Component<Props, State> {
|
|||
const parsedName = name === '' ? index : name;
|
||||
|
||||
return (
|
||||
<label
|
||||
key={parsedName}
|
||||
className="InteractExplorer-func-out form-group"
|
||||
>
|
||||
<label key={parsedName} className="InteractExplorer-func-out form-group">
|
||||
<h4 className="InteractExplorer-func-out-label">
|
||||
↳ {name}
|
||||
<span className="InteractExplorer-func-out-label-type">
|
||||
{type}
|
||||
</span>
|
||||
<span className="InteractExplorer-func-out-label-type">{type}</span>
|
||||
</h4>
|
||||
<input
|
||||
className="InteractExplorer-func-out-input form-control"
|
||||
|
@ -146,69 +131,52 @@ export default class InteractExplorer extends Component<Props, State> {
|
|||
>
|
||||
{translate('CONTRACT_Read')}
|
||||
</button>
|
||||
) : walletDecrypted ? (
|
||||
!txGenerated ? (
|
||||
<Aux>
|
||||
<label className="InteractExplorer-field form-group">
|
||||
<h4 className="InteractExplorer-field-label">Gas Limit</h4>
|
||||
<input
|
||||
name="gasLimit"
|
||||
value={gasLimit}
|
||||
onChange={handleInput('gasLimit')}
|
||||
className={classnames(
|
||||
'InteractExplorer-field-input',
|
||||
'form-control',
|
||||
{
|
||||
'is-invalid': !validGasLimit
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</label>
|
||||
<label className="InteractExplorer-field form-group">
|
||||
<h4 className="InteractExplorer-field-label">Value</h4>
|
||||
<UnitConverter
|
||||
decimal={getDecimal('ether')}
|
||||
onChange={handleInput('value')}
|
||||
>
|
||||
{({ convertedUnit, onUserInput }) => (
|
||||
<input
|
||||
name="value"
|
||||
value={convertedUnit}
|
||||
onChange={onUserInput}
|
||||
placeholder="0"
|
||||
className={classnames(
|
||||
'InteractExplorer-field-input',
|
||||
'form-control',
|
||||
{
|
||||
'is-invalid': !validValue
|
||||
}
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</UnitConverter>
|
||||
</label>
|
||||
<button
|
||||
className="InteractExplorer-func-submit btn btn-primary"
|
||||
disabled={!showContractWrite}
|
||||
onClick={handleFunctionSend(selectedFunction, inputs)}
|
||||
>
|
||||
{translate('CONTRACT_Write')}
|
||||
</button>
|
||||
</Aux>
|
||||
) : (
|
||||
<Aux>
|
||||
{txCompare}
|
||||
<button
|
||||
className="Deploy-submit btn btn-primary"
|
||||
onClick={toggleModal}
|
||||
>
|
||||
{translate('SEND_trans')}
|
||||
</button>
|
||||
</Aux>
|
||||
)
|
||||
) : !txGenerated ? (
|
||||
<Aux>
|
||||
<label className="InteractExplorer-field form-group">
|
||||
<h4 className="InteractExplorer-field-label">Gas Limit</h4>
|
||||
<input
|
||||
name="gasLimit"
|
||||
value={gasLimit}
|
||||
onChange={handleInput('gasLimit')}
|
||||
className={classnames('InteractExplorer-field-input', 'form-control', {
|
||||
'is-invalid': !validGasLimit
|
||||
})}
|
||||
/>
|
||||
</label>
|
||||
<label className="InteractExplorer-field form-group">
|
||||
<h4 className="InteractExplorer-field-label">Value</h4>
|
||||
<UnitConverter decimal={getDecimal('ether')} onChange={handleInput('value')}>
|
||||
{({ convertedUnit, onUserInput }) => (
|
||||
<input
|
||||
name="value"
|
||||
value={convertedUnit}
|
||||
onChange={onUserInput}
|
||||
placeholder="0"
|
||||
className={classnames('InteractExplorer-field-input', 'form-control', {
|
||||
'is-invalid': !validValue
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
</UnitConverter>
|
||||
</label>
|
||||
<button
|
||||
className="InteractExplorer-func-submit btn btn-primary"
|
||||
disabled={!showContractWrite}
|
||||
onClick={handleFunctionSend(selectedFunction, inputs)}
|
||||
>
|
||||
{translate('CONTRACT_Write')}
|
||||
</button>
|
||||
</Aux>
|
||||
) : (
|
||||
<WalletDecrypt />
|
||||
<Aux>
|
||||
{txCompare}
|
||||
<button className="Deploy-submit btn btn-primary" onClick={toggleModal}>
|
||||
{translate('SEND_trans')}
|
||||
</button>
|
||||
</Aux>
|
||||
)}
|
||||
{<WalletDecrypt hidden={walletDecrypted} />}
|
||||
</div>
|
||||
)}
|
||||
{displayModal && txModal}
|
||||
|
@ -240,8 +208,7 @@ export default class InteractExplorer extends Component<Props, State> {
|
|||
} catch (e) {
|
||||
this.props.showNotification(
|
||||
'warning',
|
||||
`Function call error: ${(e as Error).message}` ||
|
||||
'Invalid input parameters',
|
||||
`Function call error: ${(e as Error).message}` || 'Invalid input parameters',
|
||||
5000
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
GasField
|
||||
} from './components';
|
||||
import TransactionSucceeded from 'components/ExtendedNotifications/TransactionSucceeded';
|
||||
import NavigationPrompt from './components/NavigationPrompt';
|
||||
// CONFIG
|
||||
import { donationAddressMap, NetworkConfig } from 'config/data';
|
||||
// LIBS
|
||||
|
@ -160,9 +159,7 @@ export class SendTransaction extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
public shouldReEstimateGas(prevState) {
|
||||
// TODO listen to gas price changes here
|
||||
// TODO debounce the call
|
||||
// handle gas estimation
|
||||
// TODO listen to gas price changes here, debounce the call, and handle gas estimation
|
||||
return (
|
||||
// if any relevant fields changed
|
||||
this.haveFieldsChanged(prevState) &&
|
||||
|
@ -287,10 +284,6 @@ export class SendTransaction extends React.Component<Props, State> {
|
|||
}
|
||||
allowReadOnly={true}
|
||||
/>
|
||||
<NavigationPrompt
|
||||
when={unlocked}
|
||||
onConfirm={this.props.resetWallet}
|
||||
/>
|
||||
<div className="row">
|
||||
{/* Send Form */}
|
||||
|
||||
|
|
|
@ -6,12 +6,15 @@ import translate from 'translations';
|
|||
import { showNotification, TShowNotification } from 'actions/notifications';
|
||||
import { ISignedMessage } from 'libs/signing';
|
||||
import { IFullWallet } from 'libs/wallet';
|
||||
import FullWalletOnly from 'components/renderCbs/FullWalletOnly';
|
||||
import { AppState } from 'reducers';
|
||||
import SignButton from './SignButton';
|
||||
import { isWalletFullyUnlocked } from 'selectors/wallet';
|
||||
import './index.scss';
|
||||
|
||||
interface Props {
|
||||
showNotification: TShowNotification;
|
||||
wallet: IFullWallet;
|
||||
unlocked: boolean;
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -31,8 +34,8 @@ export class SignMessage extends Component<Props, State> {
|
|||
public state: State = initialState;
|
||||
|
||||
public render() {
|
||||
const { wallet, unlocked } = this.props;
|
||||
const { message, signedMessage } = this.state;
|
||||
|
||||
const messageBoxClass = classnames([
|
||||
'SignMessage-inputBox',
|
||||
'form-control',
|
||||
|
@ -53,10 +56,15 @@ export class SignMessage extends Component<Props, State> {
|
|||
<div className="SignMessage-help">{translate('MSG_info2')}</div>
|
||||
</div>
|
||||
|
||||
<FullWalletOnly
|
||||
withFullWallet={this.renderSignButton}
|
||||
withoutFullWallet={this.renderUnlock}
|
||||
/>
|
||||
{unlocked && (
|
||||
<SignButton
|
||||
wallet={wallet}
|
||||
message={this.state.message}
|
||||
showNotification={this.props.showNotification}
|
||||
onSignMessage={this.onSignMessage}
|
||||
/>
|
||||
)}
|
||||
<WalletDecrypt hidden={unlocked} />
|
||||
|
||||
{!!signedMessage && (
|
||||
<div>
|
||||
|
@ -84,21 +92,11 @@ export class SignMessage extends Component<Props, State> {
|
|||
private onSignMessage = (signedMessage: ISignedMessage) => {
|
||||
this.setState({ signedMessage });
|
||||
};
|
||||
|
||||
private renderSignButton = (fullWallet: IFullWallet) => {
|
||||
return (
|
||||
<SignButton
|
||||
wallet={fullWallet}
|
||||
message={this.state.message}
|
||||
showNotification={this.props.showNotification}
|
||||
onSignMessage={this.onSignMessage}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
private renderUnlock() {
|
||||
return <WalletDecrypt />;
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(null, { showNotification })(SignMessage);
|
||||
const mapStateToProps = (state: AppState) => ({
|
||||
wallet: state.wallet.inst,
|
||||
unlocked: isWalletFullyUnlocked(state)
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, { showNotification })(SignMessage);
|
||||
|
|
|
@ -9,6 +9,10 @@ export function getWalletInst(state: AppState): IWallet | null | undefined {
|
|||
return state.wallet.inst;
|
||||
}
|
||||
|
||||
export function isWalletFullyUnlocked(state: AppState): boolean | null | undefined {
|
||||
return state.wallet.inst && !state.wallet.inst.isReadOnly;
|
||||
}
|
||||
|
||||
export interface TokenBalance {
|
||||
symbol: string;
|
||||
balance: TokenValue;
|
||||
|
@ -42,9 +46,7 @@ export function getTokenBalances(state: AppState): TokenBalance[] {
|
|||
balance: state.wallet.tokens[t.symbol]
|
||||
? state.wallet.tokens[t.symbol].balance
|
||||
: TokenValue('0'),
|
||||
error: state.wallet.tokens[t.symbol]
|
||||
? state.wallet.tokens[t.symbol].error
|
||||
: null,
|
||||
error: state.wallet.tokens[t.symbol] ? state.wallet.tokens[t.symbol].error : null,
|
||||
custom: t.custom,
|
||||
decimal: t.decimal
|
||||
}));
|
||||
|
@ -62,8 +64,6 @@ export function getTxFromBroadcastTransactionStatus(
|
|||
transactions: BroadcastTransactionStatus[],
|
||||
signedTx: string
|
||||
): BroadcastTransactionStatus | null {
|
||||
const tx = transactions.find(
|
||||
transaction => transaction.signedTx === signedTx
|
||||
);
|
||||
const tx = transactions.find(transaction => transaction.signedTx === signedTx);
|
||||
return tx || null;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue