From 9717fc92a68944f2e3b6d51d5ff208f77116e53e Mon Sep 17 00:00:00 2001 From: Matt Huggins Date: Tue, 23 Jan 2018 04:42:38 -0600 Subject: [PATCH] Replaced price actions/reducers with new actions architecture (#595) --- __tests__/components/App.test.js | 15 ++-- __tests__/components/WalletInfo.test.js | 61 +++++-------- .../__snapshots__/WalletInfo.test.js.snap | 4 +- __tests__/modules/price.test.js | 71 ---------------- __tests__/store/reducers.test.js | 1 - app/actions/appActions.js | 2 + app/actions/pricesActions.js | 16 ++++ app/containers/App/Header/index.js | 11 ++- app/containers/WalletInfo/index.js | 11 ++- app/core/deprecated.js | 5 ++ app/modules/index.js | 2 - app/modules/price.js | 85 ------------------- app/modules/wallet.js | 9 +- 13 files changed, 71 insertions(+), 222 deletions(-) delete mode 100644 __tests__/modules/price.test.js create mode 100644 app/actions/pricesActions.js delete mode 100644 app/modules/price.js diff --git a/__tests__/components/App.test.js b/__tests__/components/App.test.js index c922df180..7a1b84aa7 100644 --- a/__tests__/components/App.test.js +++ b/__tests__/components/App.test.js @@ -13,7 +13,7 @@ const initialState = { api: { APP: { batch: true, - mapping: ['NETWORK', 'SETTINGS'] + mapping: ['NETWORK', 'PRICES', 'SETTINGS'] }, NETWORK: { batch: false, @@ -21,6 +21,15 @@ const initialState = { data: MAIN_NETWORK_ID, loadedCount: 1 }, + PRICES: { + batch: false, + state: LOADED, + data: { + NEO: 40.5, + GAS: 19.8 + }, + loadedCount: 1 + }, SETTINGS: { batch: false, state: LOADED, @@ -34,10 +43,6 @@ const initialState = { transactions: [] }, modal: { - }, - price: { - NEO: 40.5, - GAS: 19.8 } } const setup = (state, shallowRender = true) => { diff --git a/__tests__/components/WalletInfo.test.js b/__tests__/components/WalletInfo.test.js index e18ea7a3f..db2a0e45f 100644 --- a/__tests__/components/WalletInfo.test.js +++ b/__tests__/components/WalletInfo.test.js @@ -60,6 +60,14 @@ const initialState = { data: { currency: DEFAULT_CURRENCY_CODE } + }, + PRICES: { + batch: false, + state: LOADED, + data: { + NEO: 25.48, + GAS: 18.1 + } } }, account: { @@ -72,10 +80,6 @@ const initialState = { GAS: '1000.0001601', tokenBalances: [] }, - price: { - NEO: 25.48, - GAS: 18.1 - }, claim: { claimAmount: 0.5 } @@ -107,6 +111,7 @@ describe('WalletInfo', () => { expect(wrapper).toMatchSnapshot() done() }) + test('correctly renders data from state', done => { const { wrapper } = setup(initialState, false) @@ -128,6 +133,7 @@ describe('WalletInfo', () => { expect(gasField.text()).toEqual('1,000.0002') done() }) + test('refreshBalance is getting called on click', async () => { const { wrapper, store } = setup(initialState, false) @@ -138,54 +144,24 @@ describe('WalletInfo', () => { .then() .then() jest.runAllTimers() - const actions = store.getActions() - expect(actions.length).toEqual(7) - expect(actions[0]).toEqual({ - type: LOADING_TRANSACTIONS, - payload: { - isLoadingTransactions: true - } - }) - expect(actions[2]).toEqual({ - type: LOADING_TRANSACTIONS, - payload: { - isLoadingTransactions: false - } - }) - expect(actions[3]).toEqual({ - type: SET_TRANSACTION_HISTORY, - payload: { - transactions: [] - } - }) - expect(actions[4]).toEqual({ + const action = store.getActions().find((action) => action.type === SET_BALANCE) + + expect(action).toEqual({ type: SET_BALANCE, payload: { NEO: '1', GAS: '1' } }) - // TODO fix this to capture the notifications as well - // expect(actions[6]).toEqual({ - // type: HIDE_NOTIFICATIONS, - // payload: { - // dismissible: true, - // position: DEFAULT_POSITION - // } - // }) - // expect(actions[7]).toEqual({ - // type: SHOW_NOTIFICATION, - // payload: expect.objectContaining({ - // message: 'Received latest blockchain information.', - // level: NOTIFICATION_LEVELS.SUCCESS - // }) - // }) }) + test('correctly renders data from state with non-default currency', done => { const testState = merge(initialState, { - api: { SETTINGS: { data: { currency: 'eur' } } }, - price: { NEO: 1.11, GAS: 0.55 } + api: { + SETTINGS: { data: { currency: 'eur' } }, + PRICES: { data: { NEO: 1.11, GAS: 0.55 } } + } }) const { wrapper } = setup(testState, false) @@ -203,6 +179,7 @@ describe('WalletInfo', () => { done() }) + test('network error is shown with connectivity error', async () => { neonjs.api.neonDB.getBalance = jest.fn(() => { return new Promise((resolve, reject) => { diff --git a/__tests__/components/__snapshots__/WalletInfo.test.js.snap b/__tests__/components/__snapshots__/WalletInfo.test.js.snap index 17517a903..c17927a48 100644 --- a/__tests__/components/__snapshots__/WalletInfo.test.js.snap +++ b/__tests__/components/__snapshots__/WalletInfo.test.js.snap @@ -1,13 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`WalletInfo renders without crashing 1`] = ` - { - const NEO = 28.10 - const GAS = 18.20 - - const initialState = { - NEO: 0, - GAS: 0 - } - - describe('setNEOPrice tests', () => { - const expectedAction = { - type: SET_NEO_PRICE, - payload: { NEO } - } - - test('setNEOPrice action works', () => { - expect(setNEOPrice(NEO)).toEqual(expectedAction) - }) - - test('setNEOPrice reducer should return the initial state', () => { - expect(walletReducer(undefined, {})).toEqual(initialState) - }) - - test('wallet reducer should handle SET_NEO_PRICE', () => { - const expectedState = { - ...initialState, - NEO - } - expect(walletReducer(undefined, expectedAction)).toEqual(expectedState) - }) - }) - - describe('setGASPrice tests', () => { - const expectedAction = { - type: SET_GAS_PRICE, - payload: { GAS } - } - - test('setGASPrice action works', () => { - expect(setGASPrice(GAS)).toEqual(expectedAction) - }) - - test('setGASPrice reducer should return the initial state', () => { - expect(walletReducer(undefined, {})).toEqual(initialState) - }) - - test('wallet reducer should handle SET_GAS_PRICE', () => { - const expectedState = { - ...initialState, - GAS - } - expect(walletReducer(undefined, expectedAction)).toEqual(expectedState) - }) - }) - - describe('resetPrice tests', () => { - const expectedAction = { - type: RESET_PRICE - } - - test('resetPrice action works', () => { - expect(resetPrice()).toEqual(expectedAction) - }) - - test('wallet reducer should handle RESET_PRICE', () => { - expect(walletReducer(undefined, expectedAction)).toEqual(initialState) - }) - }) -}) diff --git a/__tests__/store/reducers.test.js b/__tests__/store/reducers.test.js index 8d1397ebc..1c487538f 100644 --- a/__tests__/store/reducers.test.js +++ b/__tests__/store/reducers.test.js @@ -8,7 +8,6 @@ describe('root reducer', () => { addressBook: expect.any(Object), generateWallet: expect.any(Object), wallet: expect.any(Object), - price: expect.any(Object), transactions: expect.any(Object), dashboard: expect.any(Object), notifications: expect.any(Object), diff --git a/app/actions/appActions.js b/app/actions/appActions.js index 63c845ca9..19d64ebfa 100644 --- a/app/actions/appActions.js +++ b/app/actions/appActions.js @@ -2,11 +2,13 @@ import createBatchActions from '../util/api/createBatchActions' import blockHeightActions from './blockHeightActions' +import pricesActions from './pricesActions' import settingsActions from './settingsActions' export const ID = 'APP' export default createBatchActions(ID, { blockHeight: blockHeightActions, + prices: pricesActions, settings: settingsActions }) diff --git a/app/actions/pricesActions.js b/app/actions/pricesActions.js new file mode 100644 index 000000000..23aa047f9 --- /dev/null +++ b/app/actions/pricesActions.js @@ -0,0 +1,16 @@ +// @flow +import { api } from 'neon-js' + +import createRequestActions from '../util/api/createRequestActions' +import { ASSETS, DEFAULT_CURRENCY_CODE } from '../core/constants' + +type Props = { + symbols?: Array, + currency?: string +} + +export const ID = 'PRICES' + +export default createRequestActions(ID, ({ symbols = [ASSETS.NEO, ASSETS.GAS], currency = DEFAULT_CURRENCY_CODE }: Props = {}) => (state: Object) => { + return api.cmc.getPrices(symbols, currency) +}) diff --git a/app/containers/App/Header/index.js b/app/containers/App/Header/index.js index a610753eb..18a6a4c0c 100644 --- a/app/containers/App/Header/index.js +++ b/app/containers/App/Header/index.js @@ -3,19 +3,23 @@ import { connect, type MapStateToProps } from 'react-redux' import { bindActionCreators } from 'redux' import { compose } from 'recompose' +import withData from '../../../hocs/api/withData' import withCurrencyData from '../../../hocs/withCurrencyData' +import pricesActions from '../../../actions/pricesActions' import { logout, getAddress, getLoggedIn } from '../../../modules/account' -import { getNEOPrice, getGASPrice } from '../../../modules/price' import Header from './Header' const mapStateToProps: MapStateToProps<*, *, *> = (state: Object) => ({ address: getAddress(state), - neoPrice: getNEOPrice(state), - gasPrice: getGASPrice(state), isLoggedIn: getLoggedIn(state) }) +const mapPricesDataToProps = ({ NEO, GAS }) => ({ + neoPrice: NEO, + gasPrice: GAS +}) + const actionCreators = { logout } @@ -24,5 +28,6 @@ const mapDispatchToProps = dispatch => bindActionCreators(actionCreators, dispat export default compose( connect(mapStateToProps, mapDispatchToProps), + withData(pricesActions, mapPricesDataToProps), withCurrencyData('currencyCode') )(Header) diff --git a/app/containers/WalletInfo/index.js b/app/containers/WalletInfo/index.js index 517e57ba2..4d70536e7 100644 --- a/app/containers/WalletInfo/index.js +++ b/app/containers/WalletInfo/index.js @@ -3,6 +3,8 @@ import { connect, type MapStateToProps } from 'react-redux' import { bindActionCreators } from 'redux' import { compose } from 'recompose' +import pricesActions from '../../actions/pricesActions' +import withData from '../../hocs/api/withData' import withActions from '../../hocs/api/withActions' import withNetworkData from '../../hocs/withNetworkData' import withCurrencyData from '../../hocs/withCurrencyData' @@ -11,7 +13,6 @@ import { getNetworks } from '../../core/networks' import { showErrorNotification, showSuccessNotification } from '../../modules/notifications' import { getAddress } from '../../modules/account' import { loadWalletData, getNEO, getGAS, getTokenBalances } from '../../modules/wallet' -import { getNEOPrice, getGASPrice } from '../../modules/price' import { showModal } from '../../modules/modal' import { participateInSale, oldParticipateInSale } from '../../modules/sale' @@ -21,12 +22,15 @@ const mapStateToProps: MapStateToProps<*, *, *> = (state: Object) => ({ NEO: getNEO(state), GAS: getGAS(state), address: getAddress(state), - neoPrice: getNEOPrice(state), - gasPrice: getGASPrice(state), tokenBalances: getTokenBalances(state), networks: getNetworks() }) +const mapPricesDataToProps = ({ NEO, GAS }) => ({ + neoPrice: NEO, + gasPrice: GAS +}) + const actionCreators = { loadWalletData, showErrorNotification, @@ -44,6 +48,7 @@ const mapActionsToProps = (actions) => ({ export default compose( connect(mapStateToProps, mapDispatchToProps), + withData(pricesActions, mapPricesDataToProps), withNetworkData(), withCurrencyData('currencyCode'), withActions(updateSettingsActions, mapActionsToProps) diff --git a/app/core/deprecated.js b/app/core/deprecated.js index c92abadf8..7123f9480 100644 --- a/app/core/deprecated.js +++ b/app/core/deprecated.js @@ -2,6 +2,7 @@ import { get } from 'lodash' import blockHeightActions from '../actions/blockHeightActions' +import pricesActions from '../actions/pricesActions' import { ID as NETWORK_ID } from '../actions/networkActions' import { ID as SETTINGS_ID } from '../actions/settingsActions' import { getNetworks } from '../core/networks' @@ -44,3 +45,7 @@ export const getTokensForNetwork = (state: Object) => { export const syncBlockHeight = (state: Object) => { return blockHeightActions.request({ networkId: getNetworkId(state) }) } + +export const getMarketPrices = (state: Object) => { + return pricesActions.request({ currency: getCurrency(state) }) +} diff --git a/app/modules/index.js b/app/modules/index.js index 968d9c6e8..974e65e90 100644 --- a/app/modules/index.js +++ b/app/modules/index.js @@ -9,7 +9,6 @@ import claim from './claim' import dashboard from './dashboard' import notifications from './notifications' import modal from './modal' -import price from './price' import addressBook from './addressBook' import sale from './sale' @@ -23,7 +22,6 @@ export default combineReducers({ claim, notifications, modal, - price, addressBook, sale }) diff --git a/app/modules/price.js b/app/modules/price.js deleted file mode 100644 index 63c9a99cd..000000000 --- a/app/modules/price.js +++ /dev/null @@ -1,85 +0,0 @@ -// @flow -import { api } from 'neon-js' - -import asyncWrap from '../core/asyncHelper' -import { getCurrency } from '../core/deprecated' - -// Constants -export const SET_NEO_PRICE = 'SET_NEO_PRICE' -export const SET_GAS_PRICE = 'SET_GAS_PRICE' -export const RESET_PRICE = 'RESET_PRICE' - -// Actions -export const setNEOPrice = (NEO: string) => ({ - type: SET_NEO_PRICE, - payload: { NEO } -}) - -export const setGASPrice = (GAS: string) => ({ - type: SET_GAS_PRICE, - payload: { GAS } -}) - -export function resetPrice () { - return { - type: RESET_PRICE - } -} - -export const getMarketPriceUSD = () => async ( - dispatch: DispatchType, - getState: GetStateType -) => { - // If API dies, still display balance - ignore _err - // eslint-disable-next-line - const [_err, price] = await asyncWrap( - api.cmc.getPrice('NEO', getCurrency(getState())) - ) - return dispatch(setNEOPrice(price)) -} - -export const getGasMarketPriceUSD = () => async ( - dispatch: DispatchType, - getState: GetStateType -) => { - // If API dies, still display balance - ignore _err - // eslint-disable-next-line - const [_err, price] = await asyncWrap( - api.cmc.getPrice('GAS', getCurrency(getState())) - ) - return dispatch(setGASPrice(price)) -} - -// state getters -export const getNEOPrice = (state: Object) => state.price.NEO -export const getGASPrice = (state: Object) => state.price.GAS - -const initialState = { - NEO: 0, - GAS: 0 -} - -export default (state: Object = initialState, action: Object) => { - switch (action.type) { - case SET_NEO_PRICE: - const { NEO } = action.payload - return { - ...state, - NEO - } - case SET_GAS_PRICE: - const { GAS } = action.payload - return { - ...state, - GAS - } - case RESET_PRICE: - return { - ...state, - NEO: 0, - GAS: 0 - } - default: - return state - } -} diff --git a/app/modules/wallet.js b/app/modules/wallet.js index e7c534ea0..f540caaca 100644 --- a/app/modules/wallet.js +++ b/app/modules/wallet.js @@ -5,11 +5,10 @@ import { isNil } from 'lodash' import { syncTransactionHistory } from './transactions' import { syncAvailableClaim } from './claim' import { LOGOUT, getAddress } from './account' -import { getMarketPriceUSD, getGasMarketPriceUSD } from './price' import { showErrorNotification } from './notifications' import { ASSETS } from '../core/constants' -import { getNetwork, getTokensForNetwork, syncBlockHeight } from '../core/deprecated' +import { getNetwork, getTokensForNetwork, getMarketPrices, syncBlockHeight } from '../core/deprecated' import asyncWrap from '../core/asyncHelper' import { getTokenBalancesMap } from '../core/wallet' import { COIN_DECIMAL_LENGTH } from '../core/formatters' @@ -17,9 +16,6 @@ import { toBigNumber } from '../core/math' // Constants export const SET_BALANCE = 'SET_BALANCE' -export const SET_NEO_PRICE = 'SET_NEO_PRICE' -export const SET_GAS_PRICE = 'SET_GAS_PRICE' -export const RESET_PRICES = 'RESET_PRICES' export const SET_TRANSACTION_HISTORY = 'SET_TRANSACTION_HISTORY' export const SET_TOKENS_BALANCE = 'SET_TOKENS_BALANCE' export const SET_IS_LOADED = 'SET_IS_LOADED' @@ -93,8 +89,7 @@ export const loadWalletData = (silent: boolean = true) => async ( dispatch(syncTransactionHistory(net, address)) dispatch(syncAvailableClaim(net, address)) dispatch(syncBlockHeight(state)) - dispatch(getMarketPriceUSD()) - dispatch(getGasMarketPriceUSD()) + dispatch(getMarketPrices(state)) await Promise.all([ dispatch(retrieveTokenBalances()), dispatch(retrieveBalance(net, address))