mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
Added customize tx nonce on ERC20 approve screen when feature is enabled (#17945)
This commit is contained in:
parent
42c3a3958b
commit
36eaaa19ed
@ -10,6 +10,7 @@
|
||||
@import 'confirm-page-container/index';
|
||||
@import 'confirm-data/index';
|
||||
@import 'confirmation-warning-modal/index';
|
||||
@import 'custom-nonce/index';
|
||||
@import 'nfts-items/index';
|
||||
@import 'nfts-tab/index';
|
||||
@import 'nft-details/index';
|
||||
|
75
ui/components/app/custom-nonce/custom-nonce.js
Normal file
75
ui/components/app/custom-nonce/custom-nonce.js
Normal file
@ -0,0 +1,75 @@
|
||||
import React, { useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { I18nContext } from '../../../../.storybook/i18n';
|
||||
import { Box, ButtonLink, Text } from '../../component-library';
|
||||
import {
|
||||
AlignItems,
|
||||
BorderRadius,
|
||||
Display,
|
||||
JustifyContent,
|
||||
Size,
|
||||
TextVariant,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
|
||||
export default function CustomNonce({
|
||||
nextNonce,
|
||||
customNonceValue,
|
||||
showCustomizeNonceModal,
|
||||
}) {
|
||||
const t = useContext(I18nContext);
|
||||
|
||||
return (
|
||||
<Box
|
||||
display={Display.Flex}
|
||||
marginBottom={4}
|
||||
paddingTop={3}
|
||||
paddingRight={3}
|
||||
paddingBottom={4}
|
||||
paddingLeft={3}
|
||||
borderRadius={BorderRadius.MD}
|
||||
alignItems={AlignItems.center}
|
||||
className="custom-nonce__content"
|
||||
>
|
||||
<Box
|
||||
className="custom-nonce__header"
|
||||
display={Display.InlineFlex}
|
||||
justifyContent={JustifyContent.flexStart}
|
||||
alignItems={AlignItems.center}
|
||||
>
|
||||
<Text variant={TextVariant.bodySm} as="h6">
|
||||
{t('nonce')}
|
||||
</Text>
|
||||
<ButtonLink
|
||||
key="editCustomNonce"
|
||||
size={Size.auto}
|
||||
marginLeft={3}
|
||||
onClick={() => showCustomizeNonceModal()}
|
||||
>
|
||||
{t('edit')}
|
||||
</ButtonLink>
|
||||
</Box>
|
||||
<Text
|
||||
className="custom-nonce__value"
|
||||
variant={TextVariant.bodySmBold}
|
||||
as="h6"
|
||||
>
|
||||
{customNonceValue || nextNonce}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
CustomNonce.propTypes = {
|
||||
/**
|
||||
* Getting the next suggested nonce
|
||||
*/
|
||||
nextNonce: PropTypes.number,
|
||||
/**
|
||||
* Custom nonce value
|
||||
*/
|
||||
customNonceValue: PropTypes.string,
|
||||
/**
|
||||
* Function that is supposed to open the customized nonce modal
|
||||
*/
|
||||
showCustomizeNonceModal: PropTypes.func,
|
||||
};
|
65
ui/components/app/custom-nonce/custom-nonce.test.js
Normal file
65
ui/components/app/custom-nonce/custom-nonce.test.js
Normal file
@ -0,0 +1,65 @@
|
||||
import React from 'react';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||
import CustomNonce from './custom-nonce';
|
||||
|
||||
describe('CustomNonce', () => {
|
||||
const store = configureMockStore()({});
|
||||
let props = {};
|
||||
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
nextNonce: 1,
|
||||
customNonceValue: '',
|
||||
showCustomizeNonceModal: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
it('should render CustomNonce component header', () => {
|
||||
const { queryByText } = renderWithProvider(
|
||||
<CustomNonce {...props} />,
|
||||
store,
|
||||
);
|
||||
|
||||
expect(queryByText('Nonce')).toBeInTheDocument();
|
||||
expect(queryByText('Edit')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render CustomNonce component value when custom nonce value is a empty string', () => {
|
||||
const { queryByText } = renderWithProvider(
|
||||
<CustomNonce {...props} />,
|
||||
store,
|
||||
);
|
||||
|
||||
expect(queryByText('Nonce')).toBeInTheDocument();
|
||||
expect(queryByText('Edit')).toBeInTheDocument();
|
||||
expect(queryByText('1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render CustomNonce component value when custom nonce value is edited', () => {
|
||||
props.customNonceValue = '3';
|
||||
const { queryByText } = renderWithProvider(
|
||||
<CustomNonce {...props} />,
|
||||
store,
|
||||
);
|
||||
|
||||
expect(queryByText('Nonce')).toBeInTheDocument();
|
||||
expect(queryByText('Edit')).toBeInTheDocument();
|
||||
expect(queryByText('3')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render CustomNonce component to show customize nonce modal', () => {
|
||||
const { queryByText, getByText } = renderWithProvider(
|
||||
<CustomNonce {...props} />,
|
||||
store,
|
||||
);
|
||||
|
||||
const editButton = getByText('Edit');
|
||||
expect(queryByText('Nonce')).toBeInTheDocument();
|
||||
expect(editButton).toBeInTheDocument();
|
||||
expect(queryByText('1')).toBeInTheDocument();
|
||||
fireEvent.click(editButton);
|
||||
expect(props.showCustomizeNonceModal).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
1
ui/components/app/custom-nonce/index.js
Normal file
1
ui/components/app/custom-nonce/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './custom-nonce';
|
15
ui/components/app/custom-nonce/index.scss
Normal file
15
ui/components/app/custom-nonce/index.scss
Normal file
@ -0,0 +1,15 @@
|
||||
.custom-nonce {
|
||||
&__content {
|
||||
height: 49px;
|
||||
border: 1px solid var(--color-border-muted);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&__header {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&__value {
|
||||
flex: 0;
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ exports[`Customize Nonce should match snapshot 1`] = `
|
||||
class="customize-nonce-modal__main-header"
|
||||
>
|
||||
<h4
|
||||
class="box mm-text customize-nonce-modal__main-title mm-text--heading-sm mm-text--font-weight-bold box--flex-direction-row box--color-text-default"
|
||||
class="box mm-text customize-nonce-modal__main-title mm-text--heading-sm box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
Edit nonce
|
||||
</h4>
|
||||
@ -30,14 +30,14 @@ exports[`Customize Nonce should match snapshot 1`] = `
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="box box--margin-top-2 box--display-inline-flex box--flex-direction-row box--align-items-center"
|
||||
class="mm-box mm-box--margin-top-2 mm-box--display-inline-flex mm-box--align-items-center"
|
||||
>
|
||||
<h6
|
||||
class="box mm-text mm-text--body-sm mm-text--font-weight-normal box--flex-direction-row box--color-text-default"
|
||||
class="box mm-text mm-text--body-md box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
This is an advanced feature, use cautiously.
|
||||
<a
|
||||
class="button btn-link customize-nonce-modal__link"
|
||||
class="box mm-text mm-button-base customize-nonce-modal__link mm-button-link mm-button-link--size-auto mm-text--body-md box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-default box--background-color-transparent"
|
||||
href="https://metamask.zendesk.com/hc/en-us/articles/7417499333531-How-to-customize-a-transaction-nonce"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
@ -47,28 +47,25 @@ exports[`Customize Nonce should match snapshot 1`] = `
|
||||
</h6>
|
||||
</div>
|
||||
<div
|
||||
class="box box--margin-top-3 box--flex-direction-row"
|
||||
class="mm-box mm-box--margin-top-4"
|
||||
>
|
||||
<div
|
||||
class="box box--display-flex box--flex-direction-row box--align-items-center"
|
||||
class="mm-box mm-box--display-flex mm-box--align-items-center"
|
||||
>
|
||||
<h6
|
||||
boxprops="[object Object]"
|
||||
class="box mm-text mm-text--body-sm mm-text--font-weight-bold box--flex-direction-row box--color-text-default"
|
||||
class="box mm-text mm-text--body-md-bold box--flex-direction-row box--width-5/6 box--color-text-default"
|
||||
>
|
||||
Edit nonce
|
||||
</h6>
|
||||
<div
|
||||
class="box box--flex-direction-row box--width-1/6"
|
||||
class="mm-box mm-box--width-1/6"
|
||||
>
|
||||
<a
|
||||
class="button btn-link customize-nonce-modal__reset"
|
||||
<button
|
||||
class="box mm-text mm-button-base customize-nonce-modal__reset mm-button-link mm-button-link--size-auto mm-text--body-md box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-default box--background-color-transparent"
|
||||
data-testid="customize-nonce-reset"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Reset
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
@ -2,21 +2,20 @@ import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Modal from '../../modal';
|
||||
import TextField from '../../../ui/text-field';
|
||||
import Button from '../../../ui/button';
|
||||
import {
|
||||
TextVariant,
|
||||
FontWeight,
|
||||
AlignItems,
|
||||
BLOCK_SIZES,
|
||||
DISPLAY,
|
||||
BlockSize,
|
||||
Display,
|
||||
} from '../../../../helpers/constants/design-system';
|
||||
import Box from '../../../ui/box';
|
||||
import withModalProps from '../../../../helpers/higher-order-components/with-modal-props';
|
||||
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
||||
import ZENDESK_URLS from '../../../../helpers/constants/zendesk-url';
|
||||
import {
|
||||
Box,
|
||||
ButtonIcon,
|
||||
ButtonIconSize,
|
||||
ButtonLink,
|
||||
IconName,
|
||||
Text,
|
||||
} from '../../../component-library';
|
||||
@ -54,7 +53,6 @@ const CustomizeNonce = ({
|
||||
className="customize-nonce-modal__main-title"
|
||||
variant={TextVariant.headingSm}
|
||||
as="h4"
|
||||
fontWeight={FontWeight.Bold}
|
||||
>
|
||||
{t('editNonceField')}
|
||||
</Text>
|
||||
@ -68,39 +66,32 @@ const CustomizeNonce = ({
|
||||
</div>
|
||||
<Box
|
||||
marginTop={2}
|
||||
display={DISPLAY.INLINE_FLEX}
|
||||
display={Display.InlineFlex}
|
||||
alignItems={AlignItems.center}
|
||||
>
|
||||
<Text
|
||||
variant={TextVariant.bodySm}
|
||||
as="h6"
|
||||
fontWeight={FontWeight.Normal}
|
||||
>
|
||||
<Text variant={TextVariant.bodyMd} as="h6">
|
||||
{t('editNonceMessage')}
|
||||
<Button
|
||||
type="link"
|
||||
<ButtonLink
|
||||
className="customize-nonce-modal__link"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href={ZENDESK_URLS.CUSTOMIZE_NONCE}
|
||||
>
|
||||
{t('learnMoreUpperCase')}
|
||||
</Button>
|
||||
</ButtonLink>
|
||||
</Text>
|
||||
</Box>
|
||||
<Box marginTop={3}>
|
||||
<Box alignItems={AlignItems.center} display={DISPLAY.FLEX}>
|
||||
<Box marginTop={4}>
|
||||
<Box alignItems={AlignItems.center} display={Display.Flex}>
|
||||
<Text
|
||||
variant={TextVariant.bodySm}
|
||||
variant={TextVariant.bodyMdBold}
|
||||
as="h6"
|
||||
fontWeight={FontWeight.Bold}
|
||||
boxProps={{ width: BLOCK_SIZES.FIVE_SIXTHS }}
|
||||
width={BlockSize.FiveSixths}
|
||||
>
|
||||
{t('editNonceField')}
|
||||
</Text>
|
||||
<Box width={BLOCK_SIZES.ONE_SIXTH}>
|
||||
<Button
|
||||
type="link"
|
||||
<Box width={BlockSize.OneSixth}>
|
||||
<ButtonLink
|
||||
className="customize-nonce-modal__reset"
|
||||
data-testid="customize-nonce-reset"
|
||||
onClick={() => {
|
||||
@ -108,7 +99,7 @@ const CustomizeNonce = ({
|
||||
}}
|
||||
>
|
||||
{t('reset')}
|
||||
</Button>
|
||||
</ButtonLink>
|
||||
</Box>
|
||||
</Box>
|
||||
<div className="customize-nonce-modal__input">
|
||||
|
@ -31,7 +31,7 @@
|
||||
}
|
||||
|
||||
& &__reset {
|
||||
@include H7;
|
||||
@include H6;
|
||||
}
|
||||
|
||||
&__input {
|
||||
|
@ -129,26 +129,24 @@ exports[`ConfirmApproveContent Component should render Confirm approve page corr
|
||||
class="confirm-approve-content__card-content"
|
||||
>
|
||||
<div
|
||||
class="confirm-approve-content__custom-nonce-content"
|
||||
class="mm-box custom-nonce__content mm-box--margin-bottom-4 mm-box--padding-top-3 mm-box--padding-right-3 mm-box--padding-bottom-4 mm-box--padding-left-3 mm-box--display-flex mm-box--align-items-center mm-box--rounded-md"
|
||||
>
|
||||
<div
|
||||
class="box confirm-approve-content__custom-nonce-header box--flex-direction-row box--justify-content-flex-start box--display-flex"
|
||||
class="mm-box custom-nonce__header mm-box--display-inline-flex mm-box--justify-content-flex-start mm-box--align-items-center"
|
||||
>
|
||||
<h6
|
||||
class="box mm-text mm-text--body-sm box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
Nonce
|
||||
</h6>
|
||||
<a
|
||||
class="button btn-link confirm-approve-content__custom-nonce-edit"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
<button
|
||||
class="box mm-text mm-button-base mm-button-link mm-button-link--size-auto mm-text--body-md box--margin-left-3 box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-default box--background-color-transparent"
|
||||
>
|
||||
Edit
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
<h6
|
||||
class="box mm-text confirm-approve-content__custom-nonce-value mm-text--body-sm-bold box--flex-direction-row box--color-text-default"
|
||||
class="box mm-text custom-nonce__value mm-text--body-sm-bold box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
2
|
||||
</h6>
|
||||
@ -316,26 +314,24 @@ exports[`ConfirmApproveContent Component should render Confirm approve page corr
|
||||
class="confirm-approve-content__card-content"
|
||||
>
|
||||
<div
|
||||
class="confirm-approve-content__custom-nonce-content"
|
||||
class="mm-box custom-nonce__content mm-box--margin-bottom-4 mm-box--padding-top-3 mm-box--padding-right-3 mm-box--padding-bottom-4 mm-box--padding-left-3 mm-box--display-flex mm-box--align-items-center mm-box--rounded-md"
|
||||
>
|
||||
<div
|
||||
class="box confirm-approve-content__custom-nonce-header box--flex-direction-row box--justify-content-flex-start box--display-flex"
|
||||
class="mm-box custom-nonce__header mm-box--display-inline-flex mm-box--justify-content-flex-start mm-box--align-items-center"
|
||||
>
|
||||
<h6
|
||||
class="box mm-text mm-text--body-sm box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
Nonce
|
||||
</h6>
|
||||
<a
|
||||
class="button btn-link confirm-approve-content__custom-nonce-edit"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
<button
|
||||
class="box mm-text mm-button-base mm-button-link mm-button-link--size-auto mm-text--body-md box--margin-left-3 box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-default box--background-color-transparent"
|
||||
>
|
||||
Edit
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
<h6
|
||||
class="box mm-text confirm-approve-content__custom-nonce-value mm-text--body-sm-bold box--flex-direction-row box--color-text-default"
|
||||
class="box mm-text custom-nonce__value mm-text--body-sm-bold box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
2
|
||||
</h6>
|
||||
@ -503,26 +499,24 @@ exports[`ConfirmApproveContent Component should render Confirm approve page corr
|
||||
class="confirm-approve-content__card-content"
|
||||
>
|
||||
<div
|
||||
class="confirm-approve-content__custom-nonce-content"
|
||||
class="mm-box custom-nonce__content mm-box--margin-bottom-4 mm-box--padding-top-3 mm-box--padding-right-3 mm-box--padding-bottom-4 mm-box--padding-left-3 mm-box--display-flex mm-box--align-items-center mm-box--rounded-md"
|
||||
>
|
||||
<div
|
||||
class="box confirm-approve-content__custom-nonce-header box--flex-direction-row box--justify-content-flex-start box--display-flex"
|
||||
class="mm-box custom-nonce__header mm-box--display-inline-flex mm-box--justify-content-flex-start mm-box--align-items-center"
|
||||
>
|
||||
<h6
|
||||
class="box mm-text mm-text--body-sm box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
Nonce
|
||||
</h6>
|
||||
<a
|
||||
class="button btn-link confirm-approve-content__custom-nonce-edit"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
<button
|
||||
class="box mm-text mm-button-base mm-button-link mm-button-link--size-auto mm-text--body-md box--margin-left-3 box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-default box--background-color-transparent"
|
||||
>
|
||||
Edit
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
<h6
|
||||
class="box mm-text confirm-approve-content__custom-nonce-value mm-text--body-sm-bold box--flex-direction-row box--color-text-default"
|
||||
class="box mm-text custom-nonce__value mm-text--body-sm-bold box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
2
|
||||
</h6>
|
||||
@ -690,26 +684,24 @@ exports[`ConfirmApproveContent Component should render Confirm approve page corr
|
||||
class="confirm-approve-content__card-content"
|
||||
>
|
||||
<div
|
||||
class="confirm-approve-content__custom-nonce-content"
|
||||
class="mm-box custom-nonce__content mm-box--margin-bottom-4 mm-box--padding-top-3 mm-box--padding-right-3 mm-box--padding-bottom-4 mm-box--padding-left-3 mm-box--display-flex mm-box--align-items-center mm-box--rounded-md"
|
||||
>
|
||||
<div
|
||||
class="box confirm-approve-content__custom-nonce-header box--flex-direction-row box--justify-content-flex-start box--display-flex"
|
||||
class="mm-box custom-nonce__header mm-box--display-inline-flex mm-box--justify-content-flex-start mm-box--align-items-center"
|
||||
>
|
||||
<h6
|
||||
class="box mm-text mm-text--body-sm box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
Nonce
|
||||
</h6>
|
||||
<a
|
||||
class="button btn-link confirm-approve-content__custom-nonce-edit"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
<button
|
||||
class="box mm-text mm-button-base mm-button-link mm-button-link--size-auto mm-text--body-md box--margin-left-3 box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-default box--background-color-transparent"
|
||||
>
|
||||
Edit
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
<h6
|
||||
class="box mm-text confirm-approve-content__custom-nonce-value mm-text--body-sm-bold box--flex-direction-row box--color-text-default"
|
||||
class="box mm-text custom-nonce__value mm-text--body-sm-bold box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
2
|
||||
</h6>
|
||||
|
@ -14,7 +14,6 @@ import MultiLayerFeeMessage from '../../../components/app/multilayer-fee-message
|
||||
import SecurityProviderBannerMessage from '../../../components/app/security-provider-banner-message/security-provider-banner-message';
|
||||
import {
|
||||
BLOCK_SIZES,
|
||||
JustifyContent,
|
||||
DISPLAY,
|
||||
TextColor,
|
||||
IconColor,
|
||||
@ -38,6 +37,7 @@ import TransactionDetailItem from '../../../components/app/transaction-detail-it
|
||||
import UserPreferencedCurrencyDisplay from '../../../components/app/user-preferenced-currency-display';
|
||||
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common';
|
||||
import { ConfirmGasDisplay } from '../../../components/app/confirm-gas-display';
|
||||
import CustomNonce from '../../../components/app/custom-nonce';
|
||||
|
||||
export default class ConfirmApproveContent extends Component {
|
||||
static contextTypes = {
|
||||
@ -351,55 +351,6 @@ export default class ConfirmApproveContent extends Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
renderCustomNonceContent() {
|
||||
const { t } = this.context;
|
||||
const {
|
||||
useNonceField,
|
||||
customNonceValue,
|
||||
updateCustomNonce,
|
||||
getNextNonce,
|
||||
nextNonce,
|
||||
showCustomizeNonceModal,
|
||||
} = this.props;
|
||||
return (
|
||||
<>
|
||||
{useNonceField && (
|
||||
<div className="confirm-approve-content__custom-nonce-content">
|
||||
<Box
|
||||
className="confirm-approve-content__custom-nonce-header"
|
||||
justifyContent={JustifyContent.flexStart}
|
||||
>
|
||||
<Text variant={TextVariant.bodySm} as="h6">
|
||||
{t('nonce')}
|
||||
</Text>
|
||||
<Button
|
||||
type="link"
|
||||
className="confirm-approve-content__custom-nonce-edit"
|
||||
onClick={() =>
|
||||
showCustomizeNonceModal({
|
||||
nextNonce,
|
||||
customNonceValue,
|
||||
updateCustomNonce,
|
||||
getNextNonce,
|
||||
})
|
||||
}
|
||||
>
|
||||
{t('edit')}
|
||||
</Button>
|
||||
</Box>
|
||||
<Text
|
||||
className="confirm-approve-content__custom-nonce-value"
|
||||
variant={TextVariant.bodySmBold}
|
||||
as="h6"
|
||||
>
|
||||
{customNonceValue || nextNonce}
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
getTokenName() {
|
||||
const { tokenId, assetName, assetStandard, tokenSymbol } = this.props;
|
||||
const { t } = this.context;
|
||||
@ -586,6 +537,11 @@ export default class ConfirmApproveContent extends Component {
|
||||
userAcknowledgedGasMissing,
|
||||
setUserAcknowledgedGasMissing,
|
||||
renderSimulationFailureWarning,
|
||||
nextNonce,
|
||||
getNextNonce,
|
||||
customNonceValue,
|
||||
updateCustomNonce,
|
||||
showCustomizeNonceModal,
|
||||
} = this.props;
|
||||
const { showFullTxDetails, setShowContractDetails } = this.state;
|
||||
|
||||
@ -709,7 +665,20 @@ export default class ConfirmApproveContent extends Component {
|
||||
{useNonceField &&
|
||||
this.renderApproveContentCard({
|
||||
showHeader: false,
|
||||
content: this.renderCustomNonceContent(),
|
||||
content: (
|
||||
<CustomNonce
|
||||
nextNonce={nextNonce}
|
||||
customNonceValue={customNonceValue}
|
||||
showCustomizeNonceModal={() => {
|
||||
showCustomizeNonceModal({
|
||||
nextNonce,
|
||||
customNonceValue,
|
||||
updateCustomNonce,
|
||||
getNextNonce,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
useNonceField,
|
||||
noBorder: !showFullTxDetails,
|
||||
footer: (
|
||||
|
@ -385,33 +385,6 @@
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
&__custom-nonce-content {
|
||||
display: flex;
|
||||
height: 49px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 6px;
|
||||
padding: 12px 12px 14px 12px;
|
||||
border: 1px solid var(--color-border-muted);
|
||||
box-sizing: border-box;
|
||||
border-radius: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__custom-nonce-header {
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__custom-nonce-value {
|
||||
flex: 0;
|
||||
}
|
||||
|
||||
& &__custom-nonce-edit {
|
||||
@include H7;
|
||||
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-approve-content--full {
|
||||
|
@ -205,6 +205,7 @@ export default function ConfirmApprove({
|
||||
tokenSymbol={tokenSymbol}
|
||||
decimals={decimals}
|
||||
fromAddressIsLedger={fromAddressIsLedger}
|
||||
warning={submitWarning}
|
||||
/>
|
||||
{showCustomizeGasPopover && !supportsEIP1559 && (
|
||||
<EditGasPopover
|
||||
|
@ -190,6 +190,7 @@ exports[`TokenAllowancePage should match snapshot 1`] = `
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="box box--display-flex box--flex-direction-row box--justify-content-center"
|
||||
>
|
||||
|
@ -38,4 +38,9 @@
|
||||
&__full-tx-content {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&__custom-nonce-warning {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
@ -33,6 +33,8 @@ import {
|
||||
getUnapprovedTransactions,
|
||||
getUseCurrencyRateCheck,
|
||||
getTargetAccountWithSendEtherInfo,
|
||||
getCustomNonceValue,
|
||||
getNextSuggestedNonce,
|
||||
} from '../../selectors';
|
||||
import { NETWORK_TO_NAME_MAP } from '../../../shared/constants/network';
|
||||
import {
|
||||
@ -40,6 +42,7 @@ import {
|
||||
cancelTxs,
|
||||
showModal,
|
||||
updateAndApproveTx,
|
||||
getNextNonce,
|
||||
updateCustomNonce,
|
||||
} from '../../store/actions';
|
||||
import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck';
|
||||
@ -63,6 +66,8 @@ import SimulationErrorMessage from '../../components/ui/simulation-error-message
|
||||
import LedgerInstructionField from '../../components/app/ledger-instruction-field/ledger-instruction-field';
|
||||
import SecurityProviderBannerMessage from '../../components/app/security-provider-banner-message/security-provider-banner-message';
|
||||
import { Text, Icon, IconName } from '../../components/component-library';
|
||||
import { ConfirmPageContainerWarning } from '../../components/app/confirm-page-container/confirm-page-container-content';
|
||||
import CustomNonce from '../../components/app/custom-nonce';
|
||||
|
||||
const ALLOWED_HOSTS = ['portfolio.metamask.io'];
|
||||
|
||||
@ -91,6 +96,7 @@ export default function TokenAllowance({
|
||||
toAddress,
|
||||
tokenSymbol,
|
||||
fromAddressIsLedger,
|
||||
warning,
|
||||
}) {
|
||||
const t = useContext(I18nContext);
|
||||
const dispatch = useDispatch();
|
||||
@ -124,6 +130,8 @@ export default function TokenAllowance({
|
||||
const unapprovedTxCount = useSelector(getUnapprovedTxCount);
|
||||
const unapprovedTxs = useSelector(getUnapprovedTransactions);
|
||||
const useCurrencyRateCheck = useSelector(getUseCurrencyRateCheck);
|
||||
const nextNonce = useSelector(getNextSuggestedNonce);
|
||||
const customNonceValue = useSelector(getCustomNonceValue);
|
||||
|
||||
const replaceCommaToDot = (inputValue) => {
|
||||
return inputValue.replace(/,/gu, '.');
|
||||
@ -175,7 +183,6 @@ export default function TokenAllowance({
|
||||
const networkName =
|
||||
NETWORK_TO_NAME_MAP[fullTxData.chainId] || networkIdentifier;
|
||||
|
||||
const customNonceValue = '';
|
||||
const customNonceMerge = (transactionData) =>
|
||||
customNonceValue
|
||||
? {
|
||||
@ -253,6 +260,39 @@ export default function TokenAllowance({
|
||||
);
|
||||
};
|
||||
|
||||
const handleNextNonce = () => {
|
||||
dispatch(getNextNonce());
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
handleNextNonce();
|
||||
}, [dispatch]);
|
||||
|
||||
const handleUpdateCustomNonce = (value) => {
|
||||
dispatch(updateCustomNonce(value));
|
||||
};
|
||||
|
||||
const handleCustomizeNonceModal = (
|
||||
/* eslint-disable no-shadow */
|
||||
useNonceField,
|
||||
nextNonce,
|
||||
customNonceValue,
|
||||
updateCustomNonce,
|
||||
getNextNonce,
|
||||
/* eslint-disable no-shadow */
|
||||
) => {
|
||||
dispatch(
|
||||
showModal({
|
||||
name: 'CUSTOMIZE_NONCE',
|
||||
useNonceField,
|
||||
nextNonce,
|
||||
customNonceValue,
|
||||
updateCustomNonce,
|
||||
getNextNonce,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const isEmpty = customSpendingCap === '';
|
||||
|
||||
const renderContractTokenValues = (
|
||||
@ -317,6 +357,11 @@ export default function TokenAllowance({
|
||||
accountAddress={userAddress}
|
||||
chainId={fullTxData.chainId}
|
||||
/>
|
||||
{warning && (
|
||||
<Box className="token-allowance-container__custom-nonce-warning">
|
||||
<ConfirmPageContainerWarning warning={warning} />
|
||||
</Box>
|
||||
)}
|
||||
<Box
|
||||
display={DISPLAY.FLEX}
|
||||
flexDirection={FLEX_DIRECTION.ROW}
|
||||
@ -459,6 +504,23 @@ export default function TokenAllowance({
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{useNonceField && (
|
||||
<Box marginTop={4} marginRight={4} marginLeft={4}>
|
||||
<CustomNonce
|
||||
nextNonce={nextNonce}
|
||||
customNonceValue={customNonceValue}
|
||||
showCustomizeNonceModal={() =>
|
||||
handleCustomizeNonceModal(
|
||||
useNonceField,
|
||||
nextNonce,
|
||||
customNonceValue,
|
||||
handleUpdateCustomNonce,
|
||||
handleNextNonce,
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<Box
|
||||
display={DISPLAY.FLEX}
|
||||
flexDirection={FLEX_DIRECTION.ROW}
|
||||
@ -647,4 +709,8 @@ TokenAllowance.propTypes = {
|
||||
* Whether the address sending the transaction is a ledger address
|
||||
*/
|
||||
fromAddressIsLedger: PropTypes.bool,
|
||||
/**
|
||||
* Customize nonce warning message
|
||||
*/
|
||||
warning: PropTypes.string,
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import { act, fireEvent } from '@testing-library/react';
|
||||
import thunk from 'redux-thunk';
|
||||
import { renderWithProvider } from '../../../test/lib/render-helpers';
|
||||
import { KeyringType } from '../../../shared/constants/keyring';
|
||||
import TokenAllowance from './token-allowance';
|
||||
@ -76,6 +77,8 @@ const state = {
|
||||
accounts: ['0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'],
|
||||
},
|
||||
],
|
||||
nextNonce: 1,
|
||||
customNonceValue: '',
|
||||
},
|
||||
history: {
|
||||
mostRecentOverviewPage: '/',
|
||||
@ -88,6 +91,8 @@ const state = {
|
||||
},
|
||||
};
|
||||
|
||||
const mockShowModal = jest.fn();
|
||||
|
||||
jest.mock('../../store/actions', () => ({
|
||||
disconnectGasFeeEstimatePoller: jest.fn(),
|
||||
getGasFeeTimeEstimate: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
@ -99,6 +104,8 @@ jest.mock('../../store/actions', () => ({
|
||||
updateTransactionGasFees: () => ({ type: 'UPDATE_TRANSACTION_PARAMS' }),
|
||||
updatePreviousGasParams: () => ({ type: 'UPDATE_TRANSACTION_PARAMS' }),
|
||||
createTransactionEventFragment: jest.fn(),
|
||||
getNextNonce: () => jest.fn(),
|
||||
showModal: () => mockShowModal,
|
||||
updateCustomNonce: () => ({ type: 'UPDATE_TRANSACTION_PARAMS' }),
|
||||
estimateGas: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
}));
|
||||
@ -146,6 +153,8 @@ describe('TokenAllowancePage', () => {
|
||||
currentTokenBalance: '10',
|
||||
toAddress: '0x9bc5baf874d2da8d216ae9f137804184ee5afef4',
|
||||
tokenSymbol: 'TST',
|
||||
showCustomizeGasModal: jest.fn(),
|
||||
warning: '',
|
||||
txData: {
|
||||
id: 3049568294499567,
|
||||
time: 1664449552289,
|
||||
@ -184,7 +193,7 @@ describe('TokenAllowancePage', () => {
|
||||
|
||||
let store;
|
||||
beforeEach(() => {
|
||||
store = configureMockStore()(state);
|
||||
store = configureMockStore([thunk])(state);
|
||||
});
|
||||
|
||||
it('should match snapshot', () => {
|
||||
@ -212,6 +221,126 @@ describe('TokenAllowancePage', () => {
|
||||
expect(onCloseBtn).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not render customize nonce modal if useNonceField is set to false', () => {
|
||||
const { queryByText } = renderWithProvider(
|
||||
<TokenAllowance {...props} />,
|
||||
store,
|
||||
);
|
||||
expect(queryByText('Nonce')).not.toBeInTheDocument();
|
||||
expect(queryByText('1')).not.toBeInTheDocument();
|
||||
expect(mockShowModal).not.toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should render customize nonce modal if useNonceField is set to true', () => {
|
||||
props.useNonceField = true;
|
||||
props.nextNonce = 1;
|
||||
const { queryByText, getByText } = renderWithProvider(
|
||||
<TokenAllowance {...props} />,
|
||||
store,
|
||||
);
|
||||
const editButton = getByText('Edit');
|
||||
expect(queryByText('Nonce')).toBeInTheDocument();
|
||||
expect(queryByText('1')).toBeInTheDocument();
|
||||
fireEvent.click(editButton);
|
||||
expect(mockShowModal).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should render nextNonce value when custom nonce value is a empty string', () => {
|
||||
props.useNonceField = true;
|
||||
props.customNonceValue = '';
|
||||
const { queryByText, getByText } = renderWithProvider(
|
||||
<TokenAllowance {...props} />,
|
||||
store,
|
||||
);
|
||||
const editButton = getByText('Edit');
|
||||
expect(queryByText('Nonce')).toBeInTheDocument();
|
||||
expect(queryByText('1')).toBeInTheDocument();
|
||||
fireEvent.click(editButton);
|
||||
expect(mockShowModal).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should render edited custom nonce value', () => {
|
||||
props.useNonceField = true;
|
||||
state.metamask.customNonceValue = '3';
|
||||
const { queryByText, getByText } = renderWithProvider(
|
||||
<TokenAllowance {...props} />,
|
||||
store,
|
||||
);
|
||||
const editButton = getByText('Edit');
|
||||
expect(queryByText('Nonce')).toBeInTheDocument();
|
||||
expect(queryByText('3')).toBeInTheDocument();
|
||||
fireEvent.click(editButton);
|
||||
expect(mockShowModal).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('should render customize nonce warning if custom nonce value is higher than nextNonce value', () => {
|
||||
props.useNonceField = true;
|
||||
props.nextNonce = 2;
|
||||
props.customNonceValue = '3';
|
||||
props.warning = 'Nonce is higher than suggested nonce of 2';
|
||||
const { getByText } = renderWithProvider(
|
||||
<TokenAllowance {...props} />,
|
||||
store,
|
||||
);
|
||||
expect(
|
||||
getByText('Nonce is higher than suggested nonce of 2'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not render customize nonce warning if custom nonce value is lower than nextNonce value', () => {
|
||||
props.useNonceField = true;
|
||||
props.nextNonce = 2;
|
||||
props.customNonceValue = '1';
|
||||
props.warning = '';
|
||||
const { container } = renderWithProvider(
|
||||
<TokenAllowance {...props} />,
|
||||
store,
|
||||
);
|
||||
const customizeNonceWarning = container.querySelector(
|
||||
'.token-allowance-container__custom-nonce-warning',
|
||||
);
|
||||
expect(customizeNonceWarning).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render customize nonce modal when next button is clicked and if useNonceField is set to true', () => {
|
||||
props.useNonceField = true;
|
||||
state.metamask.customNonceValue = '2';
|
||||
const { getByText, getAllByText, queryByText } = renderWithProvider(
|
||||
<TokenAllowance {...props} />,
|
||||
store,
|
||||
);
|
||||
|
||||
const nextButton = getByText('Next');
|
||||
fireEvent.click(nextButton);
|
||||
|
||||
const editButton = getAllByText('Edit');
|
||||
expect(queryByText('Nonce')).toBeInTheDocument();
|
||||
expect(queryByText('2')).toBeInTheDocument();
|
||||
fireEvent.click(editButton[1]);
|
||||
expect(mockShowModal).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
|
||||
it('should render customize nonce modal when next button is clicked, than back button is clicked, than return to previous page and if useNonceField is set to true', () => {
|
||||
props.useNonceField = true;
|
||||
state.metamask.customNonceValue = '2';
|
||||
const { getByText, queryByText } = renderWithProvider(
|
||||
<TokenAllowance {...props} />,
|
||||
store,
|
||||
);
|
||||
|
||||
const nextButton = getByText('Next');
|
||||
fireEvent.click(nextButton);
|
||||
|
||||
const backButton = getByText('< Back');
|
||||
fireEvent.click(backButton);
|
||||
|
||||
const editButton = getByText('Edit');
|
||||
expect(queryByText('Nonce')).toBeInTheDocument();
|
||||
expect(queryByText('2')).toBeInTheDocument();
|
||||
fireEvent.click(editButton);
|
||||
expect(mockShowModal).toHaveBeenCalledTimes(5);
|
||||
});
|
||||
|
||||
it('should click View details and show function type', () => {
|
||||
const { getByText } = renderWithProvider(
|
||||
<TokenAllowance {...props} />,
|
||||
@ -353,7 +482,7 @@ describe('TokenAllowancePage', () => {
|
||||
selectedAddress: '0xc42edfcc21ed14dda456aa0756c153f7985d8813',
|
||||
},
|
||||
};
|
||||
const newStore = configureMockStore()(newState);
|
||||
const newStore = configureMockStore([thunk])(newState);
|
||||
const { queryByText } = renderWithProvider(
|
||||
<TokenAllowance {...props} />,
|
||||
newStore,
|
||||
|
Loading…
Reference in New Issue
Block a user