Skip to content

Commit

Permalink
feat(sb): display price movement
Browse files Browse the repository at this point in the history
  • Loading branch information
Philip London committed Jul 15, 2020
1 parent 57266c7 commit 71f2096
Show file tree
Hide file tree
Showing 13 changed files with 195 additions and 38 deletions.
@@ -1,3 +1,5 @@
import { actions } from 'data'
import { bindActionCreators, Dispatch } from 'redux'
import { connect, ConnectedProps } from 'react-redux'
import { getData } from './selectors'
import { RootState } from 'data/rootReducer'
Expand All @@ -6,8 +8,6 @@ import React, { PureComponent } from 'react'
import Success from './template.success'

class CryptoItem extends PureComponent<Props> {
state = {}

render () {
return this.props.data.cata({
Success: val => <Success {...this.props} {...val} />,
Expand All @@ -22,7 +22,11 @@ const mapStateToProps = (state: RootState, ownProps: OwnProps) => ({
data: getData(state, ownProps)
})

const connector = connect(mapStateToProps)
const mapDispatchToProps = (dispatch: Dispatch) => ({
miscActions: bindActionCreators(actions.core.data.misc, dispatch)
})

const connector = connect(mapStateToProps, mapDispatchToProps)

export type OwnProps = {
onClick: (string) => void
Expand Down
@@ -1,14 +1,18 @@
import { ExtractSuccess } from 'core/types'
import { getCoinFromPair } from 'data/components/simpleBuy/model'
import { getRatesSelector } from 'core/redux/data/misc/selectors'
import { lift } from 'ramda'
import { RootState } from 'data/rootReducer'

import { OwnProps } from '.'
import { selectors } from 'data'

export const getData = (state: RootState, ownProps: OwnProps) => {
const coin = getCoinFromPair(ownProps.value.pair)
const ratesR = getRatesSelector(coin, state)
const ratesR = selectors.core.data.misc.getRatesSelector(coin, state)
const fiatCurrency = selectors.components.simpleBuy.getFiatCurrency(state)

return lift((rates: ExtractSuccess<typeof ratesR>) => ({ rates }))(ratesR)
return lift((rates: ExtractSuccess<typeof ratesR>) => ({
fiatCurrency,
rates
}))(ratesR)
}
Expand Up @@ -15,6 +15,7 @@ import {
OwnProps as ParentOwnProps,
SuccessStateType
} from '.'
import PriceMovement from '../PriceMovement'

const DisplayContainer = styled.div<{
coinType: SupportedCoinType
Expand All @@ -40,6 +41,11 @@ const Display = styled.div`
font-weight: 500;
color: ${props => props.theme.grey800};
`
const DisplayTitle = styled(Title)`
margin-top: 4px;
display: flex;
align-items: center;
`

type Props = OwnProps & ParentOwnProps & SuccessStateType

Expand All @@ -60,13 +66,14 @@ const Success: React.FC<Props> = props => {
>
<Icon size='32px' color={color} name={icon} />
<Display>
<Value>{displayName}</Value>
<Title>
<Value style={{ marginTop: '0px' }}>{displayName}</Value>
<DisplayTitle>
{fiatToString({
value: props.rates[fiat].last,
unit: fiat
})}
</Title>
<PriceMovement {...props} />
</DisplayTitle>
</Display>
<Icon name='chevron-right' size='32px' color='grey600' />
</DisplayContainer>
Expand Down
@@ -0,0 +1,86 @@
import { actions } from 'data'
import { bindActionCreators, Dispatch } from 'redux'
import { connect, ConnectedProps } from 'react-redux'
import { FiatType, SBPairType } from 'core/types'
import { getCoinFromPair } from 'data/components/simpleBuy/model'
import { RootState } from 'data/rootReducer'
import React, { PureComponent } from 'react'
import styled, { DefaultTheme } from 'styled-components'

import { getData } from './selectors'
import { SkeletonRectangle } from 'blockchain-info-components'

const Container = styled.span`
margin-left: 8px;
`
const Change = styled.span<{ color: keyof DefaultTheme }>`
font-weight: 500;
color: ${props => props.theme[props.color]};
`

const getSignFromMovement = (movement: 'none' | 'up' | 'down') => {
switch (movement) {
case 'down':
return '-'
case 'up':
return '+'
default:
return ''
}
}

const getColorFromMovement = (movement: 'none' | 'up' | 'down') => {

This comment has been minimized.

Copy link
@milan-bc

milan-bc Jul 15, 2020

Collaborator

you can move this 'none' | 'up' | 'down' to be one common type and reuse it on both places

switch (movement) {
case 'down':
return 'red600'
case 'up':
return 'green600'
default:
return 'grey600'
}
}

class PriceMovement extends PureComponent<Props, State> {
componentDidMount () {
const coin = getCoinFromPair(this.props.value.pair)
this.props.miscActions.fetchPrice24H(coin, this.props.fiatCurrency || 'EUR')
}

render () {
return (
<Container>
{this.props.data.cata({
Success: val => (
<Change color={getColorFromMovement(val.price24Hr.movement)}>
{getSignFromMovement(val.price24Hr.movement)}
{val.price24Hr.change}%
</Change>
),
Loading: () => <SkeletonRectangle height={'12px'} width={'40px'} />,
Failure: () => null,
NotAsked: () => null
})}
</Container>
)
}
}

const mapStateToProps = (state: RootState, ownProps: OwnProps) => ({
data: getData(state, ownProps)
})

const mapDispatchToProps = (dispatch: Dispatch) => ({
miscActions: bindActionCreators(actions.core.data.misc, dispatch)
})

const connector = connect(mapStateToProps, mapDispatchToProps)

export type OwnProps = {
fiatCurrency?: FiatType
value: SBPairType
}
type SuccessStateType = ReturnType<typeof getData>['data']
type Props = OwnProps & ConnectedProps<typeof connector>
type State = {}

export default connector(PriceMovement)
@@ -0,0 +1,16 @@
import { ExtractSuccess } from 'core/types'
import { getCoinFromPair } from 'data/components/simpleBuy/model'
import { lift } from 'ramda'
import { RootState } from 'data/rootReducer'

import { OwnProps } from '.'
import { selectors } from 'data'

export const getData = (state: RootState, ownProps: OwnProps) => {
const coin = getCoinFromPair(ownProps.value.pair)
const price24HrR = selectors.core.data.misc.getPrice24H(coin, state)

return lift((price24Hr: ExtractSuccess<typeof price24HrR>) => ({
price24Hr
}))(price24HrR)
}
Expand Up @@ -38,7 +38,7 @@ export default ({ rootUrl, apiUrl, get, post }) => {
get({
url: apiUrl,
endPoint: '/price/index',
data: { base, quote, time: time.milliseconds }
data: { base, quote, time: time.unix() }
})

const getPriceIndexSeries = (coin, currency, start, scale) =>
Expand Down
22 changes: 7 additions & 15 deletions packages/blockchain-wallet-v4/src/redux/data/misc/actions.ts
@@ -1,11 +1,5 @@
import * as AT from './actionTypes'
import {
CoinType,
FiatType,
MiscActionTypes,
PriceIndexResponseType
} from 'core/types'
import { Moment } from 'moment'
import { CoinType, FiatType, MiscActionTypes } from 'core/types'

// FETCH_CAPTCHA
export const fetchCaptcha = () => ({ type: AT.FETCH_CAPTCHA })
Expand All @@ -20,26 +14,24 @@ export const fetchCaptchaFailure = error => ({
})

// FETCH_PRICE_24H
export const fetchPrice24H = (
base: CoinType,
quote: FiatType,
time: Moment
) => ({
export const fetchPrice24H = (base: CoinType, quote: FiatType) => ({
type: AT.FETCH_PRICE_24H,
payload: { base, quote, time }
payload: { base, quote }
})
export const fetchPrice24HLoading = (base: CoinType): MiscActionTypes => ({
type: AT.FETCH_PRICE_24H_LOADING,
payload: { base }
})
export const fetchPrice24HSuccess = (
base: CoinType,
data: PriceIndexResponseType
change: string,
movement: 'none' | 'up' | 'down'
): MiscActionTypes => ({
type: AT.FETCH_PRICE_24H_SUCCESS,
payload: {
base,
data
change,
movement
}
})
export const fetchPrice24HFailure = (base, error): MiscActionTypes => ({
Expand Down
27 changes: 27 additions & 0 deletions packages/blockchain-wallet-v4/src/redux/data/misc/reducers.ts
Expand Up @@ -36,6 +36,33 @@ export const miscReducer = (
case AT.FETCH_CAPTCHA_SUCCESS: {
return assoc('captcha', Remote.Success(action.payload), state)
}
case AT.FETCH_PRICE_24H_LOADING: {
return {
...state,
price_24h: {
...state.price_24h,
[action.payload.base]: Remote.Loading
}
}
}
case AT.FETCH_PRICE_24H_SUCCESS: {
return {
...state,
price_24h: {
...state.price_24h,
[action.payload.base]: Remote.Success(action.payload)
}
}
}
case AT.FETCH_PRICE_24H_FAILURE: {
return {
...state,
price_24h: {
...state.price_24h,
[action.payload.base]: Remote.Failure(action.payload.error)
}
}
}
case AT.FETCH_PRICE_INDEX_SERIES_LOADING: {
return assoc('price_index_series', Remote.Loading, state)
}
Expand Down
@@ -1,5 +1,5 @@
import * as AT from './actionTypes'
import { takeLatest } from 'redux-saga/effects'
import { takeEvery, takeLatest } from 'redux-saga/effects'
import sagas from './sagas'

export default ({ api }) => {
Expand All @@ -8,6 +8,7 @@ export default ({ api }) => {
return function * coreDataMiscSaga () {
yield takeLatest(AT.AUTHORIZE_LOGIN, dataMiscSagas.authorizeLogin)
yield takeLatest(AT.FETCH_CAPTCHA, dataMiscSagas.fetchCaptcha)
yield takeEvery(AT.FETCH_PRICE_24H, dataMiscSagas.fetchPrice24H)
yield takeLatest(
AT.FETCH_PRICE_INDEX_SERIES,
dataMiscSagas.fetchPriceIndexSeries
Expand Down
20 changes: 18 additions & 2 deletions packages/blockchain-wallet-v4/src/redux/data/misc/sagas.ts
Expand Up @@ -4,6 +4,7 @@ import * as wS from '../../wallet/selectors'
import { APIType } from 'core/network/api'
import { call, put, select } from 'redux-saga/effects'
import { errorHandler } from 'blockchain-wallet-v4/src/utils'
import BigNumber from 'bignumber.js'
import moment from 'moment'
import readBlob from 'read-blob'

Expand All @@ -28,13 +29,28 @@ export default ({ api }: { api: APIType }) => {
const { base, quote } = action.payload
try {
yield put(A.fetchPrice24HLoading(base))
const response: ReturnType<typeof api.getPriceIndex> = yield call(
const yesterday: ReturnType<typeof api.getPriceIndex> = yield call(
api.getPriceIndex,
base,
quote,
moment().subtract(1, 'day')
)
yield put(A.fetchPrice24HSuccess(base, response))
const today: ReturnType<typeof api.getPriceIndex> = yield call(
api.getPriceIndex,
base,
quote,
moment()
)
const diff = new BigNumber(
(today.price - yesterday.price) / yesterday.price
).times(100)
const change = diff.abs().toFixed(2)
const movement = diff.isEqualTo(0)
? 'none'
: diff.isGreaterThan(0)
? 'up'
: 'down'
yield put(A.fetchPrice24HSuccess(base, change, movement))
} catch (e) {
const error = errorHandler(e)
yield put(A.fetchPrice24HFailure(base, error))
Expand Down
Expand Up @@ -24,7 +24,7 @@ export const handle2FAReset = path([dataPath, 'misc', 'handle_2fa_reset'])

export const verifyEmailToken = path([dataPath, 'misc', 'verify_email_token'])

export const getPrice24H = (state: RootState, coin: CoinType) =>
export const getPrice24H = (coin: CoinType, state: RootState) =>
state.dataPath.misc.price_24h[coin]

export const getRatesSelector = (
Expand Down
15 changes: 7 additions & 8 deletions packages/blockchain-wallet-v4/src/redux/data/misc/types.ts
@@ -1,10 +1,5 @@
import * as AT from './actionTypes'
import {
CoinType,
FiatType,
PriceIndexResponseType,
RemoteDataType
} from 'core/types'
import { CoinType, FiatType, RemoteDataType } from 'core/types'

// types
export type RateType = {
Expand All @@ -27,7 +22,10 @@ export type MiscStateType = {
logs: RemoteDataType<any, any>
pairing_code: RemoteDataType<any, any>
price_24h: {
[key in CoinType]: RemoteDataType<string, any>
[key in CoinType]: RemoteDataType<
string,
{ change: string; movement: 'none' | 'up' | 'down' }
>
}
price_index_series: RemoteDataType<any, any>
verify_email_token: RemoteDataType<any, any>
Expand Down Expand Up @@ -90,7 +88,8 @@ interface FetchPrice24HLoadingActionType {
interface FetchPrice24HSuccessActionType {
payload: {
base: CoinType
data: PriceIndexResponseType
change: string
movement: 'none' | 'up' | 'down'
}
type: typeof AT.FETCH_PRICE_24H_SUCCESS
}
Expand Down
7 changes: 6 additions & 1 deletion yarn.lock
Expand Up @@ -13690,11 +13690,16 @@ mock-socket@8.0.5:
dependencies:
url-parse "^1.2.0"

moment@*, moment@2.24.0:
moment@*:
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==

moment@2.27.0:
version "2.27.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d"
integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==

moo@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"
Expand Down

0 comments on commit 71f2096

Please sign in to comment.