diff --git a/packages/page-referenda/src/Referenda/Referendum.tsx b/packages/page-referenda/src/Referenda/Referendum.tsx index 344f42726b..4fa213ab82 100644 --- a/packages/page-referenda/src/Referenda/Referendum.tsx +++ b/packages/page-referenda/src/Referenda/Referendum.tsx @@ -1,7 +1,8 @@ // Copyright 2017-2022 @polkadot/app-referenda authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { ChartOptions } from 'chart.js'; +import type { ChartOptions, ChartTypeRegistry, TooltipItem } from 'chart.js'; +import type { TFunction } from 'react-i18next'; import type { PalletConvictionVotingTally, PalletRankedCollectiveTally, PalletReferendaReferendumInfoConvictionVotingTally, PalletReferendaReferendumInfoRankedCollectiveTally } from '@polkadot/types/lookup'; import type { BN } from '@polkadot/util'; import type { BaseReferendumProps as Props, CurveGraph, ReferendumProps } from '../types'; @@ -10,8 +11,9 @@ import React, { useMemo } from 'react'; import styled from 'styled-components'; import { Chart, Columar, ExpandButton, LinkExternal } from '@polkadot/react-components'; -import { useBestNumber, useToggle } from '@polkadot/react-hooks'; -import { BN_MILLION, BN_THOUSAND, formatNumber, objectSpread } from '@polkadot/util'; +import { useBestNumber, useBlockInterval, useToggle } from '@polkadot/react-hooks'; +import { calcBlockTime } from '@polkadot/react-hooks/useBlockTime'; +import { BN_MILLION, BN_THOUSAND, BN_ZERO, bnToBn, formatNumber, objectSpread } from '@polkadot/util'; import { useTranslation } from '../translate'; import Killed from './RefKilled'; @@ -74,6 +76,27 @@ interface ChartProps extends ChartResult { options: typeof OPTIONS; } +function tooltipTitle (bestNumber: BN, blockInterval: BN, t: TFunction): (items: TooltipItem[]) => string | string[] { + return (items: TooltipItem[]): string | string[] => { + const label = items[0].label; + + try { + const blocks = bnToBn(label.replace(/,/g, '')).sub(bestNumber); + + if (blocks.gt(BN_ZERO)) { + const when = new Date(Date.now() + blocks.mul(blockInterval).toNumber()).toLocaleString(); + const calc = calcBlockTime(blockInterval, blocks, t); + + return [`#${label}`, t('{{when}} (est.)', { replace: { when } }), calc[1]]; + } + } catch { + // ignore + } + + return `#${label}`; + }; +} + function getChartResult (totalEligible: BN, isConvictionVote: boolean, info: PalletReferendaReferendumInfoConvictionVotingTally | PalletReferendaReferendumInfoRankedCollectiveTally, trackGraph: CurveGraph): ChartResultExt[] | null { if (totalEligible && isConvictionVote && info.isOngoing) { const ongoing = info.asOngoing; @@ -124,7 +147,7 @@ function getChartResult (totalEligible: BN, isConvictionVote: boolean, info: Pal return null; } -function getChartProps (bestNumber: BN, chartProps: ChartResultExt[]): ChartProps[] { +function getChartProps (bestNumber: BN, chartProps: ChartResultExt[], tooltipTitle: (items: TooltipItem[]) => string | string[]): ChartProps[] { return chartProps.map(({ changeX, currentY, labels, progress, since, trackGraph: { x }, values }, index): ChartProps => { const maxX = labels.length; const currentX = maxX * ( @@ -179,6 +202,11 @@ function getChartProps (bestNumber: BN, chartProps: ChartResultExt[]): ChartProp } : {} ) + }, + tooltip: { + callbacks: { + title: tooltipTitle + } } } }, OPTIONS), @@ -191,6 +219,7 @@ function getChartProps (bestNumber: BN, chartProps: ChartResultExt[]): ChartProp function Referendum (props: Props): React.ReactElement { const { t } = useTranslation(); const bestNumber = useBestNumber(); + const blockInterval = useBlockInterval(); const { activeIssuance, className = '', palletReferenda, value: { id, info, isConvictionVote, trackGraph } } = props; const [isExpanded, toggleExpanded] = useToggle(false); @@ -205,8 +234,8 @@ function Referendum (props: Props): React.ReactElement { ); const chartProps = useMemo( - () => bestNumber && chartResult && getChartProps(bestNumber, chartResult), - [bestNumber, chartResult] + () => bestNumber && chartResult && getChartProps(bestNumber, chartResult, tooltipTitle(bestNumber, blockInterval, t)), + [bestNumber, blockInterval, chartResult, t] ); const chartLegend = useMemo( diff --git a/packages/react-hooks/src/useBlockTime.ts b/packages/react-hooks/src/useBlockTime.ts index a440095052..a02a83b07b 100644 --- a/packages/react-hooks/src/useBlockTime.ts +++ b/packages/react-hooks/src/useBlockTime.ts @@ -13,9 +13,9 @@ import { createNamedHook } from './createNamedHook'; import { useTranslation } from './translate'; import { useBlockInterval } from './useBlockInterval'; -type Result = [number, string, Time]; +type Result = [blockInterval: number, timeStr: string, time: Time]; -function calcResult (blockTime: BN, blocks: BN, t: TFunction): Result { +export function calcBlockTime (blockTime: BN, blocks: BN, t: TFunction): Result { // in the case of excessively large locks, limit to the max JS integer value const value = bnMin(BN_MAX_INTEGER, blockTime.mul(blocks)).toNumber(); @@ -59,7 +59,7 @@ function useBlockTimeImpl (blocks: number | BN = BN_ONE, apiOverride?: ApiPromis const blockTime = useBlockInterval(apiOverride); return useMemo( - () => calcResult(blockTime, bnToBn(blocks), t), + () => calcBlockTime(blockTime, bnToBn(blocks), t), [blockTime, blocks, t] ); }