Add etherscan/copy buttons in load/create safe flows
This commit is contained in:
parent
b44c30923d
commit
3c05220008
|
@ -0,0 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#B2B5B2" fill-rule="nonzero" d="M14 13h2V6h-5v2h2a1 1 0 0 1 1 1v4zM9 8V5a1 1 0 0 1 1-1h7a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1h-3v3a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1h3zm-2 2v7h5v-7H7z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 351 B |
|
@ -0,0 +1,35 @@
|
|||
// @flow
|
||||
import React from 'react'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Img from '~/components/layout/Img'
|
||||
import { copyToClipboard } from '~/utils/clipboard'
|
||||
import { xs } from '~/theme/variables'
|
||||
import CopyIcon from './copy.svg'
|
||||
|
||||
const styles = () => ({
|
||||
container: {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
padding: xs,
|
||||
borderRadius: '50%',
|
||||
transition: 'background-color .2s ease-in-out',
|
||||
'&:hover': {
|
||||
backgroundColor: '#F0EFEE',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
type CopyBtnProps = {
|
||||
content: string,
|
||||
classes: Object,
|
||||
}
|
||||
|
||||
const CopyBtn = ({ content, classes }: CopyBtnProps) => (navigator.clipboard ? (
|
||||
<div className={classes.container}>
|
||||
<Img src={CopyIcon} height={20} alt="Click to copy" onClick={() => copyToClipboard(content)} />
|
||||
</div>
|
||||
) : null)
|
||||
|
||||
export default withStyles(styles)(CopyBtn)
|
|
@ -0,0 +1,53 @@
|
|||
// @flow
|
||||
import React from 'react'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import { connect } from 'react-redux'
|
||||
import Img from '~/components/layout/Img'
|
||||
import { getEtherScanLink } from '~/logic/wallets/getWeb3'
|
||||
import { xs } from '~/theme/variables'
|
||||
import { networkSelector } from '~/logic/wallets/store/selectors'
|
||||
import SearchIcon from './search.svg'
|
||||
|
||||
|
||||
const styles = () => ({
|
||||
container: {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: xs,
|
||||
borderRadius: '50%',
|
||||
transition: 'background-color .2s ease-in-out',
|
||||
'&:hover': {
|
||||
backgroundColor: '#F0EFEE',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
type EtherscanBtnProps = {
|
||||
type: 'tx' | 'address',
|
||||
value: string,
|
||||
currentNetwork: string,
|
||||
classes: Object,
|
||||
}
|
||||
|
||||
const EtherscanBtn = ({
|
||||
type, value, currentNetwork, classes,
|
||||
}: EtherscanBtnProps) => (
|
||||
<a
|
||||
className={classes.container}
|
||||
href={getEtherScanLink(type, value, currentNetwork)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="Show Details on etherscan"
|
||||
aria-label="Show Details on etherscan"
|
||||
>
|
||||
<Img src={SearchIcon} height={20} alt="Etherscan" />
|
||||
</a>
|
||||
)
|
||||
|
||||
const EtherscanBtnWithStyles = withStyles(styles)(EtherscanBtn)
|
||||
|
||||
export default connect<Object, Object, ?Function, ?Object>(
|
||||
(state) => ({ currentNetwork: networkSelector(state) }),
|
||||
null,
|
||||
)(EtherscanBtnWithStyles)
|
|
@ -0,0 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#B2B5B2" fill-rule="nonzero" d="M18.671 17.085a1.119 1.119 0 0 1-.002 1.587 1.126 1.126 0 0 1-1.586.003l-2.68-2.68a6.6 6.6 0 1 1 .862-10.061 6.603 6.603 0 0 1 .727 8.471l2.68 2.68zm-4.923-3.335a4.455 4.455 0 0 0-6.298-6.3 4.456 4.456 0 0 0 0 6.3 4.452 4.452 0 0 0 6.298 0z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 436 B |
|
@ -26,6 +26,6 @@ const EtherscanLink = ({ type, value, currentNetwork }: EtherscanLinkProps) => (
|
|||
)
|
||||
|
||||
export default connect<Object, Object, ?Function, ?Object>(
|
||||
state => ({ currentNetwork: networkSelector(state) }),
|
||||
(state) => ({ currentNetwork: networkSelector(state) }),
|
||||
null,
|
||||
)(EtherscanLink)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// @flow
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import OpenInNew from '@material-ui/icons/OpenInNew'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Field from '~/components/forms/Field'
|
||||
import { required } from '~/components/forms/validator'
|
||||
|
@ -10,22 +9,17 @@ import Identicon from '~/components/Identicon'
|
|||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
import Col from '~/components/layout/Col'
|
||||
import Row from '~/components/layout/Row'
|
||||
import Link from '~/components/layout/Link'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import Hairline from '~/components/layout/Hairline'
|
||||
import EtherscanBtn from '~/components/EtherscanBtn'
|
||||
import CopyBtn from '~/components/CopyBtn'
|
||||
import {
|
||||
sm, md, lg, border, secondary, disabled, extraSmallFontSize,
|
||||
sm, md, lg, border, disabled, extraSmallFontSize,
|
||||
} from '~/theme/variables'
|
||||
import { getOwnerNameBy, getOwnerAddressBy } from '~/routes/open/components/fields'
|
||||
import { getEtherScanLink } from '~/logic/wallets/getWeb3'
|
||||
import { FIELD_LOAD_ADDRESS, THRESHOLD } from '~/routes/load/components/fields'
|
||||
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
|
||||
|
||||
const openIconStyle = {
|
||||
height: '16px',
|
||||
color: secondary,
|
||||
}
|
||||
|
||||
const styles = () => ({
|
||||
details: {
|
||||
padding: lg,
|
||||
|
@ -45,6 +39,7 @@ const styles = () => ({
|
|||
},
|
||||
address: {
|
||||
paddingLeft: '6px',
|
||||
marginRight: sm,
|
||||
},
|
||||
open: {
|
||||
paddingLeft: sm,
|
||||
|
@ -70,11 +65,7 @@ const styles = () => ({
|
|||
},
|
||||
})
|
||||
|
||||
type LayoutProps = {
|
||||
network: string,
|
||||
}
|
||||
|
||||
type Props = LayoutProps & {
|
||||
type Props = {
|
||||
values: Object,
|
||||
classes: Object,
|
||||
updateInitialProps: (initialValues: Object) => void,
|
||||
|
@ -92,7 +83,7 @@ const calculateSafeValues = (owners: Array<string>, threshold: Number, values: O
|
|||
const OwnerListComponent = (props: Props) => {
|
||||
const [owners, setOwners] = useState<Array<string>>([])
|
||||
const {
|
||||
values, updateInitialProps, network, classes,
|
||||
values, updateInitialProps, classes,
|
||||
} = props
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -153,9 +144,8 @@ const OwnerListComponent = (props: Props) => {
|
|||
<Paragraph size="md" color="disabled" noMargin className={classes.address}>
|
||||
{address}
|
||||
</Paragraph>
|
||||
<Link className={classes.open} to={getEtherScanLink('address', address, network)} target="_blank">
|
||||
<OpenInNew style={openIconStyle} />
|
||||
</Link>
|
||||
<CopyBtn content={address} />
|
||||
<EtherscanBtn type="address" value={address} />
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
|
|
|
@ -2,29 +2,23 @@
|
|||
import * as React from 'react'
|
||||
import classNames from 'classnames'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import OpenInNew from '@material-ui/icons/OpenInNew'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Identicon from '~/components/Identicon'
|
||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
import Col from '~/components/layout/Col'
|
||||
import Row from '~/components/layout/Row'
|
||||
import Link from '~/components/layout/Link'
|
||||
import EtherscanBtn from '~/components/EtherscanBtn'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import CopyBtn from '~/components/CopyBtn'
|
||||
import Hairline from '~/components/layout/Hairline'
|
||||
import {
|
||||
xs, sm, lg, border, secondary,
|
||||
xs, sm, lg, border,
|
||||
} from '~/theme/variables'
|
||||
import { shortVersionOf } from '~/logic/wallets/ethAddresses'
|
||||
import { getAccountsFrom } from '~/routes/open/utils/safeDataExtractor'
|
||||
import { getOwnerNameBy, getOwnerAddressBy, getNumOwnersFrom } from '~/routes/open/components/fields'
|
||||
import { getEtherScanLink } from '~/logic/wallets/getWeb3'
|
||||
import { FIELD_LOAD_NAME, FIELD_LOAD_ADDRESS, THRESHOLD } from '~/routes/load/components/fields'
|
||||
|
||||
const openIconStyle = {
|
||||
height: '16px',
|
||||
color: secondary,
|
||||
}
|
||||
|
||||
const styles = () => ({
|
||||
root: {
|
||||
minHeight: '300px',
|
||||
|
@ -51,6 +45,9 @@ const styles = () => ({
|
|||
},
|
||||
user: {
|
||||
justifyContent: 'left',
|
||||
'& > p': {
|
||||
marginRight: sm,
|
||||
},
|
||||
},
|
||||
open: {
|
||||
paddingLeft: sm,
|
||||
|
@ -65,15 +62,12 @@ const styles = () => ({
|
|||
},
|
||||
address: {
|
||||
paddingLeft: '6px',
|
||||
marginRight: sm,
|
||||
},
|
||||
})
|
||||
|
||||
type LayoutProps = {
|
||||
network: string,
|
||||
type Props = {
|
||||
userAddress: string,
|
||||
}
|
||||
|
||||
type Props = LayoutProps & {
|
||||
values: Object,
|
||||
classes: Object,
|
||||
}
|
||||
|
@ -97,9 +91,7 @@ const checkUserAddressOwner = (values: Object, userAddress: string): boolean =>
|
|||
|
||||
class ReviewComponent extends React.PureComponent<Props, State> {
|
||||
render() {
|
||||
const {
|
||||
values, classes, network, userAddress,
|
||||
} = this.props
|
||||
const { values, classes, userAddress } = this.props
|
||||
|
||||
const isOwner = checkUserAddressOwner(values, userAddress)
|
||||
const owners = getAccountsFrom(values)
|
||||
|
@ -132,9 +124,8 @@ class ReviewComponent extends React.PureComponent<Props, State> {
|
|||
<Paragraph size="md" color="disabled" noMargin className={classes.address}>
|
||||
{shortVersionOf(safeAddress, 4)}
|
||||
</Paragraph>
|
||||
<Link className={classes.open} to={getEtherScanLink('address', safeAddress, network)} target="_blank">
|
||||
<OpenInNew style={openIconStyle} />
|
||||
</Link>
|
||||
<CopyBtn content={safeAddress} />
|
||||
<EtherscanBtn type="address" value={safeAddress} />
|
||||
</Row>
|
||||
</Block>
|
||||
<Block margin="lg">
|
||||
|
@ -177,13 +168,8 @@ class ReviewComponent extends React.PureComponent<Props, State> {
|
|||
<Paragraph size="md" color="disabled" noMargin>
|
||||
{address}
|
||||
</Paragraph>
|
||||
<Link
|
||||
className={classes.open}
|
||||
to={getEtherScanLink('address', address, network)}
|
||||
target="_blank"
|
||||
>
|
||||
<OpenInNew style={openIconStyle} />
|
||||
</Link>
|
||||
<CopyBtn content={address} />
|
||||
<EtherscanBtn type="address" value={address} />
|
||||
</Block>
|
||||
</Block>
|
||||
</Col>
|
||||
|
|
|
@ -2,30 +2,25 @@
|
|||
import * as React from 'react'
|
||||
import classNames from 'classnames'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import OpenInNew from '@material-ui/icons/OpenInNew'
|
||||
import { estimateGasForDeployingSafe } from '~/logic/contracts/safeContracts'
|
||||
import { getNamesFrom, getAccountsFrom } from '~/routes/open/utils/safeDataExtractor'
|
||||
import Block from '~/components/layout/Block'
|
||||
import EtherscanBtn from '~/components/EtherscanBtn'
|
||||
import CopyBtn from '~/components/CopyBtn'
|
||||
import Identicon from '~/components/Identicon'
|
||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
import Col from '~/components/layout/Col'
|
||||
import Row from '~/components/layout/Row'
|
||||
import Link from '~/components/layout/Link'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import {
|
||||
sm, md, lg, border, secondary, background,
|
||||
sm, md, lg, border, background,
|
||||
} from '~/theme/variables'
|
||||
import Hairline from '~/components/layout/Hairline'
|
||||
import { getEtherScanLink, getWeb3 } from '~/logic/wallets/getWeb3'
|
||||
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
||||
import { FIELD_NAME, FIELD_CONFIRMATIONS, getNumOwnersFrom } from '../fields'
|
||||
|
||||
const { useEffect, useState } = React
|
||||
|
||||
const openIconStyle = {
|
||||
height: '16px',
|
||||
color: secondary,
|
||||
}
|
||||
|
||||
const styles = () => ({
|
||||
root: {
|
||||
minHeight: '300px',
|
||||
|
@ -58,6 +53,9 @@ const styles = () => ({
|
|||
},
|
||||
user: {
|
||||
justifyContent: 'left',
|
||||
'& > p': {
|
||||
marginRight: sm,
|
||||
},
|
||||
},
|
||||
open: {
|
||||
paddingLeft: sm,
|
||||
|
@ -69,15 +67,12 @@ const styles = () => ({
|
|||
})
|
||||
|
||||
type Props = {
|
||||
network: string,
|
||||
values: Object,
|
||||
classes: Object,
|
||||
userAccount: string,
|
||||
}
|
||||
|
||||
const ReviewComponent = ({
|
||||
values, classes, network, userAccount,
|
||||
}: Props) => {
|
||||
const ReviewComponent = ({ values, classes, userAccount }: Props) => {
|
||||
const [gasCosts, setGasCosts] = useState<string>('0.00')
|
||||
const names = getNamesFrom(values)
|
||||
const addresses = getAccountsFrom(values)
|
||||
|
@ -152,13 +147,8 @@ const ReviewComponent = ({
|
|||
<Paragraph size="md" color="disabled" noMargin>
|
||||
{addresses[index]}
|
||||
</Paragraph>
|
||||
<Link
|
||||
className={classes.open}
|
||||
to={getEtherScanLink('address', addresses[index], network)}
|
||||
target="_blank"
|
||||
>
|
||||
<OpenInNew style={openIconStyle} />
|
||||
</Link>
|
||||
<CopyBtn content={addresses[index]} />
|
||||
<EtherscanBtn type="address" value={addresses[index]} />
|
||||
</Block>
|
||||
</Block>
|
||||
</Col>
|
||||
|
@ -184,10 +174,10 @@ ETH in this wallet to fund this transaction.
|
|||
|
||||
const ReviewPage = withStyles(styles)(ReviewComponent)
|
||||
|
||||
const Review = ({ network }: LayoutProps) => (controls: React.Node, { values }: Object) => (
|
||||
const Review = () => (controls: React.Node, { values }: Object) => (
|
||||
<>
|
||||
<OpenPaper controls={controls} padding={false}>
|
||||
<ReviewPage network={network} values={values} />
|
||||
<ReviewPage values={values} />
|
||||
</OpenPaper>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { createStructuredSelector } from 'reselect'
|
||||
import { providerNameSelector, userAccountSelector, networkSelector } from '~/logic/wallets/store/selectors'
|
||||
|
||||
export default createStructuredSelector({
|
||||
export default createStructuredSelector<Object, *>({
|
||||
provider: providerNameSelector,
|
||||
network: networkSelector,
|
||||
userAccount: userAccountSelector,
|
||||
|
|
|
@ -75,7 +75,7 @@ type Props = {
|
|||
const Receive = ({
|
||||
classes, onClose, safeAddress, safeName, etherScanLink,
|
||||
}: Props) => (
|
||||
<React.Fragment>
|
||||
<>
|
||||
<Row align="center" grow className={classes.heading}>
|
||||
<Paragraph className={classes.manage} weight="bolder" noMargin>
|
||||
Receive funds
|
||||
|
@ -113,7 +113,7 @@ const Receive = ({
|
|||
Done
|
||||
</Button>
|
||||
</Row>
|
||||
</React.Fragment>
|
||||
</>
|
||||
)
|
||||
|
||||
export default withStyles(styles)(Receive)
|
||||
|
|
|
@ -40,7 +40,7 @@ const Tokens = (props: Props) => {
|
|||
} = props
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<>
|
||||
<Row align="center" grow className={classes.heading}>
|
||||
<Paragraph className={classes.manage} noMargin weight="bolder">
|
||||
Manage Tokens
|
||||
|
@ -72,7 +72,7 @@ const Tokens = (props: Props) => {
|
|||
tokens={tokens}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ export const styles = () => ({
|
|||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
'&:first-child': {
|
||||
borderRadius: '8px',
|
||||
borderTopLeftRadius: '8px',
|
||||
},
|
||||
},
|
||||
active: {
|
||||
|
|
Loading…
Reference in New Issue