Merge pull request #17 from gnosis/feature/WA-230-add-balance-selector

WA-230 Adding get balance selector
This commit is contained in:
Adolfo Panizo 2018-04-17 17:03:46 +02:00 committed by GitHub
commit f376bfcba4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 157 additions and 21 deletions

View File

@ -6,10 +6,10 @@ import GnoSafe from './Safe'
type Props = SelectorProps type Props = SelectorProps
const Layout = ({ safe, provider }: Props) => ( const Layout = ({ safe, balance, provider }: Props) => (
<React.Fragment> <React.Fragment>
{ safe { safe
? <GnoSafe safe={safe} /> ? <GnoSafe safe={safe} balance={balance} />
: <NoSafe provider={provider} text="Not found safe" /> : <NoSafe provider={provider} text="Not found safe" />
} }
</React.Fragment> </React.Fragment>

View File

@ -14,12 +14,31 @@ const FrameDecorator = story => (
storiesOf('Routes /safe:address', module) storiesOf('Routes /safe:address', module)
.addDecorator(FrameDecorator) .addDecorator(FrameDecorator)
.add('Safe undefined being connected', () => <Component safe={undefined} provider="METAMASK" />) .add('Safe undefined being connected', () => (
.add('Safe undefined NOT connected', () => <Component safe={undefined} provider="" />) <Component
safe={undefined}
provider="METAMASK"
balance="0"
fetchBalance={() => {}}
/>
))
.add('Safe undefined NOT connected', () => (
<Component
safe={undefined}
provider=""
balance="0"
fetchBalance={() => {}}
/>
))
.add('Safe with 2 owners', () => { .add('Safe with 2 owners', () => {
const safe = SafeFactory.twoOwnersSafe const safe = SafeFactory.twoOwnersSafe
return ( return (
<Component safe={safe} provider="METAMASK" /> <Component
safe={safe}
provider="METAMASK"
balance="2"
fetchBalance={() => {}}
/>
) )
}) })

View File

@ -10,9 +10,10 @@ import { type Safe } from '~/routes/safe/store/model/safe'
type SafeProps = { type SafeProps = {
safe: Safe, safe: Safe,
balance: string,
} }
const GnoSafe = ({ safe }: SafeProps) => ( const GnoSafe = ({ safe, balance }: SafeProps) => (
<React.Fragment> <React.Fragment>
<Row> <Row>
<Col xs={12}> <Col xs={12}>
@ -21,6 +22,18 @@ const GnoSafe = ({ safe }: SafeProps) => (
</Paragraph> </Paragraph>
</Col> </Col>
</Row> </Row>
<Row>
<Paragraph size="lg">
<Bold>Balance</Bold>
</Paragraph>
</Row>
<Row>
<Block>
<Paragraph>
{balance} - ETH
</Paragraph>
</Block>
</Row>
<Row> <Row>
<Paragraph size="lg"> <Paragraph size="lg">
<Bold>Address</Bold> <Bold>Address</Bold>

View File

@ -0,0 +1,10 @@
// @flow
import fetchBalance from '~/routes/safe/store/actions/fetchBalance'
export type Actions = {
fetchBalance: typeof fetchBalance,
}
export default {
fetchBalance,
}

View File

@ -4,19 +4,30 @@ import { connect } from 'react-redux'
import Page from '~/components/layout/Page' import Page from '~/components/layout/Page'
import Layout from '~/routes/safe/component/Layout' import Layout from '~/routes/safe/component/Layout'
import selector, { type SelectorProps } from './selector' import selector, { type SelectorProps } from './selector'
import actions, { type Actions } from './actions'
type Props = SelectorProps class SafeView extends React.PureComponent<Actions & SelectorProps> {
componentDidMount() {
const { safe, fetchBalance } = this.props
if (!safe) { return }
const safeAddress: string = safe.get('address')
fetchBalance(safeAddress)
}
class SafeView extends React.PureComponent<Props> {
render() { render() {
const { safe, provider } = this.props const { safe, provider, balance } = this.props
return ( return (
<Page> <Page>
<Layout provider={provider} safe={safe} /> <Layout
balance={balance}
provider={provider}
safe={safe}
/>
</Page> </Page>
) )
} }
} }
export default connect(selector)(SafeView) export default connect(selector, actions)(SafeView)

View File

@ -1,14 +1,16 @@
// @flow // @flow
import { createStructuredSelector } from 'reselect' import { createStructuredSelector } from 'reselect'
import { safeSelector, type SafeSelectorProps } from '~/routes/safe/store/selectors' import { balanceSelector, safeSelector, type SafeSelectorProps } from '~/routes/safe/store/selectors'
import { providerNameSelector } from '~/wallets/store/selectors/index' import { providerNameSelector } from '~/wallets/store/selectors/index'
export type SelectorProps = { export type SelectorProps = {
safe: SafeSelectorProps, safe: SafeSelectorProps,
provider: string, provider: string,
balance: string,
} }
export default createStructuredSelector({ export default createStructuredSelector({
safe: safeSelector, safe: safeSelector,
provider: providerNameSelector, provider: providerNameSelector,
balance: balanceSelector,
}) })

View File

@ -6,6 +6,7 @@ import { type GlobalState } from '~/store/index'
import { SAFE_PARAM_ADDRESS } from '~/routes/routes' import { SAFE_PARAM_ADDRESS } from '~/routes/routes'
import { type Safe } from '~/routes/safe/store/model/safe' import { type Safe } from '~/routes/safe/store/model/safe'
import { safesMapSelector } from '~/routes/safeList/store/selectors' import { safesMapSelector } from '~/routes/safeList/store/selectors'
import { BALANCE_REDUCER_ID } from '~/routes/safe/store/reducer/balances'
type RouterProps = { type RouterProps = {
match: Match, match: Match,
@ -13,6 +14,8 @@ type RouterProps = {
const safeAddessSelector = (state: GlobalState, props: RouterProps) => props.match.params[SAFE_PARAM_ADDRESS] || '' const safeAddessSelector = (state: GlobalState, props: RouterProps) => props.match.params[SAFE_PARAM_ADDRESS] || ''
const balancesSelector = (state: GlobalState) => state[BALANCE_REDUCER_ID]
export type SafeSelectorProps = Safe | typeof undefined export type SafeSelectorProps = Safe | typeof undefined
export const safeSelector: Selector<GlobalState, RouterProps, SafeSelectorProps> = createSelector( export const safeSelector: Selector<GlobalState, RouterProps, SafeSelectorProps> = createSelector(
@ -27,6 +30,18 @@ export const safeSelector: Selector<GlobalState, RouterProps, SafeSelectorProps>
}, },
) )
export const balanceSelector: Selector<GlobalState, RouterProps, string> = createSelector(
balancesSelector,
safeAddessSelector,
(balances: Map<string, string>, address: string) => {
if (!address) {
return '0'
}
return balances.get(address) || '0'
},
)
export default createStructuredSelector({ export default createStructuredSelector({
safe: safeSelector, safe: safeSelector,
}) })

View File

@ -6,10 +6,10 @@ import { getWeb3 } from '~/wallets/getWeb3'
import { promisify } from '~/utils/promisify' import { promisify } from '~/utils/promisify'
import { aDeployedSafe } from './builder/deployedSafe.builder' import { aDeployedSafe } from './builder/deployedSafe.builder'
const addOneEtherTo = async (address: string) => { const addEtherTo = async (address: string, eth: string) => {
const web3 = getWeb3() const web3 = getWeb3()
const accounts = await promisify(cb => web3.eth.getAccounts(cb)) const accounts = await promisify(cb => web3.eth.getAccounts(cb))
const txData = { from: accounts[0], to: address, value: web3.toWei('1', 'ether') } const txData = { from: accounts[0], to: address, value: web3.toWei(eth, 'ether') }
return promisify(cb => web3.eth.sendTransaction(txData, cb)) return promisify(cb => web3.eth.sendTransaction(txData, cb))
} }
@ -34,19 +34,19 @@ const balanceReducerTests = () => {
expect(balances.get(address)).toBe('0') expect(balances.get(address)).toBe('0')
}) })
it('reducer should return 1 ETH as funds to safe with 1 ETH', async () => { it('reducer should return 1.3456 ETH as funds to safe with 1 ETH', async () => {
// GIVEN // GIVEN
const safeTx = await aDeployedSafe(store) const safeTx = await aDeployedSafe(store)
const address = safeTx.contractAddress const address = safeTx.contractAddress
// WHEN // WHEN
await addOneEtherTo(address) await addEtherTo(address, '1.3456')
await store.dispatch(fetchBalance(address)) await store.dispatch(fetchBalance(address))
// THEN // THEN
const balances = store.getState()[BALANCE_REDUCER_ID] const balances = store.getState()[BALANCE_REDUCER_ID]
expect(balances).not.toBe(undefined) expect(balances).not.toBe(undefined)
expect(balances.get(address)).toBe('1') expect(balances.get(address)).toBe('1.3456')
}) })
}) })
} }

View File

@ -0,0 +1,61 @@
// @flow
import { type Match } from 'react-router-dom'
import addBalance from '~/routes/safe/store/actions/addBalance'
import { aNewStore } from '~/store'
import { balanceSelector } from '../selectors'
const buildMathPropsFrom = (address): Match => ({
params: {
address,
},
isExact: true,
path: '',
url: '',
})
const balanceSelectorTests = () => {
describe('Safe Selector[balanceSelector]', () => {
it('should return 0 when safe address is not found', () => {
// GIVEN
const safeAddress = 'foo'
const match = buildMathPropsFrom(safeAddress)
const store = aNewStore()
// WHEN
const balance = balanceSelector(store.getState(), { match })
// THEN
expect(balance).toBe('0')
})
it('should return 0 when safe has no funds', async () => {
// GIVEN
const safeAddress = 'foo'
const match = buildMathPropsFrom(safeAddress)
const store = aNewStore()
// WHEN
await store.dispatch(addBalance('bar', '1'))
const balance = balanceSelector(store.getState(), { match })
// THEN
expect(balance).toBe('0')
})
it('should return safe funds', async () => {
// GIVEN
const safeAddress = 'foo'
const match = buildMathPropsFrom(safeAddress)
const store = aNewStore()
// WHEN
await store.dispatch(addBalance(safeAddress, '1.3456'))
const balance = balanceSelector(store.getState(), { match })
// THEN
expect(balance).toBe('1.3456')
})
})
}
export default balanceSelectorTests

View File

@ -1,6 +1,7 @@
// @flow // @flow
import balanceReducerTests from './balance.reducer' import balanceReducerTests from './balance.reducer'
import safeReducerTests from './safe.reducer' import safeReducerTests from './safe.reducer'
import balanceSelectorTests from './balance.selector'
import safeSelectorTests from './safe.selector' import safeSelectorTests from './safe.selector'
describe('Safe Test suite', () => { describe('Safe Test suite', () => {
@ -10,4 +11,7 @@ describe('Safe Test suite', () => {
// SAFE SELECTOR // SAFE SELECTOR
safeSelectorTests() safeSelectorTests()
// BALANCE SELECTOR
balanceSelectorTests()
}) })

View File

@ -33,4 +33,5 @@ const initialState = { [SAFE_REDUCER_ID]: calculateInitialState() }
export const store: Store<GlobalState> = createStore(reducers, initialState, finalCreateStore) export const store: Store<GlobalState> = createStore(reducers, initialState, finalCreateStore)
export const aNewStore = (): Store<GlobalState> => createStore(reducers, initialState, finalCreateStore) export const aNewStore = (localState?: Object): Store<GlobalState> =>
createStore(reducers, localState, finalCreateStore)