WA-232 Displaying safe's token in settings route

This commit is contained in:
apanizo 2018-07-10 16:48:20 +02:00
parent 0b5d14c8f2
commit 3afefea0a7
10 changed files with 63 additions and 35 deletions

View File

@ -11,6 +11,11 @@ const Safe = Loadable({
loading: Loader, loading: Loader,
}) })
const Settings = Loadable({
loader: () => import('./tokens/container'),
loading: Loader,
})
const SafeList = Loadable({ const SafeList = Loadable({
loader: () => import('./safeList/container'), loader: () => import('./safeList/container'),
loading: Loader, loading: Loader,
@ -22,6 +27,9 @@ const Open = Loadable({
}) })
const SAFE_ADDRESS = `${SAFELIST_ADDRESS}/:${SAFE_PARAM_ADDRESS}` const SAFE_ADDRESS = `${SAFELIST_ADDRESS}/:${SAFE_PARAM_ADDRESS}`
const SAFE_SETTINGS = `${SAFE_ADDRESS}/settings`
export const settingsUrlFrom = (safeAddress: string) => `${SAFELIST_ADDRESS}/${safeAddress}/settings`
const Routes = () => ( const Routes = () => (
<Switch> <Switch>
@ -30,6 +38,7 @@ const Routes = () => (
<Route exact path={OPEN_ADDRESS} component={Open} /> <Route exact path={OPEN_ADDRESS} component={Open} />
<Route exact path={SAFELIST_ADDRESS} component={SafeList} /> <Route exact path={SAFELIST_ADDRESS} component={SafeList} />
<Route exact path={SAFE_ADDRESS} component={Safe} /> <Route exact path={SAFE_ADDRESS} component={Safe} />
<Route exact path={SAFE_SETTINGS} component={Settings} />
</Switch> </Switch>
) )

View File

@ -1,7 +1,9 @@
// @flow // @flow
import * as React from 'react' import * as React from 'react'
import classNames from 'classnames' import classNames from 'classnames'
import Link from '~/components/layout/Link'
import AccountBalance from '@material-ui/icons/AccountBalance' import AccountBalance from '@material-ui/icons/AccountBalance'
import Settings from '@material-ui/icons/Settings'
import Avatar from '@material-ui/core/Avatar' import Avatar from '@material-ui/core/Avatar'
import Collapse from '@material-ui/core/Collapse' import Collapse from '@material-ui/core/Collapse'
import IconButton from '@material-ui/core/IconButton' import IconButton from '@material-ui/core/IconButton'
@ -18,8 +20,10 @@ import Button from '~/components/layout/Button'
import openHoc, { type Open } from '~/components/hoc/OpenHoc' import openHoc, { type Open } from '~/components/hoc/OpenHoc'
import { type WithStyles } from '~/theme/mui' import { type WithStyles } from '~/theme/mui'
import { type Token } from '~/routes/tokens/store/model/token' import { type Token } from '~/routes/tokens/store/model/token'
import { settingsUrlFrom } from '~/routes'
type Props = Open & WithStyles & { type Props = Open & WithStyles & {
safeAddress: string,
tokens: Map<string, Token>, tokens: Map<string, Token>,
onMoveFunds: (token: Token) => void, onMoveFunds: (token: Token) => void,
} }
@ -33,9 +37,10 @@ const styles = {
export const MOVE_FUNDS_BUTTON_TEXT = 'Move' export const MOVE_FUNDS_BUTTON_TEXT = 'Move'
const BalanceComponent = openHoc(({ const BalanceComponent = openHoc(({
open, toggle, tokens, classes, onMoveFunds, open, toggle, tokens, classes, onMoveFunds, safeAddress,
}: Props) => { }: Props) => {
const hasBalances = tokens.count() > 0 const hasBalances = tokens.count() > 0
const settingsUrl = settingsUrlFrom(safeAddress)
return ( return (
<React.Fragment> <React.Fragment>
@ -44,6 +49,11 @@ const BalanceComponent = openHoc(({
<AccountBalance /> <AccountBalance />
</Avatar> </Avatar>
<ListItemText primary="Balance" secondary="List of different token balances" /> <ListItemText primary="Balance" secondary="List of different token balances" />
<ListItemIcon>
<IconButton to={settingsUrl} component={Link} className={classes.button} aria-label="Delete">
<Settings />
</IconButton>
</ListItemIcon>
<ListItemIcon> <ListItemIcon>
{open {open
? <IconButton disableRipple><ExpandLess /></IconButton> ? <IconButton disableRipple><ExpandLess /></IconButton>

View File

@ -110,12 +110,13 @@ class GnoSafe extends React.PureComponent<SafeProps, State> {
const { safe, tokens, userAddress } = this.props const { safe, tokens, userAddress } = this.props
const { component } = this.state const { component } = this.state
const ethBalance = getEthBalanceFrom(tokens) const ethBalance = getEthBalanceFrom(tokens)
const address = safe.get('address')
return ( return (
<Row grow> <Row grow>
<Col sm={12} top="xs" md={5} margin="xl" overflow> <Col sm={12} top="xs" md={5} margin="xl" overflow>
<List style={listStyle}> <List style={listStyle}>
<BalanceInfo tokens={tokens} onMoveFunds={this.onMoveTokens} /> <BalanceInfo tokens={tokens} onMoveFunds={this.onMoveTokens} safeAddress={address} />
<Owners <Owners
owners={safe.owners} owners={safe.owners}
onAddOwner={this.onAddOwner} onAddOwner={this.onAddOwner}
@ -123,7 +124,7 @@ class GnoSafe extends React.PureComponent<SafeProps, State> {
onRemoveOwner={this.onRemoveOwner} onRemoveOwner={this.onRemoveOwner}
/> />
<Confirmations confirmations={safe.get('threshold')} onEditThreshold={this.onEditThreshold} /> <Confirmations confirmations={safe.get('threshold')} onEditThreshold={this.onEditThreshold} />
<Address address={safe.get('address')} /> <Address address={address} />
<DailyLimit balance={ethBalance} dailyLimit={safe.get('dailyLimit')} onWithdraw={this.onWithdraw} onEditDailyLimit={this.onEditDailyLimit} /> <DailyLimit balance={ethBalance} dailyLimit={safe.get('dailyLimit')} onWithdraw={this.onWithdraw} onEditDailyLimit={this.onEditDailyLimit} />
<MultisigTx onSeeTxs={this.onListTransactions} /> <MultisigTx onSeeTxs={this.onListTransactions} />
</List> </List>

View File

@ -10,6 +10,7 @@ import { type Token } from '~/routes/tokens/store/model/token'
import { createTransaction } from '~/wallets/createTransactions' import { createTransaction } from '~/wallets/createTransactions'
import { EMPTY_DATA } from '~/wallets/ethTransactions' import { EMPTY_DATA } from '~/wallets/ethTransactions'
import { toNative } from '~/wallets/tokens' import { toNative } from '~/wallets/tokens'
import { isEther } from '~/utils/tokens'
import actions, { type Actions } from './actions' import actions, { type Actions } from './actions'
import selector, { type SelectorProps } from './selector' import selector, { type SelectorProps } from './selector'
import SendTokenForm, { TKN_DESTINATION_PARAM, TKN_VALUE_PARAM } from './SendTokenForm' import SendTokenForm, { TKN_DESTINATION_PARAM, TKN_VALUE_PARAM } from './SendTokenForm'
@ -31,8 +32,6 @@ type State = {
export const SEE_TXS_BUTTON_TEXT = 'VISIT TXS' export const SEE_TXS_BUTTON_TEXT = 'VISIT TXS'
const isEther = (symbol: string) => symbol === 'ETH'
const getTransferData = async (tokenAddress: string, to: string, amount: BigNumber) => { const getTransferData = async (tokenAddress: string, to: string, amount: BigNumber) => {
const StandardToken = await getStandardTokenContract() const StandardToken = await getStandardTokenContract()
const myToken = await StandardToken.at(tokenAddress) const myToken = await StandardToken.at(tokenAddress)

View File

@ -1,5 +1,5 @@
// @flow // @flow
import * as MuiList from '@material-ui/core/List' import MuiList from '@material-ui/core/List'
import * as React from 'react' import * as React from 'react'
import Block from '~/components/layout/Block' import Block from '~/components/layout/Block'
import Col from '~/components/layout/Col' import Col from '~/components/layout/Col'
@ -24,6 +24,7 @@ type State = {
const listStyle = { const listStyle = {
width: '100%', width: '100%',
paddingBottom: 0,
} }
class TokenLayout extends React.PureComponent<TokenProps, State> { class TokenLayout extends React.PureComponent<TokenProps, State> {
@ -64,6 +65,7 @@ class TokenLayout extends React.PureComponent<TokenProps, State> {
<Col sm={12} top="xs" md={5} margin="xl" overflow> <Col sm={12} top="xs" md={5} margin="xl" overflow>
<MuiList style={listStyle}> <MuiList style={listStyle}>
{tokens.map((token: Token) => (<TokenComponent {tokens.map((token: Token) => (<TokenComponent
key={token.get('symbol')}
token={token} token={token}
onDisableToken={this.onDisableToken} onDisableToken={this.onDisableToken}
onEnableToken={this.onEnableToken} onEnableToken={this.onEnableToken}

View File

@ -3,12 +3,12 @@ import * as React from 'react'
import { type Token } from '~/routes/tokens/store/model/token' import { type Token } from '~/routes/tokens/store/model/token'
import { withStyles } from '@material-ui/core/styles' import { withStyles } from '@material-ui/core/styles'
import Block from '~/components/layout/Block' import Block from '~/components/layout/Block'
import Bold from '~/components/layout/Bold'
import Checkbox from '@material-ui/core/Checkbox' import Checkbox from '@material-ui/core/Checkbox'
import Card from '@material-ui/core/Card' import Card from '@material-ui/core/Card'
import CardContent from '@material-ui/core/CardContent' import CardContent from '@material-ui/core/CardContent'
import CardMedia from '@material-ui/core/CardMedia' import CardMedia from '@material-ui/core/CardMedia'
import Typography from '@material-ui/core/Typography' import Typography from '@material-ui/core/Typography'
import { isEther } from '~/utils/tokens'
// import Delete from '@material-ui/icons/Delete' // import Delete from '@material-ui/icons/Delete'
// import IconButton from '@material-ui/core/IconButton' // import IconButton from '@material-ui/core/IconButton'
import { type WithStyles } from '~/theme/mui' import { type WithStyles } from '~/theme/mui'
@ -24,7 +24,7 @@ type State = {
checked: boolean, checked: boolean,
} }
const styles = theme => ({ const styles = () => ({
card: { card: {
display: 'flex', display: 'flex',
}, },
@ -36,18 +36,9 @@ const styles = theme => ({
flex: '1 0 auto', flex: '1 0 auto',
}, },
cover: { cover: {
width: 45, width: 150,
height: 45, margin: 10,
}, backgroundSize: '50%',
controls: {
display: 'flex',
alignItems: 'center',
paddingLeft: theme.spacing.unit,
paddingBottom: theme.spacing.unit,
},
playIcon: {
height: 38,
width: 38,
}, },
}) })
@ -60,14 +51,15 @@ class TokenComponent extends React.Component<Props, State> {
handleChange = (e: SyntheticInputEvent<HTMLInputElement>) => { handleChange = (e: SyntheticInputEvent<HTMLInputElement>) => {
const { checked } = e.target const { checked } = e.target
const callback = checked ? this.props.onDisableToken : this.props.onDisableToken const callback = checked ? this.props.onEnableToken : this.props.onDisableToken
this.setState(() => ({ checked: e.target.checked }), () => callback(this.props.token)) this.setState(() => ({ checked }), () => callback(this.props.token))
} }
render() { render() {
const { classes, token } = this.props const { classes, token } = this.props
const name = token.get('name') const name = token.get('name')
const symbol = token.get('symbol') const symbol = token.get('symbol')
const disabled = isEther(symbol)
return ( return (
<Card className={classes.card}> <Card className={classes.card}>
@ -75,25 +67,23 @@ class TokenComponent extends React.Component<Props, State> {
<CardContent className={classes.content}> <CardContent className={classes.content}>
<Typography variant="headline">{name}</Typography> <Typography variant="headline">{name}</Typography>
<Typography variant="subheading" color="textSecondary"> <Typography variant="subheading" color="textSecondary">
<Checkbox
disabled={disabled}
checked={this.state.checked}
onChange={this.handleChange}
color="primary"
/>
{symbol} {symbol}
</Typography> </Typography>
</CardContent> </CardContent>
</Block>
{/*
<Block className={classes.controls}> <Block className={classes.controls}>
<Bold>
{symbol}
</Bold>
<Checkbox
checked={this.state.checked}
onChange={this.handleChange}
color="primary"
/>
{/*
<IconButton aria-label="Delete" onClick={this.onRemoveClick}> <IconButton aria-label="Delete" onClick={this.onRemoveClick}>
<Delete /> <Delete />
</IconButton> </IconButton>
*/}
</Block> </Block>
</Block> */}
<CardMedia <CardMedia
className={classes.cover} className={classes.cover}
image={token.get('logoUrl')} image={token.get('logoUrl')}

View File

@ -1,6 +1,7 @@
// @flow // @flow
import enableToken from '~/routes/tokens/store/actions/enableToken' import enableToken from '~/routes/tokens/store/actions/enableToken'
import disableToken from '~/routes/tokens/store/actions/disableToken' import disableToken from '~/routes/tokens/store/actions/disableToken'
import { fetchTokens } from '~/routes/tokens/store/actions/fetchTokens'
export type Actions = { export type Actions = {
enableToken: typeof enableToken, enableToken: typeof enableToken,
@ -10,4 +11,5 @@ export type Actions = {
export default { export default {
enableToken, enableToken,
disableToken, disableToken,
fetchTokens,
} }

View File

@ -3,12 +3,24 @@ import * as React from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import Page from '~/components/layout/Page' import Page from '~/components/layout/Page'
import Layout from '~/routes/tokens/component/Layout' import Layout from '~/routes/tokens/component/Layout'
import { fetchTokens } from '~/routes/tokens/store/actions/fetchTokens'
import selector, { type SelectorProps } from './selector' import selector, { type SelectorProps } from './selector'
import actions, { type Actions } from './actions' import actions, { type Actions } from './actions'
type Props = Actions & SelectorProps type Props = Actions & SelectorProps & {
safeAddress: string,
fetchTokens: typeof fetchTokens,
}
class TokensView extends React.PureComponent<Props> { class TokensView extends React.PureComponent<Props> {
componentDidUpdate() {
const { safeAddress } = this.props
if (this.props.tokens.count() === 0) {
this.props.fetchTokens(safeAddress)
}
}
render() { render() {
const { const {
tokens, addresses, safe, disableToken, enableToken, tokens, addresses, safe, disableToken, enableToken,

View File

@ -3,7 +3,7 @@ import { List } from 'immutable'
import { createStructuredSelector } from 'reselect' import { createStructuredSelector } from 'reselect'
import { tokenListSelector, tokenAddressesSelector } from '~/routes/tokens/store/selectors' import { tokenListSelector, tokenAddressesSelector } from '~/routes/tokens/store/selectors'
import { type Safe } from '~/routes/safe/store/model/safe' import { type Safe } from '~/routes/safe/store/model/safe'
import { safeSelector } from '~/routes/safe/store/selectors' import { safeSelector, safeParamAddressSelector } from '~/routes/safe/store/selectors'
import { type Token } from '~/routes/tokens/store/model/token' import { type Token } from '~/routes/tokens/store/model/token'
export type SelectorProps = { export type SelectorProps = {
@ -14,6 +14,7 @@ export type SelectorProps = {
export default createStructuredSelector({ export default createStructuredSelector({
safe: safeSelector, safe: safeSelector,
safeAddress: safeParamAddressSelector,
tokens: tokenListSelector, tokens: tokenListSelector,
addresses: tokenAddressesSelector, addresses: tokenAddressesSelector,
}) })

2
src/utils/tokens.js Normal file
View File

@ -0,0 +1,2 @@
// @flow
export const isEther = (symbol: string) => symbol === 'ETH'