From 09ab308201ef1aa81ad760a5f2e84ce34862f78d Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Tue, 10 May 2022 12:58:36 +0200 Subject: [PATCH 01/15] Display Reward graph --- .../wallet/staking/dashboard-revamp/StakingTabs.js | 11 +++++++++++ .../wallet/staking/dashboard/GraphWrapper.js | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js index 1772547c78..43a5d00fe9 100644 --- a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js +++ b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js @@ -12,6 +12,7 @@ import { defineMessages, injectIntl } from 'react-intl'; import type { $npm$ReactIntl$IntlShape } from 'react-intl'; import globalMessages from '../../../../i18n/global-messages'; import type { PoolData } from '../../../../containers/wallet/staking/SeizaFetcher'; +import { Graph as RewardGraph } from '../dashboard/GraphWrapper'; type Props = {| delegatedPool: PoolData, @@ -47,6 +48,16 @@ function StakingTabs({ delegatedPool, undelegate, intl }: Props & Intl): Node { + + ), }, diff --git a/packages/yoroi-extension/app/components/wallet/staking/dashboard/GraphWrapper.js b/packages/yoroi-extension/app/components/wallet/staking/dashboard/GraphWrapper.js index 59b105d324..819f9338fd 100644 --- a/packages/yoroi-extension/app/components/wallet/staking/dashboard/GraphWrapper.js +++ b/packages/yoroi-extension/app/components/wallet/staking/dashboard/GraphWrapper.js @@ -95,7 +95,7 @@ const GraphTabs: {| // ); // }; -const Graph: {| +export const Graph: {| data: Array, epochTitle: string, stakepoolNameTitle: string, From d6d853577ddff585ce0bc1a2714e9199b9efb587 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Tue, 10 May 2022 13:51:24 +0200 Subject: [PATCH 02/15] Add new utils to generate graph data --- .../wallet/staking/StakingDashboardPage.js | 8 +- packages/yoroi-extension/app/utils/graph.js | 145 ++++++++++++++++++ 2 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 packages/yoroi-extension/app/utils/graph.js diff --git a/packages/yoroi-extension/app/containers/wallet/staking/StakingDashboardPage.js b/packages/yoroi-extension/app/containers/wallet/staking/StakingDashboardPage.js index ce381681de..3417bb35e0 100644 --- a/packages/yoroi-extension/app/containers/wallet/staking/StakingDashboardPage.js +++ b/packages/yoroi-extension/app/containers/wallet/staking/StakingDashboardPage.js @@ -70,6 +70,7 @@ import { import type { TokenInfoMap } from '../../../stores/toplevel/TokenInfoStore'; import { getTokenName, genLookupOrFail } from '../../../stores/stateless/tokenHelpers'; import { truncateToken } from '../../../utils/formatters'; +import { generateGraphData } from '../../../utils/graph'; export type GeneratedData = typeof StakingDashboardPage.prototype.generated; @@ -144,9 +145,14 @@ export default class StakingDashboardPage extends Component { errorIfPresent, })} upcomingRewards={rewardInfo?.rewardPopup} - graphData={this._generateGraphData({ + graphData={generateGraphData({ delegationRequests, publicDeriver, + currentEpoch: + this.generated.stores.time.getCurrentTimeRequests(publicDeriver).currentEpoch, + shouldHideBalance: this.generated.stores.profile.shouldHideBalance, + getLocalPoolInfo: this.generated.stores.delegation.getLocalPoolInfo, + tokenInfo: this.generated.stores.tokenInfoStore.tokenInfo, })} delegationHistory={delegationRequests.getCurrentDelegation.result?.fullHistory} epochLength={this.getEpochLengthInDays(publicDeriver)} diff --git a/packages/yoroi-extension/app/utils/graph.js b/packages/yoroi-extension/app/utils/graph.js new file mode 100644 index 0000000000..c084f9e71c --- /dev/null +++ b/packages/yoroi-extension/app/utils/graph.js @@ -0,0 +1,145 @@ +// @flow + +import { getCardanoHaskellBaseConfig, isCardanoHaskell, isJormungandr } from '../api/ada/lib/storage/database/prepackaged/networks'; +import { MultiToken } from '../api/common/lib/MultiToken'; + +const generateRewardGraphData: ({| + delegationRequests: DelegationRequests, + currentEpoch: number, + publicDeriver: PublicDeriver<>, + getLocalPoolInfo: ($ReadOnly, string) => void | PoolMeta, + tokenInfo: TokenInfoMap, +|}) => ?{| + totalRewards: Array, + perEpochRewards: Array, +|} = request => { + console.log({request}) + const defaultToken = request.publicDeriver.getParent().getDefaultToken(); + + const history = request.delegationRequests.rewardHistory.result; + if (history == null) { + return null; + } + if (!request.delegationRequests.getCurrentDelegation.wasExecuted) { + return null; + } + let historyIterator = 0; + + // the reward history endpoint doesn't contain entries when the reward was 0 + // so we need to insert these manually + const totalRewards: Array = []; + const perEpochRewards: Array = []; + let amountSum = new MultiToken([], defaultToken); + + const startEpoch = (() => { + if (isCardanoHaskell(request.publicDeriver.getParent().getNetworkInfo())) { + const shelleyConfig = getCardanoHaskellBaseConfig( + request.publicDeriver.getParent().getNetworkInfo() + )[1]; + return shelleyConfig.StartAt; + } + return 0; + })(); + const endEpoch = (() => { + if (isCardanoHaskell(request.publicDeriver.getParent().getNetworkInfo())) { + // TODO: -1 since cardano-db-sync doesn't expose this information for some reason + return request.currentEpoch - 1; + } + if (isJormungandr(request.publicDeriver.getParent().getNetworkInfo())) { + // note: reward history includes the current epoch + // since it tells you the reward you got at slot 0 of the new epoch + return request.currentEpoch + 1; + } + throw new Error( + `${nameof(this._generateRewardGraphData)} can't compute endEpoch for rewards` + ); + })(); + + const getMiniPoolInfo = (poolHash: string) => { + // const meta = this.generated.stores.delegation.getLocalPoolInfo( + // request.publicDeriver.getParent().getNetworkInfo(), + // poolHash + // ); + const meta = request.getLocalPoolInfo( + request.publicDeriver.getParent().getNetworkInfo(), + poolHash + ); + if (meta == null || meta.info == null || meta.info.ticker == null || meta.info.name == null) { + return poolHash; + } + return `[${meta.info.ticker}] ${meta.info.name}`; + } + + const getNormalized = (tokenEntry) => { + // const tokenRow = this.generated.stores.tokenInfoStore.tokenInfo + // .get(tokenEntry.networkId.toString()) + // ?.get(tokenEntry.identifier); + const tokenRow = request.tokenInfo + .get(tokenEntry.networkId.toString()) + ?.get(tokenEntry.identifier); + if (tokenRow == null) throw new Error(`${nameof(generateRewardGraphData)} no token info for ${JSON.stringify(tokenEntry)}`); + return tokenEntry.amount.shiftedBy(-tokenRow.Metadata.numberOfDecimals); + } + for (let i = startEpoch; i < endEpoch; i++) { + if (historyIterator < history.length && i === history[historyIterator][0]) { + // exists a reward for this epoch + const poolHash = history[historyIterator][2]; + const nextReward = history[historyIterator][1]; + amountSum = amountSum.joinAddMutable(nextReward); + totalRewards.push({ + name: i, + primary: getNormalized(amountSum.getDefaultEntry()).toNumber(), + poolName: getMiniPoolInfo(poolHash), + }); + perEpochRewards.push({ + name: i, + primary: getNormalized(nextReward.getDefaultEntry()).toNumber(), + poolName: getMiniPoolInfo(poolHash), + }); + historyIterator++; + } else { + // no reward for this epoch + totalRewards.push({ + name: i, + primary: getNormalized(amountSum.getDefaultEntry()).toNumber(), + poolName: '', + }); + perEpochRewards.push({ + name: i, + primary: 0, + poolName: '', + }); + } + } + + return { + totalRewards, + perEpochRewards, + }; +}; + + +export const generateGraphData: ({| + delegationRequests: DelegationRequests, + publicDeriver: PublicDeriver<>, + currentEpoch: boolean, + shouldHideBalance: boolean, + getLocalPoolInfo: string, + tokenInfo: string, +|}) => GraphData = request => { + // const timeStore = this.generated.stores.time; + // const currTimeRequests = timeStore.getCurrentTimeRequests(request.publicDeriver); + return { + rewardsGraphData: { + error: request.delegationRequests.rewardHistory.error, + items: generateRewardGraphData({ + delegationRequests: request.delegationRequests, + currentEpoch: request.currentEpoch, + publicDeriver: request.publicDeriver, + getLocalPoolInfo: request.getLocalPoolInfo, + tokenInfo: request.tokenInfo, + }), + hideYAxis: request.shouldHideBalance, + }, + }; +}; \ No newline at end of file From b6404b1136e34a7f63f942b9d0753178dfa2beed Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Tue, 10 May 2022 13:55:49 +0200 Subject: [PATCH 03/15] Remove old methods & Add data types --- .../wallet/staking/StakingDashboardPage.js | 128 ------------------ packages/yoroi-extension/app/utils/graph.js | 14 +- 2 files changed, 9 insertions(+), 133 deletions(-) diff --git a/packages/yoroi-extension/app/containers/wallet/staking/StakingDashboardPage.js b/packages/yoroi-extension/app/containers/wallet/staking/StakingDashboardPage.js index 3417bb35e0..64c56d5229 100644 --- a/packages/yoroi-extension/app/containers/wallet/staking/StakingDashboardPage.js +++ b/packages/yoroi-extension/app/containers/wallet/staking/StakingDashboardPage.js @@ -7,8 +7,6 @@ import { observer } from 'mobx-react'; import type { InjectedOrGenerated } from '../../../types/injectedPropsType'; import StakingDashboard from '../../../components/wallet/staking/dashboard/StakingDashboard'; -import type { GraphData } from '../../../components/wallet/staking/dashboard/StakingDashboard'; -import type { GraphItems } from '../../../components/wallet/staking/dashboard/GraphWrapper'; import UserSummary from '../../../components/wallet/staking/dashboard/UserSummary'; import StakePool from '../../../components/wallet/staking/dashboard/StakePool'; import UndelegateDialog from '../../../components/wallet/staking/dashboard/UndelegateDialog'; @@ -58,7 +56,6 @@ import type { NetworkRow, TokenRow, } from '../../../api/ada/lib/storage/databas import { isJormungandr, isCardanoHaskell, - getCardanoHaskellBaseConfig, } from '../../../api/ada/lib/storage/database/prepackaged/networks'; import DeregisterDialogContainer from '../../transfer/DeregisterDialogContainer'; import type { GeneratedData as DeregisterDialogContainerData } from '../../transfer/DeregisterDialogContainer'; @@ -816,131 +813,6 @@ export default class StakingDashboardPage extends Component { return adaDelegationRequests.getRegistrationHistory.result?.current; }; - _generateRewardGraphData: ({| - delegationRequests: DelegationRequests, - currentEpoch: number, - publicDeriver: PublicDeriver<>, - |}) => ?{| - totalRewards: Array, - perEpochRewards: Array, - |} = request => { - const defaultToken = request.publicDeriver.getParent().getDefaultToken(); - - const history = request.delegationRequests.rewardHistory.result; - if (history == null) { - return null; - } - if (!request.delegationRequests.getCurrentDelegation.wasExecuted) { - return null; - } - let historyIterator = 0; - - // the reward history endpoint doesn't contain entries when the reward was 0 - // so we need to insert these manually - const totalRewards: Array = []; - const perEpochRewards: Array = []; - let amountSum = new MultiToken([], defaultToken); - - const startEpoch = (() => { - if (isCardanoHaskell(request.publicDeriver.getParent().getNetworkInfo())) { - const shelleyConfig = getCardanoHaskellBaseConfig( - request.publicDeriver.getParent().getNetworkInfo() - )[1]; - return shelleyConfig.StartAt; - } - return 0; - })(); - const endEpoch = (() => { - if (isCardanoHaskell(request.publicDeriver.getParent().getNetworkInfo())) { - // TODO: -1 since cardano-db-sync doesn't expose this information for some reason - return request.currentEpoch - 1; - } - if (isJormungandr(request.publicDeriver.getParent().getNetworkInfo())) { - // note: reward history includes the current epoch - // since it tells you the reward you got at slot 0 of the new epoch - return request.currentEpoch + 1; - } - throw new Error( - `${nameof(this._generateRewardGraphData)} can't compute endEpoch for rewards` - ); - })(); - - const getMiniPoolInfo = (poolHash: string) => { - const meta = this.generated.stores.delegation.getLocalPoolInfo( - request.publicDeriver.getParent().getNetworkInfo(), - poolHash - ); - if (meta == null || meta.info == null || meta.info.ticker == null || meta.info.name == null) { - return poolHash; - } - return `[${meta.info.ticker}] ${meta.info.name}`; - } - - const getNormalized = (tokenEntry) => { - const tokenRow = this.generated.stores.tokenInfoStore.tokenInfo - .get(tokenEntry.networkId.toString()) - ?.get(tokenEntry.identifier); - if (tokenRow == null) throw new Error(`${nameof(StakingDashboardPage)} no token info for ${JSON.stringify(tokenEntry)}`); - - return tokenEntry.amount.shiftedBy(-tokenRow.Metadata.numberOfDecimals); - } - for (let i = startEpoch; i < endEpoch; i++) { - if (historyIterator < history.length && i === history[historyIterator][0]) { - // exists a reward for this epoch - const poolHash = history[historyIterator][2]; - const nextReward = history[historyIterator][1]; - amountSum = amountSum.joinAddMutable(nextReward); - totalRewards.push({ - name: i, - primary: getNormalized(amountSum.getDefaultEntry()).toNumber(), - poolName: getMiniPoolInfo(poolHash), - }); - perEpochRewards.push({ - name: i, - primary: getNormalized(nextReward.getDefaultEntry()).toNumber(), - poolName: getMiniPoolInfo(poolHash), - }); - historyIterator++; - } else { - // no reward for this epoch - totalRewards.push({ - name: i, - primary: getNormalized(amountSum.getDefaultEntry()).toNumber(), - poolName: '', - }); - perEpochRewards.push({ - name: i, - primary: 0, - poolName: '', - }); - } - } - return { - totalRewards, - perEpochRewards, - }; - }; - - _generateGraphData: ({| - delegationRequests: DelegationRequests, - publicDeriver: PublicDeriver<>, - |}) => GraphData = request => { - const timeStore = this.generated.stores.time; - const currTimeRequests = timeStore.getCurrentTimeRequests(request.publicDeriver); - - return { - rewardsGraphData: { - error: request.delegationRequests.rewardHistory.error, - items: this._generateRewardGraphData({ - delegationRequests: request.delegationRequests, - currentEpoch: currTimeRequests.currentEpoch, - publicDeriver: request.publicDeriver, - }), - hideYAxis: this.generated.stores.profile.shouldHideBalance, - }, - }; - }; - @computed get generated(): {| UnmangleTxDialogContainerProps: InjectedOrGenerated, DeregisterDialogContainerProps: InjectedOrGenerated, diff --git a/packages/yoroi-extension/app/utils/graph.js b/packages/yoroi-extension/app/utils/graph.js index c084f9e71c..0375095841 100644 --- a/packages/yoroi-extension/app/utils/graph.js +++ b/packages/yoroi-extension/app/utils/graph.js @@ -1,7 +1,12 @@ // @flow - +import type { GraphData } from '../components/wallet/staking/dashboard/StakingDashboard'; +import type { GraphItems } from '../components/wallet/staking/dashboard/GraphWrapper'; import { getCardanoHaskellBaseConfig, isCardanoHaskell, isJormungandr } from '../api/ada/lib/storage/database/prepackaged/networks'; import { MultiToken } from '../api/common/lib/MultiToken'; +import { PublicDeriver } from '../api/ada/lib/storage/models/PublicDeriver/index'; +import type { NetworkRow } from '../api/ada/lib/storage/database/primitives/tables'; +import type { PoolMeta, DelegationRequests } from '../stores/toplevel/DelegationStore'; +import type { TokenInfoMap } from '../stores/toplevel/TokenInfoStore'; const generateRewardGraphData: ({| delegationRequests: DelegationRequests, @@ -13,7 +18,6 @@ const generateRewardGraphData: ({| totalRewards: Array, perEpochRewards: Array, |} = request => { - console.log({request}) const defaultToken = request.publicDeriver.getParent().getDefaultToken(); const history = request.delegationRequests.rewardHistory.result; @@ -122,10 +126,10 @@ const generateRewardGraphData: ({| export const generateGraphData: ({| delegationRequests: DelegationRequests, publicDeriver: PublicDeriver<>, - currentEpoch: boolean, + currentEpoch: number, shouldHideBalance: boolean, - getLocalPoolInfo: string, - tokenInfo: string, + getLocalPoolInfo: ($ReadOnly, string) => void | PoolMeta, + tokenInfo: TokenInfoMap, |}) => GraphData = request => { // const timeStore = this.generated.stores.time; // const currTimeRequests = timeStore.getCurrentTimeRequests(request.publicDeriver); From 64862c7085c54f13678e6aed1c8cef1ee3c206a1 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Tue, 10 May 2022 14:40:17 +0200 Subject: [PATCH 04/15] Pass graph data to props --- .../containers/wallet/staking/StakingPage.js | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/yoroi-extension/app/containers/wallet/staking/StakingPage.js b/packages/yoroi-extension/app/containers/wallet/staking/StakingPage.js index d043dfbff7..165e09f3b8 100644 --- a/packages/yoroi-extension/app/containers/wallet/staking/StakingPage.js +++ b/packages/yoroi-extension/app/containers/wallet/staking/StakingPage.js @@ -50,6 +50,8 @@ import type { GeneratedData as WithdrawalTxDialogContainerData } from '../../tra import type { GeneratedData as DeregisterDialogContainerData } from '../../transfer/DeregisterDialogContainer'; import UndelegateDialog from '../../../components/wallet/staking/dashboard/UndelegateDialog'; import type { PoolRequest } from '../../../api/jormungandr/lib/storage/bridge/delegationUtils'; +import { generateGraphData } from '../../../utils/graph'; +import { ApiOptions, getApiForNetwork, } from '../../../api/common/utils'; export type GeneratedData = typeof StakingPage.prototype.generated; // populated by ConfigWebpackPlugin @@ -227,6 +229,15 @@ class StakingPage extends Component { } : undefined } + graphData={generateGraphData({ + delegationRequests, + publicDeriver, + currentEpoch: + this.generated.stores.time.getCurrentTimeRequests(publicDeriver).currentEpoch, + shouldHideBalance: this.generated.stores.profile.shouldHideBalance, + getLocalPoolInfo: this.generated.stores.delegation.getLocalPoolInfo, + tokenInfo: this.generated.stores.tokenInfoStore.tokenInfo, + })} /> ); }; @@ -445,6 +456,9 @@ class StakingPage extends Component { getLocalPoolInfo: ($ReadOnly, string) => void | PoolMeta, getDelegationRequests: (PublicDeriver<>) => void | DelegationRequests, |}, + time: {| + getCurrentTimeRequests: (PublicDeriver<>) => CurrentTimeRequests, + |}, profile: {| shouldHideBalance: boolean, unitOfAccount: UnitOfAccountSettingType, @@ -472,6 +486,22 @@ class StakingPage extends Component { if (selected == null) { throw new Error(`${nameof(EpochProgressContainer)} no wallet selected`); } + const api = getApiForNetwork(selected.getParent().getNetworkInfo()); + const time = (() => { + if (api === ApiOptions.ada) { + return { + getCurrentTimeRequests: stores.substores.ada.time.getCurrentTimeRequests, + }; + } + if (api === ApiOptions.jormungandr) { + return { + getCurrentTimeRequests: stores.substores.jormungandr.time.getCurrentTimeRequests, + }; + } + return { + getCurrentTimeRequests: () => { throw new Error(`${nameof(StakingPage)} api not supported`) }, + }; + })(); return Object.freeze({ stores: { @@ -502,6 +532,7 @@ class StakingPage extends Component { coinPriceStore: { getCurrentPrice: stores.coinPriceStore.getCurrentPrice, }, + time, substores: { ada: { delegation: { From 8773f01c5ed8e0d01cb723363d20bb0cf51ea65b Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Tue, 10 May 2022 14:50:08 +0200 Subject: [PATCH 05/15] Display per epoch rewards graph --- .../wallet/staking/dashboard-revamp/StakingTabs.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js index 43a5d00fe9..fc9c20f94a 100644 --- a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js +++ b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js @@ -30,13 +30,14 @@ const messages = defineMessages({ }, }); -function StakingTabs({ delegatedPool, undelegate, intl }: Props & Intl): Node { +function StakingTabs({ delegatedPool, undelegate, intl, graphData }: Props & Intl): Node { const [value, setValue] = useState(0); const handleChange = (event, newValue) => { setValue(newValue); }; + const { hideYAxis, items } = graphData.rewardsGraphData const tabs = [ { id: 0, @@ -55,8 +56,8 @@ function StakingTabs({ delegatedPool, undelegate, intl }: Props & Intl): Node { xAxisLabel='X Label' yAxisLabel='Y Label' primaryBarLabel='Bar Label' - data={[]} - hideYAxis={false} + data={items.perEpochRewards} + hideYAxis={hideYAxis} /> ), From 9e35183cbafabe98e5121e00026207df7751ee7f Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Tue, 10 May 2022 14:56:14 +0200 Subject: [PATCH 06/15] Show only stacking page for cardano wallets --- .../app/stores/stateless/sidebarCategories.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js b/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js index 3a0e1e4c45..6355d0cdf1 100644 --- a/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js +++ b/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js @@ -20,6 +20,7 @@ import votingIcon from '../../assets/images/sidebar/revamp/voting.inline.svg'; import settingIcon from '../../assets/images/sidebar/revamp/setting.inline.svg'; import faqIcon from '../../assets/images/sidebar/revamp/faq.inline.svg'; import { PublicDeriver } from '../../api/ada/lib/storage/models/PublicDeriver'; +import { isCardanoHaskell } from '../../api/ada/lib/storage/database/prepackaged/networks'; // import newUpdatesIcon from '../../assets/images/sidebar/revamp/new-updates.inline.svg'; // import feedbackIcon from '../../assets/images/sidebar/revamp/feedback.inline.svg'; @@ -135,7 +136,9 @@ export const allCategoriesRevamp: Array = [ route: ROUTES.STAKING, icon: stakingIcon, label: globalMessages.sidebarStaking, - isVisible: _request => _request.selected !== null, + isVisible: ({ selected }) => ( + selected && isCardanoHaskell(selected.getParent().getNetworkInfo()) + ), }, { className: 'assets', From 4383c948c4414804c111495c92d4ee6bcde65809 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Thu, 12 May 2022 12:26:06 +0200 Subject: [PATCH 07/15] Display labels --- .../staking/dashboard-revamp/RewardsGraph.js | 138 ++++++++++++++++++ .../staking/dashboard-revamp/StakingTabs.js | 38 ++++- .../containers/wallet/staking/StakingPage.js | 20 +++ 3 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js diff --git a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js new file mode 100644 index 0000000000..634903ecfa --- /dev/null +++ b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js @@ -0,0 +1,138 @@ +// @flow +import { Component } from 'react'; +import type { Node } from 'react'; +import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Label, ResponsiveContainer } from 'recharts'; +import { readCssVar } from '../../../../styles/utils'; +import { Box } from '@mui/system'; + +const graphVars = { + axisTickColor: readCssVar('--yoroi-dashboard-graph-axis-tick-color'), + axisTextColor: readCssVar('--yoroi-dashboard-graph-axis-text-color'), + barWidth: readCssVar('--yoroi-dashboard-graph-bar-width'), + barHoverBgColor: readCssVar('--yoroi-dashboard-graph-bar-hover-background-color'), + barPrimaryColor: readCssVar('--yoroi-palette-gray-300'), + fontSize: '0.75rem', + lineHeight: 14, +}; + +type Props = {| + data: Array, + epochTitle: string, + stakepoolNameTitle: string, + xAxisLabel: string, + yAxisLabel: string, + primaryBarLabel: string, + hideYAxis: boolean, +|} + +export default class RewardGraph extends Component { + render(): Node { + const { + hideYAxis, + data, + xAxisLabel, + yAxisLabel, + primaryBarLabel, + epochTitle, + stakepoolNameTitle + } = this.props; + + const formatYAxis = (value) => ( + !hideYAxis ? value : '∗∗∗ ' + ); + const GraphTooltip = ( + { active, payload, label }: {| active: boolean, payload: ?[any], label: string |} + ) => { + if (active && payload != null) { + const { poolName } = payload[0].payload + return ( + +

+ {epochTitle}:  + {label} +

+

+ {primaryBarLabel}:  + {payload[0].value} +

+ {poolName && ( +

+ {stakepoolNameTitle}:  + {payload[0].payload.poolName} +

)} +
+ ); + } + return null; + }; + + // $FlowExpectedError[prop-missing] props are passed implicitly which causes a flow error + const graphTooltip = (); + + return ( + + + + + + + + + + + + ); + } +}; \ No newline at end of file diff --git a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js index fc9c20f94a..619ce68f41 100644 --- a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js +++ b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js @@ -12,7 +12,7 @@ import { defineMessages, injectIntl } from 'react-intl'; import type { $npm$ReactIntl$IntlShape } from 'react-intl'; import globalMessages from '../../../../i18n/global-messages'; import type { PoolData } from '../../../../containers/wallet/staking/SeizaFetcher'; -import { Graph as RewardGraph } from '../dashboard/GraphWrapper'; +import RewardGraph from './RewardsGraph'; type Props = {| delegatedPool: PoolData, @@ -28,15 +28,39 @@ const messages = defineMessages({ defaultMessage: '!!!The first reward to receive takes 3-4 epochs which is equal to 15-20 days, learn more.', }, + epochAxisLabel: { + id: 'wallet.dashboard.graph.epochAxisLabel', + defaultMessage: '!!!Epoch ({epochLength} days)', + }, + singleEpochAxisLabel: { + id: 'wallet.dashboard.graph.singleEpochAxisLabel', + defaultMessage: '!!!Epoch (1 day)', + }, }); -function StakingTabs({ delegatedPool, undelegate, intl, graphData }: Props & Intl): Node { +function StakingTabs({ + delegatedPool, + epochLength, + undelegate, + intl, + graphData + }: Props & Intl): Node { const [value, setValue] = useState(0); const handleChange = (event, newValue) => { setValue(newValue); }; + const getEpochLengthLabel: void => string = () => { + if (epochLength == null) { + return intl.formatMessage(globalMessages.epochLabel); + } + + return epochLength === 1 + ? intl.formatMessage(messages.singleEpochAxisLabel) + : intl.formatMessage(messages.epochAxisLabel, { epochLength }); + } + const { hideYAxis, items } = graphData.rewardsGraphData const tabs = [ { @@ -51,11 +75,11 @@ function StakingTabs({ delegatedPool, undelegate, intl, graphData }: Props & Int diff --git a/packages/yoroi-extension/app/containers/wallet/staking/StakingPage.js b/packages/yoroi-extension/app/containers/wallet/staking/StakingPage.js index 165e09f3b8..b6cb40e9be 100644 --- a/packages/yoroi-extension/app/containers/wallet/staking/StakingPage.js +++ b/packages/yoroi-extension/app/containers/wallet/staking/StakingPage.js @@ -167,6 +167,21 @@ class StakingPage extends Component { return undefined; }; + getEpochLengthInDays: (PublicDeriver<>) => ?number = publicDeriver => { + const timeStore = this.generated.stores.time; + const timeCalcRequests = timeStore.getTimeCalcRequests(publicDeriver); + const getEpochLength = timeCalcRequests.requests.currentEpochLength.result; + if (getEpochLength == null) return null; + + const getSlotLength = timeCalcRequests.requests.currentSlotLength.result; + if (getSlotLength == null) return null; + + const epochLengthInSeconds = getEpochLength() * getSlotLength(); + const epochLengthInDays = epochLengthInSeconds / (60 * 60 * 24); + return epochLengthInDays; + }; + + getStakePools: (PublicDeriver<>) => Node | void = publicDeriver => { const delegationStore = this.generated.stores.delegation; const delegationRequests = delegationStore.getDelegationRequests(publicDeriver); @@ -238,6 +253,7 @@ class StakingPage extends Component { getLocalPoolInfo: this.generated.stores.delegation.getLocalPoolInfo, tokenInfo: this.generated.stores.tokenInfoStore.tokenInfo, })} + epochLength={this.getEpochLengthInDays(publicDeriver)} /> ); }; @@ -458,6 +474,7 @@ class StakingPage extends Component { |}, time: {| getCurrentTimeRequests: (PublicDeriver<>) => CurrentTimeRequests, + getTimeCalcRequests: (PublicDeriver<>) => TimeCalcRequests, |}, profile: {| shouldHideBalance: boolean, @@ -490,15 +507,18 @@ class StakingPage extends Component { const time = (() => { if (api === ApiOptions.ada) { return { + getTimeCalcRequests: stores.substores.ada.time.getTimeCalcRequests, getCurrentTimeRequests: stores.substores.ada.time.getCurrentTimeRequests, }; } if (api === ApiOptions.jormungandr) { return { + getTimeCalcRequests: stores.substores.jormungandr.time.getTimeCalcRequests, getCurrentTimeRequests: stores.substores.jormungandr.time.getCurrentTimeRequests, }; } return { + getTimeCalcRequests: (undefined: any), getCurrentTimeRequests: () => { throw new Error(`${nameof(StakingPage)} api not supported`) }, }; })(); From bf55a4509c43c70b03cad65280a64ac56c685e20 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Thu, 12 May 2022 12:41:03 +0200 Subject: [PATCH 08/15] Remove y-label --- .../staking/dashboard-revamp/RewardsGraph.js | 134 +++++++++--------- 1 file changed, 64 insertions(+), 70 deletions(-) diff --git a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js index 634903ecfa..cc9cffe016 100644 --- a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js +++ b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js @@ -1,7 +1,7 @@ // @flow import { Component } from 'react'; import type { Node } from 'react'; -import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Label, ResponsiveContainer } from 'recharts'; +import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; import { readCssVar } from '../../../../styles/utils'; import { Box } from '@mui/system'; @@ -27,65 +27,64 @@ type Props = {| export default class RewardGraph extends Component { render(): Node { - const { - hideYAxis, - data, - xAxisLabel, - yAxisLabel, - primaryBarLabel, - epochTitle, - stakepoolNameTitle - } = this.props; + const { + hideYAxis, + data, + xAxisLabel, + yAxisLabel, + primaryBarLabel, + epochTitle, + stakepoolNameTitle + } = this.props; - const formatYAxis = (value) => ( - !hideYAxis ? value : '∗∗∗ ' - ); - const GraphTooltip = ( - { active, payload, label }: {| active: boolean, payload: ?[any], label: string |} - ) => { - if (active && payload != null) { - const { poolName } = payload[0].payload - return ( - -

- {epochTitle}:  - {label} -

-

- {primaryBarLabel}:  - {payload[0].value} -

- {poolName && ( -

- {stakepoolNameTitle}:  - {payload[0].payload.poolName} -

)} -
- ); - } - return null; - }; - - // $FlowExpectedError[prop-missing] props are passed implicitly which causes a flow error - const graphTooltip = (); + const formatYAxis = (value) => ( + !hideYAxis ? value : '∗∗∗ ' + ); + const GraphTooltip = ( + { active, payload, label }: {| active: boolean, payload: ?[any], label: string |} + ) => { + if (active && payload != null) { + const { poolName } = payload[0].payload + return ( + +

+ {epochTitle}:  + {label} +

+

+ {primaryBarLabel}:  + {payload[0].value} +

+ {poolName && ( +

+ {stakepoolNameTitle}:  + {payload[0].payload.poolName} +

)} +
+ ); + } + return null; + }; - return ( + // $FlowExpectedError[prop-missing] props are passed implicitly which causes a flow error + const graphTooltip = (); + return ( + <> +

{yAxisLabel}

{ dataKey="name" height={50} label={{ - value: xAxisLabel, - position: 'insideBottom', - fontSize: graphVars.fontSize, - fill: graphVars.axisTextColor + value: xAxisLabel, + position: 'insideBottom', + fontSize: graphVars.fontSize, + fill: graphVars.axisTextColor }} + tickLine={false} /> { fontSize: graphVars.fontSize, lineHeight: graphVars.lineHeight }} - > - + tickLine={false} + /> { maxBarSize={graphVars.barWidth} dataKey="primary" stackId="a" - fill={graphVars.barPrimaryColor} + fill="#C4CAD7" /> - ); + + ); } }; \ No newline at end of file From 866d74faed49404cd7d06c483c2574b8fd754320 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Thu, 12 May 2022 12:46:40 +0200 Subject: [PATCH 09/15] Add rewards label to the chart --- .../staking/dashboard-revamp/RewardsGraph.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js index cc9cffe016..46b2d04d18 100644 --- a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js +++ b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js @@ -4,6 +4,7 @@ import type { Node } from 'react'; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; import { readCssVar } from '../../../../styles/utils'; import { Box } from '@mui/system'; +import { Typography } from '@mui/material'; const graphVars = { axisTickColor: readCssVar('--yoroi-dashboard-graph-axis-tick-color'), @@ -78,12 +79,21 @@ export default class RewardGraph extends Component { const graphTooltip = (); return ( <> -

{yAxisLabel}

- + + {yAxisLabel} + + From 5e6039dc994a651b3afe5b4fd4445094ec2720f3 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Thu, 12 May 2022 12:56:01 +0200 Subject: [PATCH 10/15] Update bars style --- .../components/wallet/staking/dashboard-revamp/RewardsGraph.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js index 46b2d04d18..29b5f40851 100644 --- a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js +++ b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js @@ -130,6 +130,7 @@ export default class RewardGraph extends Component { Date: Thu, 12 May 2022 13:41:04 +0200 Subject: [PATCH 11/15] code typo --- .../components/wallet/staking/dashboard-revamp/RewardsGraph.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js index 29b5f40851..a905e80e85 100644 --- a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js +++ b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js @@ -24,7 +24,7 @@ type Props = {| yAxisLabel: string, primaryBarLabel: string, hideYAxis: boolean, -|} +|}; export default class RewardGraph extends Component { render(): Node { From 6580da436154a5391a14383273618cba00b6e0d7 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Thu, 12 May 2022 13:50:25 +0200 Subject: [PATCH 12/15] Flow --- .../wallet/staking/dashboard-revamp/RewardsGraph.js | 1 + .../wallet/staking/dashboard-revamp/StakingTabs.js | 6 +++++- .../app/containers/wallet/staking/StakingPage.js | 1 + .../app/stores/stateless/sidebarCategories.js | 2 +- packages/yoroi-extension/app/utils/graph.js | 2 +- 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js index a905e80e85..b74a43e5b1 100644 --- a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js +++ b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js @@ -5,6 +5,7 @@ import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContaine import { readCssVar } from '../../../../styles/utils'; import { Box } from '@mui/system'; import { Typography } from '@mui/material'; +import type { GraphItems } from '../dashboard/GraphWrapper'; const graphVars = { axisTickColor: readCssVar('--yoroi-dashboard-graph-axis-tick-color'), diff --git a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js index 619ce68f41..6d4e5c3a9d 100644 --- a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js +++ b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/StakingTabs.js @@ -13,11 +13,15 @@ import type { $npm$ReactIntl$IntlShape } from 'react-intl'; import globalMessages from '../../../../i18n/global-messages'; import type { PoolData } from '../../../../containers/wallet/staking/SeizaFetcher'; import RewardGraph from './RewardsGraph'; +import type { GraphData } from '../dashboard/StakingDashboard'; type Props = {| delegatedPool: PoolData, +undelegate: void | (void => Promise), + +epochLength: ?number, + +graphData: GraphData |}; + type Intl = {| intl: $npm$ReactIntl$IntlShape, |}; @@ -80,7 +84,7 @@ function StakingTabs({ xAxisLabel={getEpochLengthLabel()} yAxisLabel={intl.formatMessage(globalMessages.rewardsLabel)} primaryBarLabel={intl.formatMessage(globalMessages.rewardsLabel)} - data={items.perEpochRewards} + data={items ? items.perEpochRewards : []} hideYAxis={hideYAxis} /> diff --git a/packages/yoroi-extension/app/containers/wallet/staking/StakingPage.js b/packages/yoroi-extension/app/containers/wallet/staking/StakingPage.js index b6cb40e9be..64fd1ceb1d 100644 --- a/packages/yoroi-extension/app/containers/wallet/staking/StakingPage.js +++ b/packages/yoroi-extension/app/containers/wallet/staking/StakingPage.js @@ -52,6 +52,7 @@ import UndelegateDialog from '../../../components/wallet/staking/dashboard/Undel import type { PoolRequest } from '../../../api/jormungandr/lib/storage/bridge/delegationUtils'; import { generateGraphData } from '../../../utils/graph'; import { ApiOptions, getApiForNetwork, } from '../../../api/common/utils'; +import type { CurrentTimeRequests, TimeCalcRequests } from '../../../stores/base/BaseCardanoTimeStore'; export type GeneratedData = typeof StakingPage.prototype.generated; // populated by ConfigWebpackPlugin diff --git a/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js b/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js index 6355d0cdf1..67ca287593 100644 --- a/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js +++ b/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js @@ -137,7 +137,7 @@ export const allCategoriesRevamp: Array = [ icon: stakingIcon, label: globalMessages.sidebarStaking, isVisible: ({ selected }) => ( - selected && isCardanoHaskell(selected.getParent().getNetworkInfo()) + !!selected && isCardanoHaskell(selected.getParent().getNetworkInfo()) ), }, { diff --git a/packages/yoroi-extension/app/utils/graph.js b/packages/yoroi-extension/app/utils/graph.js index 0375095841..acddfc2a73 100644 --- a/packages/yoroi-extension/app/utils/graph.js +++ b/packages/yoroi-extension/app/utils/graph.js @@ -55,7 +55,7 @@ const generateRewardGraphData: ({| return request.currentEpoch + 1; } throw new Error( - `${nameof(this._generateRewardGraphData)} can't compute endEpoch for rewards` + `${nameof(generateRewardGraphData)} can't compute endEpoch for rewards` ); })(); From 52ebeac72b435c221a5df75012f4c0a377ca8f2b Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Fri, 13 May 2022 14:48:56 +0200 Subject: [PATCH 13/15] Remove code comments --- packages/yoroi-extension/app/utils/graph.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/yoroi-extension/app/utils/graph.js b/packages/yoroi-extension/app/utils/graph.js index acddfc2a73..99394dda91 100644 --- a/packages/yoroi-extension/app/utils/graph.js +++ b/packages/yoroi-extension/app/utils/graph.js @@ -60,10 +60,6 @@ const generateRewardGraphData: ({| })(); const getMiniPoolInfo = (poolHash: string) => { - // const meta = this.generated.stores.delegation.getLocalPoolInfo( - // request.publicDeriver.getParent().getNetworkInfo(), - // poolHash - // ); const meta = request.getLocalPoolInfo( request.publicDeriver.getParent().getNetworkInfo(), poolHash @@ -75,9 +71,6 @@ const generateRewardGraphData: ({| } const getNormalized = (tokenEntry) => { - // const tokenRow = this.generated.stores.tokenInfoStore.tokenInfo - // .get(tokenEntry.networkId.toString()) - // ?.get(tokenEntry.identifier); const tokenRow = request.tokenInfo .get(tokenEntry.networkId.toString()) ?.get(tokenEntry.identifier); From 9c48b9bcabba403b32d93a1a449ace44610f1345 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Fri, 13 May 2022 15:01:25 +0200 Subject: [PATCH 14/15] Use hex colors --- .../wallet/staking/dashboard-revamp/RewardsGraph.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js index b74a43e5b1..f3c22c9fb2 100644 --- a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js +++ b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js @@ -11,8 +11,6 @@ const graphVars = { axisTickColor: readCssVar('--yoroi-dashboard-graph-axis-tick-color'), axisTextColor: readCssVar('--yoroi-dashboard-graph-axis-text-color'), barWidth: readCssVar('--yoroi-dashboard-graph-bar-width'), - barHoverBgColor: readCssVar('--yoroi-dashboard-graph-bar-hover-background-color'), - barPrimaryColor: readCssVar('--yoroi-palette-gray-300'), fontSize: '0.75rem', lineHeight: 14, }; @@ -125,7 +123,7 @@ export default class RewardGraph extends Component { /> Date: Fri, 13 May 2022 15:17:20 +0200 Subject: [PATCH 15/15] Add Custom axis --- .../wallet/staking/dashboard-revamp/RewardsGraph.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js index f3c22c9fb2..6cc87ea28a 100644 --- a/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js +++ b/packages/yoroi-extension/app/components/wallet/staking/dashboard-revamp/RewardsGraph.js @@ -8,8 +8,6 @@ import { Typography } from '@mui/material'; import type { GraphItems } from '../dashboard/GraphWrapper'; const graphVars = { - axisTickColor: readCssVar('--yoroi-dashboard-graph-axis-tick-color'), - axisTextColor: readCssVar('--yoroi-dashboard-graph-axis-text-color'), barWidth: readCssVar('--yoroi-dashboard-graph-bar-width'), fontSize: '0.75rem', lineHeight: 14, @@ -98,7 +96,7 @@ export default class RewardGraph extends Component { { value: xAxisLabel, position: 'insideBottom', fontSize: graphVars.fontSize, - fill: graphVars.axisTextColor + fill: '#A7AFC0' }} + stroke="#A7AFC0" tickLine={false} />