2021-02-04 19:15:23 +01:00
|
|
|
import React, { useState, useEffect, useContext, useRef } from 'react';
|
|
|
|
import { useSelector } from 'react-redux';
|
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import classnames from 'classnames';
|
|
|
|
import { Duration } from 'luxon';
|
|
|
|
import { I18nContext } from '../../../contexts/i18n';
|
|
|
|
import InfoTooltip from '../../../components/ui/info-tooltip';
|
2022-04-04 15:14:50 +02:00
|
|
|
import {
|
|
|
|
getSwapsQuoteRefreshTime,
|
|
|
|
getSwapsQuotePrefetchingRefreshTime,
|
|
|
|
} from '../../../ducks/swaps/swaps';
|
2021-06-10 21:27:03 +02:00
|
|
|
import { SECOND } from '../../../../shared/constants/time';
|
2020-10-06 20:28:38 +02:00
|
|
|
|
|
|
|
// Return the mm:ss start time of the countdown timer.
|
|
|
|
// If time has elapsed between `timeStarted` the time current time,
|
|
|
|
// then that elapsed time will be subtracted from the timer before
|
|
|
|
// rendering
|
2020-11-03 00:41:28 +01:00
|
|
|
function getNewTimer(currentTime, timeStarted, timeBaseStart) {
|
2021-02-04 19:15:23 +01:00
|
|
|
const timeAlreadyElapsed = currentTime - timeStarted;
|
|
|
|
return timeBaseStart - timeAlreadyElapsed;
|
2020-10-06 20:28:38 +02:00
|
|
|
}
|
|
|
|
|
2020-11-03 00:41:28 +01:00
|
|
|
function decreaseTimerByOne(timer) {
|
2021-06-10 21:27:03 +02:00
|
|
|
return Math.max(timer - SECOND, 0);
|
2020-10-06 20:28:38 +02:00
|
|
|
}
|
|
|
|
|
2020-11-03 00:41:28 +01:00
|
|
|
function timeBelowWarningTime(timer, warningTime) {
|
2021-02-04 19:15:23 +01:00
|
|
|
const [warningTimeMinutes, warningTimeSeconds] = warningTime.split(':');
|
2020-11-03 00:41:28 +01:00
|
|
|
return (
|
|
|
|
timer <=
|
2021-06-10 21:27:03 +02:00
|
|
|
(Number(warningTimeMinutes) * 60 + Number(warningTimeSeconds)) * SECOND
|
2021-02-04 19:15:23 +01:00
|
|
|
);
|
2020-10-06 20:28:38 +02:00
|
|
|
}
|
|
|
|
|
2020-11-03 00:41:28 +01:00
|
|
|
export default function CountdownTimer({
|
2020-10-06 20:28:38 +02:00
|
|
|
timeStarted,
|
|
|
|
timeOnly,
|
2020-12-15 21:24:22 +01:00
|
|
|
timerBase,
|
2020-10-06 20:28:38 +02:00
|
|
|
warningTime,
|
|
|
|
labelKey,
|
|
|
|
infoTooltipLabelKey,
|
|
|
|
}) {
|
2021-02-04 19:15:23 +01:00
|
|
|
const t = useContext(I18nContext);
|
|
|
|
const intervalRef = useRef();
|
|
|
|
const initialTimeStartedRef = useRef();
|
2020-10-06 20:28:38 +02:00
|
|
|
|
2021-02-04 19:15:23 +01:00
|
|
|
const swapsQuoteRefreshTime = useSelector(getSwapsQuoteRefreshTime);
|
2022-04-04 15:14:50 +02:00
|
|
|
const swapsQuotePrefetchingRefreshTime = useSelector(
|
|
|
|
getSwapsQuotePrefetchingRefreshTime,
|
|
|
|
);
|
|
|
|
const refreshTime = initialTimeStartedRef.current
|
|
|
|
? swapsQuoteRefreshTime
|
|
|
|
: swapsQuotePrefetchingRefreshTime;
|
|
|
|
const timerStart = Number(timerBase) || refreshTime;
|
2020-12-15 21:24:22 +01:00
|
|
|
|
2021-02-04 19:15:23 +01:00
|
|
|
const [currentTime, setCurrentTime] = useState(() => Date.now());
|
2020-11-03 00:41:28 +01:00
|
|
|
const [timer, setTimer] = useState(() =>
|
2020-12-15 21:24:22 +01:00
|
|
|
getNewTimer(currentTime, timeStarted, timerStart),
|
2021-02-04 19:15:23 +01:00
|
|
|
);
|
2020-10-06 20:28:38 +02:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (intervalRef.current === undefined) {
|
|
|
|
intervalRef.current = setInterval(() => {
|
2021-02-04 19:15:23 +01:00
|
|
|
setTimer(decreaseTimerByOne);
|
2021-06-10 21:27:03 +02:00
|
|
|
}, SECOND);
|
2020-10-06 20:28:38 +02:00
|
|
|
}
|
|
|
|
|
2020-11-03 00:41:28 +01:00
|
|
|
return function cleanup() {
|
2021-02-04 19:15:23 +01:00
|
|
|
clearInterval(intervalRef.current);
|
|
|
|
};
|
|
|
|
}, []);
|
2020-10-06 20:28:38 +02:00
|
|
|
|
|
|
|
// Reset the timer that timer has hit '0:00' and the timeStarted prop has changed
|
|
|
|
useEffect(() => {
|
|
|
|
if (!initialTimeStartedRef.current) {
|
2021-02-04 19:15:23 +01:00
|
|
|
initialTimeStartedRef.current = timeStarted || Date.now();
|
2020-10-06 20:28:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (timer === 0 && timeStarted !== initialTimeStartedRef.current) {
|
2021-02-04 19:15:23 +01:00
|
|
|
initialTimeStartedRef.current = timeStarted;
|
|
|
|
const newCurrentTime = Date.now();
|
|
|
|
setCurrentTime(newCurrentTime);
|
|
|
|
setTimer(getNewTimer(newCurrentTime, timeStarted, timerStart));
|
2020-10-06 20:28:38 +02:00
|
|
|
|
2021-02-04 19:15:23 +01:00
|
|
|
clearInterval(intervalRef.current);
|
2020-10-06 20:28:38 +02:00
|
|
|
intervalRef.current = setInterval(() => {
|
2021-02-04 19:15:23 +01:00
|
|
|
setTimer(decreaseTimerByOne);
|
2021-06-10 21:27:03 +02:00
|
|
|
}, SECOND);
|
2020-10-06 20:28:38 +02:00
|
|
|
}
|
2021-02-04 19:15:23 +01:00
|
|
|
}, [timeStarted, timer, timerStart]);
|
2020-10-06 20:28:38 +02:00
|
|
|
|
2021-02-04 19:15:23 +01:00
|
|
|
const formattedTimer = Duration.fromMillis(timer).toFormat('m:ss');
|
|
|
|
let time;
|
2020-10-06 20:28:38 +02:00
|
|
|
if (timeOnly) {
|
2021-02-04 19:15:23 +01:00
|
|
|
time = <div className="countdown-timer__time">{formattedTimer}</div>;
|
2020-10-06 20:28:38 +02:00
|
|
|
} else if (labelKey) {
|
2020-11-03 00:41:28 +01:00
|
|
|
time = t(labelKey, [
|
|
|
|
<div key="countdown-time-1" className="countdown-timer__time">
|
|
|
|
{formattedTimer}
|
|
|
|
</div>,
|
2021-02-04 19:15:23 +01:00
|
|
|
]);
|
2020-10-06 20:28:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="countdown-timer">
|
|
|
|
<div
|
Increase Jest unit test coverage for the Swaps feature to ~25% (#10900)
* Swaps: Show a network name dynamically in a tooltip
* Replace “Ethereum” with “$1”, change “Test” to “Testnet”
* Replace 이더리움 with $1
* Translate network names, use ‘Ethereum’ by default if a translation is not available yet
* Reorder messages to resolve ESLint issues
* Add a snapshot test for the FeeCard component, increase Jest threshold
* Enable snapshot testing into external .snap files in ESLint
* Add the “networkNameEthereum” key in ko/messages.json, remove default “Ethereum” value
* Throw an error if chain ID is not supported by the Swaps feature
* Use string literals when calling the `t` fn,
* Watch Jest tests silently (no React warnings in terminal, only errors)
* Add @testing-library/jest-dom, import it before running Jest tests
* Add snapshot testing of Swaps’ React components for happy paths, increase minimum threshold for Jest
* Add the test/jest folder for Jest setup and shared functions, use it in Swaps Jest tests
* Fix ESLint issues, update linting config
* Enable ESLint for .snap files (Jest snapshots), throw an error if a snapshot is bigger than 50 lines
* Don’t run lint:fix for .snap files
* Move `createProps` outside of `describe` blocks, move store creation inside tests
* Use translations instead of keys, update a rendering function to load translations
* Make sure all Jest snapshots are shorter than 50 lines (default limit)
* Add / update props for Swaps tests
* Fix React warnings when running tests for Swaps
2021-04-21 21:34:35 +02:00
|
|
|
data-testid="countdown-timer__timer-container"
|
2020-10-06 20:28:38 +02:00
|
|
|
className={classnames('countdown-timer__timer-container', {
|
2020-11-03 00:41:28 +01:00
|
|
|
'countdown-timer__timer-container--warning':
|
|
|
|
warningTime && timeBelowWarningTime(timer, warningTime),
|
2020-10-06 20:28:38 +02:00
|
|
|
})}
|
|
|
|
>
|
|
|
|
{time}
|
|
|
|
</div>
|
2020-11-03 00:41:28 +01:00
|
|
|
{!timeOnly && infoTooltipLabelKey ? (
|
|
|
|
<InfoTooltip position="bottom" contentText={t(infoTooltipLabelKey)} />
|
|
|
|
) : null}
|
2020-10-06 20:28:38 +02:00
|
|
|
</div>
|
2021-02-04 19:15:23 +01:00
|
|
|
);
|
2020-10-06 20:28:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CountdownTimer.propTypes = {
|
2022-03-31 20:31:31 +02:00
|
|
|
/**
|
|
|
|
* Unix timestamp that indicates the time at which this timer has started
|
|
|
|
* running.
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
timeStarted: PropTypes.number,
|
2022-03-31 20:31:31 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Boolean indicating whether to display only the time (`true`) or to also
|
|
|
|
* display a label (`false`), given by the `labelKey` parameter.
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
timeOnly: PropTypes.bool,
|
2022-03-31 20:31:31 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The duration of this timer in milliseconds.
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
timerBase: PropTypes.number,
|
2022-03-31 20:31:31 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The time at which this timer should turn red, indicating it has almost run
|
|
|
|
* out of time. Given in the format `mm:ss`.
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
warningTime: PropTypes.string,
|
2022-03-31 20:31:31 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The key of the label to display next to the timer, defined in
|
|
|
|
* `app/_locales/`.
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
labelKey: PropTypes.string,
|
2022-03-31 20:31:31 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The key of the label to display in the tooltip when hovering over the info
|
|
|
|
* icon, defined in `app/_locales/`.
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
infoTooltipLabelKey: PropTypes.string,
|
2021-02-04 19:15:23 +01:00
|
|
|
};
|