1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-26 12:29:06 +01:00

Improvement on approval pages (#18545)

This commit is contained in:
Jyoti Puri 2023-04-13 22:54:33 +05:30 committed by Dan Miller
parent 467858fb78
commit ca7f61126c
7 changed files with 80 additions and 2 deletions

View File

@ -1,4 +1,4 @@
import React, { useState, useContext, useEffect } from 'react'; import React, { useState, useContext, useEffect, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classnames from 'classnames'; import classnames from 'classnames';
@ -41,6 +41,7 @@ export default function CustomSpendingCap({
}) { }) {
const t = useContext(I18nContext); const t = useContext(I18nContext);
const dispatch = useDispatch(); const dispatch = useDispatch();
const inputRef = useRef(null);
const value = useSelector(getCustomTokenAmount); const value = useSelector(getCustomTokenAmount);
@ -139,6 +140,15 @@ export default function CustomSpendingCap({
passTheErrorText(error); passTheErrorText(error);
}, [error, passTheErrorText]); }, [error, passTheErrorText]);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus({
preventScroll: true,
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [inputRef.current]);
const chooseTooltipContentText = decConversionGreaterThan( const chooseTooltipContentText = decConversionGreaterThan(
value, value,
currentTokenBalance, currentTokenBalance,
@ -182,8 +192,8 @@ export default function CustomSpendingCap({
} }
> >
<FormField <FormField
inputRef={inputRef}
dataTestId="custom-spending-cap-input" dataTestId="custom-spending-cap-input"
autoFocus
wrappingLabelProps={{ as: 'div' }} wrappingLabelProps={{ as: 'div' }}
id={ id={
decConversionGreaterThan(value, currentTokenBalance) decConversionGreaterThan(value, currentTokenBalance)

View File

@ -43,6 +43,7 @@ export default function FormField({
id, id,
inputProps, inputProps,
wrappingLabelProps, wrappingLabelProps,
inputRef,
}) { }) {
return ( return (
<div <div
@ -110,6 +111,7 @@ export default function FormField({
dataTestId={dataTestId} dataTestId={dataTestId}
placeholder={placeholder} placeholder={placeholder}
id={id} id={id}
inputRef={inputRef}
/> />
) : ( ) : (
<input <input
@ -125,6 +127,7 @@ export default function FormField({
data-testid={dataTestId} data-testid={dataTestId}
placeholder={placeholder} placeholder={placeholder}
id={id} id={id}
ref={inputRef}
{...inputProps} {...inputProps}
/> />
)} )}
@ -285,4 +288,8 @@ FormField.propTypes = {
* If used ensure the id prop is set on the input and a label element is present using htmlFor with the same id to ensure accessibility. * If used ensure the id prop is set on the input and a label element is present using htmlFor with the same id to ensure accessibility.
*/ */
wrappingLabelProps: PropTypes.object, wrappingLabelProps: PropTypes.object,
/**
* ref for input component
*/
inputRef: PropTypes.object,
}; };

View File

@ -20,6 +20,7 @@ export default function NumericInput({
placeholder, placeholder,
id, id,
name, name,
inputRef,
}) { }) {
return ( return (
<div <div
@ -48,6 +49,7 @@ export default function NumericInput({
placeholder={placeholder} placeholder={placeholder}
id={id} id={id}
name={name} name={name}
ref={inputRef}
/> />
{detailText && ( {detailText && (
<Typography <Typography
@ -80,4 +82,5 @@ NumericInput.propTypes = {
* The id of the input element. Should be used with htmlFor with a label element. * The id of the input element. Should be used with htmlFor with a label element.
*/ */
id: PropTypes.string, id: PropTypes.string,
inputRef: PropTypes.object,
}; };

View File

@ -11,6 +11,8 @@ import Button from '../../../components/ui/button';
import SimulationErrorMessage from '../../../components/ui/simulation-error-message'; import SimulationErrorMessage from '../../../components/ui/simulation-error-message';
import EditGasFeeButton from '../../../components/app/edit-gas-fee-button'; import EditGasFeeButton from '../../../components/app/edit-gas-fee-button';
import MultiLayerFeeMessage from '../../../components/app/multilayer-fee-message'; import MultiLayerFeeMessage from '../../../components/app/multilayer-fee-message';
import SecurityProviderBannerMessage from '../../../components/app/security-provider-banner-message/security-provider-banner-message';
import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../../../components/app/security-provider-banner-message/security-provider-banner-message.constants';
import { import {
BLOCK_SIZES, BLOCK_SIZES,
JustifyContent, JustifyContent,
@ -555,6 +557,15 @@ export default class ConfirmApproveContent extends Component {
'confirm-approve-content--full': showFullTxDetails, 'confirm-approve-content--full': showFullTxDetails,
})} })}
> >
{(txData?.securityProviderResponse?.flagAsDangerous !== undefined &&
txData?.securityProviderResponse?.flagAsDangerous !==
SECURITY_PROVIDER_MESSAGE_SEVERITIES.NOT_MALICIOUS) ||
(txData?.securityProviderResponse &&
Object.keys(txData.securityProviderResponse).length === 0) ? (
<SecurityProviderBannerMessage
securityProviderResponse={txData.securityProviderResponse}
/>
) : null}
{warning && ( {warning && (
<div className="confirm-approve-content__custom-nonce-warning"> <div className="confirm-approve-content__custom-nonce-warning">
<ConfirmPageContainerWarning warning={warning} /> <ConfirmPageContainerWarning warning={warning} />

View File

@ -325,4 +325,22 @@ describe('ConfirmApproveContent Component', () => {
expect(container).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
it('should render security provider response if transaction is malicious', () => {
const securityProviderResponse = {
flagAsDangerous: 1,
reason:
'This has been flagged as potentially suspicious. If you sign, you could lose access to all of your NFTs and any funds or other assets in your wallet.',
reason_header: 'Warning',
};
const { getByText } = renderComponent({
...props,
txData: {
...props.txData,
securityProviderResponse,
},
});
expect(getByText(securityProviderResponse.reason)).toBeInTheDocument();
});
}); });

View File

@ -67,6 +67,8 @@ import {
ICON_NAMES, ICON_NAMES,
} from '../../components/component-library/icon/deprecated'; } from '../../components/component-library/icon/deprecated';
import LedgerInstructionField from '../../components/app/ledger-instruction-field/ledger-instruction-field'; import LedgerInstructionField from '../../components/app/ledger-instruction-field/ledger-instruction-field';
import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../../components/app/security-provider-banner-message/security-provider-banner-message.constants';
import SecurityProviderBannerMessage from '../../components/app/security-provider-banner-message/security-provider-banner-message';
const ALLOWED_HOSTS = ['portfolio.metamask.io']; const ALLOWED_HOSTS = ['portfolio.metamask.io'];
@ -272,6 +274,15 @@ export default function TokenAllowance({
<Box> <Box>
<ConfirmPageContainerNavigation /> <ConfirmPageContainerNavigation />
</Box> </Box>
{(txData?.securityProviderResponse?.flagAsDangerous !== undefined &&
txData?.securityProviderResponse?.flagAsDangerous !==
SECURITY_PROVIDER_MESSAGE_SEVERITIES.NOT_MALICIOUS) ||
(txData?.securityProviderResponse &&
Object.keys(txData.securityProviderResponse).length === 0) ? (
<SecurityProviderBannerMessage
securityProviderResponse={txData.securityProviderResponse}
/>
) : null}
<Box <Box
paddingLeft={4} paddingLeft={4}
paddingRight={4} paddingRight={4}

View File

@ -278,4 +278,22 @@ describe('TokenAllowancePage', () => {
expect(queryByText('Prior to clicking confirm:')).toBeNull(); expect(queryByText('Prior to clicking confirm:')).toBeNull();
}); });
it('should render security provider response if transaction is malicious', () => {
const securityProviderResponse = {
flagAsDangerous: 1,
reason:
'This has been flagged as potentially suspicious. If you sign, you could lose access to all of your NFTs and any funds or other assets in your wallet.',
reason_header: 'Warning',
};
const { getByText } = renderWithProvider(
<TokenAllowance
{...props}
txData={{ ...props.txData, securityProviderResponse }}
/>,
store,
);
expect(getByText(securityProviderResponse.reason)).toBeInTheDocument();
});
}); });