From 007844f4e993d2dffcfb8874d6cda3f2a871066d Mon Sep 17 00:00:00 2001 From: Renan Ferreira Date: Tue, 15 Mar 2022 16:49:16 -0300 Subject: [PATCH 1/6] [DDW-1012] Stake pool list performance --- package.json | 2 + .../stake-pools/StakePoolsTable.hooks.tsx | 324 +++++++++++ .../stake-pools/StakePoolsTable.messages.tsx | 110 ++++ .../staking/stake-pools/StakePoolsTable.scss | 39 +- .../staking/stake-pools/StakePoolsTable.tsx | 541 ++++++------------ .../stake-pools/StakePoolsTableBody.tsx | 134 ----- .../stake-pools/StakePoolsTableHeader.tsx | 51 +- .../staking/widgets/PoolPopOver.tsx | 1 + .../app/i18n/locales/defaultMessages.json | 116 ++-- source/renderer/app/types/i18nTypes.ts | 1 + .../staking/StakePoolsTable.stories.tsx | 136 ++--- storybook/webpack.config.js | 4 +- yarn.lock | 10 + 13 files changed, 807 insertions(+), 662 deletions(-) create mode 100644 source/renderer/app/components/staking/stake-pools/StakePoolsTable.hooks.tsx create mode 100644 source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx delete mode 100644 source/renderer/app/components/staking/stake-pools/StakePoolsTableBody.tsx diff --git a/package.json b/package.json index 8c35df26dd..30d27dd123 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,7 @@ "@types/qrcode.react": "^1.0.2", "@types/react": "^17.0.38", "@types/react-svg-inline": "^2.1.3", + "@types/react-table": "^7.7.9", "@typescript-eslint/eslint-plugin": "^5.10.1", "@typescript-eslint/parser": "^5.10.1", "asar": "2.1.0", @@ -260,6 +261,7 @@ "react-router": "5.2.0", "react-router-dom": "5.2.0", "react-svg-inline": "2.1.1", + "react-table": "7.7.0", "react-virtualized": "9.22.3", "recharts": "1.8.5", "rimraf": "3.0.2", diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.hooks.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.hooks.tsx new file mode 100644 index 0000000000..af6376a216 --- /dev/null +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.hooks.tsx @@ -0,0 +1,324 @@ +import React, { useMemo } from 'react'; +import { orderBy } from 'lodash'; +import classNames from 'classnames'; +import BigNumber from 'bignumber.js'; +import moment from 'moment'; +import { FormattedHTMLMessage } from 'react-intl'; +import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { PoolPopOver } from '../widgets/PoolPopOver'; +import { Intl } from '../../../types/i18nTypes'; + +import styles from './StakePoolsTable.scss'; +import StakePool from '../../../domains/StakePool'; +import { getColorFromRange, getSaturationColor } from '../../../utils/colors'; +import { + formattedWalletAmount, + toFixedUserFormat, +} from '../../../utils/formatters'; +import { messages } from './StakePoolsTable.messages'; + +export const defaultTableOrdering = { + ranking: 'asc', + ticker: 'asc', + saturation: 'asc', + cost: 'asc', + profitMargin: 'asc', + producedBlocks: 'desc', + nonMyopicMemberRewards: 'desc', + pledge: 'asc', + retiring: 'asc', +}; + +interface UseSortedStakePoolListArgs { + stakePoolList: StakePool[]; + sortBy: string; + order: 'asc' | 'desc'; +} + +export const useSortedStakePoolList = ({ + stakePoolList, + sortBy, + order, +}: UseSortedStakePoolListArgs) => + useMemo( + () => + orderBy( + stakePoolList.map((stakePool) => { + let calculatedPledge; + let calculatedCost; + let formattedTicker; + + if (sortBy === 'ticker') { + formattedTicker = stakePool.ticker + .replace(/[^\w\s]/gi, '') + .toLowerCase(); + } + + if (sortBy === 'pledge') { + const formattedPledgeValue = stakePool.pledge.toFixed(2); + calculatedPledge = Number( + parseFloat(formattedPledgeValue).toFixed(2) + ); + } + + if (sortBy === 'cost') { + const formattedCostValue = stakePool.cost.toFixed(2); + calculatedCost = Number(parseFloat(formattedCostValue).toFixed(2)); + } + + return { + ...stakePool, + calculatedPledge, + calculatedCost, + formattedTicker, + }; + }), + ['formattedTicker', 'calculatedPledge', 'calculatedCost', sortBy], + [order, order, order, order] + ), + [stakePoolList, order, sortBy] + ); + +type UseCreateColumnsArgs = { + currentTheme: string; + showWithSelectButton?: boolean; + onSelect?: (...args: Array) => any; + containerClassName: string; + numberOfRankedStakePools: number; + selectedPoolId?: string; + onOpenExternalLink: (...args: Array) => any; + intl: Intl; +}; + +export const useCreateColumns = ({ + numberOfRankedStakePools, + intl, + currentTheme, + onOpenExternalLink, + onSelect, + selectedPoolId, + containerClassName, + showWithSelectButton, +}: UseCreateColumnsArgs) => + useMemo( + () => [ + { + id: 'ranking', + Header: ( + + + + } + > + {intl.formatMessage(messages.tableHeaderRank)} + + ), + accessor: 'ranking', + Cell: ({ row }) => { + const { potentialRewards, ranking }: StakePool = row.original; + const memberRewards = new BigNumber(potentialRewards); + + return ( + <> + {!memberRewards.isZero() ? ( + ranking + ) : ( + <> + {numberOfRankedStakePools + 1} + * + + )} + + ); + }, + }, + + { + id: 'ticker', + Header: intl.formatMessage(messages.tableHeaderTicker), + accessor: 'ticker', + Cell: ({ row }) => { + const stakePool: StakePool = row.original; + const color = getColorFromRange( + stakePool.ranking, + numberOfRankedStakePools + ); + + return ( + + + {stakePool.ticker} + + + ); + }, + }, + { + id: 'saturation', + Header: ( + + {intl.formatMessage(messages.tableHeaderSaturation)} + + ), + accessor: 'saturation', + Cell: ({ row }) => { + const { saturation }: StakePool = row.original; + const progressBarContentClassnames = classNames([ + styles.progressBarContent, + styles[getSaturationColor(saturation)], + ]); + + return ( +
+
+
+
+
+
+
+ {`${toFixedUserFormat(saturation, 2)}%`} +
+
+ ); + }, + }, + { + id: 'cost', + Header: ( + + {intl.formatMessage(messages.tableHeaderCost)} + + ), + accessor: 'cost', + Cell: ({ value }) => { + const cost = new BigNumber(value); + const costValue = formattedWalletAmount(cost, false, false); + + return costValue; + }, + }, + { + id: 'profitMargin', + Header: ( + + {intl.formatMessage(messages.tableHeaderMargin)} + + ), + accessor: 'profitMargin', + Cell: ({ value }) => { + return `${toFixedUserFormat(value, 2)}%`; + }, + }, + { + id: 'producedBlocks', + Header: ( + + {intl.formatMessage(messages.tableHeaderProducedBlocks)} + + ), + accessor: 'producedBlocks', + Cell: ({ value }) => { + return toFixedUserFormat(value, 0); + }, + }, + { + id: 'nonMyopicMemberRewards', + Header: ( + + {intl.formatMessage(messages.tableHeaderPotentialRewards)} + + ), + accessor: 'nonMyopicMemberRewards', + Cell: ({ row }) => { + const stakePool: StakePool = row.original; + const memberRewards = new BigNumber(stakePool.potentialRewards); + const potentialRewards = formattedWalletAmount(memberRewards); + return potentialRewards; + }, + }, + { + id: 'pledge', + Header: ( + + {intl.formatMessage(messages.tableHeaderPledge)} + + ), + accessor: 'pledge', + Cell: ({ row }) => { + const stakePool: StakePool = row.original; + const pledge = new BigNumber(stakePool.pledge); + const pledgeValue = formattedWalletAmount(pledge, false, false); + return pledgeValue; + }, + }, + { + id: 'retiring', + Header: intl.formatMessage(messages.tableHeaderRetiring), + accessor: 'retiring', + Cell: ({ row }) => { + const stakePool: StakePool = row.original; + const retirement = + stakePool.retiring && + moment(stakePool.retiring).locale(intl.locale).fromNow(true); + + return ( + <> + {retirement ? ( + {retirement} + ) : ( + '-' + )} + + ); + }, + }, + ], + [] + ); diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx new file mode 100644 index 0000000000..07e1c7ae23 --- /dev/null +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx @@ -0,0 +1,110 @@ +import { defineMessages } from 'react-intl'; + +export const messages = defineMessages({ + tableHeaderRank: { + id: 'staking.stakePools.tableHeader.rank', + defaultMessage: '!!!Rank', + description: 'Table header "Rank" label on stake pools list view page', + }, + tableHeaderRankTooltip: { + id: 'staking.stakePools.tooltip.rankingTooltip', + defaultMessage: + '!!!

A hierarchical ranking based on the potential rewards you will earn if you delegate the intended amount of stake to this pool, assuming that it reaches saturation.

*Stake pools with the potential rewards estimated at zero have the same ranking. Please set the stake slider to a higher value for more pools to get potential rewards estimated at more than zero.

', + description: '"Rank" tooltip for the Stake Pools Table.', + }, + tableHeaderTicker: { + id: 'staking.stakePools.tableHeader.ticker', + defaultMessage: '!!!Ticker', + description: 'Table header "Ticker" label on stake pools list view page', + }, + tableHeaderSaturation: { + id: 'staking.stakePools.tableHeader.saturation', + defaultMessage: '!!!Saturation', + description: + 'Table header "Saturation" label on stake pools list view page', + }, + tableHeaderSaturationTooltip: { + id: 'staking.stakePools.tooltip.saturationTooltip', + defaultMessage: + '!!!Saturation measures the stake in the pool and indicates the point at which rewards stop increasing with increases in stake. This capping mechanism encourages decentralization by discouraging users from delegating to oversaturated stake pools.', + description: '"Saturation" tooltip for the Stake Pools Table.', + }, + tableHeaderPerformance: { + id: 'staking.stakePools.tableHeader.performance', + defaultMessage: '!!!Performance', + description: + 'Table header "Performance" label on stake pools list view page', + }, + tableHeaderUptime: { + id: 'staking.stakePools.tableHeader.uptime', + defaultMessage: '!!!Uptime (days)', + description: 'Table header "Uptime" label on stake pools list view page', + }, + tableHeaderMargin: { + id: 'staking.stakePools.tableHeader.margin', + defaultMessage: '!!!Margin', + description: 'Table header "Margin" label on stake pools list view page', + }, + tableHeaderMarginTooltip: { + id: 'staking.stakePools.tooltip.profitMarginTooltip', + defaultMessage: + "!!!The pool's profit, defined as the rewards percentage kept by the pool from the stake that was delegated to it.", + description: '"Pool margin" tooltip for the Stake Pools Table.', + }, + tableHeaderRoi: { + id: 'staking.stakePools.tableHeader.roi', + defaultMessage: '!!!Roi', + description: 'Table header "Roi" label on stake pools list view page', + }, + tableHeaderCost: { + id: 'staking.stakePools.tableHeader.cost', + defaultMessage: '!!!Cost (ADA)', + description: 'Table header "Cost" label on stake pools list view page', + }, + tableHeaderCostTooltip: { + id: 'staking.stakePools.tooltip.costPerEpochTooltip', + defaultMessage: + '!!!Fixed operational costs that the stake pool retains from any rewards earned during each epoch.', + description: '"Cost per epoch" tooltip for the Stake Pools Table.', + }, + tableHeaderProducedBlocks: { + id: 'staking.stakePools.tableHeader.producedBlocks', + defaultMessage: '!!!Produced Blocks', + description: + 'Table header "Produced Blocks" label on stake pools list view page', + }, + tableHeaderProducedBlocksTooltip: { + id: 'staking.stakePools.tooltip.producedBlocksTooltip', + defaultMessage: + '!!!The total number of blocks the stake pool has produced.', + description: '"Blocks" tooltip for the Stake Pools Table.', + }, + tableHeaderPotentialRewards: { + id: 'staking.stakePools.tableHeader.potentialRewards', + defaultMessage: '!!!Potential rewards', + description: + 'Table header "Potential rewards" label on stake pools list view page', + }, + tableHeaderPotentialRewardsTooltip: { + id: 'staking.stakePools.tooltip.potentialRewardsTooltip', + defaultMessage: + "!!!An estimation of the potential rewards you will earn per epoch if you delegate the intended amount of stake. The system looks at the pool's parameters and historical performance data to calculate potential rewards, assuming that the pool reaches optimal saturation.", + description: '"Rewards" tooltip for the Stake Pools Table.', + }, + tableHeaderPledge: { + id: 'staking.stakePools.tableHeader.pledge', + defaultMessage: '!!!Pledge (ADA)', + description: 'Table header "Pledge" label on stake pools list view page', + }, + tableHeaderPledgeTooltip: { + id: 'staking.stakePools.tooltip.pledgeTooltip', + defaultMessage: + '!!!The amount of stake that a pool operator contributes to a pool. Pools with higher pledge amounts earn more rewards for themselves and their delegators. Pools that do not honor their pledge earn zero rewards and accrue low ranking.', + description: '"Pledge" tooltip for the Stake Pools Table.', + }, + tableHeaderRetiring: { + id: 'staking.stakePools.tableHeader.retiring', + defaultMessage: '!!!Retiring in', + description: 'Table header "Retiring" label on stake pools list view page', + }, +}); diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.scss b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.scss index c87f455cea..9f0a352d15 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.scss +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.scss @@ -92,12 +92,12 @@ } } - table { + .table { border-style: hidden; user-select: text; width: 100%; - thead { + .thead { background: var(--theme-bordered-box-background-color); border-radius: 4px 4px 0 0; box-shadow: 0 10px 10px -10px rgba(0, 0, 0, 0.2); @@ -108,7 +108,7 @@ top: 0; z-index: $sticky-header-z-index; - tr { + .tr { border: 0; display: flex; justify-content: space-between; @@ -131,8 +131,8 @@ } } - tbody { - tr { + .tbody { + .tr { display: flex; justify-content: space-between; width: 100%; @@ -145,11 +145,11 @@ } } - tr { + .tr { border-bottom: 1px solid var(--theme-staking-table-border-color); } - th { + .th { color: var(--theme-staking-font-color-regular); cursor: pointer; font-family: var(--font-semibold); @@ -207,7 +207,7 @@ } } - td { + .td { color: var(--theme-staking-font-color-regular); font-family: var(--font-regular); font-size: 12px; @@ -266,6 +266,7 @@ display: flex; flex-direction: row; height: 100%; + justify-content: center; } %saturationContainer { @@ -346,8 +347,8 @@ } } - th, - td { + .th, + .td { &:nth-child(1), &:nth-child(3), &:nth-child(4), @@ -363,39 +364,39 @@ // Rank &:nth-child(1) { - width: 7%; + width: 7% !important; } // Ticker &:nth-child(2) { - width: 9%; + width: 9% !important; } // Saturation &:nth-child(3) { - width: 11%; + width: 11% !important; } // Cost &:nth-child(4) { - width: 10%; + width: 10% !important; } // Margin &:nth-child(5) { - width: 8%; + width: 8% !important; } // Produced Blocks &:nth-child(6) { - width: 15%; + width: 15% !important; } // Potential Rewards &:nth-child(7) { - width: 16%; + width: 16% !important; } // Pledge &:nth-child(8) { - width: 12%; + width: 12% !important; } // Retiring in &:nth-child(9) { - width: 12%; + width: 12% !important; } } } diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx index ca95bf4e2a..b6c9efc0cd 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx @@ -1,124 +1,24 @@ -import React, { Component } from 'react'; +import React, { useEffect, useState, useCallback } from 'react'; import { observer } from 'mobx-react'; -import { orderBy } from 'lodash'; import classNames from 'classnames'; -import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; -import { PopOver } from 'react-polymorph/lib/components/PopOver'; +import { injectIntl } from 'react-intl'; +import { useTable, useFlexLayout } from 'react-table'; +import List from 'react-virtualized/dist/commonjs/List'; +import WindowScroller from 'react-virtualized/dist/commonjs/WindowScroller'; +import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer'; +import { Intl } from '../../../types/i18nTypes'; + +import { StakingPageScrollContext } from '../layouts/StakingWithNavigation'; import styles from './StakePoolsTable.scss'; import StakePool from '../../../domains/StakePool'; import LoadingSpinner from '../../widgets/LoadingSpinner'; import BorderedBox from '../../widgets/BorderedBox'; import { StakePoolsTableHeader } from './StakePoolsTableHeader'; -import { StakePoolsTableBody } from './StakePoolsTableBody'; +import { + useSortedStakePoolList, + useCreateColumns, +} from './StakePoolsTable.hooks'; -const messages = defineMessages({ - tableHeaderRank: { - id: 'staking.stakePools.tableHeader.rank', - defaultMessage: '!!!Rank', - description: 'Table header "Rank" label on stake pools list view page', - }, - tableHeaderRankTooltip: { - id: 'staking.stakePools.tooltip.rankingTooltip', - defaultMessage: - '!!!

A hierarchical ranking based on the potential rewards you will earn if you delegate the intended amount of stake to this pool, assuming that it reaches saturation.

*Stake pools with the potential rewards estimated at zero have the same ranking. Please set the stake slider to a higher value for more pools to get potential rewards estimated at more than zero.

', - description: '"Rank" tooltip for the Stake Pools Table.', - }, - tableHeaderTicker: { - id: 'staking.stakePools.tableHeader.ticker', - defaultMessage: '!!!Ticker', - description: 'Table header "Ticker" label on stake pools list view page', - }, - tableHeaderSaturation: { - id: 'staking.stakePools.tableHeader.saturation', - defaultMessage: '!!!Saturation', - description: - 'Table header "Saturation" label on stake pools list view page', - }, - tableHeaderSaturationTooltip: { - id: 'staking.stakePools.tooltip.saturationTooltip', - defaultMessage: - '!!!Saturation measures the stake in the pool and indicates the point at which rewards stop increasing with increases in stake. This capping mechanism encourages decentralization by discouraging users from delegating to oversaturated stake pools.', - description: '"Saturation" tooltip for the Stake Pools Table.', - }, - tableHeaderPerformance: { - id: 'staking.stakePools.tableHeader.performance', - defaultMessage: '!!!Performance', - description: - 'Table header "Performance" label on stake pools list view page', - }, - tableHeaderUptime: { - id: 'staking.stakePools.tableHeader.uptime', - defaultMessage: '!!!Uptime (days)', - description: 'Table header "Uptime" label on stake pools list view page', - }, - tableHeaderMargin: { - id: 'staking.stakePools.tableHeader.margin', - defaultMessage: '!!!Margin', - description: 'Table header "Margin" label on stake pools list view page', - }, - tableHeaderMarginTooltip: { - id: 'staking.stakePools.tooltip.profitMarginTooltip', - defaultMessage: - "!!!The pool's profit, defined as the rewards percentage kept by the pool from the stake that was delegated to it.", - description: '"Pool margin" tooltip for the Stake Pools Table.', - }, - tableHeaderRoi: { - id: 'staking.stakePools.tableHeader.roi', - defaultMessage: '!!!Roi', - description: 'Table header "Roi" label on stake pools list view page', - }, - tableHeaderCost: { - id: 'staking.stakePools.tableHeader.cost', - defaultMessage: '!!!Cost (ADA)', - description: 'Table header "Cost" label on stake pools list view page', - }, - tableHeaderCostTooltip: { - id: 'staking.stakePools.tooltip.costPerEpochTooltip', - defaultMessage: - '!!!Fixed operational costs that the stake pool retains from any rewards earned during each epoch.', - description: '"Cost per epoch" tooltip for the Stake Pools Table.', - }, - tableHeaderProducedBlocks: { - id: 'staking.stakePools.tableHeader.producedBlocks', - defaultMessage: '!!!Produced Blocks', - description: - 'Table header "Produced Blocks" label on stake pools list view page', - }, - tableHeaderProducedBlocksTooltip: { - id: 'staking.stakePools.tooltip.producedBlocksTooltip', - defaultMessage: - '!!!The total number of blocks the stake pool has produced.', - description: '"Blocks" tooltip for the Stake Pools Table.', - }, - tableHeaderPotentialRewards: { - id: 'staking.stakePools.tableHeader.potentialRewards', - defaultMessage: '!!!Potential rewards', - description: - 'Table header "Potential rewards" label on stake pools list view page', - }, - tableHeaderPotentialRewardsTooltip: { - id: 'staking.stakePools.tooltip.potentialRewardsTooltip', - defaultMessage: - "!!!An estimation of the potential rewards you will earn per epoch if you delegate the intended amount of stake. The system looks at the pool's parameters and historical performance data to calculate potential rewards, assuming that the pool reaches optimal saturation.", - description: '"Rewards" tooltip for the Stake Pools Table.', - }, - tableHeaderPledge: { - id: 'staking.stakePools.tableHeader.pledge', - defaultMessage: '!!!Pledge (ADA)', - description: 'Table header "Pledge" label on stake pools list view page', - }, - tableHeaderPledgeTooltip: { - id: 'staking.stakePools.tooltip.pledgeTooltip', - defaultMessage: - '!!!The amount of stake that a pool operator contributes to a pool. Pools with higher pledge amounts earn more rewards for themselves and their delegators. Pools that do not honor their pledge earn zero rewards and accrue low ranking.', - description: '"Pledge" tooltip for the Stake Pools Table.', - }, - tableHeaderRetiring: { - id: 'staking.stakePools.tableHeader.retiring', - defaultMessage: '!!!Retiring in', - description: 'Table header "Retiring" label on stake pools list view page', - }, -}); export const defaultTableOrdering = { ranking: 'asc', ticker: 'asc', @@ -142,277 +42,206 @@ type Props = { onSelect?: (...args: Array) => any; containerClassName: string; numberOfRankedStakePools: number; - selectedPoolId?: number | null | undefined; + selectedPoolId?: string; onOpenExternalLink: (...args: Array) => any; currentLocale: string; onTableHeaderMouseEnter: (...args: Array) => any; onTableHeaderMouseLeave: (...args: Array) => any; + intl: Intl; }; type State = { isPreloading: boolean; - stakePoolsOrder: string; + stakePoolsOrder: 'asc' | 'desc'; stakePoolsSortBy: string; }; -const initialState = { +const initialState: State = { isPreloading: true, stakePoolsOrder: 'asc', stakePoolsSortBy: 'ranking', }; -@observer -class StakePoolsTable extends Component { - static contextTypes = { - intl: intlShape.isRequired, - }; - static defaultProps = { - isListActive: true, - showWithSelectButton: false, - }; - state = { ...initialState }; - scrollableDomElement: HTMLElement | null | undefined = null; - searchInput: HTMLElement | null | undefined = null; - _isMounted = false; +function StakePoolsTableComponent({ + stakePoolsList = [], + listName, + onTableHeaderMouseEnter, + onTableHeaderMouseLeave, + showWithSelectButton = false, + intl, + numberOfRankedStakePools, + currentTheme, + onOpenExternalLink, + containerClassName, + onSelect, + selectedPoolId, +}: Props) { + const [state, setState] = useState(initialState); - componentDidMount() { - this._isMounted = true; - setTimeout(() => { - if (this._isMounted) - this.setState({ - isPreloading: false, - }); - }, 0); - } + useEffect(() => { + setState((s) => ({ ...s, isPreloading: false })); + }, []); - componentWillUnmount() { - this._isMounted = false; - this.scrollableDomElement = document.querySelector( - `.${this.props.containerClassName}` - ); - } + const handleSort = useCallback( + (newSortBy: string) => { + const { stakePoolsOrder, stakePoolsSortBy } = state; + let newOrder = defaultTableOrdering[newSortBy]; + + if (newSortBy === stakePoolsSortBy) { + newOrder = stakePoolsOrder === 'asc' ? 'desc' : 'asc'; + } + + setState((s) => ({ + ...s, + stakePoolsOrder: newOrder, + stakePoolsSortBy: newSortBy, + })); + }, + [state] + ); - handleSort = (newSortBy: string) => { - const { stakePoolsOrder, stakePoolsSortBy } = this.state; - let newOrder = defaultTableOrdering[newSortBy]; + const { isPreloading, stakePoolsSortBy, stakePoolsOrder } = state; - if (newSortBy === stakePoolsSortBy) { - newOrder = stakePoolsOrder === 'asc' ? 'desc' : 'asc'; - } + const sortedStakePoolList = useSortedStakePoolList({ + order: stakePoolsOrder, + sortBy: stakePoolsSortBy, + stakePoolList: stakePoolsList, + }); - this.setState({ - stakePoolsOrder: newOrder, - stakePoolsSortBy: newSortBy, - }); - }; + const columns = useCreateColumns({ + containerClassName, + currentTheme, + intl, + numberOfRankedStakePools, + onOpenExternalLink, + onSelect, + selectedPoolId, + showWithSelectButton, + }); - render() { - const { - currentTheme, - onOpenExternalLink, - showWithSelectButton, - stakePoolsList, - containerClassName, - numberOfRankedStakePools, - listName, - onSelect, - selectedPoolId, - currentLocale, - onTableHeaderMouseEnter, - onTableHeaderMouseLeave, - } = this.props; - const { isPreloading, stakePoolsSortBy, stakePoolsOrder } = this.state; - const { intl } = this.context; - const componentClasses = classNames([styles.component, listName]); - if (stakePoolsList.length > PRELOADER_THRESHOLD && isPreloading) + const { + getTableProps, + getTableBodyProps, + headerGroups, + rows, + prepareRow, + } = useTable( + { + columns, + data: sortedStakePoolList, + }, + useFlexLayout + ); + + const RenderRow = useCallback( + ({ index, style }) => { + const row = rows[index]; + prepareRow(row); return ( -
- +
+ {row.cells.map((cell) => { + return ( + /* eslint-disable-next-line react/jsx-key */ +
+ {cell.render('Cell')} +
+ ); + })}
); - const sortedStakePoolList = orderBy( - stakePoolsList.map((stakePool) => { - let calculatedPledge; - let calculatedCost; - let formattedTicker; + }, + [prepareRow, rows] + ); - if (stakePoolsSortBy === 'ticker') { - formattedTicker = stakePool.ticker - .replace(/[^\w\s]/gi, '') - .toLowerCase(); - } + const componentClasses = classNames([styles.component, listName]); - if (stakePoolsSortBy === 'pledge') { - const formattedPledgeValue = stakePool.pledge.toFixed(2); - calculatedPledge = Number( - parseFloat(formattedPledgeValue).toFixed(2) - ); - } + if (stakePoolsList.length > PRELOADER_THRESHOLD && isPreloading) + return ( +
+ +
+ ); - if (stakePoolsSortBy === 'cost') { - const formattedCostValue = stakePool.cost.toFixed(2); - calculatedCost = Number(parseFloat(formattedCostValue).toFixed(2)); - } + return ( + + {(stakePoolsScrollContext) => ( + + {({ + height, + isScrolling, + registerChild, + onChildScroll, + scrollTop, + }) => ( +
+
+ {sortedStakePoolList.length > 0 && ( + +
+
+ {headerGroups.map((headerGroup) => ( + /* eslint-disable-next-line react/jsx-key */ +
+ {headerGroup.headers.map((column) => ( + + {column.render('Header')} + + ))} +
+ ))} +
- return { - ...stakePool, - calculatedPledge, - calculatedCost, - formattedTicker, - }; - }), - [ - 'formattedTicker', - 'calculatedPledge', - 'calculatedCost', - stakePoolsSortBy, - ], - // @ts-ignore ts-migrate(2769) FIXME: No overload matches this call. - [stakePoolsOrder, stakePoolsOrder, stakePoolsOrder, stakePoolsOrder] - ); - const availableTableHeaders = [ - { - name: 'ranking', - title: ( - - + + {({ width }) => ( +
+ +
+ )} +
+
+
+ )}
- } - > - {intl.formatMessage(messages.tableHeaderRank)} - - ), - }, - { - name: 'ticker', - title: intl.formatMessage(messages.tableHeaderTicker), - }, - { - name: 'saturation', - title: ( - - {intl.formatMessage(messages.tableHeaderSaturation)} - - ), - }, - { - name: 'cost', - title: ( - - {intl.formatMessage(messages.tableHeaderCost)} - - ), - }, - { - name: 'profitMargin', - title: ( - - {intl.formatMessage(messages.tableHeaderMargin)} - - ), - }, - { - name: 'producedBlocks', - title: ( - - {intl.formatMessage(messages.tableHeaderProducedBlocks)} - - ), - }, - { - name: 'nonMyopicMemberRewards', - title: ( - - {intl.formatMessage(messages.tableHeaderPotentialRewards)} - - ), - }, - { - name: 'pledge', - title: ( - - {intl.formatMessage(messages.tableHeaderPledge)} - - ), - }, - { - name: 'retiring', - title: intl.formatMessage(messages.tableHeaderRetiring), - }, - ]; - return ( -
-
- {sortedStakePoolList.length > 0 && ( - - - - - - - - - - -
-
+
)} -
-
- ); - } +
+ )} +
+ ); } -export { StakePoolsTable }; +export const StakePoolsTable = injectIntl(observer(StakePoolsTableComponent)); diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTableBody.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsTableBody.tsx deleted file mode 100644 index 69d1b24e77..0000000000 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsTableBody.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import React, { Component } from 'react'; -import { observer } from 'mobx-react'; -import { intlShape } from 'react-intl'; -import { get, map } from 'lodash'; -import BigNumber from 'bignumber.js'; -import moment from 'moment'; -import classNames from 'classnames'; -import { - formattedWalletAmount, - toFixedUserFormat, -} from '../../../utils/formatters'; -import { PoolPopOver } from '../widgets/PoolPopOver'; -import styles from './StakePoolsTable.scss'; -import { getColorFromRange, getSaturationColor } from '../../../utils/colors'; -import StakePool from '../../../domains/StakePool'; - -type TableBodyProps = { - sortedStakePoolList: StakePool; - numberOfRankedStakePools: number; - currentTheme: string; - onOpenExternalLink: (...args: Array) => any; - showWithSelectButton?: boolean; - containerClassName: string; - onSelect?: (poolId: string) => void; - selectedPoolId?: number | null | undefined; -}; - -@observer -class StakePoolsTableBody extends Component { - static contextTypes = { - intl: intlShape.isRequired, - }; - - render() { - const { - sortedStakePoolList, - numberOfRankedStakePools, - currentTheme, - onSelect, - onOpenExternalLink, - showWithSelectButton, - containerClassName, - selectedPoolId, - } = this.props; - const { intl } = this.context; - return map(sortedStakePoolList, (stakePool, key) => { - const rank = get(stakePool, 'ranking', ''); - const ticker = get(stakePool, 'ticker', ''); - const saturation = get(stakePool, 'saturation', ''); - const cost = new BigNumber(get(stakePool, 'cost', '')); - const margin = get(stakePool, 'profitMargin', ''); - const producedBlocks = get(stakePool, 'producedBlocks', ''); - const pledge = new BigNumber(get(stakePool, 'pledge', '')); - const retiring = get(stakePool, 'retiring', ''); - const memberRewards = new BigNumber( - get(stakePool, 'potentialRewards', '') - ); - const potentialRewards = formattedWalletAmount(memberRewards); - const retirement = - retiring && moment(retiring).locale(intl.locale).fromNow(true); - const pledgeValue = formattedWalletAmount(pledge, false, false); - const costValue = formattedWalletAmount(cost, false, false); - const progressBarContentClassnames = classNames([ - styles.progressBarContent, - styles[getSaturationColor(saturation)], - ]); - const color = getColorFromRange(rank, numberOfRankedStakePools); - return ( - - - {!memberRewards.isZero() ? ( - rank - ) : ( - <> - {numberOfRankedStakePools + 1} - * - - )} - - - - - {ticker} - - - - -
-
-
-
-
-
-
- {`${toFixedUserFormat(saturation, 2)}%`} -
-
- - {costValue} - {`${toFixedUserFormat(margin, 2)}%`} - {toFixedUserFormat(producedBlocks, 0)} - {potentialRewards} - {pledgeValue} - - {retirement ? ( - {retirement} - ) : ( - '-' - )} - - - ); - }); - } -} - -export { StakePoolsTableBody }; diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeader.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeader.tsx index dd63573049..af2d503765 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeader.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeader.tsx @@ -9,45 +9,44 @@ import sortIcon from '../../../assets/images/ascending.inline.svg'; import { defaultTableOrdering } from './StakePoolsTable'; type TableHeaderProps = { - availableTableHeaders: Array<{ - name: string; - title: any; - }>; + name: string; stakePoolsSortBy: string; stakePoolsOrder: string; onHandleSort: (...args: Array) => any; + children: React.ReactNode; }; @observer class StakePoolsTableHeader extends Component { render() { const { - availableTableHeaders, + name, stakePoolsSortBy, stakePoolsOrder, onHandleSort, + children, + ...headerProps } = this.props; - return map(availableTableHeaders, (tableHeader) => { - const isSorted = - tableHeader.name === stakePoolsSortBy || - (tableHeader.name === 'ticker' && stakePoolsSortBy === 'ticker'); - const defaultOrdering = defaultTableOrdering[tableHeader.name]; - const sortIconClasses = classNames([ - styles.sortIcon, - isSorted ? styles.sorted : null, - isSorted && styles[`${stakePoolsOrder}CurrentOrdering`], - styles[`${defaultOrdering}DefaultOrdering`], - ]); - return ( - onHandleSort(tableHeader.name)} - > - {tableHeader.title} - - - ); - }); + const isSorted = + name === stakePoolsSortBy || + (name === 'ticker' && stakePoolsSortBy === 'ticker'); + const defaultOrdering = defaultTableOrdering[name]; + const sortIconClasses = classNames([ + styles.sortIcon, + isSorted ? styles.sorted : null, + isSorted && styles[`${stakePoolsOrder}CurrentOrdering`], + styles[`${defaultOrdering}DefaultOrdering`], + ]); + return ( +
onHandleSort(name)} + {...headerProps} + > + {children} + +
+ ); } } diff --git a/source/renderer/app/components/staking/widgets/PoolPopOver.tsx b/source/renderer/app/components/staking/widgets/PoolPopOver.tsx index 592c3ea904..13f35f781c 100644 --- a/source/renderer/app/components/staking/widgets/PoolPopOver.tsx +++ b/source/renderer/app/components/staking/widgets/PoolPopOver.tsx @@ -30,6 +30,7 @@ export function PoolPopOver(props: { containerClassName: string; numberOfRankedStakePools: number; isGridRewardsView?: boolean; + isSelected?: boolean; }) { // Track hover state manually to optimize performance by lazy init pop overs const [isHovered, setIsHovered] = useState(false); diff --git a/source/renderer/app/i18n/locales/defaultMessages.json b/source/renderer/app/i18n/locales/defaultMessages.json index ca037a45b1..95bf5045f1 100644 --- a/source/renderer/app/i18n/locales/defaultMessages.json +++ b/source/renderer/app/i18n/locales/defaultMessages.json @@ -6784,13 +6784,13 @@ "description": "Table header \"Rank\" label on stake pools list view page", "end": { "column": 3, - "line": 19 + "line": 8 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tableHeader.rank", "start": { "column": 19, - "line": 15 + "line": 4 } }, { @@ -6798,13 +6798,13 @@ "description": "\"Rank\" tooltip for the Stake Pools Table.", "end": { "column": 3, - "line": 25 + "line": 14 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tooltip.rankingTooltip", "start": { "column": 26, - "line": 20 + "line": 9 } }, { @@ -6812,13 +6812,13 @@ "description": "Table header \"Ticker\" label on stake pools list view page", "end": { "column": 3, - "line": 30 + "line": 19 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tableHeader.ticker", "start": { "column": 21, - "line": 26 + "line": 15 } }, { @@ -6826,13 +6826,13 @@ "description": "Table header \"Saturation\" label on stake pools list view page", "end": { "column": 3, - "line": 36 + "line": 25 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tableHeader.saturation", "start": { "column": 25, - "line": 31 + "line": 20 } }, { @@ -6840,13 +6840,13 @@ "description": "\"Saturation\" tooltip for the Stake Pools Table.", "end": { "column": 3, - "line": 42 + "line": 31 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tooltip.saturationTooltip", "start": { "column": 32, - "line": 37 + "line": 26 } }, { @@ -6854,13 +6854,13 @@ "description": "Table header \"Performance\" label on stake pools list view page", "end": { "column": 3, - "line": 48 + "line": 37 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tableHeader.performance", "start": { "column": 26, - "line": 43 + "line": 32 } }, { @@ -6868,13 +6868,13 @@ "description": "Table header \"Uptime\" label on stake pools list view page", "end": { "column": 3, - "line": 53 + "line": 42 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tableHeader.uptime", "start": { "column": 21, - "line": 49 + "line": 38 } }, { @@ -6882,13 +6882,13 @@ "description": "Table header \"Margin\" label on stake pools list view page", "end": { "column": 3, - "line": 58 + "line": 47 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tableHeader.margin", "start": { "column": 21, - "line": 54 + "line": 43 } }, { @@ -6896,13 +6896,13 @@ "description": "\"Pool margin\" tooltip for the Stake Pools Table.", "end": { "column": 3, - "line": 64 + "line": 53 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tooltip.profitMarginTooltip", "start": { "column": 28, - "line": 59 + "line": 48 } }, { @@ -6910,13 +6910,13 @@ "description": "Table header \"Roi\" label on stake pools list view page", "end": { "column": 3, - "line": 69 + "line": 58 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tableHeader.roi", "start": { "column": 18, - "line": 65 + "line": 54 } }, { @@ -6924,13 +6924,13 @@ "description": "Table header \"Cost\" label on stake pools list view page", "end": { "column": 3, - "line": 74 + "line": 63 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tableHeader.cost", "start": { "column": 19, - "line": 70 + "line": 59 } }, { @@ -6938,13 +6938,13 @@ "description": "\"Cost per epoch\" tooltip for the Stake Pools Table.", "end": { "column": 3, - "line": 80 + "line": 69 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tooltip.costPerEpochTooltip", "start": { "column": 26, - "line": 75 + "line": 64 } }, { @@ -6952,13 +6952,13 @@ "description": "Table header \"Produced Blocks\" label on stake pools list view page", "end": { "column": 3, - "line": 86 + "line": 75 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tableHeader.producedBlocks", "start": { "column": 29, - "line": 81 + "line": 70 } }, { @@ -6966,13 +6966,13 @@ "description": "\"Blocks\" tooltip for the Stake Pools Table.", "end": { "column": 3, - "line": 92 + "line": 81 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tooltip.producedBlocksTooltip", "start": { "column": 36, - "line": 87 + "line": 76 } }, { @@ -6980,13 +6980,13 @@ "description": "Table header \"Potential rewards\" label on stake pools list view page", "end": { "column": 3, - "line": 98 + "line": 87 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tableHeader.potentialRewards", "start": { "column": 31, - "line": 93 + "line": 82 } }, { @@ -6994,13 +6994,13 @@ "description": "\"Rewards\" tooltip for the Stake Pools Table.", "end": { "column": 3, - "line": 104 + "line": 93 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tooltip.potentialRewardsTooltip", "start": { "column": 38, - "line": 99 + "line": 88 } }, { @@ -7008,13 +7008,13 @@ "description": "Table header \"Pledge\" label on stake pools list view page", "end": { "column": 3, - "line": 109 + "line": 98 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tableHeader.pledge", "start": { "column": 21, - "line": 105 + "line": 94 } }, { @@ -7022,13 +7022,13 @@ "description": "\"Pledge\" tooltip for the Stake Pools Table.", "end": { "column": 3, - "line": 115 + "line": 104 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tooltip.pledgeTooltip", "start": { "column": 28, - "line": 110 + "line": 99 } }, { @@ -7036,17 +7036,17 @@ "description": "Table header \"Retiring\" label on stake pools list view page", "end": { "column": 3, - "line": 120 + "line": 109 }, - "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx", + "file": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.tsx", "id": "staking.stakePools.tableHeader.retiring", "start": { "column": 23, - "line": 116 + "line": 105 } } ], - "path": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.json" + "path": "source/renderer/app/components/staking/stake-pools/StakePoolsTable.messages.json" }, { "descriptors": [ diff --git a/source/renderer/app/types/i18nTypes.ts b/source/renderer/app/types/i18nTypes.ts index ebd255b854..1d101f218d 100644 --- a/source/renderer/app/types/i18nTypes.ts +++ b/source/renderer/app/types/i18nTypes.ts @@ -9,4 +9,5 @@ export type Intl = { message: ReactIntlMessage, values?: Record ) => string; + locale: string; }; diff --git a/storybook/stories/staking/StakePoolsTable.stories.tsx b/storybook/stories/staking/StakePoolsTable.stories.tsx index 3db6532c27..22f5c94d0e 100644 --- a/storybook/stories/staking/StakePoolsTable.stories.tsx +++ b/storybook/stories/staking/StakePoolsTable.stories.tsx @@ -14,69 +14,56 @@ const listTitle = { type Props = { currentTheme: string; }; -export const StakePoolsTableStory = (props: Props) => ( - -
- -

{ + return ( + +
- -

- + + + ( max: 300, step: 1, }) - ).length - } - onTableHeaderMouseEnter={() => {}} - onTableHeaderMouseLeave={() => {}} - /> -
-
-); + )} + currentLocale="en-US" + currentTheme={props.currentTheme} + onOpenExternalLink={action('onOpenExternalLink')} + containerClassName="StakingWithNavigation_page" + numberOfRankedStakePools={ + STAKE_POOLS.slice( + 0, + number('Pools', 300, { + range: true, + min: 37, + max: 300, + step: 1, + }) + ).length + } + onTableHeaderMouseEnter={() => {}} + onTableHeaderMouseLeave={() => {}} + /> +
+ + ); +}; diff --git a/storybook/webpack.config.js b/storybook/webpack.config.js index bda2b67914..61d5ff7a6c 100644 --- a/storybook/webpack.config.js +++ b/storybook/webpack.config.js @@ -42,10 +42,10 @@ module.exports = async ({ config }) => { 'moment', 'pbkdf2', 'qrcode.react', - 'react', + // 'react', 'react-copy-to-clipboard', 'react-datetime', - 'react-dom', + // 'react-dom', 'react-router', 'react-svg-inline', 'recharts', diff --git a/yarn.lock b/yarn.lock index 499acabd4e..07ec68460f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3292,6 +3292,12 @@ dependencies: "@types/react" "*" +"@types/react-table@^7.7.9": + version "7.7.9" + resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-7.7.9.tgz#ea82875775fc6ee71a28408dcc039396ae067c92" + dependencies: + "@types/react" "*" + "@types/react-textarea-autosize@^4.3.3": version "4.3.5" resolved "https://registry.yarnpkg.com/@types/react-textarea-autosize/-/react-textarea-autosize-4.3.5.tgz#6c4d2753fa1864c98c0b2b517f67bb1f6e4c46de" @@ -14432,6 +14438,10 @@ react-syntax-highlighter@^11.0.2: prismjs "^1.8.4" refractor "^2.4.1" +react-table@7.7.0: + version "7.7.0" + resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.7.0.tgz#e2ce14d7fe3a559f7444e9ecfe8231ea8373f912" + react-textarea-autosize@^7.1.0: version "7.1.2" resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-7.1.2.tgz#70fdb333ef86bcca72717e25e623e90c336e2cda" From d06b0f021b8a06f62b90caca563ffee888a2cda2 Mon Sep 17 00:00:00 2001 From: Renan Ferreira Date: Wed, 16 Mar 2022 14:28:57 -0300 Subject: [PATCH 2/6] [DDW-1012] Add changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f57bff07b4..41b4115072 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Fix warning sign displayed when recommend decimals is zero ([PR 2905](https://github.com/input-output-hk/daedalus/pull/2905)) - Fixed discrete tooltip being clipped by loading overlay when stake pools are adjusted ([PR 2902](https://github.com/input-output-hk/daedalus/pull/2902)) - Sets minimum transaction fee to ada input field when tokens are removed ([PR 2918](https://github.com/input-output-hk/daedalus/pull/2918)) +- Fixed performance issue on stake pool list view ([PR 2924](https://github.com/input-output-hk/daedalus/pull/2924)) ### Chores From cd698f98a7baca2972ffb6666e869aeb614570bd Mon Sep 17 00:00:00 2001 From: Renan Ferreira Date: Thu, 17 Mar 2022 15:04:59 -0300 Subject: [PATCH 3/6] [DDW-1012] Review changes --- .../stake-pools/StakePoolsTable.hooks.tsx | 23 +++++++++++-------- .../staking/stake-pools/StakePoolsTable.scss | 18 +++++++-------- .../staking/stake-pools/StakePoolsTable.tsx | 11 ++++++--- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.hooks.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.hooks.tsx index af6376a216..d77b4a6b03 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.hooks.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.hooks.tsx @@ -17,16 +17,19 @@ import { } from '../../../utils/formatters'; import { messages } from './StakePoolsTable.messages'; +const ascOrder = 'asc'; +const descOrder = 'desc'; + export const defaultTableOrdering = { - ranking: 'asc', - ticker: 'asc', - saturation: 'asc', - cost: 'asc', - profitMargin: 'asc', - producedBlocks: 'desc', - nonMyopicMemberRewards: 'desc', - pledge: 'asc', - retiring: 'asc', + ranking: ascOrder, + ticker: ascOrder, + saturation: ascOrder, + cost: ascOrder, + profitMargin: ascOrder, + producedBlocks: descOrder, + nonMyopicMemberRewards: descOrder, + pledge: ascOrder, + retiring: ascOrder, }; interface UseSortedStakePoolListArgs { @@ -320,5 +323,5 @@ export const useCreateColumns = ({ }, }, ], - [] + [numberOfRankedStakePools] ); diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.scss b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.scss index 9f0a352d15..cda9cd648a 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.scss +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.scss @@ -364,39 +364,39 @@ // Rank &:nth-child(1) { - width: 7% !important; + width: 7%; } // Ticker &:nth-child(2) { - width: 9% !important; + width: 9%; } // Saturation &:nth-child(3) { - width: 11% !important; + width: 11%; } // Cost &:nth-child(4) { - width: 10% !important; + width: 10%; } // Margin &:nth-child(5) { - width: 8% !important; + width: 8%; } // Produced Blocks &:nth-child(6) { - width: 15% !important; + width: 15%; } // Potential Rewards &:nth-child(7) { - width: 16% !important; + width: 16%; } // Pledge &:nth-child(8) { - width: 12% !important; + width: 12%; } // Retiring in &:nth-child(9) { - width: 12% !important; + width: 12%; } } } diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx index b6c9efc0cd..d449722c8f 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx @@ -145,7 +145,10 @@ function StakePoolsTableComponent({ {row.cells.map((cell) => { return ( /* eslint-disable-next-line react/jsx-key */ -
+
{cell.render('Cell')}
); @@ -196,7 +199,9 @@ function StakePoolsTableComponent({ > {headerGroup.headers.map((column) => ( Date: Thu, 17 Mar 2022 15:20:55 -0300 Subject: [PATCH 4/6] [DDW-1012] Remove unused property --- .../components/staking/stake-pools/StakePoolsTable.hooks.tsx | 3 --- .../app/components/staking/stake-pools/StakePoolsTable.tsx | 1 - source/renderer/app/components/staking/widgets/PoolPopOver.tsx | 1 - 3 files changed, 5 deletions(-) diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.hooks.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.hooks.tsx index d77b4a6b03..1c1a0defcf 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.hooks.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.hooks.tsx @@ -88,7 +88,6 @@ type UseCreateColumnsArgs = { onSelect?: (...args: Array) => any; containerClassName: string; numberOfRankedStakePools: number; - selectedPoolId?: string; onOpenExternalLink: (...args: Array) => any; intl: Intl; }; @@ -99,7 +98,6 @@ export const useCreateColumns = ({ currentTheme, onOpenExternalLink, onSelect, - selectedPoolId, containerClassName, showWithSelectButton, }: UseCreateColumnsArgs) => @@ -157,7 +155,6 @@ export const useCreateColumns = ({ currentTheme={currentTheme} onOpenExternalLink={onOpenExternalLink} onSelect={onSelect} - isSelected={selectedPoolId === stakePool.id} stakePool={stakePool} containerClassName={containerClassName} numberOfRankedStakePools={numberOfRankedStakePools} diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx index d449722c8f..c4de320b7e 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx @@ -113,7 +113,6 @@ function StakePoolsTableComponent({ numberOfRankedStakePools, onOpenExternalLink, onSelect, - selectedPoolId, showWithSelectButton, }); diff --git a/source/renderer/app/components/staking/widgets/PoolPopOver.tsx b/source/renderer/app/components/staking/widgets/PoolPopOver.tsx index 13f35f781c..592c3ea904 100644 --- a/source/renderer/app/components/staking/widgets/PoolPopOver.tsx +++ b/source/renderer/app/components/staking/widgets/PoolPopOver.tsx @@ -30,7 +30,6 @@ export function PoolPopOver(props: { containerClassName: string; numberOfRankedStakePools: number; isGridRewardsView?: boolean; - isSelected?: boolean; }) { // Track hover state manually to optimize performance by lazy init pop overs const [isHovered, setIsHovered] = useState(false); From 79190d9110682bed8d661da66f054841563a1c72 Mon Sep 17 00:00:00 2001 From: Renan Ferreira Date: Thu, 24 Mar 2022 15:38:44 -0300 Subject: [PATCH 5/6] [DDW-1012] Review changes --- CHANGELOG.md | 2 +- .../staking/stake-pools/StakePoolsTable.tsx | 41 +++---- ...ader.tsx => StakePoolsTableHeaderCell.tsx} | 8 +- .../staking/stake-pools/hooks/index.ts | 5 + .../useCreateColumns.tsx} | 103 ++++-------------- .../hooks/useSortedStakePoolList.tsx | 54 +++++++++ storybook/webpack.config.js | 4 +- 7 files changed, 107 insertions(+), 110 deletions(-) rename source/renderer/app/components/staking/stake-pools/{StakePoolsTableHeader.tsx => StakePoolsTableHeaderCell.tsx} (86%) create mode 100644 source/renderer/app/components/staking/stake-pools/hooks/index.ts rename source/renderer/app/components/staking/stake-pools/{StakePoolsTable.hooks.tsx => hooks/useCreateColumns.tsx} (73%) create mode 100644 source/renderer/app/components/staking/stake-pools/hooks/useSortedStakePoolList.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 41b4115072..ab36efc106 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ ### Fixes +- Fixed performance issue on stake pool list view ([PR 2924](https://github.com/input-output-hk/daedalus/pull/2924)) - Fixed catalyst fund name ([PR 2946](https://github.com/input-output-hk/daedalus/pull/2946)) - Fixed position of popup on syncing screen ([PR 2921](https://github.com/input-output-hk/daedalus/pull/2921)) - Fixed issue with missing character when copying address from PDF ([PR 2925](https://github.com/input-output-hk/daedalus/pull/2925)) @@ -21,7 +22,6 @@ - Fix warning sign displayed when recommend decimals is zero ([PR 2905](https://github.com/input-output-hk/daedalus/pull/2905)) - Fixed discrete tooltip being clipped by loading overlay when stake pools are adjusted ([PR 2902](https://github.com/input-output-hk/daedalus/pull/2902)) - Sets minimum transaction fee to ada input field when tokens are removed ([PR 2918](https://github.com/input-output-hk/daedalus/pull/2918)) -- Fixed performance issue on stake pool list view ([PR 2924](https://github.com/input-output-hk/daedalus/pull/2924)) ### Chores diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx index c4de320b7e..c30b475c66 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx @@ -7,28 +7,28 @@ import List from 'react-virtualized/dist/commonjs/List'; import WindowScroller from 'react-virtualized/dist/commonjs/WindowScroller'; import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer'; import { Intl } from '../../../types/i18nTypes'; - import { StakingPageScrollContext } from '../layouts/StakingWithNavigation'; import styles from './StakePoolsTable.scss'; import StakePool from '../../../domains/StakePool'; import LoadingSpinner from '../../widgets/LoadingSpinner'; import BorderedBox from '../../widgets/BorderedBox'; -import { StakePoolsTableHeader } from './StakePoolsTableHeader'; +import { StakePoolsTableHeaderCell } from './StakePoolsTableHeaderCell'; import { useSortedStakePoolList, useCreateColumns, -} from './StakePoolsTable.hooks'; + StakePoolsOrder, +} from './hooks'; export const defaultTableOrdering = { - ranking: 'asc', - ticker: 'asc', - saturation: 'asc', - cost: 'asc', - profitMargin: 'asc', - producedBlocks: 'desc', - nonMyopicMemberRewards: 'desc', - pledge: 'asc', - retiring: 'asc', + ranking: StakePoolsOrder.Asc, + ticker: StakePoolsOrder.Asc, + saturation: StakePoolsOrder.Asc, + cost: StakePoolsOrder.Asc, + profitMargin: StakePoolsOrder.Asc, + producedBlocks: StakePoolsOrder.Desc, + nonMyopicMemberRewards: StakePoolsOrder.Desc, + pledge: StakePoolsOrder.Asc, + retiring: StakePoolsOrder.Asc, }; // Maximum number of stake pools for which we do not need to use the preloading const PRELOADER_THRESHOLD = 100; @@ -51,12 +51,12 @@ type Props = { }; type State = { isPreloading: boolean; - stakePoolsOrder: 'asc' | 'desc'; - stakePoolsSortBy: string; + stakePoolsOrder: StakePoolsOrder; + stakePoolsSortBy: keyof StakePool; }; const initialState: State = { isPreloading: true, - stakePoolsOrder: 'asc', + stakePoolsOrder: StakePoolsOrder.Asc, stakePoolsSortBy: 'ranking', }; @@ -81,12 +81,15 @@ function StakePoolsTableComponent({ }, []); const handleSort = useCallback( - (newSortBy: string) => { + (newSortBy: keyof StakePool) => { const { stakePoolsOrder, stakePoolsSortBy } = state; let newOrder = defaultTableOrdering[newSortBy]; if (newSortBy === stakePoolsSortBy) { - newOrder = stakePoolsOrder === 'asc' ? 'desc' : 'asc'; + newOrder = + stakePoolsOrder === StakePoolsOrder.Asc + ? StakePoolsOrder.Desc + : StakePoolsOrder.Asc; } setState((s) => ({ @@ -197,7 +200,7 @@ function StakePoolsTableComponent({ className={styles.tr} > {headerGroup.headers.map((column) => ( - {column.render('Header')} - + ))}
))} diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeader.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeaderCell.tsx similarity index 86% rename from source/renderer/app/components/staking/stake-pools/StakePoolsTableHeader.tsx rename to source/renderer/app/components/staking/stake-pools/StakePoolsTableHeaderCell.tsx index af2d503765..931b1826c7 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeader.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeaderCell.tsx @@ -17,7 +17,7 @@ type TableHeaderProps = { }; @observer -class StakePoolsTableHeader extends Component { +class StakePoolsTableHeaderCell extends Component { render() { const { name, @@ -27,9 +27,7 @@ class StakePoolsTableHeader extends Component { children, ...headerProps } = this.props; - const isSorted = - name === stakePoolsSortBy || - (name === 'ticker' && stakePoolsSortBy === 'ticker'); + const isSorted = name === stakePoolsSortBy; const defaultOrdering = defaultTableOrdering[name]; const sortIconClasses = classNames([ styles.sortIcon, @@ -50,4 +48,4 @@ class StakePoolsTableHeader extends Component { } } -export { StakePoolsTableHeader }; +export { StakePoolsTableHeaderCell }; diff --git a/source/renderer/app/components/staking/stake-pools/hooks/index.ts b/source/renderer/app/components/staking/stake-pools/hooks/index.ts new file mode 100644 index 0000000000..7e594bc5ff --- /dev/null +++ b/source/renderer/app/components/staking/stake-pools/hooks/index.ts @@ -0,0 +1,5 @@ +export { useCreateColumns } from './useCreateColumns'; +export { + useSortedStakePoolList, + StakePoolsOrder, +} from './useSortedStakePoolList'; diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.hooks.tsx b/source/renderer/app/components/staking/stake-pools/hooks/useCreateColumns.tsx similarity index 73% rename from source/renderer/app/components/staking/stake-pools/StakePoolsTable.hooks.tsx rename to source/renderer/app/components/staking/stake-pools/hooks/useCreateColumns.tsx index 1c1a0defcf..7009b01409 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.hooks.tsx +++ b/source/renderer/app/components/staking/stake-pools/hooks/useCreateColumns.tsx @@ -1,94 +1,31 @@ import React, { useMemo } from 'react'; -import { orderBy } from 'lodash'; import classNames from 'classnames'; import BigNumber from 'bignumber.js'; import moment from 'moment'; import { FormattedHTMLMessage } from 'react-intl'; +import { Column } from 'react-table'; import { PopOver } from 'react-polymorph/lib/components/PopOver'; -import { PoolPopOver } from '../widgets/PoolPopOver'; -import { Intl } from '../../../types/i18nTypes'; - -import styles from './StakePoolsTable.scss'; -import StakePool from '../../../domains/StakePool'; -import { getColorFromRange, getSaturationColor } from '../../../utils/colors'; +import { PoolPopOver } from '../../widgets/PoolPopOver'; +import { Intl } from '../../../../types/i18nTypes'; +import styles from '../StakePoolsTable.scss'; +import StakePool from '../../../../domains/StakePool'; +import { + getColorFromRange, + getSaturationColor, +} from '../../../../utils/colors'; import { formattedWalletAmount, toFixedUserFormat, -} from '../../../utils/formatters'; -import { messages } from './StakePoolsTable.messages'; - -const ascOrder = 'asc'; -const descOrder = 'desc'; - -export const defaultTableOrdering = { - ranking: ascOrder, - ticker: ascOrder, - saturation: ascOrder, - cost: ascOrder, - profitMargin: ascOrder, - producedBlocks: descOrder, - nonMyopicMemberRewards: descOrder, - pledge: ascOrder, - retiring: ascOrder, -}; - -interface UseSortedStakePoolListArgs { - stakePoolList: StakePool[]; - sortBy: string; - order: 'asc' | 'desc'; -} - -export const useSortedStakePoolList = ({ - stakePoolList, - sortBy, - order, -}: UseSortedStakePoolListArgs) => - useMemo( - () => - orderBy( - stakePoolList.map((stakePool) => { - let calculatedPledge; - let calculatedCost; - let formattedTicker; - - if (sortBy === 'ticker') { - formattedTicker = stakePool.ticker - .replace(/[^\w\s]/gi, '') - .toLowerCase(); - } - - if (sortBy === 'pledge') { - const formattedPledgeValue = stakePool.pledge.toFixed(2); - calculatedPledge = Number( - parseFloat(formattedPledgeValue).toFixed(2) - ); - } - - if (sortBy === 'cost') { - const formattedCostValue = stakePool.cost.toFixed(2); - calculatedCost = Number(parseFloat(formattedCostValue).toFixed(2)); - } - - return { - ...stakePool, - calculatedPledge, - calculatedCost, - formattedTicker, - }; - }), - ['formattedTicker', 'calculatedPledge', 'calculatedCost', sortBy], - [order, order, order, order] - ), - [stakePoolList, order, sortBy] - ); +} from '../../../../utils/formatters'; +import { messages } from '../StakePoolsTable.messages'; type UseCreateColumnsArgs = { currentTheme: string; showWithSelectButton?: boolean; - onSelect?: (...args: Array) => any; + onSelect?: (poolId: string) => void; containerClassName: string; numberOfRankedStakePools: number; - onOpenExternalLink: (...args: Array) => any; + onOpenExternalLink: (url: string) => void; intl: Intl; }; @@ -101,7 +38,7 @@ export const useCreateColumns = ({ containerClassName, showWithSelectButton, }: UseCreateColumnsArgs) => - useMemo( + useMemo[]>( () => [ { id: 'ranking', @@ -120,7 +57,7 @@ export const useCreateColumns = ({ ), accessor: 'ranking', Cell: ({ row }) => { - const { potentialRewards, ranking }: StakePool = row.original; + const { potentialRewards, ranking } = row.original; const memberRewards = new BigNumber(potentialRewards); return ( @@ -143,7 +80,7 @@ export const useCreateColumns = ({ Header: intl.formatMessage(messages.tableHeaderTicker), accessor: 'ticker', Cell: ({ row }) => { - const stakePool: StakePool = row.original; + const stakePool = row.original; const color = getColorFromRange( stakePool.ranking, numberOfRankedStakePools @@ -180,7 +117,7 @@ export const useCreateColumns = ({ ), accessor: 'saturation', Cell: ({ row }) => { - const { saturation }: StakePool = row.original; + const { saturation } = row.original; const progressBarContentClassnames = classNames([ styles.progressBarContent, styles[getSaturationColor(saturation)], @@ -273,7 +210,7 @@ export const useCreateColumns = ({ ), accessor: 'nonMyopicMemberRewards', Cell: ({ row }) => { - const stakePool: StakePool = row.original; + const stakePool = row.original; const memberRewards = new BigNumber(stakePool.potentialRewards); const potentialRewards = formattedWalletAmount(memberRewards); return potentialRewards; @@ -292,7 +229,7 @@ export const useCreateColumns = ({ ), accessor: 'pledge', Cell: ({ row }) => { - const stakePool: StakePool = row.original; + const stakePool = row.original; const pledge = new BigNumber(stakePool.pledge); const pledgeValue = formattedWalletAmount(pledge, false, false); return pledgeValue; @@ -303,7 +240,7 @@ export const useCreateColumns = ({ Header: intl.formatMessage(messages.tableHeaderRetiring), accessor: 'retiring', Cell: ({ row }) => { - const stakePool: StakePool = row.original; + const stakePool = row.original; const retirement = stakePool.retiring && moment(stakePool.retiring).locale(intl.locale).fromNow(true); diff --git a/source/renderer/app/components/staking/stake-pools/hooks/useSortedStakePoolList.tsx b/source/renderer/app/components/staking/stake-pools/hooks/useSortedStakePoolList.tsx new file mode 100644 index 0000000000..2c1dbfc1e7 --- /dev/null +++ b/source/renderer/app/components/staking/stake-pools/hooks/useSortedStakePoolList.tsx @@ -0,0 +1,54 @@ +import { useMemo } from 'react'; +import { orderBy } from 'lodash'; +import StakePool from '../../../../domains/StakePool'; + +export enum StakePoolsOrder { + Asc = 'asc', + Desc = 'desc', +} + +interface UseSortedStakePoolListArgs { + stakePoolList: StakePool[]; + sortBy: keyof StakePool; + order: StakePoolsOrder; +} + +export const useSortedStakePoolList = ({ + stakePoolList, + sortBy, + order, +}: UseSortedStakePoolListArgs) => + useMemo( + () => + orderBy( + stakePoolList.map((stakePool) => { + let calculatedPledge; + let calculatedCost; + let formattedTicker; + + if (sortBy === 'ticker') { + formattedTicker = stakePool.ticker + .replace(/[^\w\s]/gi, '') + .toLowerCase(); + } + + if (sortBy === 'pledge') { + calculatedPledge = parseFloat(stakePool.pledge.toFixed(2)); + } + + if (sortBy === 'cost') { + calculatedCost = parseFloat(stakePool.cost.toFixed(2)); + } + + return { + ...stakePool, + calculatedPledge, + calculatedCost, + formattedTicker, + }; + }), + ['formattedTicker', 'calculatedPledge', 'calculatedCost', sortBy], + [order, order, order, order] + ), + [stakePoolList, order, sortBy] + ); diff --git a/storybook/webpack.config.js b/storybook/webpack.config.js index 61d5ff7a6c..bda2b67914 100644 --- a/storybook/webpack.config.js +++ b/storybook/webpack.config.js @@ -42,10 +42,10 @@ module.exports = async ({ config }) => { 'moment', 'pbkdf2', 'qrcode.react', - // 'react', + 'react', 'react-copy-to-clipboard', 'react-datetime', - // 'react-dom', + 'react-dom', 'react-router', 'react-svg-inline', 'recharts', From 02612067ee7b3c9c7aee4087faa017849253c962 Mon Sep 17 00:00:00 2001 From: Renan Ferreira Date: Wed, 6 Apr 2022 14:13:12 -0300 Subject: [PATCH 6/6] [DDW-1012] Review changes --- .../staking/stake-pools/StakePoolsTable.tsx | 11 +++++++---- .../stake-pools/StakePoolsTableHeaderCell.tsx | 9 ++++----- .../components/staking/stake-pools/hooks/index.ts | 1 + .../components/staking/stake-pools/hooks/types.ts | 14 ++++++++++++++ .../staking/stake-pools/hooks/useCreateColumns.tsx | 8 +++++++- .../stake-pools/hooks/useSortedStakePoolList.tsx | 3 ++- 6 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 source/renderer/app/components/staking/stake-pools/hooks/types.ts diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx index c30b475c66..4fd9214c3f 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsTable.tsx @@ -17,9 +17,13 @@ import { useSortedStakePoolList, useCreateColumns, StakePoolsOrder, + StakePoolSortableProps, } from './hooks'; -export const defaultTableOrdering = { +export const defaultTableOrdering: Record< + StakePoolSortableProps, + StakePoolsOrder +> = { ranking: StakePoolsOrder.Asc, ticker: StakePoolsOrder.Asc, saturation: StakePoolsOrder.Asc, @@ -52,7 +56,7 @@ type Props = { type State = { isPreloading: boolean; stakePoolsOrder: StakePoolsOrder; - stakePoolsSortBy: keyof StakePool; + stakePoolsSortBy: StakePoolSortableProps; }; const initialState: State = { isPreloading: true, @@ -72,7 +76,6 @@ function StakePoolsTableComponent({ onOpenExternalLink, containerClassName, onSelect, - selectedPoolId, }: Props) { const [state, setState] = useState(initialState); @@ -81,7 +84,7 @@ function StakePoolsTableComponent({ }, []); const handleSort = useCallback( - (newSortBy: keyof StakePool) => { + (newSortBy: StakePoolSortableProps) => { const { stakePoolsOrder, stakePoolsSortBy } = state; let newOrder = defaultTableOrdering[newSortBy]; diff --git a/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeaderCell.tsx b/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeaderCell.tsx index 931b1826c7..22575f18ce 100644 --- a/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeaderCell.tsx +++ b/source/renderer/app/components/staking/stake-pools/StakePoolsTableHeaderCell.tsx @@ -1,18 +1,17 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; -import { map } from 'lodash'; import classNames from 'classnames'; import SVGInline from 'react-svg-inline'; import styles from './StakePoolsTable.scss'; -// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/ascendi... Remove this comment to see the full error message import sortIcon from '../../../assets/images/ascending.inline.svg'; import { defaultTableOrdering } from './StakePoolsTable'; +import { StakePoolsOrder, StakePoolSortableProps } from './hooks'; type TableHeaderProps = { name: string; - stakePoolsSortBy: string; - stakePoolsOrder: string; - onHandleSort: (...args: Array) => any; + stakePoolsSortBy: StakePoolSortableProps; + stakePoolsOrder: StakePoolsOrder; + onHandleSort: (name: string) => void; children: React.ReactNode; }; diff --git a/source/renderer/app/components/staking/stake-pools/hooks/index.ts b/source/renderer/app/components/staking/stake-pools/hooks/index.ts index 7e594bc5ff..ac08a1ed77 100644 --- a/source/renderer/app/components/staking/stake-pools/hooks/index.ts +++ b/source/renderer/app/components/staking/stake-pools/hooks/index.ts @@ -3,3 +3,4 @@ export { useSortedStakePoolList, StakePoolsOrder, } from './useSortedStakePoolList'; +export { StakePoolSortableProps } from './types'; diff --git a/source/renderer/app/components/staking/stake-pools/hooks/types.ts b/source/renderer/app/components/staking/stake-pools/hooks/types.ts new file mode 100644 index 0000000000..6b751348b5 --- /dev/null +++ b/source/renderer/app/components/staking/stake-pools/hooks/types.ts @@ -0,0 +1,14 @@ +import StakePool from '../../../../domains/StakePool'; + +export type StakePoolSortableProps = keyof Pick< + StakePool, + | 'ranking' + | 'ticker' + | 'saturation' + | 'cost' + | 'profitMargin' + | 'producedBlocks' + | 'nonMyopicMemberRewards' + | 'pledge' + | 'retiring' +>; diff --git a/source/renderer/app/components/staking/stake-pools/hooks/useCreateColumns.tsx b/source/renderer/app/components/staking/stake-pools/hooks/useCreateColumns.tsx index 7009b01409..f4fcccbdb5 100644 --- a/source/renderer/app/components/staking/stake-pools/hooks/useCreateColumns.tsx +++ b/source/renderer/app/components/staking/stake-pools/hooks/useCreateColumns.tsx @@ -18,6 +18,7 @@ import { toFixedUserFormat, } from '../../../../utils/formatters'; import { messages } from '../StakePoolsTable.messages'; +import { StakePoolSortableProps } from './types'; type UseCreateColumnsArgs = { currentTheme: string; @@ -29,6 +30,11 @@ type UseCreateColumnsArgs = { intl: Intl; }; +type StakePoolColumn = Column & { + id: StakePoolSortableProps; + accessor: StakePoolSortableProps; +}; + export const useCreateColumns = ({ numberOfRankedStakePools, intl, @@ -38,7 +44,7 @@ export const useCreateColumns = ({ containerClassName, showWithSelectButton, }: UseCreateColumnsArgs) => - useMemo[]>( + useMemo( () => [ { id: 'ranking', diff --git a/source/renderer/app/components/staking/stake-pools/hooks/useSortedStakePoolList.tsx b/source/renderer/app/components/staking/stake-pools/hooks/useSortedStakePoolList.tsx index 2c1dbfc1e7..216970e196 100644 --- a/source/renderer/app/components/staking/stake-pools/hooks/useSortedStakePoolList.tsx +++ b/source/renderer/app/components/staking/stake-pools/hooks/useSortedStakePoolList.tsx @@ -1,6 +1,7 @@ import { useMemo } from 'react'; import { orderBy } from 'lodash'; import StakePool from '../../../../domains/StakePool'; +import { StakePoolSortableProps } from './types'; export enum StakePoolsOrder { Asc = 'asc', @@ -9,7 +10,7 @@ export enum StakePoolsOrder { interface UseSortedStakePoolListArgs { stakePoolList: StakePool[]; - sortBy: keyof StakePool; + sortBy: StakePoolSortableProps; order: StakePoolsOrder; }