diff --git a/src/components/transaction/container/transactionContainer.js b/src/components/transaction/container/transactionContainer.js index a6d1899b6..6cd96f355 100644 --- a/src/components/transaction/container/transactionContainer.js +++ b/src/components/transaction/container/transactionContainer.js @@ -1,4 +1,5 @@ import React, { PureComponent } from 'react'; +import { connect } from 'react-redux'; // Component import TransactionView from '../view/transactionView'; @@ -21,10 +22,14 @@ class TransactionContainer extends PureComponent { // Component Functions render() { - const { walletData } = this.props; + const { walletData, steemPerMVests } = this.props; - return ; + return ; } } -export default TransactionContainer; +const mapStateToProps = state => ({ + steemPerMVests: state.account.globalProps.steemPerMVests, +}); + +export default connect(mapStateToProps)(TransactionContainer); diff --git a/src/components/transaction/view/transactionView.js b/src/components/transaction/view/transactionView.js index 1dad7d864..60fd18572 100644 --- a/src/components/transaction/view/transactionView.js +++ b/src/components/transaction/view/transactionView.js @@ -36,7 +36,7 @@ class TransactionView extends PureComponent { walletData: { transactions }, intl, intl: { formatNumber }, - walletData, + steemPerMVests, } = this.props; return ( @@ -53,16 +53,16 @@ class TransactionView extends PureComponent { {transactions && transactions.map((item, index) => { - const transactionData = groomingTransactionData(item, walletData, formatNumber); + const transactionData = groomingTransactionData(item, steemPerMVests, formatNumber); return ( {(!!transactionData.details || !!transactionData.memo) && ( ({ upvotePercent: state.application.upvotePercent, pinCode: state.account.pin, currentAccount: state.account.currentAccount, + globalProps: state.account.globalProps, }); export default connect(mapStateToProps)(UpvoteContainer); diff --git a/src/components/upvote/view/upvoteView.js b/src/components/upvote/view/upvoteView.js index 26159790c..d56a95331 100644 --- a/src/components/upvote/view/upvoteView.js +++ b/src/components/upvote/view/upvoteView.js @@ -6,12 +6,17 @@ import { injectIntl } from 'react-intl'; import { Popover, PopoverController } from 'react-native-modal-popover'; import Slider from 'react-native-slider'; +// Utils +import parseToken from '../../../utils/parseToken'; +import { vestsToRshares } from '../../../utils/conversions'; + // Components import { Icon } from '../../icon'; import { PulseAnimation } from '../../animations'; import { TextButton } from '../../buttons'; + // STEEM -import { upvoteAmount, vote } from '../../../providers/steem/dsteem'; +import { vote } from '../../../providers/steem/dsteem'; // Styles import styles from './upvoteStyles'; @@ -53,21 +58,22 @@ class UpvoteView extends Component { // Component Functions _calculateEstimatedAmount = async () => { - const { currentAccount } = this.props; - // Calculate total vesting shares + const { currentAccount, globalProps } = this.props; + if (currentAccount) { const { sliderValue } = this.state; - const totalVests = parseFloat(currentAccount.vesting_shares) - + parseFloat(currentAccount.received_vesting_shares) - - parseFloat(currentAccount.delegated_vesting_shares); + const { + fundRecentClaims, fundRewardBalance, base, quote, + } = globalProps; - const finalVest = totalVests * 1e6; + const votingPower = currentAccount.voting_power; + const totalVests = parseToken(currentAccount.vesting_shares) + + parseToken(currentAccount.received_vesting_shares) + - parseToken(currentAccount.delegated_vesting_shares); + const votePct = sliderValue * 10000; - const power = (currentAccount.voting_power * (sliderValue * 10000)) / 10000 / 50; - - const rshares = (power * finalVest) / 10000; - - const estimated = await upvoteAmount(rshares); + const rShares = vestsToRshares(totalVests, votingPower, votePct); + const estimated = (rShares / fundRecentClaims) * fundRewardBalance * (base / quote); this.setState({ amount: estimated.toFixed(5), @@ -252,18 +258,18 @@ class UpvoteView extends Component { { - closePopover(); - this._upvoteContent(); - }} + closePopover(); + this._upvoteContent(); + }} style={styles.upvoteButton} > + size={20} + style={[styles.upvoteIcon, { color: '#007ee5' }]} + active={!isLoggedIn} + iconType={iconType} + name={iconName} + /> {_amount} { - this.setState({ sliderValue: value }, () => { - this._calculateEstimatedAmount(); - }); - }} + this.setState({ sliderValue: value }, () => { + this._calculateEstimatedAmount(); + }); + }} /> {_percent} diff --git a/src/components/wallet/container/walletContainer.js b/src/components/wallet/container/walletContainer.js index 4543e5f59..7c47c2b0e 100644 --- a/src/components/wallet/container/walletContainer.js +++ b/src/components/wallet/container/walletContainer.js @@ -47,8 +47,8 @@ class WalletContainer extends Component { // Components functions _getWalletData = async (selectedUser) => { - const { setEstimatedWalletValue } = this.props; - const walletData = await groomingWalletData(selectedUser); + const { setEstimatedWalletValue, globalProps } = this.props; + const walletData = await groomingWalletData(selectedUser, globalProps); this.setState({ walletData }); setEstimatedWalletValue(walletData.estimatedValue); @@ -133,6 +133,7 @@ const mapStateToProps = state => ({ currentAccount: state.account.currentAccount, pinCode: state.account.pin, isDarkTheme: state.application.isDarkTheme, + globalProps: state.account.globalProps, }); export default injectIntl(connect(mapStateToProps)(WalletContainer)); diff --git a/src/providers/steem/dsteem.js b/src/providers/steem/dsteem.js index f82ed366f..a74c20d6d 100644 --- a/src/providers/steem/dsteem.js +++ b/src/providers/steem/dsteem.js @@ -10,13 +10,12 @@ import { decryptKey } from '../../utils/crypto'; import { parsePosts, parsePost, parseComments } from '../../utils/postParser'; import { getName, getAvatar } from '../../utils/user'; import { getReputation } from '../../utils/reputation'; +import parseToken from '../../utils/parseToken'; // Constant import AUTH_TYPE from '../../constants/authType'; const DEFAULT_SERVER = 'https://api.steemit.com'; -let rewardFund = null; -let medianPrice = null; let client = new Client(DEFAULT_SERVER); const _getClient = async () => { @@ -35,6 +34,50 @@ _getClient(); export const getDigitPinCode = pin => decryptKey(pin, Config.PIN_KEY); +export const getDynamicGlobalProperties = () => client.database.getDynamicGlobalProperties(); + +export const getRewardFund = () => client.database.call('get_reward_fund', ['post']); + +export const getFeedHistory = async () => { + try { + const feedHistory = await client.database.call('get_feed_history'); + return feedHistory; + } catch (error) { + return error; + } +}; + +export const fetchGlobalProps = async () => { + let globalDynamic; + let feedHistory; + let rewardFund; + + try { + globalDynamic = await getDynamicGlobalProperties(); + feedHistory = await getFeedHistory(); + rewardFund = await getRewardFund(); + } catch (e) { + return; + } + + const steemPerMVests = (parseToken(globalDynamic.total_vesting_fund_steem) + / parseToken(globalDynamic.total_vesting_shares)) + * 1e6; + const base = parseToken(feedHistory.current_median_history.base); + const quote = parseToken(feedHistory.current_median_history.quote); + const fundRecentClaims = rewardFund.recent_claims; + const fundRewardBalance = parseToken(rewardFund.reward_balance); + const globalProps = { + steemPerMVests, + base, + quote, + fundRecentClaims, + fundRewardBalance, + }; + + return globalProps; +}; + /** * @method getAccount get account data * @param user username @@ -127,22 +170,6 @@ export const getFollows = user => new Promise((resolve, reject) => { }); }); -/** - * @method getFollowers - * @param user username - * TODO: Pagination - */ -// export const getFollowers = (user, limit = 100) => new Promise((resolve, reject) => { -// client -// .call('follow_api', 'get_followers', [user, '', 'blog', limit]) -// .then((result) => { -// resolve(result); -// }) -// .catch((err) => { -// reject(err); -// }); -// }); - /** * @method getFollowing * @param user username @@ -426,18 +453,12 @@ export const vote = async (currentAccount, pin, author, permlink, weight) => { * @method upvoteAmount estimate upvote amount */ export const upvoteAmount = async (input) => { - if (!rewardFund || !medianPrice) { - rewardFund = await client.database.call('get_reward_fund', ['post']); + let medianPrice; + const rewardFund = await getRewardFund(); - await client.database - .getCurrentMedianHistoryPrice() - .then((res) => { - medianPrice = res; - }) - .catch((err) => { - // reject(err); - }); - } + await client.database.getCurrentMedianHistoryPrice().then((res) => { + medianPrice = res; + }); const estimated = (input / parseFloat(rewardFund.recent_claims)) * parseFloat(rewardFund.reward_balance) @@ -563,24 +584,6 @@ export const delegate = (data, activeKey) => { }); }; -export const globalProps = async () => { - try { - const globalProperties = await client.database.getDynamicGlobalProperties(); - return globalProperties; - } catch (error) { - return error; - } -}; - -export const getFeedHistory = async () => { - try { - const feedHistory = await client.database.call('get_feed_history'); - return feedHistory; - } catch (error) { - return error; - } -}; - export const transferToVesting = (data, activeKey) => { const privateKey = PrivateKey.fromString(activeKey); @@ -832,15 +835,4 @@ const getAnyPrivateKey = (local, pin) => { } return false; - - // ['postingKey', 'activeKey'].forEach((key) => { - // console.log('key :', key); - // console.log('pin :', pin); - // console.log('local[key] :', local[key]); - // if (key) { - // const privateKey = decryptKey(local[key], pin); - // console.log('privateKey :', privateKey); - // return true; - // } - // }); }; diff --git a/src/redux/actions/accountAction.js b/src/redux/actions/accountAction.js index 90b3dbaa7..ba9421ca6 100644 --- a/src/redux/actions/accountAction.js +++ b/src/redux/actions/accountAction.js @@ -1,24 +1,18 @@ -import { getUser } from '../../providers/steem/dsteem'; +import { fetchGlobalProps } from '../../providers/steem/dsteem'; import { - FETCH_ACCOUNT_FAIL, - FETCHING_ACCOUNT, ADD_OTHER_ACCOUNT, + FETCH_ACCOUNT_FAIL, + REMOVE_OTHER_ACCOUNT, + SET_GLOBAL_PROPS, + SET_PIN_CODE, UPDATE_CURRENT_ACCOUNT, UPDATE_UNREAD_ACTIVITY_COUNT, - REMOVE_OTHER_ACCOUNT, - SET_PIN_CODE, } from '../constants/constants'; -export const fetchAccountFromSteem = (username, password) => (dispatch) => { - dispatch({ type: FETCHING_ACCOUNT }); - - return getUser(username) - .then(res => dispatch({ - type: UPDATE_CURRENT_ACCOUNT, - payload: { ...res, password }, - })) - .catch(err => dispatch({ type: FETCH_ACCOUNT_FAIL, payload: err })); -}; +export const fetchGlobalProperties = () => dispatch => fetchGlobalProps().then(res => dispatch({ + type: SET_GLOBAL_PROPS, + payload: { ...res }, +})); export const updateCurrentAccount = data => ({ type: UPDATE_CURRENT_ACCOUNT, @@ -49,3 +43,8 @@ export const setPinCode = data => ({ type: SET_PIN_CODE, payload: data, }); + +export const setGlobalProps = data => ({ + type: SET_GLOBAL_PROPS, + payload: data, +}); diff --git a/src/redux/constants/constants.js b/src/redux/constants/constants.js index 51cf422b5..a69f73e32 100644 --- a/src/redux/constants/constants.js +++ b/src/redux/constants/constants.js @@ -24,11 +24,12 @@ export const SET_UPVOTE_PERCENT = 'SET_UPVOTE_PERCENT'; // Accounts export const ADD_OTHER_ACCOUNT = 'ADD_OTHER_ACCOUNT'; export const FETCH_ACCOUNT_FAIL = 'FETCH_ACCOUNT_FAIL'; -export const FETCHING_ACCOUNT = 'FETCHING_ACCOUNT'; export const REMOVE_OTHER_ACCOUNT = 'REMOVE_OTHER_ACCOUNT'; export const UPDATE_CURRENT_ACCOUNT = 'UPDATE_CURRENT_ACCOUNT'; export const UPDATE_UNREAD_ACTIVITY_COUNT = 'UPDATE_UNREAD_ACTIVITY_COUNT'; export const SET_PIN_CODE = 'SET_PIN_CODE'; +export const SET_GLOBAL_PROPS = 'SET_GLOBAL_PROPS'; +export const FETCH_GLOBAL_PROPS = 'FETCH_GLOBAL_PROPS'; // UI export const IS_COLLAPSE_POST_BUTTON = 'IS_COLLAPSE_POST_BUTTON'; diff --git a/src/redux/reducers/accountReducer.js b/src/redux/reducers/accountReducer.js index 4de63d4f3..0d8685f1b 100644 --- a/src/redux/reducers/accountReducer.js +++ b/src/redux/reducers/accountReducer.js @@ -7,6 +7,7 @@ import { REMOVE_OTHER_ACCOUNT, LOGOUT_FAIL, SET_PIN_CODE, + SET_GLOBAL_PROPS, } from '../constants/constants'; const initialState = { @@ -29,6 +30,12 @@ export default function (state = initialState, action) { errorMessage: null, }; + case SET_GLOBAL_PROPS: + return { + ...state, + globalProps: action.payload, + }; + case FETCH_ACCOUNT_FAIL: return { ...state, diff --git a/src/screens/application/container/applicationContainer.js b/src/screens/application/container/applicationContainer.js index e465ffd1d..16310eaca 100644 --- a/src/screens/application/container/applicationContainer.js +++ b/src/screens/application/container/applicationContainer.js @@ -5,6 +5,7 @@ import { addLocaleData } from 'react-intl'; import Config from 'react-native-config'; import AppCenter from 'appcenter'; import { NavigationActions } from 'react-navigation'; +import { bindActionCreators } from 'redux'; // Constants import en from 'react-intl/locale-data/en'; @@ -37,6 +38,7 @@ import { updateCurrentAccount, updateUnreadActivityCount, removeOtherAccount, + fetchGlobalProperties, } from '../../../redux/actions/accountAction'; import { activeApplication, @@ -69,8 +71,11 @@ class ApplicationContainer extends Component { componentDidMount = async () => { BackHandler.addEventListener('hardwareBackPress', this._onBackPress); + this._refreshGlobalProps(); await this._getUserData(); await this._getSettings(); + + this.globalInterval = setInterval(this._refreshGlobalProps, 60000); }; componentWillReceiveProps(nextProps) { @@ -87,6 +92,7 @@ class ApplicationContainer extends Component { componentWillUnmount() { BackHandler.removeEventListener('hardwareBackPress', this.onBackPress); + clearInterval(this.globalInterval); } _onBackPress = () => { @@ -100,6 +106,12 @@ class ApplicationContainer extends Component { return true; }; + _refreshGlobalProps = () => { + const { actions } = this.props; + + actions.fetchGlobalProperties(); + }; + _getUserData = async () => { const { dispatch, pinCode } = this.props; let realmData = []; @@ -280,20 +292,25 @@ class ApplicationContainer extends Component { } } -const mapStateToProps = state => ({ - // Application - isDarkTheme: state.application.isDarkTheme, - selectedLanguage: state.application.language, - notificationSettings: state.application.isNotificationOpen, - isLogingOut: state.application.isLogingOut, - isLoggedIn: state.application.isLoggedIn, - nav: state.nav.routes, +export default connect( + state => ({ + // Application + isDarkTheme: state.application.isDarkTheme, + selectedLanguage: state.application.language, + notificationSettings: state.application.isNotificationOpen, + isLogingOut: state.application.isLogingOut, + isLoggedIn: state.application.isLoggedIn, - // Account - unreadActivityCount: state.account.currentAccount.unread_activity_count, - currentAccount: state.account.currentAccount, - otherAccounts: state.account.otherAccounts, - pinCode: state.account.pin, -}); - -export default connect(mapStateToProps)(ApplicationContainer); + // Account + unreadActivityCount: state.account.currentAccount.unread_activity_count, + currentAccount: state.account.currentAccount, + otherAccounts: state.account.otherAccounts, + pinCode: state.account.pin, + }), + (dispatch, props) => ({ + dispatch, + actions: { + ...bindActionCreators({ fetchGlobalProperties }, dispatch), + }, + }), +)(ApplicationContainer); \ No newline at end of file diff --git a/src/utils/conversions.js b/src/utils/conversions.js index 495b7a0c2..d62bfcfb2 100644 --- a/src/utils/conversions.js +++ b/src/utils/conversions.js @@ -1 +1,7 @@ export const vestsToSp = (vests, steemPerMVests) => (vests / 1e6) * steemPerMVests; + +export const vestsToRshares = (vests, votingPower, votePerc) => { + const vestingShares = parseInt(vests * 1e6, 10); + const power = (votingPower * votePerc) / 1e4 / 50 + 1; + return (power * vestingShares) / 1e4; +}; diff --git a/src/utils/wallet.js b/src/utils/wallet.js index 0cd9243ea..f9ee862a4 100644 --- a/src/utils/wallet.js +++ b/src/utils/wallet.js @@ -1,10 +1,11 @@ import parseDate from './parseDate'; import parseToken from './parseToken'; import { vestsToSp } from './conversions'; -import { globalProps, getState, getFeedHistory } from '../providers/steem/dsteem'; +import { getState, getFeedHistory } from '../providers/steem/dsteem'; -export const groomingTransactionData = (transaction, walletData, formatNumber) => { - if (!transaction || !walletData) { + +export const groomingTransactionData = (transaction, steemPerMVests, formatNumber) => { + if (!transaction || !steemPerMVests) { return []; } @@ -23,7 +24,7 @@ export const groomingTransactionData = (transaction, walletData, formatNumber) = const { reward } = opData; const { comment_author: commentAuthor, comment_permlink: commentPermlink } = opData; - result.value = `${formatNumber(vestsToSp(parseToken(reward), walletData.steemPerMVests), { + result.value = `${formatNumber(vestsToSp(parseToken(reward), steemPerMVests), { minimumFractionDigits: 3, })} SP`; result.details = `@${commentAuthor}/${commentPermlink}`; @@ -41,7 +42,7 @@ export const groomingTransactionData = (transaction, walletData, formatNumber) = sbdPayout = formatNumber(parseToken(sbdPayout), { minimumFractionDigits: 3 }); steemPayout = formatNumber(parseToken(steemPayout), { minimumFractionDigits: 3 }); vestingPayout = formatNumber( - vestsToSp(parseToken(vestingPayout), walletData.steemPerMVests), + vestsToSp(parseToken(vestingPayout), steemPerMVests), { minimumFractionDigits: 3 }, ); @@ -59,7 +60,7 @@ export const groomingTransactionData = (transaction, walletData, formatNumber) = rewardSdb = formatNumber(parseToken(rewardSdb), { minimumFractionDigits: 3 }); rewardSteem = formatNumber(parseToken(rewardSteem), { minimumFractionDigits: 3 }); - rewardVests = formatNumber(vestsToSp(parseToken(rewardVests), walletData.steemPerMVests), { + rewardVests = formatNumber(vestsToSp(parseToken(rewardVests), steemPerMVests), { minimumFractionDigits: 3, }); @@ -83,7 +84,7 @@ export const groomingTransactionData = (transaction, walletData, formatNumber) = let { vesting_shares: opVestingShares } = opData; opVestingShares = parseToken(opVestingShares); - result.value = `${formatNumber(vestsToSp(opVestingShares, walletData.steemPerMVests), { + result.value = `${formatNumber(vestsToSp(opVestingShares, steemPerMVests), { minimumFractionDigits: 3, })} SP`; result.icon = 'attach-money'; @@ -101,14 +102,13 @@ export const groomingTransactionData = (transaction, walletData, formatNumber) = return result; }; -export const groomingWalletData = async (user) => { +export const groomingWalletData = async (user, globalProps) => { const walletData = {}; if (!user) { return walletData; } - const global = await globalProps(); const state = await getState(`/@${user.name}/transfers`); const { accounts } = state; @@ -133,9 +133,7 @@ export const groomingWalletData = async (user) => { const base = parseToken(feedHistory.current_median_history.base); const quote = parseToken(feedHistory.current_median_history.quote); - walletData.steemPerMVests = ( - parseToken(global.total_vesting_fund_steem) / parseToken(global.total_vesting_shares) - ) * 1e6; + walletData.steemPerMVests = globalProps.steemPerMVests; walletData.estimatedValue = ( vestsToSp(walletData.vestingShares, walletData.steemPerMVests) * (base / quote)