Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

Commit

Permalink
Merge pull request #16 from gnosis/feature/WA-230-display-eth-funds
Browse files Browse the repository at this point in the history
WA-230 Display and update ETH funds
  • Loading branch information
apanizo authored Apr 24, 2018
2 parents 0094abd + 1c7aff9 commit 58e935d
Show file tree
Hide file tree
Showing 25 changed files with 385 additions and 52 deletions.
6 changes: 3 additions & 3 deletions src/components/layout/Paragraph/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
}

.soft {
color: #888888;
color: #888888;
}

.medium {
color: #686868;
color: #686868;
}

.dark {
color: black;
color: black;
}

.primary {
Expand Down
21 changes: 3 additions & 18 deletions src/routes/open/components/Layout.test.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,18 @@
// @flow
import * as React from 'react'
import TestUtils from 'react-dom/test-utils'
import Open from '~/routes/open/container/Open'
import { Provider } from 'react-redux'
import { ConnectedRouter } from 'react-router-redux'
import { store } from '~/store'
import { FIELD_NAME, FIELD_OWNERS, FIELD_CONFIRMATIONS, getOwnerNameBy, getOwnerAddressBy } from '~/routes/open/components/fields'
import { DEPLOYED_COMPONENT_ID } from '~/routes/open/components/FormConfirmation'
import { history, store } from '~/store'
import { sleep } from '~/utils/timer'
import { getProviderInfo } from '~/wallets/getWeb3'
import addProvider from '~/wallets/store/actions/addProvider'
import { makeProvider } from '~/wallets/store/model/provider'
import { renderSafe } from '~/routes/safe/store/test/builder/deployedSafe.builder'

describe('React DOM TESTS > Create Safe form', () => {
let open
let provider
beforeEach(async () => {
// init app web3 instance
provider = await getProviderInfo()
const walletRecord = makeProvider(provider)
store.dispatch(addProvider(walletRecord))

open = TestUtils.renderIntoDocument((
<Provider store={store}>
<ConnectedRouter history={history}>
<Open />
</ConnectedRouter>
</Provider>
))
open = await renderSafe(store)
})

it('should create a 1 owner safe after rendering correctly the form', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/routes/safe/component/Layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import GnoSafe from './Safe'

type Props = SelectorProps

const Layout = ({ safe, provider }: Props) => (
const Layout = ({ safe, balance, provider }: Props) => (
<React.Fragment>
{ safe
? <GnoSafe safe={safe} />
? <GnoSafe safe={safe} balance={balance} />
: <NoSafe provider={provider} text="Not found safe" />
}
</React.Fragment>
Expand Down
27 changes: 23 additions & 4 deletions src/routes/safe/component/Layout.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { storiesOf } from '@storybook/react'
import * as React from 'react'
import styles from '~/components/layout/PageFrame/index.scss'
import { SafeFactory } from '~/routes/safe/store/test/builder/index.builder'
import { SafeFactory } from '~/routes/safe/store/test/builder/safe.builder'
import Component from './Layout'


Expand All @@ -14,12 +14,31 @@ const FrameDecorator = story => (

storiesOf('Routes /safe:address', module)
.addDecorator(FrameDecorator)
.add('Safe undefined being connected', () => <Component safe={undefined} provider="METAMASK" />)
.add('Safe undefined NOT connected', () => <Component safe={undefined} provider="" />)
.add('Safe undefined being connected', () => (
<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', () => {
const safe = SafeFactory.twoOwnersSafe

return (
<Component safe={safe} provider="METAMASK" />
<Component
safe={safe}
provider="METAMASK"
balance="2"
fetchBalance={() => {}}
/>
)
})
15 changes: 14 additions & 1 deletion src/routes/safe/component/Safe.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import { type Safe } from '~/routes/safe/store/model/safe'

type SafeProps = {
safe: Safe,
balance: string,
}

const GnoSafe = ({ safe }: SafeProps) => (
const GnoSafe = ({ safe, balance }: SafeProps) => (
<React.Fragment>
<Row>
<Col xs={12}>
Expand All @@ -21,6 +22,18 @@ const GnoSafe = ({ safe }: SafeProps) => (
</Paragraph>
</Col>
</Row>
<Row>
<Paragraph size="lg">
<Bold>Balance</Bold>
</Paragraph>
</Row>
<Row>
<Block>
<Paragraph>
{balance} - ETH
</Paragraph>
</Block>
</Row>
<Row>
<Paragraph size="lg">
<Bold>Address</Bold>
Expand Down
10 changes: 10 additions & 0 deletions src/routes/safe/container/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// @flow
import fetchBalance from '~/routes/safe/store/actions/fetchBalance'

export type Actions = {
fetchBalance: typeof fetchBalance,
}

export default {
fetchBalance,
}
29 changes: 25 additions & 4 deletions src/routes/safe/container/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,40 @@ import { connect } from 'react-redux'
import Page from '~/components/layout/Page'
import Layout from '~/routes/safe/component/Layout'
import selector, { type SelectorProps } from './selector'
import actions, { type Actions } from './actions'

type Props = SelectorProps
type Props = Actions & SelectorProps

class SafeView extends React.PureComponent<Props> {
componentDidMount() {
this.intervalId = setInterval(() => {
const { safe, fetchBalance } = this.props
if (!safe) { return }

const safeAddress: string = safe.get('address')
fetchBalance(safeAddress)
}, 1500)
}

componentWillUnmount() {
clearInterval(this.intervalId)
}

intervalId: IntervalID

render() {
const { safe, provider } = this.props
const { safe, provider, balance } = this.props

return (
<Page>
<Layout provider={provider} safe={safe} />
<Layout
balance={balance}
provider={provider}
safe={safe}
/>
</Page>
)
}
}

export default connect(selector)(SafeView)
export default connect(selector, actions)(SafeView)
4 changes: 3 additions & 1 deletion src/routes/safe/container/selector.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
// @flow
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'

export type SelectorProps = {
safe: SafeSelectorProps,
provider: string,
balance: string,
}

export default createStructuredSelector({
safe: safeSelector,
provider: providerNameSelector,
balance: balanceSelector,
})
19 changes: 19 additions & 0 deletions src/routes/safe/store/actions/addBalance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// @flow
import { createAction } from 'redux-actions'

export const ADD_BALANCE = 'ADD_BALANCE'

type BalanceProps = {
safeAddress: string,
funds: string,
}

const addBalance = createAction(
ADD_BALANCE,
(safeAddress: string, funds: string): BalanceProps => ({
safeAddress,
funds,
}),
)

export default addBalance
11 changes: 11 additions & 0 deletions src/routes/safe/store/actions/fetchBalance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @flow
import type { Dispatch as ReduxDispatch } from 'redux'
import { getBalanceInEtherOf } from '~/wallets/getWeb3'
import { type GlobalState } from '~/store/index'
import addBalance from './addBalance'

export default (safeAddress: string) => async (dispatch: ReduxDispatch<GlobalState>) => {
const balance = await getBalanceInEtherOf(safeAddress)

return dispatch(addBalance(safeAddress, balance))
}
13 changes: 13 additions & 0 deletions src/routes/safe/store/reducer/balances.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @flow
import { Map } from 'immutable'
import { handleActions, type ActionType } from 'redux-actions'
import addBalance, { ADD_BALANCE } from '~/routes/safe/store/actions/addBalance'

export const BALANCE_REDUCER_ID = 'balances'

export type State = Map<string, string>

export default handleActions({
[ADD_BALANCE]: (state: State, action: ActionType<typeof addBalance>): State =>
state.set(action.payload.safeAddress, action.payload.funds),
}, Map())
15 changes: 15 additions & 0 deletions src/routes/safe/store/selectors/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import { type GlobalState } from '~/store/index'
import { SAFE_PARAM_ADDRESS } from '~/routes/routes'
import { type Safe } from '~/routes/safe/store/model/safe'
import { safesMapSelector } from '~/routes/safeList/store/selectors'
import { BALANCE_REDUCER_ID } from '~/routes/safe/store/reducer/balances'

type RouterProps = {
match: Match,
}

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 const safeSelector: Selector<GlobalState, RouterProps, SafeSelectorProps> = createSelector(
Expand All @@ -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({
safe: safeSelector,
})
54 changes: 54 additions & 0 deletions src/routes/safe/store/test/balance.reducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// @flow
import { BALANCE_REDUCER_ID } from '~/routes/safe/store/reducer/balances'
import fetchBalance from '~/routes/safe/store/actions/fetchBalance'
import { aNewStore } from '~/store'
import { getWeb3 } from '~/wallets/getWeb3'
import { promisify } from '~/utils/promisify'
import { aDeployedSafe } from './builder/deployedSafe.builder'

const addEtherTo = async (address: string, eth: string) => {
const web3 = getWeb3()
const accounts = await promisify(cb => web3.eth.getAccounts(cb))
const txData = { from: accounts[0], to: address, value: web3.toWei(eth, 'ether') }
return promisify(cb => web3.eth.sendTransaction(txData, cb))
}

const balanceReducerTests = () => {
describe('Safe Actions[fetchBalance]', () => {
let store
beforeEach(async () => {
store = aNewStore()
})

it('reducer should return 0 to just deployed safe', async () => {
// GIVEN
const safeTx = await aDeployedSafe(store)
const address = safeTx.contractAddress

// WHEN
await store.dispatch(fetchBalance(address))

// THEN
const balances = store.getState()[BALANCE_REDUCER_ID]
expect(balances).not.toBe(undefined)
expect(balances.get(address)).toBe('0')
})

it('reducer should return 1.3456 ETH as funds to safe with 1 ETH', async () => {
// GIVEN
const safeTx = await aDeployedSafe(store)
const address = safeTx.contractAddress

// WHEN
await addEtherTo(address, '1.3456')
await store.dispatch(fetchBalance(address))

// THEN
const balances = store.getState()[BALANCE_REDUCER_ID]
expect(balances).not.toBe(undefined)
expect(balances.get(address)).toBe('1.3456')
})
})
}

export default balanceReducerTests
Loading

0 comments on commit 58e935d

Please sign in to comment.