2020-06-12 20:46:01 +02:00
|
|
|
import { useSelector } from 'react-redux'
|
|
|
|
import { useRef, useEffect, useState, useMemo } from 'react'
|
|
|
|
import { isEqual } from 'lodash'
|
2020-07-17 01:29:22 +02:00
|
|
|
import { captureException } from '@sentry/browser'
|
2020-08-18 21:18:25 +02:00
|
|
|
import { hexWEIToDecGWEI } from '../helpers/utils/conversions.util'
|
|
|
|
import { getEstimatedGasPrices, getEstimatedGasTimes, getFeatureFlags, getIsMainnet } from '../selectors'
|
2020-06-12 20:46:01 +02:00
|
|
|
import { getRawTimeEstimateData } from '../helpers/utils/gas-time-estimates.util'
|
|
|
|
import { getCurrentLocale } from '../ducks/metamask/metamask'
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculate the number of minutes remaining until the transaction completes.
|
|
|
|
* @param {number} initialTimeEstimate - timestamp for the projected completion time
|
|
|
|
* @param {number} submittedTime - timestamp of when the tx was submitted
|
|
|
|
* @return {number} minutes remaining
|
|
|
|
*/
|
|
|
|
function calcTransactionTimeRemaining (initialTimeEstimate, submittedTime) {
|
|
|
|
const currentTime = (new Date()).getTime()
|
|
|
|
const timeElapsedSinceSubmission = (currentTime - submittedTime) / 1000
|
|
|
|
const timeRemainingOnEstimate = initialTimeEstimate - timeElapsedSinceSubmission
|
|
|
|
|
|
|
|
const renderingTimeRemainingEstimate = Math.round(timeRemainingOnEstimate / 60)
|
|
|
|
return renderingTimeRemainingEstimate
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* returns a string representing the number of minutes predicted for the transaction to be
|
|
|
|
* completed. Only returns this prediction if the transaction is the earliest pending
|
|
|
|
* transaction, and the feature flag for showing timing is enabled.
|
|
|
|
* @param {bool} isPending - is the transaction currently pending
|
|
|
|
* @param {bool} isEarliestNonce - is this transaction the earliest nonce in list
|
|
|
|
* @param {number} submittedTime - the timestamp for when the transaction was submitted
|
|
|
|
* @param {number} currentGasPrice - gas price to use for calculation of time
|
2020-10-06 20:28:38 +02:00
|
|
|
* @param {boolean} dontFormat - Whether the result should be be formatted, or just a number of minutes
|
2020-06-12 20:46:01 +02:00
|
|
|
* @returns {string | undefined} i18n formatted string if applicable
|
|
|
|
*/
|
|
|
|
export function useTransactionTimeRemaining (
|
|
|
|
isPending,
|
|
|
|
isEarliestNonce,
|
|
|
|
submittedTime,
|
2020-07-14 17:20:41 +02:00
|
|
|
currentGasPrice,
|
2020-10-06 20:28:38 +02:00
|
|
|
forceAllow,
|
|
|
|
dontFormat,
|
2020-06-12 20:46:01 +02:00
|
|
|
) {
|
|
|
|
// the following two selectors return the result of mapping over an array, as such they
|
|
|
|
// will always be new objects and trigger effects. To avoid this, we use isEqual as the
|
|
|
|
// equalityFn to only update when the data is new.
|
|
|
|
const gasPrices = useSelector(getEstimatedGasPrices, isEqual)
|
|
|
|
const estimatedTimes = useSelector(getEstimatedGasTimes, isEqual)
|
|
|
|
const locale = useSelector(getCurrentLocale)
|
|
|
|
const isMainNet = useSelector(getIsMainnet)
|
|
|
|
const interval = useRef()
|
|
|
|
const [timeRemaining, setTimeRemaining] = useState(null)
|
|
|
|
const featureFlags = useSelector(getFeatureFlags)
|
|
|
|
const transactionTimeFeatureActive = featureFlags?.transactionTime
|
|
|
|
|
2020-07-02 22:13:23 +02:00
|
|
|
const rtf = new Intl.RelativeTimeFormat(locale.replace('_', '-'), { numeric: 'auto', style: 'narrow' })
|
2020-06-12 20:46:01 +02:00
|
|
|
|
|
|
|
// Memoize this value so it can be used as a dependency in the effect below
|
|
|
|
const initialTimeEstimate = useMemo(() => {
|
|
|
|
const customGasPrice = Number(hexWEIToDecGWEI(currentGasPrice))
|
2020-07-17 01:29:22 +02:00
|
|
|
try {
|
|
|
|
const {
|
|
|
|
newTimeEstimate,
|
|
|
|
} = getRawTimeEstimateData(customGasPrice, gasPrices, estimatedTimes)
|
|
|
|
return newTimeEstimate
|
|
|
|
} catch (error) {
|
|
|
|
captureException(error)
|
|
|
|
return NaN
|
|
|
|
}
|
2020-08-19 18:27:05 +02:00
|
|
|
}, [currentGasPrice, gasPrices, estimatedTimes])
|
2020-06-12 20:46:01 +02:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (
|
2020-10-06 20:28:38 +02:00
|
|
|
(isMainNet &&
|
|
|
|
(transactionTimeFeatureActive || forceAllow)) &&
|
2020-06-12 20:46:01 +02:00
|
|
|
isPending &&
|
|
|
|
isEarliestNonce &&
|
|
|
|
!isNaN(initialTimeEstimate)
|
|
|
|
) {
|
|
|
|
clearInterval(interval.current)
|
|
|
|
setTimeRemaining(
|
2020-07-14 17:20:41 +02:00
|
|
|
calcTransactionTimeRemaining(initialTimeEstimate, submittedTime),
|
2020-06-12 20:46:01 +02:00
|
|
|
)
|
|
|
|
interval.current = setInterval(() => {
|
|
|
|
setTimeRemaining(
|
2020-07-14 17:20:41 +02:00
|
|
|
calcTransactionTimeRemaining(initialTimeEstimate, submittedTime),
|
2020-06-12 20:46:01 +02:00
|
|
|
)
|
|
|
|
}, 10000)
|
|
|
|
return () => clearInterval(interval.current)
|
|
|
|
}
|
2020-08-12 21:06:57 +02:00
|
|
|
return undefined
|
2020-06-12 20:46:01 +02:00
|
|
|
}, [
|
|
|
|
isMainNet,
|
|
|
|
transactionTimeFeatureActive,
|
|
|
|
isEarliestNonce,
|
|
|
|
isPending,
|
|
|
|
submittedTime,
|
|
|
|
initialTimeEstimate,
|
2020-10-06 20:28:38 +02:00
|
|
|
forceAllow,
|
2020-06-12 20:46:01 +02:00
|
|
|
])
|
|
|
|
|
|
|
|
// there are numerous checks to determine if time should be displayed.
|
|
|
|
// if any of the following are true, the timeRemaining will be null
|
|
|
|
// User is currently not on the mainnet
|
|
|
|
// User does not have the transactionTime feature flag enabled
|
|
|
|
// The transaction is not pending, or isn't the earliest nonce
|
2020-10-06 20:28:38 +02:00
|
|
|
const usedFormat = dontFormat
|
|
|
|
? timeRemaining
|
|
|
|
: rtf.format(timeRemaining, 'minute')
|
|
|
|
return timeRemaining ? usedFormat : undefined
|
2020-06-12 20:46:01 +02:00
|
|
|
}
|