From 65e72a9c952cdbc9f02f0a65c36e1937feeaa70e Mon Sep 17 00:00:00 2001 From: plondon Date: Thu, 31 May 2018 17:34:32 -0400 Subject: [PATCH] feat(Watch Only): add watch only balance to separate section of balance dropdown --- .../Balance/BtcWatchOnlyBalance/index.js | 53 +++++++++++++++++++ .../Balance/BtcWatchOnlyBalance/selectors.js | 3 ++ .../BtcWatchOnlyBalance/template.error.js | 22 ++++++++ .../BtcWatchOnlyBalance/template.loading.js | 21 ++++++++ .../BtcWatchOnlyBalance/template.success.js | 46 ++++++++++++++++ .../layouts/Wallet/MenuTop/Balance/index.js | 2 +- .../Wallet/MenuTop/Balance/selectors.js | 4 +- .../MenuTop/Balance/template.success.js | 18 +++++-- .../scenes/Home/BalancesChart/selectors.js | 2 +- .../src/redux/data/bitcoin/actionTypes.js | 6 +++ .../src/redux/data/bitcoin/actions.js | 6 +++ .../src/redux/data/bitcoin/reducers.js | 12 +++++ .../src/redux/data/bitcoin/sagaRegister.js | 1 + .../src/redux/data/bitcoin/sagas.js | 12 +++++ .../src/redux/data/bitcoin/selectors.js | 2 + 15 files changed, 201 insertions(+), 9 deletions(-) create mode 100644 packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/index.js create mode 100644 packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/selectors.js create mode 100644 packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/template.error.js create mode 100644 packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/template.loading.js create mode 100644 packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/template.success.js diff --git a/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/index.js b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/index.js new file mode 100644 index 00000000000..c2581e8b48e --- /dev/null +++ b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/index.js @@ -0,0 +1,53 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' +import { bindActionCreators } from 'redux' + +import { Remote } from 'blockchain-wallet-v4/src' +import { actions } from 'data' +import { getData } from './selectors' +import Error from './template.error' +import Loading from './template.loading' +import Success from './template.success' + +class BtcBalance extends React.PureComponent { + constructor (props) { + super(props) + this.handleRefresh = this.handleRefresh.bind(this) + } + + componentWillMount () { + if (Remote.NotAsked.is(this.props.data)) { + this.props.actions.fetchUnspendableBalance(this.props.context) + } + } + + handleRefresh () { + this.props.actions.fetchUnspendableBalance(this.props.context) + } + + render () { + const { data, large } = this.props + + return data.cata({ + Success: (value) => , + Failure: (message) => , + Loading: () => , + NotAsked: () => + }) + } +} + +BtcBalance.propTypes = { + context: PropTypes.string.isRequired +} + +const mapStateToProps = (state) => ({ + data: getData(state) +}) + +const mapDispatchToProps = (dispatch) => ({ + actions: bindActionCreators(actions.core.data.bitcoin, dispatch) +}) + +export default connect(mapStateToProps, mapDispatchToProps)(BtcBalance) diff --git a/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/selectors.js b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/selectors.js new file mode 100644 index 00000000000..cf1d1897a8e --- /dev/null +++ b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/selectors.js @@ -0,0 +1,3 @@ +import { selectors } from 'data' + +export const getData = selectors.core.data.bitcoin.getUnspendableBalance diff --git a/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/template.error.js b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/template.error.js new file mode 100644 index 00000000000..090973a53f7 --- /dev/null +++ b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/template.error.js @@ -0,0 +1,22 @@ +import React from 'react' +import styled from 'styled-components' +import { FormattedMessage } from 'react-intl' +import { Link } from 'blockchain-info-components' + +const Wrapper = styled.div` + display: flex; + box-sizing: border-box; + justify-content: flex-end; + padding-right: 25px; +` +const ErrorLink = styled(Link)` + text-decoration: underline; +` + +export default (props) => ( + + props.onRefresh()}> + + + +) diff --git a/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/template.loading.js b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/template.loading.js new file mode 100644 index 00000000000..861608801ed --- /dev/null +++ b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/template.loading.js @@ -0,0 +1,21 @@ +import React from 'react' +import styled from 'styled-components' + +import { FlatLoader } from 'blockchain-info-components' + +const Wrapper = styled.div` + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + padding: 5px; + box-sizing: border-box; +` + +export default (props) => { + return ( + + + + ) +} diff --git a/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/template.success.js b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/template.success.js new file mode 100644 index 00000000000..69081dcf504 --- /dev/null +++ b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/BtcWatchOnlyBalance/template.success.js @@ -0,0 +1,46 @@ +import React from 'react' +import PropTypes from 'prop-types' +import styled from 'styled-components' +import FiatDisplay from 'components/Display/FiatDisplay' +import { LinkContainer } from 'react-router-bootstrap' +import { FormattedMessage } from 'react-intl' +import { Banner, Text } from 'blockchain-info-components' + +const Wrapper = styled.div` + display: inline-flex; + flex-direction: row; + align-items: center; + padding-left: 5px; + margin-bottom: 10px; + padding-right: ${props => props.large ? '15px' : '25px'}; + > div:last-child { + margin-left: 10px; + > div { + color: ${props => props.theme['gray-3']} + } + } +` + +const Success = props => { + const { balance } = props + + return ( + + + BTC + + {balance} + + + + + + + ) +} + +Success.propTypes = { + balance: PropTypes.number.isRequired +} + +export default Success diff --git a/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/index.js b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/index.js index 1f4f4547ca0..f0601fb079b 100644 --- a/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/index.js +++ b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/index.js @@ -12,7 +12,7 @@ class Balance extends React.PureComponent { render () { const { data } = this.props return data.cata({ - Success: (value) => , + Success: (value) => , Failure: (message) => {message}, Loading: () => , NotAsked: () => diff --git a/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/selectors.js b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/selectors.js index df066f34ca0..ae35f3ab196 100644 --- a/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/selectors.js +++ b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/selectors.js @@ -12,9 +12,9 @@ export const getData = (state) => { const transform = lift((btcActiveAddresses, btcActiveAccounts, ethContext, bchContext) => { const spendable = (a) => a.priv const accounts = btcActiveAccounts.map(prop('xpub')) - const btcWatchOnlyContext = reject(spendable, btcActiveAddresses).map(prop('addr')) + const btcUnspendableContext = reject(spendable, btcActiveAddresses).map(prop('addr')) const btcSpendableContext = filter(spendable, btcActiveAddresses).map(prop('addr')).concat(accounts) - return {btcSpendableContext, btcWatchOnlyContext, ethContext, bchContext, path} + return {btcSpendableContext, btcUnspendableContext, ethContext, bchContext, path} }) return transform(btcActiveAddressesR, btcActiveAccountsR, ethContextR, bchContextR) } diff --git a/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/template.success.js b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/template.success.js index da3fd97938d..3359c458ede 100644 --- a/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/template.success.js +++ b/packages/blockchain-wallet-v4-frontend/src/layouts/Wallet/MenuTop/Balance/template.success.js @@ -5,9 +5,12 @@ import TotalBalance from './TotalBalance' import BtcBalance from './BtcBalance' import EthBalance from './EthBalance' import BchBalance from './BchBalance' +import BtcWatchOnlyBalance from './BtcWatchOnlyBalance' import { FormattedMessage } from 'react-intl' -import { ComponentDropdown, Text } from 'blockchain-info-components' +import { ComponentDropdown, Separator, Text } from 'blockchain-info-components' + +const Fragment = React.Fragment const Wrapper = styled.div` display: flex; @@ -25,7 +28,6 @@ const BalanceText = styled(Text)` font-size: 16px; } ` - const BalanceDropdown = styled.div` margin-top: 4px; > div > ul { @@ -34,7 +36,6 @@ const BalanceDropdown = styled.div` padding: 0; padding-top: 5px; position: absolute; - background: transparent; > li { padding: 0px 6px; text-align: right; @@ -65,7 +66,7 @@ const BalanceDropdown = styled.div` ` const Success = props => { - const { btcContext, ethContext, bchContext, path } = props + const { btcContext, ethContext, bchContext, btcUnspendableContext, path } = props const getComponentOrder = () => { switch (path) { @@ -85,6 +86,13 @@ const Success = props => { } } + const getSubBalances = () => { + return btcUnspendableContext.length ? + + + : null + } + return ( @@ -96,7 +104,7 @@ const Success = props => { forceSelected color={'gray-5'} selectedComponent={getComponentOrder()[0]} - components={getComponentOrder()} + components={getComponentOrder().concat(getSubBalances())} callback={() => {}} /> diff --git a/packages/blockchain-wallet-v4-frontend/src/scenes/Home/BalancesChart/selectors.js b/packages/blockchain-wallet-v4-frontend/src/scenes/Home/BalancesChart/selectors.js index aaa24578295..1f40cfa1b8e 100644 --- a/packages/blockchain-wallet-v4-frontend/src/scenes/Home/BalancesChart/selectors.js +++ b/packages/blockchain-wallet-v4-frontend/src/scenes/Home/BalancesChart/selectors.js @@ -4,7 +4,7 @@ import { Exchange } from 'blockchain-wallet-v4/src' import { Color } from 'blockchain-info-components' export const getData = (state) => { - const btcBalanceR = selectors.core.data.bitcoin.getBalance(state) + const btcBalanceR = selectors.core.data.bitcoin.getSpendableBalance(state) const ethBalanceR = selectors.core.data.ethereum.getBalance(state) const bchBalanceR = selectors.core.data.bch.getBalance(state) const btcBalance = btcBalanceR.getOrElse(0) diff --git a/packages/blockchain-wallet-v4/src/redux/data/bitcoin/actionTypes.js b/packages/blockchain-wallet-v4/src/redux/data/bitcoin/actionTypes.js index 4555ea26ad0..ee69cd7a86e 100644 --- a/packages/blockchain-wallet-v4/src/redux/data/bitcoin/actionTypes.js +++ b/packages/blockchain-wallet-v4/src/redux/data/bitcoin/actionTypes.js @@ -41,3 +41,9 @@ export const FETCH_BITCOIN_SPENDABLE_BALANCE = '@CORE.FETCH_BITCOIN_SPENDABLE_BA export const FETCH_BITCOIN_SPENDABLE_BALANCE_LOADING = '@CORE.FETCH_BITCOIN_SPENDABLE_BALANCE_LOADING' export const FETCH_BITCOIN_SPENDABLE_BALANCE_SUCCESS = '@CORE.FETCH_BITCOIN_SPENDABLE_BALANCE_SUCCESS' export const FETCH_BITCOIN_SPENDABLE_BALANCE_FAILURE = '@CORE.FETCH_BITCOIN_SPENDABLE_BALANCE_FAILURE' + +// FETCH_BITCOIN_TRANSACTION_HISTORY +export const FETCH_BITCOIN_UNSPENDABLE_BALANCE = '@CORE.FETCH_BITCOIN_UNSPENDABLE_BALANCE' +export const FETCH_BITCOIN_UNSPENDABLE_BALANCE_LOADING = '@CORE.FETCH_BITCOIN_UNSPENDABLE_BALANCE_LOADING' +export const FETCH_BITCOIN_UNSPENDABLE_BALANCE_SUCCESS = '@CORE.FETCH_BITCOIN_UNSPENDABLE_BALANCE_SUCCESS' +export const FETCH_BITCOIN_UNSPENDABLE_BALANCE_FAILURE = '@CORE.FETCH_BITCOIN_UNSPENDABLE_BALANCE_FAILURE' diff --git a/packages/blockchain-wallet-v4/src/redux/data/bitcoin/actions.js b/packages/blockchain-wallet-v4/src/redux/data/bitcoin/actions.js index b46860fb7c7..8f1072aed79 100644 --- a/packages/blockchain-wallet-v4/src/redux/data/bitcoin/actions.js +++ b/packages/blockchain-wallet-v4/src/redux/data/bitcoin/actions.js @@ -43,3 +43,9 @@ export const fetchSpendableBalance = (context) => ({ type: AT.FETCH_BITCOIN_SPEN export const fetchSpendableBalanceLoading = () => ({ type: AT.FETCH_BITCOIN_SPENDABLE_BALANCE_LOADING }) export const fetchSpendableBalanceSuccess = (data) => ({ type: AT.FETCH_BITCOIN_SPENDABLE_BALANCE_SUCCESS, payload: data }) export const fetchSpendableBalanceFailure = (error) => ({ type: AT.FETCH_BITCOIN_SPENDABLE_BALANCE_FAILURE, payload: error }) + +// FETCH_BITCOIN_UNSPENDABLE_BALANCE +export const fetchUnspendableBalance = (context) => ({ type: AT.FETCH_BITCOIN_UNSPENDABLE_BALANCE, payload: { context } }) +export const fetchUnspendableBalanceLoading = () => ({ type: AT.FETCH_BITCOIN_UNSPENDABLE_BALANCE_LOADING }) +export const fetchUnspendableBalanceSuccess = (data) => ({ type: AT.FETCH_BITCOIN_UNSPENDABLE_BALANCE_SUCCESS, payload: data }) +export const fetchUnspendableBalanceFailure = (error) => ({ type: AT.FETCH_BITCOIN_UNSPENDABLE_BALANCE_FAILURE, payload: error }) diff --git a/packages/blockchain-wallet-v4/src/redux/data/bitcoin/reducers.js b/packages/blockchain-wallet-v4/src/redux/data/bitcoin/reducers.js index b87ce00314c..78cce585e6f 100644 --- a/packages/blockchain-wallet-v4/src/redux/data/bitcoin/reducers.js +++ b/packages/blockchain-wallet-v4/src/redux/data/bitcoin/reducers.js @@ -11,6 +11,7 @@ const INITIAL_STATE = { transactions: [], transactions_fiat: {}, spendable_balance: Remote.NotAsked, + unspendable_balance: Remote.NotAsked, transaction_history: Remote.NotAsked } @@ -111,6 +112,17 @@ const bitcoinReducer = (state = INITIAL_STATE, action) => { case AT.FETCH_BITCOIN_SPENDABLE_BALANCE_FAILURE: { return assoc('spendable_balance', Remote.Failure(payload), state) } + case AT.FETCH_BITCOIN_UNSPENDABLE_BALANCE_LOADING: { + return assoc('unspendable_balance', Remote.Loading, state) + } + case AT.FETCH_BITCOIN_UNSPENDABLE_BALANCE_SUCCESS: { + const { wallet } = payload + const balance = wallet.final_balance + return assoc('unspendable_balance', Remote.Success(balance), state) + } + case AT.FETCH_BITCOIN_UNSPENDABLE_BALANCE_FAILURE: { + return assoc('unspendable_balance', Remote.Failure(payload), state) + } default: return state } diff --git a/packages/blockchain-wallet-v4/src/redux/data/bitcoin/sagaRegister.js b/packages/blockchain-wallet-v4/src/redux/data/bitcoin/sagaRegister.js index 0b622da7d9a..a79e15cc1ad 100644 --- a/packages/blockchain-wallet-v4/src/redux/data/bitcoin/sagaRegister.js +++ b/packages/blockchain-wallet-v4/src/redux/data/bitcoin/sagaRegister.js @@ -12,6 +12,7 @@ export default ({ api }) => { yield takeLatest(AT.FETCH_BITCOIN_RATES, dataBtcSagas.fetchRates) yield fork(dataBtcSagas.watchTransactions) yield takeLatest(AT.FETCH_BITCOIN_SPENDABLE_BALANCE, dataBtcSagas.fetchSpendableBalance) + yield takeLatest(AT.FETCH_BITCOIN_UNSPENDABLE_BALANCE, dataBtcSagas.fetchUnspendableBalance) yield takeLatest(AT.FETCH_BITCOIN_TRANSACTION_HISTORY, dataBtcSagas.fetchTransactionHistory) } } diff --git a/packages/blockchain-wallet-v4/src/redux/data/bitcoin/sagas.js b/packages/blockchain-wallet-v4/src/redux/data/bitcoin/sagas.js index 1bd80dda06b..38fc76ecb78 100644 --- a/packages/blockchain-wallet-v4/src/redux/data/bitcoin/sagas.js +++ b/packages/blockchain-wallet-v4/src/redux/data/bitcoin/sagas.js @@ -116,6 +116,17 @@ export default ({ api }) => { } } + const fetchUnspendableBalance = function * (action) { + try { + const { context } = action.payload + yield put(A.fetchUnspendableBalanceLoading()) + const data = yield call(api.fetchBlockchainData, context) + yield put(A.fetchUnspendableBalanceSuccess(data)) + } catch (e) { + yield put(A.fetchUnspendableBalanceFailure(e)) + } + } + const multiaddrSaga = function * (data) { const btcData = { addresses: indexBy(prop('address'), prop('addresses', data)), @@ -133,6 +144,7 @@ export default ({ api }) => { fetchFiatAtTime, watchTransactions, fetchSpendableBalance, + fetchUnspendableBalance, fetchTransactionHistory } } diff --git a/packages/blockchain-wallet-v4/src/redux/data/bitcoin/selectors.js b/packages/blockchain-wallet-v4/src/redux/data/bitcoin/selectors.js index 19941d9f771..c0fbc19e385 100644 --- a/packages/blockchain-wallet-v4/src/redux/data/bitcoin/selectors.js +++ b/packages/blockchain-wallet-v4/src/redux/data/bitcoin/selectors.js @@ -19,6 +19,8 @@ export const getCoins = path([dataPath, 'bitcoin', 'payment', 'coins']) export const getSpendableBalance = path([dataPath, 'bitcoin', 'spendable_balance']) +export const getUnspendableBalance = path([dataPath, 'bitcoin', 'unspendable_balance']) + // Specific export const getChangeIndex = curry((xpub, state) => getAddresses(state).map(path([xpub, 'change_index'])))