mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Restore heartbeat to transaction confirmation, use isGasEstimatesLoading more broadly (#11781)
This commit is contained in:
parent
804fefcd36
commit
a0bd496d56
@ -234,6 +234,7 @@ export const createSwapsMockStore = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
gasLoadingAnimationIsShowing: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -7,6 +7,8 @@ import FormField from '../../ui/form-field';
|
|||||||
import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas';
|
import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas';
|
||||||
import { getGasFormErrorText } from '../../../helpers/constants/gas';
|
import { getGasFormErrorText } from '../../../helpers/constants/gas';
|
||||||
import { checkNetworkAndAccountSupports1559 } from '../../../selectors';
|
import { checkNetworkAndAccountSupports1559 } from '../../../selectors';
|
||||||
|
import { getIsGasEstimatesLoading } from '../../../ducks/metamask/metamask';
|
||||||
|
import { getGasLoadingAnimationIsShowing } from '../../../ducks/app/app';
|
||||||
|
|
||||||
export default function AdvancedGasControls({
|
export default function AdvancedGasControls({
|
||||||
gasEstimateType,
|
gasEstimateType,
|
||||||
@ -28,6 +30,12 @@ export default function AdvancedGasControls({
|
|||||||
const networkAndAccountSupport1559 = useSelector(
|
const networkAndAccountSupport1559 = useSelector(
|
||||||
checkNetworkAndAccountSupports1559,
|
checkNetworkAndAccountSupports1559,
|
||||||
);
|
);
|
||||||
|
const isGasEstimatesLoading = useSelector(getIsGasEstimatesLoading);
|
||||||
|
const isGasLoadingAnimationIsShowing = useSelector(
|
||||||
|
getGasLoadingAnimationIsShowing,
|
||||||
|
);
|
||||||
|
const disableFormFields =
|
||||||
|
isGasEstimatesLoading || isGasLoadingAnimationIsShowing;
|
||||||
|
|
||||||
const showFeeMarketFields =
|
const showFeeMarketFields =
|
||||||
networkAndAccountSupport1559 &&
|
networkAndAccountSupport1559 &&
|
||||||
@ -71,6 +79,7 @@ export default function AdvancedGasControls({
|
|||||||
? getGasFormErrorText(gasErrors.maxPriorityFee, t)
|
? getGasFormErrorText(gasErrors.maxPriorityFee, t)
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
disabled={disableFormFields}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
titleText={t('maxFee')}
|
titleText={t('maxFee')}
|
||||||
@ -88,6 +97,7 @@ export default function AdvancedGasControls({
|
|||||||
? getGasFormErrorText(gasErrors.maxFee, t)
|
? getGasFormErrorText(gasErrors.maxFee, t)
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
disabled={disableFormFields}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@ -107,6 +117,7 @@ export default function AdvancedGasControls({
|
|||||||
? getGasFormErrorText(gasErrors.gasPrice, t)
|
? getGasFormErrorText(gasErrors.gasPrice, t)
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
disabled={disableFormFields}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -2,7 +2,7 @@ import React, { useCallback, useContext, useState } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { useGasFeeInputs } from '../../../hooks/useGasFeeInputs';
|
import { useGasFeeInputs } from '../../../hooks/useGasFeeInputs';
|
||||||
import { useShouldAnimateGasEstimations } from '../../../hooks/useShouldAnimateGasEstimations';
|
import { getGasLoadingAnimationIsShowing } from '../../../ducks/app/app';
|
||||||
|
|
||||||
import { EDIT_GAS_MODES, GAS_LIMITS } from '../../../../shared/constants/gas';
|
import { EDIT_GAS_MODES, GAS_LIMITS } from '../../../../shared/constants/gas';
|
||||||
|
|
||||||
@ -45,8 +45,9 @@ export default function EditGasPopover({
|
|||||||
const networkAndAccountSupport1559 = useSelector(
|
const networkAndAccountSupport1559 = useSelector(
|
||||||
checkNetworkAndAccountSupports1559,
|
checkNetworkAndAccountSupports1559,
|
||||||
);
|
);
|
||||||
|
const gasLoadingAnimationIsShowing = useSelector(
|
||||||
const shouldAnimate = useShouldAnimateGasEstimations();
|
getGasLoadingAnimationIsShowing,
|
||||||
|
);
|
||||||
|
|
||||||
const showEducationButton =
|
const showEducationButton =
|
||||||
(mode === EDIT_GAS_MODES.MODIFY_IN_PLACE ||
|
(mode === EDIT_GAS_MODES.MODIFY_IN_PLACE ||
|
||||||
@ -192,7 +193,12 @@ export default function EditGasPopover({
|
|||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
disabled={hasGasErrors || isGasEstimatesLoading || balanceError}
|
disabled={
|
||||||
|
hasGasErrors ||
|
||||||
|
isGasEstimatesLoading ||
|
||||||
|
balanceError ||
|
||||||
|
gasLoadingAnimationIsShowing
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{footerButtonText}
|
{footerButtonText}
|
||||||
</Button>
|
</Button>
|
||||||
@ -205,9 +211,7 @@ export default function EditGasPopover({
|
|||||||
<EditGasDisplayEducation />
|
<EditGasDisplayEducation />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{process.env.IN_TEST === 'true' ? null : (
|
{process.env.IN_TEST === 'true' ? null : <LoadingHeartBeat />}
|
||||||
<LoadingHeartBeat active={shouldAnimate} />
|
|
||||||
)}
|
|
||||||
<EditGasDisplay
|
<EditGasDisplay
|
||||||
showEducationButton={showEducationButton}
|
showEducationButton={showEducationButton}
|
||||||
warning={warning}
|
warning={warning}
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
|
|
||||||
import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas';
|
import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas';
|
||||||
|
|
||||||
import { useGasFeeEstimates } from '../../../hooks/useGasFeeEstimates';
|
|
||||||
import { usePrevious } from '../../../hooks/usePrevious';
|
import { usePrevious } from '../../../hooks/usePrevious';
|
||||||
import { I18nContext } from '../../../contexts/i18n';
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getGasEstimateType,
|
||||||
|
getGasFeeEstimates,
|
||||||
|
getIsGasEstimatesLoading,
|
||||||
|
} from '../../../ducks/metamask/metamask';
|
||||||
|
|
||||||
import Typography from '../../ui/typography/typography';
|
import Typography from '../../ui/typography/typography';
|
||||||
import {
|
import {
|
||||||
TYPOGRAPHY,
|
TYPOGRAPHY,
|
||||||
@ -33,11 +39,9 @@ export default function GasTiming({
|
|||||||
maxFeePerGas = 0,
|
maxFeePerGas = 0,
|
||||||
maxPriorityFeePerGas = 0,
|
maxPriorityFeePerGas = 0,
|
||||||
}) {
|
}) {
|
||||||
const {
|
const gasEstimateType = useSelector(getGasEstimateType);
|
||||||
gasFeeEstimates,
|
const gasFeeEstimates = useSelector(getGasFeeEstimates);
|
||||||
isGasEstimatesLoading,
|
const isGasEstimatesLoading = useSelector(getIsGasEstimatesLoading);
|
||||||
gasEstimateType,
|
|
||||||
} = useGasFeeEstimates();
|
|
||||||
|
|
||||||
const [customEstimatedTime, setCustomEstimatedTime] = useState(null);
|
const [customEstimatedTime, setCustomEstimatedTime] = useState(null);
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
|
|
||||||
@ -9,7 +10,13 @@ import messages from '../../../../app/_locales/en/messages.json';
|
|||||||
import { getMessage } from '../../../helpers/utils/i18n-helper';
|
import { getMessage } from '../../../helpers/utils/i18n-helper';
|
||||||
|
|
||||||
import * as i18nhooks from '../../../hooks/useI18nContext';
|
import * as i18nhooks from '../../../hooks/useI18nContext';
|
||||||
import * as useGasFeeEstimatesExport from '../../../hooks/useGasFeeEstimates';
|
|
||||||
|
import { checkNetworkAndAccountSupports1559 } from '../../../selectors';
|
||||||
|
import {
|
||||||
|
getGasEstimateType,
|
||||||
|
getGasFeeEstimates,
|
||||||
|
getIsGasEstimatesLoading,
|
||||||
|
} from '../../../ducks/metamask/metamask';
|
||||||
|
|
||||||
import GasTiming from '.';
|
import GasTiming from '.';
|
||||||
|
|
||||||
@ -35,58 +42,103 @@ const MOCK_FEE_ESTIMATE = {
|
|||||||
estimatedBaseFee: '50',
|
estimatedBaseFee: '50',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
jest.mock('react-redux', () => {
|
||||||
|
const actual = jest.requireActual('react-redux');
|
||||||
|
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
useSelector: jest.fn(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const DEFAULT_OPTS = {
|
||||||
|
gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
|
||||||
|
gasFeeEstimates: {
|
||||||
|
low: '10',
|
||||||
|
medium: '20',
|
||||||
|
high: '30',
|
||||||
|
},
|
||||||
|
isGasEstimatesLoading: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateUseSelectorRouter = (opts = DEFAULT_OPTS) => (selector) => {
|
||||||
|
if (selector === checkNetworkAndAccountSupports1559) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (selector === getGasEstimateType) {
|
||||||
|
return opts.gasEstimateType ?? DEFAULT_OPTS.gasEstimateType;
|
||||||
|
}
|
||||||
|
if (selector === getGasFeeEstimates) {
|
||||||
|
return opts.gasFeeEstimates ?? DEFAULT_OPTS.gasFeeEstimates;
|
||||||
|
}
|
||||||
|
if (selector === getIsGasEstimatesLoading) {
|
||||||
|
return opts.isGasEstimatesLoading ?? DEFAULT_OPTS.isGasEstimatesLoading;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
describe('Gas timing', () => {
|
describe('Gas timing', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const useI18nContext = sinon.stub(i18nhooks, 'useI18nContext');
|
const useI18nContext = sinon.stub(i18nhooks, 'useI18nContext');
|
||||||
useI18nContext.returns((key, variables) =>
|
useI18nContext.returns((key, variables) =>
|
||||||
getMessage('en', messages, key, variables),
|
getMessage('en', messages, key, variables),
|
||||||
);
|
);
|
||||||
|
jest.clearAllMocks();
|
||||||
|
useSelector.mockImplementation(generateUseSelectorRouter());
|
||||||
});
|
});
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
sinon.restore();
|
sinon.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders nothing when gas is loading', () => {
|
it('renders nothing when gas is loading', () => {
|
||||||
sinon.stub(useGasFeeEstimatesExport, 'useGasFeeEstimates').returns({
|
useSelector.mockImplementation(
|
||||||
isGasEstimatesLoading: true,
|
generateUseSelectorRouter({
|
||||||
gasFeeEstimates: null,
|
isGasEstimatesLoading: true,
|
||||||
gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
|
gasFeeEstimates: null,
|
||||||
});
|
gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const wrapper = shallow(<GasTiming />);
|
const wrapper = shallow(<GasTiming />);
|
||||||
expect(wrapper.html()).toBeNull();
|
expect(wrapper.html()).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders "very likely" when high estimate is chosen', () => {
|
it('renders "very likely" when high estimate is chosen', () => {
|
||||||
sinon.stub(useGasFeeEstimatesExport, 'useGasFeeEstimates').returns({
|
useSelector.mockImplementation(
|
||||||
isGasEstimatesLoading: false,
|
generateUseSelectorRouter({
|
||||||
gasFeeEstimates: MOCK_FEE_ESTIMATE,
|
isGasEstimatesLoading: false,
|
||||||
gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
|
gasFeeEstimates: MOCK_FEE_ESTIMATE,
|
||||||
});
|
gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const wrapper = shallow(<GasTiming maxPriorityFeePerGas={10} />);
|
const wrapper = shallow(<GasTiming maxPriorityFeePerGas="10" />);
|
||||||
expect(wrapper.html()).toContain('gasTimingVeryPositive');
|
expect(wrapper.html()).toContain('gasTimingVeryPositive');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders "likely" when medium estimate is chosen', () => {
|
it('renders "likely" when medium estimate is chosen', () => {
|
||||||
sinon.stub(useGasFeeEstimatesExport, 'useGasFeeEstimates').returns({
|
useSelector.mockImplementation(
|
||||||
isGasEstimatesLoading: false,
|
generateUseSelectorRouter({
|
||||||
gasFeeEstimates: MOCK_FEE_ESTIMATE,
|
isGasEstimatesLoading: false,
|
||||||
gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
|
gasFeeEstimates: MOCK_FEE_ESTIMATE,
|
||||||
});
|
gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const wrapper = shallow(<GasTiming maxPriorityFeePerGas={8} />);
|
const wrapper = shallow(<GasTiming maxPriorityFeePerGas="8" />);
|
||||||
expect(wrapper.html()).toContain('gasTimingPositive');
|
expect(wrapper.html()).toContain('gasTimingPositive');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders "maybe" when low estimate is chosen', () => {
|
it('renders "maybe" when low estimate is chosen', () => {
|
||||||
sinon.stub(useGasFeeEstimatesExport, 'useGasFeeEstimates').returns({
|
useSelector.mockImplementation(
|
||||||
isGasEstimatesLoading: false,
|
generateUseSelectorRouter({
|
||||||
gasFeeEstimates: MOCK_FEE_ESTIMATE,
|
isGasEstimatesLoading: false,
|
||||||
gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
|
gasFeeEstimates: MOCK_FEE_ESTIMATE,
|
||||||
});
|
gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const wrapper = shallow(<GasTiming maxPriorityFeePerGas={3} />);
|
const wrapper = shallow(<GasTiming maxPriorityFeePerGas="3" />);
|
||||||
expect(wrapper.html()).toContain('gasTimingNegative');
|
expect(wrapper.html()).toContain('gasTimingNegative');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,18 +1,33 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import { getIsGasEstimatesLoading } from '../../../ducks/metamask/metamask';
|
||||||
|
import { getGasLoadingAnimationIsShowing } from '../../../ducks/app/app';
|
||||||
|
|
||||||
import { I18nContext } from '../../../contexts/i18n';
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
|
|
||||||
import TransactionDetailItem from '../transaction-detail-item/transaction-detail-item.component';
|
import TransactionDetailItem from '../transaction-detail-item/transaction-detail-item.component';
|
||||||
|
import LoadingHeartBeat from '../../ui/loading-heartbeat';
|
||||||
|
|
||||||
export default function TransactionDetail({ rows = [], onEdit }) {
|
export default function TransactionDetail({ rows = [], onEdit }) {
|
||||||
const t = useContext(I18nContext);
|
const t = useContext(I18nContext);
|
||||||
|
const isGasEstimatesLoading = useSelector(getIsGasEstimatesLoading);
|
||||||
|
const gasLoadingAnimationIsShowing = useSelector(
|
||||||
|
getGasLoadingAnimationIsShowing,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="transaction-detail">
|
<div className="transaction-detail">
|
||||||
|
{process.env.IN_TEST === 'true' ? null : <LoadingHeartBeat />}
|
||||||
{onEdit && (
|
{onEdit && (
|
||||||
<div className="transaction-detail-edit">
|
<div className="transaction-detail-edit">
|
||||||
<button onClick={onEdit}>{t('edit')}</button>
|
<button
|
||||||
|
onClick={onEdit}
|
||||||
|
disabled={isGasEstimatesLoading || gasLoadingAnimationIsShowing}
|
||||||
|
>
|
||||||
|
{t('edit')}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="transaction-detail-rows">{rows}</div>
|
<div className="transaction-detail-rows">{rows}</div>
|
||||||
|
@ -28,6 +28,7 @@ export default function FormField({
|
|||||||
autoFocus,
|
autoFocus,
|
||||||
password,
|
password,
|
||||||
allowDecimals,
|
allowDecimals,
|
||||||
|
disabled,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -81,6 +82,7 @@ export default function FormField({
|
|||||||
detailText={detailText}
|
detailText={detailText}
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
allowDecimals={allowDecimals}
|
allowDecimals={allowDecimals}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<input
|
<input
|
||||||
@ -91,6 +93,7 @@ export default function FormField({
|
|||||||
value={value}
|
value={value}
|
||||||
type={password ? 'password' : 'text'}
|
type={password ? 'password' : 'text'}
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{error && (
|
{error && (
|
||||||
@ -120,6 +123,7 @@ FormField.propTypes = {
|
|||||||
numeric: PropTypes.bool,
|
numeric: PropTypes.bool,
|
||||||
password: PropTypes.bool,
|
password: PropTypes.bool,
|
||||||
allowDecimals: PropTypes.bool,
|
allowDecimals: PropTypes.bool,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
FormField.defaultProps = {
|
FormField.defaultProps = {
|
||||||
@ -135,4 +139,5 @@ FormField.defaultProps = {
|
|||||||
numeric: false,
|
numeric: false,
|
||||||
password: false,
|
password: false,
|
||||||
allowDecimals: true,
|
allowDecimals: true,
|
||||||
|
disabled: false,
|
||||||
};
|
};
|
||||||
|
@ -1,36 +1,25 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import React from 'react';
|
||||||
import React, { useEffect, useRef } from 'react';
|
import { useSelector } from 'react-redux';
|
||||||
|
import { getGasLoadingAnimationIsShowing } from '../../../ducks/app/app';
|
||||||
|
import { useShouldAnimateGasEstimations } from '../../../hooks/useShouldAnimateGasEstimations';
|
||||||
|
|
||||||
export default function LoadingHeartBeat({ active }) {
|
const BASE_CLASS = 'loading-heartbeat';
|
||||||
const heartNode = useRef(null);
|
const LOADING_CLASS = `${BASE_CLASS}--active`;
|
||||||
|
|
||||||
const LOADING_CLASS = 'loading-heartbeat--active';
|
export default function LoadingHeartBeat() {
|
||||||
|
useShouldAnimateGasEstimations();
|
||||||
// When the loading animation completes, remove the className to disappear again
|
const active = useSelector(getGasLoadingAnimationIsShowing);
|
||||||
useEffect(() => {
|
|
||||||
const eventName = 'animationend';
|
|
||||||
const node = heartNode?.current;
|
|
||||||
const eventHandler = () => {
|
|
||||||
node?.classList.remove(LOADING_CLASS);
|
|
||||||
};
|
|
||||||
|
|
||||||
node?.addEventListener(eventName, eventHandler);
|
|
||||||
return () => {
|
|
||||||
node?.removeEventListener(eventName, eventHandler);
|
|
||||||
};
|
|
||||||
}, [heartNode]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames('loading-heartbeat', {
|
className={classNames('loading-heartbeat', {
|
||||||
[LOADING_CLASS]: active,
|
[LOADING_CLASS]: active,
|
||||||
})}
|
})}
|
||||||
ref={heartNode}
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
></div>
|
></div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadingHeartBeat.propTypes = {
|
|
||||||
active: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
@ -7,10 +7,14 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
display: none;
|
display: none;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
&--active {
|
&--active {
|
||||||
display: block;
|
display: block;
|
||||||
animation: heartbeat 2s ease-in-out;
|
animation-name: heartbeat;
|
||||||
|
animation-duration: 2s;
|
||||||
|
animation-timing-function: ease-in-out;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ export default function NumericInput({
|
|||||||
error = '',
|
error = '',
|
||||||
autoFocus = false,
|
autoFocus = false,
|
||||||
allowDecimals = true,
|
allowDecimals = true,
|
||||||
|
disabled = false,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -29,6 +30,7 @@ export default function NumericInput({
|
|||||||
}}
|
}}
|
||||||
min="0"
|
min="0"
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
{detailText && (
|
{detailText && (
|
||||||
<Typography color={COLORS.UI4} variant={TYPOGRAPHY.H7} tag="span">
|
<Typography color={COLORS.UI4} variant={TYPOGRAPHY.H7} tag="span">
|
||||||
@ -46,4 +48,5 @@ NumericInput.propTypes = {
|
|||||||
error: PropTypes.string,
|
error: PropTypes.string,
|
||||||
autoFocus: PropTypes.bool,
|
autoFocus: PropTypes.bool,
|
||||||
allowDecimals: PropTypes.bool,
|
allowDecimals: PropTypes.bool,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
@ -53,6 +53,7 @@ export default function reduceApp(state = {}, action) {
|
|||||||
singleExceptions: {
|
singleExceptions: {
|
||||||
testKey: null,
|
testKey: null,
|
||||||
},
|
},
|
||||||
|
gasLoadingAnimationIsShowing: false,
|
||||||
...state,
|
...state,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -358,6 +359,12 @@ export default function reduceApp(state = {}, action) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
case actionConstants.TOGGLE_GAS_LOADING_ANIMATION:
|
||||||
|
return {
|
||||||
|
...appState,
|
||||||
|
gasLoadingAnimationIsShowing: action.value,
|
||||||
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return appState;
|
return appState;
|
||||||
}
|
}
|
||||||
@ -377,7 +384,15 @@ export function hideWhatsNewPopup() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toggleGasLoadingAnimation(value) {
|
||||||
|
return { type: actionConstants.TOGGLE_GAS_LOADING_ANIMATION, value };
|
||||||
|
}
|
||||||
|
|
||||||
// Selectors
|
// Selectors
|
||||||
export function getQrCodeData(state) {
|
export function getQrCodeData(state) {
|
||||||
return state.appState.qrCodeData;
|
return state.appState.qrCodeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getGasLoadingAnimationIsShowing(state) {
|
||||||
|
return state.appState.gasLoadingAnimationIsShowing;
|
||||||
|
}
|
||||||
|
@ -4,11 +4,13 @@ import { ALERT_TYPES } from '../../../shared/constants/alerts';
|
|||||||
import { NETWORK_TYPE_RPC } from '../../../shared/constants/network';
|
import { NETWORK_TYPE_RPC } from '../../../shared/constants/network';
|
||||||
import {
|
import {
|
||||||
accountsWithSendEtherInfoSelector,
|
accountsWithSendEtherInfoSelector,
|
||||||
|
checkNetworkAndAccountSupports1559,
|
||||||
getAddressBook,
|
getAddressBook,
|
||||||
} from '../../selectors';
|
} from '../../selectors';
|
||||||
import { updateTransaction } from '../../store/actions';
|
import { updateTransaction } from '../../store/actions';
|
||||||
import { setCustomGasLimit, setCustomGasPrice } from '../gas/gas.duck';
|
import { setCustomGasLimit, setCustomGasPrice } from '../gas/gas.duck';
|
||||||
import { decGWEIToHexWEI } from '../../helpers/utils/conversions.util';
|
import { decGWEIToHexWEI } from '../../helpers/utils/conversions.util';
|
||||||
|
import { GAS_ESTIMATE_TYPES } from '../../../shared/constants/gas';
|
||||||
|
|
||||||
export default function reduceMetamask(state = {}, action) {
|
export default function reduceMetamask(state = {}, action) {
|
||||||
const metamaskState = {
|
const metamaskState = {
|
||||||
@ -299,3 +301,24 @@ export function getGasFeeEstimates(state) {
|
|||||||
export function getEstimatedGasFeeTimeBounds(state) {
|
export function getEstimatedGasFeeTimeBounds(state) {
|
||||||
return state.metamask.estimatedGasFeeTimeBounds;
|
return state.metamask.estimatedGasFeeTimeBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getIsGasEstimatesLoading(state) {
|
||||||
|
const networkAndAccountSupports1559 = checkNetworkAndAccountSupports1559(
|
||||||
|
state,
|
||||||
|
);
|
||||||
|
const gasEstimateType = getGasEstimateType(state);
|
||||||
|
|
||||||
|
// We consider the gas estimate to be loading if the gasEstimateType is
|
||||||
|
// 'NONE' or if the current gasEstimateType cannot be supported by the current
|
||||||
|
// network
|
||||||
|
const isEIP1559TolerableEstimateType =
|
||||||
|
gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET ||
|
||||||
|
gasEstimateType === GAS_ESTIMATE_TYPES.ETH_GASPRICE;
|
||||||
|
const isGasEstimatesLoading =
|
||||||
|
gasEstimateType === GAS_ESTIMATE_TYPES.NONE ||
|
||||||
|
(networkAndAccountSupports1559 && !isEIP1559TolerableEstimateType) ||
|
||||||
|
(!networkAndAccountSupports1559 &&
|
||||||
|
gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET);
|
||||||
|
|
||||||
|
return isGasEstimatesLoading;
|
||||||
|
}
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { GAS_ESTIMATE_TYPES } from '../../shared/constants/gas';
|
|
||||||
import {
|
import {
|
||||||
getEstimatedGasFeeTimeBounds,
|
getEstimatedGasFeeTimeBounds,
|
||||||
getGasEstimateType,
|
getGasEstimateType,
|
||||||
getGasFeeEstimates,
|
getGasFeeEstimates,
|
||||||
|
getIsGasEstimatesLoading,
|
||||||
} from '../ducks/metamask/metamask';
|
} from '../ducks/metamask/metamask';
|
||||||
import { checkNetworkAndAccountSupports1559 } from '../selectors';
|
|
||||||
import { useSafeGasEstimatePolling } from './useSafeGasEstimatePolling';
|
import { useSafeGasEstimatePolling } from './useSafeGasEstimatePolling';
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {keyof typeof GAS_ESTIMATE_TYPES} GasEstimateTypes
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} GasEstimates
|
* @typedef {object} GasEstimates
|
||||||
* @property {GasEstimateTypes} gasEstimateType - The type of estimate provided
|
* @property {GasEstimateTypes} gasEstimateType - The type of estimate provided
|
||||||
@ -35,26 +30,12 @@ import { useSafeGasEstimatePolling } from './useSafeGasEstimatePolling';
|
|||||||
* @returns {GasFeeEstimates} - GasFeeEstimates object
|
* @returns {GasFeeEstimates} - GasFeeEstimates object
|
||||||
*/
|
*/
|
||||||
export function useGasFeeEstimates() {
|
export function useGasFeeEstimates() {
|
||||||
const networkAndAccountSupports1559 = useSelector(
|
|
||||||
checkNetworkAndAccountSupports1559,
|
|
||||||
);
|
|
||||||
const gasEstimateType = useSelector(getGasEstimateType);
|
const gasEstimateType = useSelector(getGasEstimateType);
|
||||||
const gasFeeEstimates = useSelector(getGasFeeEstimates);
|
const gasFeeEstimates = useSelector(getGasFeeEstimates);
|
||||||
const estimatedGasFeeTimeBounds = useSelector(getEstimatedGasFeeTimeBounds);
|
const estimatedGasFeeTimeBounds = useSelector(getEstimatedGasFeeTimeBounds);
|
||||||
|
const isGasEstimatesLoading = useSelector(getIsGasEstimatesLoading);
|
||||||
useSafeGasEstimatePolling();
|
useSafeGasEstimatePolling();
|
||||||
|
|
||||||
// We consider the gas estimate to be loading if the gasEstimateType is
|
|
||||||
// 'NONE' or if the current gasEstimateType cannot be supported by the current
|
|
||||||
// network
|
|
||||||
const isEIP1559TolerableEstimateType =
|
|
||||||
gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET ||
|
|
||||||
gasEstimateType === GAS_ESTIMATE_TYPES.ETH_GASPRICE;
|
|
||||||
const isGasEstimatesLoading =
|
|
||||||
gasEstimateType === GAS_ESTIMATE_TYPES.NONE ||
|
|
||||||
(networkAndAccountSupports1559 && !isEIP1559TolerableEstimateType) ||
|
|
||||||
(!networkAndAccountSupports1559 &&
|
|
||||||
gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
gasFeeEstimates,
|
gasFeeEstimates,
|
||||||
gasEstimateType,
|
gasEstimateType,
|
||||||
|
@ -5,12 +5,14 @@ import createRandomId from '../../shared/modules/random-id';
|
|||||||
import {
|
import {
|
||||||
getGasEstimateType,
|
getGasEstimateType,
|
||||||
getGasFeeEstimates,
|
getGasFeeEstimates,
|
||||||
|
getIsGasEstimatesLoading,
|
||||||
} from '../ducks/metamask/metamask';
|
} from '../ducks/metamask/metamask';
|
||||||
import { checkNetworkAndAccountSupports1559 } from '../selectors';
|
import { checkNetworkAndAccountSupports1559 } from '../selectors';
|
||||||
import {
|
import {
|
||||||
disconnectGasFeeEstimatePoller,
|
disconnectGasFeeEstimatePoller,
|
||||||
getGasFeeEstimatesAndStartPolling,
|
getGasFeeEstimatesAndStartPolling,
|
||||||
} from '../store/actions';
|
} from '../store/actions';
|
||||||
|
|
||||||
import { useGasFeeEstimates } from './useGasFeeEstimates';
|
import { useGasFeeEstimates } from './useGasFeeEstimates';
|
||||||
|
|
||||||
jest.mock('../store/actions', () => ({
|
jest.mock('../store/actions', () => ({
|
||||||
@ -37,6 +39,7 @@ const DEFAULT_OPTS = {
|
|||||||
medium: '20',
|
medium: '20',
|
||||||
high: '30',
|
high: '30',
|
||||||
},
|
},
|
||||||
|
isGasEstimatesLoading: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUseSelectorRouter = (opts = DEFAULT_OPTS) => (selector) => {
|
const generateUseSelectorRouter = (opts = DEFAULT_OPTS) => (selector) => {
|
||||||
@ -52,6 +55,9 @@ const generateUseSelectorRouter = (opts = DEFAULT_OPTS) => (selector) => {
|
|||||||
if (selector === getGasFeeEstimates) {
|
if (selector === getGasFeeEstimates) {
|
||||||
return opts.gasFeeEstimates ?? DEFAULT_OPTS.gasFeeEstimates;
|
return opts.gasFeeEstimates ?? DEFAULT_OPTS.gasFeeEstimates;
|
||||||
}
|
}
|
||||||
|
if (selector === getIsGasEstimatesLoading) {
|
||||||
|
return opts.isGasEstimatesLoading ?? DEFAULT_OPTS.isGasEstimatesLoading;
|
||||||
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -68,15 +74,16 @@ describe('useGasFeeEstimates', () => {
|
|||||||
disconnectGasFeeEstimatePoller.mockImplementation((token) => {
|
disconnectGasFeeEstimatePoller.mockImplementation((token) => {
|
||||||
tokens = tokens.filter((tkn) => tkn !== token);
|
tokens = tokens.filter((tkn) => tkn !== token);
|
||||||
});
|
});
|
||||||
useSelector.mockImplementation(generateUseSelectorRouter());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('registers with the controller', () => {
|
it('registers with the controller', () => {
|
||||||
|
useSelector.mockImplementation(generateUseSelectorRouter());
|
||||||
renderHook(() => useGasFeeEstimates());
|
renderHook(() => useGasFeeEstimates());
|
||||||
expect(tokens).toHaveLength(1);
|
expect(tokens).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('clears token with the controller on unmount', async () => {
|
it('clears token with the controller on unmount', async () => {
|
||||||
|
useSelector.mockImplementation(generateUseSelectorRouter());
|
||||||
renderHook(() => useGasFeeEstimates());
|
renderHook(() => useGasFeeEstimates());
|
||||||
expect(tokens).toHaveLength(1);
|
expect(tokens).toHaveLength(1);
|
||||||
const expectedToken = tokens[0];
|
const expectedToken = tokens[0];
|
||||||
@ -87,6 +94,11 @@ describe('useGasFeeEstimates', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('works with LEGACY gas prices', () => {
|
it('works with LEGACY gas prices', () => {
|
||||||
|
useSelector.mockImplementation(
|
||||||
|
generateUseSelectorRouter({
|
||||||
|
isGasEstimatesLoading: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
const {
|
const {
|
||||||
result: { current },
|
result: { current },
|
||||||
} = renderHook(() => useGasFeeEstimates());
|
} = renderHook(() => useGasFeeEstimates());
|
||||||
@ -104,6 +116,7 @@ describe('useGasFeeEstimates', () => {
|
|||||||
generateUseSelectorRouter({
|
generateUseSelectorRouter({
|
||||||
gasEstimateType: GAS_ESTIMATE_TYPES.ETH_GASPRICE,
|
gasEstimateType: GAS_ESTIMATE_TYPES.ETH_GASPRICE,
|
||||||
gasFeeEstimates,
|
gasFeeEstimates,
|
||||||
|
isGasEstimatesLoading: false,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -145,6 +158,7 @@ describe('useGasFeeEstimates', () => {
|
|||||||
checkNetworkAndAccountSupports1559: true,
|
checkNetworkAndAccountSupports1559: true,
|
||||||
gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
|
gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
|
||||||
gasFeeEstimates,
|
gasFeeEstimates,
|
||||||
|
isGasEstimatesLoading: false,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,10 +1,20 @@
|
|||||||
import { useRef } from 'react';
|
import { useRef, useEffect } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getGasLoadingAnimationIsShowing,
|
||||||
|
toggleGasLoadingAnimation,
|
||||||
|
} from '../ducks/app/app';
|
||||||
import { useGasFeeEstimates } from './useGasFeeEstimates';
|
import { useGasFeeEstimates } from './useGasFeeEstimates';
|
||||||
|
|
||||||
export function useShouldAnimateGasEstimations() {
|
export function useShouldAnimateGasEstimations() {
|
||||||
const { isGasEstimatesLoading, gasFeeEstimates } = useGasFeeEstimates();
|
const { isGasEstimatesLoading, gasFeeEstimates } = useGasFeeEstimates();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const isGasLoadingAnimationActive = useSelector(
|
||||||
|
getGasLoadingAnimationIsShowing,
|
||||||
|
);
|
||||||
|
|
||||||
// Do the animation only when gas prices have changed...
|
// Do the animation only when gas prices have changed...
|
||||||
const lastGasEstimates = useRef(gasFeeEstimates);
|
const lastGasEstimates = useRef(gasFeeEstimates);
|
||||||
@ -24,5 +34,17 @@ export function useShouldAnimateGasEstimations() {
|
|||||||
const showLoadingAnimation =
|
const showLoadingAnimation =
|
||||||
isGasEstimatesLoading || (gasEstimatesChanged && !gasJustLoaded);
|
isGasEstimatesLoading || (gasEstimatesChanged && !gasJustLoaded);
|
||||||
|
|
||||||
return showLoadingAnimation;
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
isGasLoadingAnimationActive === false &&
|
||||||
|
showLoadingAnimation === true
|
||||||
|
) {
|
||||||
|
dispatch(toggleGasLoadingAnimation(true));
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('Killing the toggleGasLoadingAnimation to false');
|
||||||
|
dispatch(toggleGasLoadingAnimation(false));
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
}, [dispatch, isGasLoadingAnimationActive, showLoadingAnimation]);
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,7 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
setDefaultHomeActiveTabName: PropTypes.func,
|
setDefaultHomeActiveTabName: PropTypes.func,
|
||||||
primaryTotalTextOverride: PropTypes.string,
|
primaryTotalTextOverride: PropTypes.string,
|
||||||
secondaryTotalTextOverride: PropTypes.string,
|
secondaryTotalTextOverride: PropTypes.string,
|
||||||
|
gasIsLoading: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -772,6 +773,7 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
hideSenderToRecipient,
|
hideSenderToRecipient,
|
||||||
showAccountInHeader,
|
showAccountInHeader,
|
||||||
txData,
|
txData,
|
||||||
|
gasIsLoading,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const {
|
const {
|
||||||
submitting,
|
submitting,
|
||||||
@ -838,7 +840,7 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
lastTx={lastTx}
|
lastTx={lastTx}
|
||||||
ofText={ofText}
|
ofText={ofText}
|
||||||
requestsWaitingText={requestsWaitingText}
|
requestsWaitingText={requestsWaitingText}
|
||||||
disabled={!valid || submitting}
|
disabled={!valid || submitting || gasIsLoading}
|
||||||
onEdit={() => this.handleEdit()}
|
onEdit={() => this.handleEdit()}
|
||||||
onCancelAll={() => this.handleCancelAll()}
|
onCancelAll={() => this.handleCancelAll()}
|
||||||
onCancel={() => this.handleCancel()}
|
onCancel={() => this.handleCancel()}
|
||||||
|
@ -32,7 +32,11 @@ import {
|
|||||||
import { getMostRecentOverviewPage } from '../../ducks/history/history';
|
import { getMostRecentOverviewPage } from '../../ducks/history/history';
|
||||||
import { transactionMatchesNetwork } from '../../../shared/modules/transaction.utils';
|
import { transactionMatchesNetwork } from '../../../shared/modules/transaction.utils';
|
||||||
import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils';
|
import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils';
|
||||||
import { updateTransactionGasFees } from '../../ducks/metamask/metamask';
|
import {
|
||||||
|
updateTransactionGasFees,
|
||||||
|
getIsGasEstimatesLoading,
|
||||||
|
} from '../../ducks/metamask/metamask';
|
||||||
|
import { getGasLoadingAnimationIsShowing } from '../../ducks/app/app';
|
||||||
import ConfirmTransactionBase from './confirm-transaction-base.component';
|
import ConfirmTransactionBase from './confirm-transaction-base.component';
|
||||||
|
|
||||||
const casedContractMap = Object.keys(contractMap).reduce((acc, base) => {
|
const casedContractMap = Object.keys(contractMap).reduce((acc, base) => {
|
||||||
@ -60,6 +64,10 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
const { id: paramsTransactionId } = params;
|
const { id: paramsTransactionId } = params;
|
||||||
const isMainnet = getIsMainnet(state);
|
const isMainnet = getIsMainnet(state);
|
||||||
const supportsEIP1599 = checkNetworkAndAccountSupports1559(state);
|
const supportsEIP1599 = checkNetworkAndAccountSupports1559(state);
|
||||||
|
|
||||||
|
const isGasEstimatesLoading = getIsGasEstimatesLoading(state);
|
||||||
|
const gasLoadingAnimationIsShowing = getGasLoadingAnimationIsShowing(state);
|
||||||
|
|
||||||
const { confirmTransaction, metamask } = state;
|
const { confirmTransaction, metamask } = state;
|
||||||
const {
|
const {
|
||||||
ensResolutionsByAddress,
|
ensResolutionsByAddress,
|
||||||
@ -185,6 +193,7 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
isEthGasPrice,
|
isEthGasPrice,
|
||||||
noGasPrice,
|
noGasPrice,
|
||||||
supportsEIP1599,
|
supportsEIP1599,
|
||||||
|
gasIsLoading: isGasEstimatesLoading || gasLoadingAnimationIsShowing,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import configureMockStore from 'redux-mock-store';
|
import configureMockStore from 'redux-mock-store';
|
||||||
import thunk from 'redux-thunk';
|
import thunk from 'redux-thunk';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import { checkNetworkAndAccountSupports1559 } from '../../../selectors';
|
||||||
|
import {
|
||||||
|
getGasEstimateType,
|
||||||
|
getGasFeeEstimates,
|
||||||
|
getIsGasEstimatesLoading,
|
||||||
|
} from '../../../ducks/metamask/metamask';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
renderWithProvider,
|
renderWithProvider,
|
||||||
@ -13,21 +21,34 @@ import FeeCard from '.';
|
|||||||
|
|
||||||
const middleware = [thunk];
|
const middleware = [thunk];
|
||||||
|
|
||||||
jest.mock('../../../hooks/useGasFeeEstimates', () => {
|
jest.mock('react-redux', () => {
|
||||||
|
const actual = jest.requireActual('react-redux');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
useGasFeeEstimates: () => {
|
...actual,
|
||||||
return {
|
useSelector: jest.fn(),
|
||||||
gasFeeEstimates: MOCKS.createGasFeeEstimatesForFeeMarket(),
|
|
||||||
gasEstimateType: 'fee-market',
|
|
||||||
estimatedGasFeeTimeBounds: undefined,
|
|
||||||
isGasEstimatesLoading: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const generateUseSelectorRouter = () => (selector) => {
|
||||||
|
if (selector === checkNetworkAndAccountSupports1559) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (selector === getGasEstimateType) {
|
||||||
|
return 'fee-market';
|
||||||
|
}
|
||||||
|
if (selector === getGasFeeEstimates) {
|
||||||
|
return MOCKS.createGasFeeEstimatesForFeeMarket();
|
||||||
|
}
|
||||||
|
if (selector === getIsGasEstimatesLoading) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
setBackgroundConnection({
|
setBackgroundConnection({
|
||||||
getGasFeeTimeEstimate: jest.fn(),
|
getGasFeeTimeEstimate: jest.fn(),
|
||||||
|
getGasFeeEstimatesAndStartPolling: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const createProps = (customProps = {}) => {
|
const createProps = (customProps = {}) => {
|
||||||
@ -65,6 +86,7 @@ const createProps = (customProps = {}) => {
|
|||||||
|
|
||||||
describe('FeeCard', () => {
|
describe('FeeCard', () => {
|
||||||
it('renders the component with initial props', () => {
|
it('renders the component with initial props', () => {
|
||||||
|
useSelector.mockImplementation(generateUseSelectorRouter());
|
||||||
const props = createProps();
|
const props = createProps();
|
||||||
const { getByText } = renderWithProvider(<FeeCard {...props} />);
|
const { getByText } = renderWithProvider(<FeeCard {...props} />);
|
||||||
expect(getByText('Using the best quote')).toBeInTheDocument();
|
expect(getByText('Using the best quote')).toBeInTheDocument();
|
||||||
|
@ -102,3 +102,5 @@ export const SET_OPEN_METAMASK_TAB_IDS = 'SET_OPEN_METAMASK_TAB_IDS';
|
|||||||
|
|
||||||
// Home Screen
|
// Home Screen
|
||||||
export const HIDE_WHATS_NEW_POPUP = 'HIDE_WHATS_NEW_POPUP';
|
export const HIDE_WHATS_NEW_POPUP = 'HIDE_WHATS_NEW_POPUP';
|
||||||
|
|
||||||
|
export const TOGGLE_GAS_LOADING_ANIMATION = 'TOGGLE_GAS_LOADING_ANIMATION';
|
||||||
|
Loading…
Reference in New Issue
Block a user