Address component for checksumming + linking to explorer (#1168)

* Add address component that checksums and optionally links to block explorer.

* tscheck

* Add exclusive props, type guard.
This commit is contained in:
William O'Beirne 2018-02-28 21:54:43 -05:00 committed by Daniel Ternyak
parent 5fcec1ecd7
commit 1eb57a38bc
9 changed files with 75 additions and 28 deletions

View File

@ -1,4 +1,4 @@
import { Identicon, UnitDisplay } from 'components/ui';
import { Identicon, UnitDisplay, Address, NewTabLink } from 'components/ui';
import { IWallet, Balance, TrezorWallet, LedgerWallet } from 'libs/wallet';
import React from 'react';
import translate from 'translations';
@ -103,7 +103,9 @@ class AccountInfo extends React.Component<Props, State> {
<Identicon address={address} size="100%" />
</div>
<div className="AccountInfo-address-wrapper">
<div className="AccountInfo-address-addr">{address}</div>
<div className="AccountInfo-address-addr">
<Address address={address} />
</div>
<CopyToClipboard onCopy={this.onCopy} text={address}>
<div
className={`AccountInfo-copy-icon${this.state.copied ? '-copied' : ''}`}
@ -183,24 +185,16 @@ class AccountInfo extends React.Component<Props, State> {
<ul className="AccountInfo-list">
{!!blockExplorer && (
<li className="AccountInfo-list-item">
<a
href={blockExplorer.addressUrl(address)}
target="_blank"
rel="noopener noreferrer"
>
<NewTabLink href={blockExplorer.addressUrl(address)}>
{`${network.name} (${blockExplorer.origin})`}
</a>
</NewTabLink>
</li>
)}
{!!tokenExplorer && (
<li className="AccountInfo-list-item">
<a
href={tokenExplorer.address(address)}
target="_blank"
rel="noopener noreferrer"
>
<NewTabLink href={tokenExplorer.address(address)}>
{`Tokens (${tokenExplorer.name})`}
</a>
</NewTabLink>
</li>
)}
</ul>

View File

@ -5,6 +5,7 @@ import { getCurrentTo, ICurrentTo } from 'selectors/transaction';
import { getAllTokens } from 'selectors/config';
import { getWalletInst } from 'selectors/wallet';
import { getAddressMessage } from 'config';
import { Address } from 'components/ui';
import { Token } from 'types/network';
interface ReduxProps {
@ -72,7 +73,10 @@ class CurrentCustomMessageClass extends PureComponent<Props, State> {
<React.Fragment>
<p>
<small>
A message regarding <strong>{address}</strong>:
A message regarding{' '}
<strong>
<Address address={address} />
</strong>:
</small>
</p>
<p>{msg.msg}</p>

View File

@ -1,6 +1,6 @@
import React from 'react';
import translate from 'translations';
import { Identicon, UnitDisplay, NewTabLink } from 'components/ui';
import { Identicon, UnitDisplay, NewTabLink, Address } from 'components/ui';
import { TransactionData, TransactionReceipt } from 'libs/nodes';
import { NetworkConfig } from 'types/network';
import './TransactionDataTable.scss';
@ -98,7 +98,7 @@ const TransactionDataTable: React.SFC<Props> = ({ data, receipt, network }) => {
data: (
<MaybeLink href={explorer.from}>
<Identicon address={data.from} size="26px" />
{data.from}
<Address address={data.from} />
</MaybeLink>
)
},
@ -107,7 +107,7 @@ const TransactionDataTable: React.SFC<Props> = ({ data, receipt, network }) => {
data: (
<MaybeLink href={explorer.to}>
<Identicon address={data.to} size="26px" />
{data.to}
<Address address={data.to} />
</MaybeLink>
)
},
@ -141,7 +141,9 @@ const TransactionDataTable: React.SFC<Props> = ({ data, receipt, network }) => {
label: translate('New contract address'),
data: receipt &&
receipt.contractAddress && (
<MaybeLink href={explorer.contract}>{receipt.contractAddress}</MaybeLink>
<MaybeLink href={explorer.contract}>
<Address address={receipt.contractAddress} />
</MaybeLink>
)
},
{

View File

@ -0,0 +1,40 @@
import React from 'react';
import { toChecksumAddress } from 'ethereumjs-util';
import NewTabLink from './NewTabLink';
import { IWallet } from 'libs/wallet';
import { BlockExplorerConfig } from 'types/network';
interface BaseProps {
explorer?: BlockExplorerConfig | null;
}
interface AddressProps extends BaseProps {
address: string;
}
interface WalletProps extends BaseProps {
wallet: IWallet;
}
type Props = AddressProps | WalletProps;
const isAddressProps = (props: Props): props is AddressProps =>
typeof (props as AddressProps).address === 'string';
const Address: React.SFC<Props> = props => {
let addr = '';
if (isAddressProps(props)) {
addr = props.address;
} else {
addr = props.wallet.getAddressString();
}
addr = toChecksumAddress(addr);
if (props.explorer) {
return <NewTabLink href={props.explorer.addressUrl(addr)}>{addr}</NewTabLink>;
} else {
return <React.Fragment>{addr}</React.Fragment>;
}
};
export default Address;

View File

@ -12,6 +12,7 @@ export { default as SwapDropdown } from './SwapDropdown';
export { default as Tooltip } from './Tooltip';
export { default as TitleBar } from './TitleBar';
export { default as HelpLink } from './HelpLink';
export { default as Address } from './Address';
export * from './ConditionalInput';
export * from './Expandable';
export * from './InlineSpinner';

View File

@ -1,6 +1,6 @@
import React from 'react';
import { IOwnedDomainRequest } from 'libs/ens';
import { NewTabLink } from 'components/ui';
import { NewTabLink, Address } from 'components/ui';
const lookupLink = name => `https://etherscan.io/enslookup?q=${name}`;
type ChildrenProps = any;
@ -40,7 +40,9 @@ export const NameOwned: React.SFC<IOwnedDomainRequest> = ({
</tr>
<tr>
<td>Owner:</td>
<MonoTd>{ownerAddress}</MonoTd>
<MonoTd>
<Address address={ownerAddress} />
</MonoTd>
</tr>
<tr>
<td>Highest Bidder (Deed Owner): </td>
@ -50,7 +52,9 @@ export const NameOwned: React.SFC<IOwnedDomainRequest> = ({
</tr>
<tr>
<td>Resolved Address: </td>
<MonoTd>{resolvedAddress}</MonoTd>
<MonoTd>
<Address address={resolvedAddress} />
</MonoTd>
</tr>
</tbody>
</table>

View File

@ -8,6 +8,7 @@ $pw-max-width: 40rem;
}
&-password {
display: block;
position: relative;
max-width: $pw-max-width;
width: 100%;

View File

@ -1,4 +1,5 @@
import React from 'react';
import { toChecksumAddress } from 'ethereumjs-util';
import translate, { translateRaw } from 'translations';
import { IWallet } from 'libs/wallet';
import { print } from 'components/PrintableWallet';
@ -26,12 +27,12 @@ export default class WalletInfo extends React.PureComponent<Props, State> {
};
public componentDidMount() {
this.setWalletAsyncState(this.props.wallet);
this.setStateFromWallet(this.props.wallet);
}
public componentWillReceiveProps(nextProps: Props) {
if (this.props.wallet !== nextProps.wallet) {
this.setWalletAsyncState(nextProps.wallet);
this.setStateFromWallet(nextProps.wallet);
}
}
@ -114,9 +115,9 @@ export default class WalletInfo extends React.PureComponent<Props, State> {
);
}
private async setWalletAsyncState(wallet: IWallet) {
const address = wallet.getAddressString();
const privateKey = wallet.getPrivateKeyString ? await wallet.getPrivateKeyString() : '';
private setStateFromWallet(wallet: IWallet) {
const address = toChecksumAddress(wallet.getAddressString());
const privateKey = wallet.getPrivateKeyString ? wallet.getPrivateKeyString() : '';
this.setState({ address, privateKey });
}

View File

@ -2,7 +2,7 @@ import { StaticNetworksState, CustomNetworksState } from 'reducers/config/networ
type StaticNetworkIds = 'ETH' | 'Ropsten' | 'Kovan' | 'Rinkeby' | 'ETC' | 'UBQ' | 'EXP';
interface BlockExplorerConfig {
export interface BlockExplorerConfig {
name: string;
origin: string;
txUrl(txHash: string): string;