|
| 1 | +import React, { useMemo } from 'react'; |
| 2 | +import { orderBy } from 'lodash'; |
| 3 | +import classNames from 'classnames'; |
| 4 | +import BigNumber from 'bignumber.js'; |
| 5 | +import moment from 'moment'; |
| 6 | +import { FormattedHTMLMessage } from 'react-intl'; |
| 7 | +import { PopOver } from 'react-polymorph/lib/components/PopOver'; |
| 8 | +import { PoolPopOver } from '../widgets/PoolPopOver'; |
| 9 | +import { Intl } from '../../../types/i18nTypes'; |
| 10 | + |
| 11 | +import styles from './StakePoolsTable.scss'; |
| 12 | +import StakePool from '../../../domains/StakePool'; |
| 13 | +import { getColorFromRange, getSaturationColor } from '../../../utils/colors'; |
| 14 | +import { |
| 15 | + formattedWalletAmount, |
| 16 | + toFixedUserFormat, |
| 17 | +} from '../../../utils/formatters'; |
| 18 | +import { messages } from './StakePoolsTable.messages'; |
| 19 | + |
| 20 | +export const defaultTableOrdering = { |
| 21 | + ranking: 'asc', |
| 22 | + ticker: 'asc', |
| 23 | + saturation: 'asc', |
| 24 | + cost: 'asc', |
| 25 | + profitMargin: 'asc', |
| 26 | + producedBlocks: 'desc', |
| 27 | + nonMyopicMemberRewards: 'desc', |
| 28 | + pledge: 'asc', |
| 29 | + retiring: 'asc', |
| 30 | +}; |
| 31 | + |
| 32 | +interface UseSortedStakePoolListArgs { |
| 33 | + stakePoolList: StakePool[]; |
| 34 | + sortBy: string; |
| 35 | + order: 'asc' | 'desc'; |
| 36 | +} |
| 37 | + |
| 38 | +export const useSortedStakePoolList = ({ |
| 39 | + stakePoolList, |
| 40 | + sortBy, |
| 41 | + order, |
| 42 | +}: UseSortedStakePoolListArgs) => |
| 43 | + useMemo( |
| 44 | + () => |
| 45 | + orderBy( |
| 46 | + stakePoolList.map((stakePool) => { |
| 47 | + let calculatedPledge; |
| 48 | + let calculatedCost; |
| 49 | + let formattedTicker; |
| 50 | + |
| 51 | + if (sortBy === 'ticker') { |
| 52 | + formattedTicker = stakePool.ticker |
| 53 | + .replace(/[^\w\s]/gi, '') |
| 54 | + .toLowerCase(); |
| 55 | + } |
| 56 | + |
| 57 | + if (sortBy === 'pledge') { |
| 58 | + const formattedPledgeValue = stakePool.pledge.toFixed(2); |
| 59 | + calculatedPledge = Number( |
| 60 | + parseFloat(formattedPledgeValue).toFixed(2) |
| 61 | + ); |
| 62 | + } |
| 63 | + |
| 64 | + if (sortBy === 'cost') { |
| 65 | + const formattedCostValue = stakePool.cost.toFixed(2); |
| 66 | + calculatedCost = Number(parseFloat(formattedCostValue).toFixed(2)); |
| 67 | + } |
| 68 | + |
| 69 | + return { |
| 70 | + ...stakePool, |
| 71 | + calculatedPledge, |
| 72 | + calculatedCost, |
| 73 | + formattedTicker, |
| 74 | + }; |
| 75 | + }), |
| 76 | + ['formattedTicker', 'calculatedPledge', 'calculatedCost', sortBy], |
| 77 | + [order, order, order, order] |
| 78 | + ), |
| 79 | + [stakePoolList, order, sortBy] |
| 80 | + ); |
| 81 | + |
| 82 | +type UseCreateColumnsArgs = { |
| 83 | + currentTheme: string; |
| 84 | + showWithSelectButton?: boolean; |
| 85 | + onSelect?: (...args: Array<any>) => any; |
| 86 | + containerClassName: string; |
| 87 | + numberOfRankedStakePools: number; |
| 88 | + selectedPoolId?: string; |
| 89 | + onOpenExternalLink: (...args: Array<any>) => any; |
| 90 | + intl: Intl; |
| 91 | +}; |
| 92 | + |
| 93 | +export const useCreateColumns = ({ |
| 94 | + numberOfRankedStakePools, |
| 95 | + intl, |
| 96 | + currentTheme, |
| 97 | + onOpenExternalLink, |
| 98 | + onSelect, |
| 99 | + selectedPoolId, |
| 100 | + containerClassName, |
| 101 | + showWithSelectButton, |
| 102 | +}: UseCreateColumnsArgs) => |
| 103 | + useMemo( |
| 104 | + () => [ |
| 105 | + { |
| 106 | + id: 'ranking', |
| 107 | + Header: ( |
| 108 | + <PopOver |
| 109 | + key="ranking" |
| 110 | + placement="bottom" |
| 111 | + content={ |
| 112 | + <div className={styles.tooltipWithHtmlContent}> |
| 113 | + <FormattedHTMLMessage {...messages.tableHeaderRankTooltip} /> |
| 114 | + </div> |
| 115 | + } |
| 116 | + > |
| 117 | + {intl.formatMessage(messages.tableHeaderRank)} |
| 118 | + </PopOver> |
| 119 | + ), |
| 120 | + accessor: 'ranking', |
| 121 | + Cell: ({ row }) => { |
| 122 | + const { potentialRewards, ranking }: StakePool = row.original; |
| 123 | + const memberRewards = new BigNumber(potentialRewards); |
| 124 | + |
| 125 | + return ( |
| 126 | + <> |
| 127 | + {!memberRewards.isZero() ? ( |
| 128 | + ranking |
| 129 | + ) : ( |
| 130 | + <> |
| 131 | + {numberOfRankedStakePools + 1} |
| 132 | + <span className={styles.asterisk}>*</span> |
| 133 | + </> |
| 134 | + )} |
| 135 | + </> |
| 136 | + ); |
| 137 | + }, |
| 138 | + }, |
| 139 | + |
| 140 | + { |
| 141 | + id: 'ticker', |
| 142 | + Header: intl.formatMessage(messages.tableHeaderTicker), |
| 143 | + accessor: 'ticker', |
| 144 | + Cell: ({ row }) => { |
| 145 | + const stakePool: StakePool = row.original; |
| 146 | + const color = getColorFromRange( |
| 147 | + stakePool.ranking, |
| 148 | + numberOfRankedStakePools |
| 149 | + ); |
| 150 | + |
| 151 | + return ( |
| 152 | + <PoolPopOver |
| 153 | + color={color} |
| 154 | + currentTheme={currentTheme} |
| 155 | + onOpenExternalLink={onOpenExternalLink} |
| 156 | + onSelect={onSelect} |
| 157 | + isSelected={selectedPoolId === stakePool.id} |
| 158 | + stakePool={stakePool} |
| 159 | + containerClassName={containerClassName} |
| 160 | + numberOfRankedStakePools={numberOfRankedStakePools} |
| 161 | + showWithSelectButton={showWithSelectButton} |
| 162 | + > |
| 163 | + <span className={styles.ticker} role="presentation"> |
| 164 | + {stakePool.ticker} |
| 165 | + </span> |
| 166 | + </PoolPopOver> |
| 167 | + ); |
| 168 | + }, |
| 169 | + }, |
| 170 | + { |
| 171 | + id: 'saturation', |
| 172 | + Header: ( |
| 173 | + <PopOver |
| 174 | + key="saturation" |
| 175 | + placement="bottom" |
| 176 | + content={intl.formatMessage(messages.tableHeaderSaturationTooltip)} |
| 177 | + > |
| 178 | + {intl.formatMessage(messages.tableHeaderSaturation)} |
| 179 | + </PopOver> |
| 180 | + ), |
| 181 | + accessor: 'saturation', |
| 182 | + Cell: ({ row }) => { |
| 183 | + const { saturation }: StakePool = row.original; |
| 184 | + const progressBarContentClassnames = classNames([ |
| 185 | + styles.progressBarContent, |
| 186 | + styles[getSaturationColor(saturation)], |
| 187 | + ]); |
| 188 | + |
| 189 | + return ( |
| 190 | + <div className={styles.saturation}> |
| 191 | + <div className={styles.progressBar}> |
| 192 | + <div className={styles.progressBarContainer}> |
| 193 | + <div |
| 194 | + className={progressBarContentClassnames} |
| 195 | + style={{ |
| 196 | + width: `${saturation.toFixed(2)}%`, |
| 197 | + }} |
| 198 | + /> |
| 199 | + </div> |
| 200 | + </div> |
| 201 | + <div className={styles.saturationLabel}> |
| 202 | + {`${toFixedUserFormat(saturation, 2)}%`} |
| 203 | + </div> |
| 204 | + </div> |
| 205 | + ); |
| 206 | + }, |
| 207 | + }, |
| 208 | + { |
| 209 | + id: 'cost', |
| 210 | + Header: ( |
| 211 | + <PopOver |
| 212 | + key="cost" |
| 213 | + placement="bottom" |
| 214 | + content={intl.formatMessage(messages.tableHeaderCostTooltip)} |
| 215 | + > |
| 216 | + {intl.formatMessage(messages.tableHeaderCost)} |
| 217 | + </PopOver> |
| 218 | + ), |
| 219 | + accessor: 'cost', |
| 220 | + Cell: ({ value }) => { |
| 221 | + const cost = new BigNumber(value); |
| 222 | + const costValue = formattedWalletAmount(cost, false, false); |
| 223 | + |
| 224 | + return costValue; |
| 225 | + }, |
| 226 | + }, |
| 227 | + { |
| 228 | + id: 'profitMargin', |
| 229 | + Header: ( |
| 230 | + <PopOver |
| 231 | + key="profitMargin" |
| 232 | + placement="bottom" |
| 233 | + content={intl.formatMessage(messages.tableHeaderMarginTooltip)} |
| 234 | + > |
| 235 | + {intl.formatMessage(messages.tableHeaderMargin)} |
| 236 | + </PopOver> |
| 237 | + ), |
| 238 | + accessor: 'profitMargin', |
| 239 | + Cell: ({ value }) => { |
| 240 | + return `${toFixedUserFormat(value, 2)}%`; |
| 241 | + }, |
| 242 | + }, |
| 243 | + { |
| 244 | + id: 'producedBlocks', |
| 245 | + Header: ( |
| 246 | + <PopOver |
| 247 | + key="producedBlocks" |
| 248 | + placement="bottom" |
| 249 | + content={intl.formatMessage( |
| 250 | + messages.tableHeaderProducedBlocksTooltip |
| 251 | + )} |
| 252 | + > |
| 253 | + {intl.formatMessage(messages.tableHeaderProducedBlocks)} |
| 254 | + </PopOver> |
| 255 | + ), |
| 256 | + accessor: 'producedBlocks', |
| 257 | + Cell: ({ value }) => { |
| 258 | + return toFixedUserFormat(value, 0); |
| 259 | + }, |
| 260 | + }, |
| 261 | + { |
| 262 | + id: 'nonMyopicMemberRewards', |
| 263 | + Header: ( |
| 264 | + <PopOver |
| 265 | + key="nonMyopicMemberRewards" |
| 266 | + placement="bottom" |
| 267 | + content={intl.formatMessage( |
| 268 | + messages.tableHeaderPotentialRewardsTooltip |
| 269 | + )} |
| 270 | + > |
| 271 | + {intl.formatMessage(messages.tableHeaderPotentialRewards)} |
| 272 | + </PopOver> |
| 273 | + ), |
| 274 | + accessor: 'nonMyopicMemberRewards', |
| 275 | + Cell: ({ row }) => { |
| 276 | + const stakePool: StakePool = row.original; |
| 277 | + const memberRewards = new BigNumber(stakePool.potentialRewards); |
| 278 | + const potentialRewards = formattedWalletAmount(memberRewards); |
| 279 | + return potentialRewards; |
| 280 | + }, |
| 281 | + }, |
| 282 | + { |
| 283 | + id: 'pledge', |
| 284 | + Header: ( |
| 285 | + <PopOver |
| 286 | + key="pledge" |
| 287 | + placement="bottom" |
| 288 | + content={intl.formatMessage(messages.tableHeaderPledgeTooltip)} |
| 289 | + > |
| 290 | + {intl.formatMessage(messages.tableHeaderPledge)} |
| 291 | + </PopOver> |
| 292 | + ), |
| 293 | + accessor: 'pledge', |
| 294 | + Cell: ({ row }) => { |
| 295 | + const stakePool: StakePool = row.original; |
| 296 | + const pledge = new BigNumber(stakePool.pledge); |
| 297 | + const pledgeValue = formattedWalletAmount(pledge, false, false); |
| 298 | + return pledgeValue; |
| 299 | + }, |
| 300 | + }, |
| 301 | + { |
| 302 | + id: 'retiring', |
| 303 | + Header: intl.formatMessage(messages.tableHeaderRetiring), |
| 304 | + accessor: 'retiring', |
| 305 | + Cell: ({ row }) => { |
| 306 | + const stakePool: StakePool = row.original; |
| 307 | + const retirement = |
| 308 | + stakePool.retiring && |
| 309 | + moment(stakePool.retiring).locale(intl.locale).fromNow(true); |
| 310 | + |
| 311 | + return ( |
| 312 | + <> |
| 313 | + {retirement ? ( |
| 314 | + <span className={styles.retiring}>{retirement}</span> |
| 315 | + ) : ( |
| 316 | + '-' |
| 317 | + )} |
| 318 | + </> |
| 319 | + ); |
| 320 | + }, |
| 321 | + }, |
| 322 | + ], |
| 323 | + [] |
| 324 | + ); |
0 commit comments