1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 09:57:02 +01:00

Added the ability to navigate multiple SIWE notifications (#18103)

This commit is contained in:
aleksandar-mihajlovic 2023-06-08 13:02:08 +02:00 committed by GitHub
parent 2e856894cc
commit f3147bcfb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 209 additions and 3 deletions

View File

@ -5,6 +5,76 @@ exports[`SignatureRequestSIWE (Sign in with Ethereum) should match snapshot 1`]
<div
class="signature-request-siwe"
>
<div
class="request-signature__navigation"
>
<div
class="confirm-page-container-navigation"
style="display: none;"
>
<div
class="confirm-page-container-navigation__container"
data-testid="navigation-container"
style="visibility: hidden;"
>
<button
class="confirm-page-container-navigation__arrow"
data-testid="first-page"
>
<i
class="fa fa-angle-double-left fa-2x"
/>
</button>
<button
class="confirm-page-container-navigation__arrow"
data-testid="previous-page"
>
<i
class="fa fa-angle-left fa-2x"
/>
</button>
</div>
<div
class="confirm-page-container-navigation__textcontainer"
>
<div
class="confirm-page-container-navigation__navtext"
>
0
of
1
</div>
<div
class="confirm-page-container-navigation__longtext"
>
requests waiting to be acknowledged
</div>
</div>
<div
class="confirm-page-container-navigation__container"
style="visibility: initial;"
>
<button
class="confirm-page-container-navigation__arrow"
data-testid="next-page"
>
<i
class="fa fa-angle-right fa-2x"
/>
</button>
<button
class="confirm-page-container-navigation__arrow"
data-testid="last-page"
>
<i
class="fa fa-angle-double-right fa-2x"
/>
</button>
</div>
</div>
</div>
<div
class="box network-account-balance-header box--padding-4 box--display-flex box--flex-direction-row box--justify-content-space-between box--align-items-center"
>

View File

@ -1,20 +1,25 @@
import React, { useCallback, useContext, useState } from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { useSelector, useDispatch } from 'react-redux';
import { useHistory } from 'react-router-dom';
import log from 'loglevel';
import { isValidSIWEOrigin } from '@metamask/controller-utils';
import { BannerAlert, Text } from '../../component-library';
import Popover from '../../ui/popover';
import Checkbox from '../../ui/check-box';
import Button from '../../ui/button';
import { I18nContext } from '../../../contexts/i18n';
import { PageContainerFooter } from '../../ui/page-container';
import { isAddressLedger } from '../../../ducks/metamask/metamask';
import {
accountsWithSendEtherInfoSelector,
getSubjectMetadata,
getTotalUnapprovedMessagesCount,
unconfirmedMessagesHashSelector,
} from '../../../selectors';
import { getAccountByAddress } from '../../../helpers/utils/util';
import { getAccountByAddress, valuesFor } from '../../../helpers/utils/util';
import { formatMessageParams } from '../../../../shared/modules/siwe';
import { clearConfirmTransaction } from '../../../ducks/confirm-transaction/confirm-transaction.duck';
import {
SEVERITIES,
@ -23,6 +28,9 @@ import {
import SecurityProviderBannerMessage from '../security-provider-banner-message/security-provider-banner-message';
import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../security-provider-banner-message/security-provider-banner-message.constants';
import ConfirmPageContainerNavigation from '../confirm-page-container/confirm-page-container-navigation';
import { getMostRecentOverviewPage } from '../../../ducks/history/history';
import { showModal, cancelMsgs } from '../../../store/actions';
import LedgerInstructionField from '../ledger-instruction-field';
import SignatureRequestHeader from '../signature-request-header';
@ -34,11 +42,17 @@ export default function SignatureRequestSIWE({
cancelPersonalMessage,
signPersonalMessage,
}) {
const dispatch = useDispatch();
const history = useHistory();
const t = useContext(I18nContext);
const allAccounts = useSelector(accountsWithSendEtherInfoSelector);
const subjectMetadata = useSelector(getSubjectMetadata);
const messagesCount = useSelector(getTotalUnapprovedMessagesCount);
const messagesList = useSelector(unconfirmedMessagesHashSelector);
const mostRecentOverviewPage = useSelector(getMostRecentOverviewPage);
const {
msgParams: {
from,
@ -90,8 +104,29 @@ export default function SignatureRequestSIWE({
[cancelPersonalMessage],
);
const handleCancelAll = () => {
const unapprovedTxCount = messagesCount;
dispatch(
showModal({
name: 'REJECT_TRANSACTIONS',
unapprovedTxCount,
onSubmit: async () => {
await dispatch(cancelMsgs(valuesFor(messagesList)));
dispatch(clearConfirmTransaction());
history.push(mostRecentOverviewPage);
},
}),
);
};
const rejectNText = t('rejectRequestsN', [messagesCount]);
return (
<div className="signature-request-siwe">
<div className="request-signature__navigation">
<ConfirmPageContainerNavigation />
</div>
<SignatureRequestHeader txData={txData} />
<Header
fromAccount={fromAccount}
@ -150,6 +185,18 @@ export default function SignatureRequestSIWE({
submitText={t('signin')}
submitButtonType={isSIWEDomainValid ? 'primary' : 'danger-primary'}
/>
{messagesCount > 1 ? (
<Button
type="link"
className="request-signature__container__reject"
onClick={(e) => {
e.preventDefault();
handleCancelAll();
}}
>
{rejectNText}
</Button>
) : null}
{isShowingDomainWarning && (
<Popover
onClose={() => setIsShowingDomainWarning(false)}

View File

@ -1,5 +1,6 @@
import React from 'react';
import { cloneDeep } from 'lodash';
import { fireEvent } from '@testing-library/react';
import { MESSAGE_TYPE } from '../../../../shared/constants/app';
import mockState from '../../../../test/data/mock-state.json';
import { renderWithProvider } from '../../../../test/lib/render-helpers';
@ -21,6 +22,14 @@ const mockStoreInitialState = {
},
};
const mockShowModal = jest.fn();
jest.mock('../../../store/actions.ts', () => {
return {
showModal: () => mockShowModal,
};
});
const mockProps = {
cancelPersonalMessage: jest.fn(),
signPersonalMessage: jest.fn(),
@ -136,4 +145,84 @@ describe('SignatureRequestSIWE (Sign in with Ethereum)', () => {
container.querySelector('.mock-ledger-instruction-field'),
).toBeTruthy();
});
describe('when there is only one unconfirmed tx', () => {
it('should not show multiple notifications header', () => {
const store = configureStore(mockStoreInitialState);
const txData = cloneDeep(mockProps.txData);
const { container } = renderWithProvider(
<SignatureRequestSIWE {...mockProps} txData={txData} />,
store,
);
expect(
container.querySelector('.confirm-page-container-navigation'),
).toHaveStyle('display: none');
});
it('should not show Reject request button', () => {
const { container } = render();
expect(
container.querySelector('.request-signature__container__reject'),
).not.toBeInTheDocument();
});
});
describe('when there is more than one unconfirmed tx', () => {
let renderResult;
beforeEach(() => {
const store = configureStore({
...mockStoreInitialState,
metamask: {
...mockStoreInitialState.metamask,
unapprovedTxs: {
...mockStoreInitialState.metamask.unapprovedTxs,
'0x12333': {
chainId: mockStoreInitialState.metamask.providerConfig.chainId,
},
},
unapprovedMsgCount: 2,
},
});
const txData = cloneDeep(mockProps.txData);
renderResult = renderWithProvider(
<SignatureRequestSIWE {...mockProps} txData={txData} />,
store,
);
});
afterEach(() => {
renderResult = null;
});
it('should show multiple notifications header', () => {
const { container } = renderResult;
expect(
container.getElementsByClassName('signature-request-siwe-header'),
).toHaveLength(1);
expect(
container.querySelector('.confirm-page-container-navigation'),
).toHaveStyle('display: flex');
});
it('should show Reject request button', () => {
const { getByText } = renderResult;
const cancelAll = getByText('Reject 2 requests');
expect(cancelAll).toHaveClass('request-signature__container__reject');
expect(cancelAll).toBeInTheDocument();
});
it('should show cancel all modal on Reject request button click', () => {
const { getByText } = renderResult;
const cancelAll = getByText('Reject 2 requests');
fireEvent.click(cancelAll);
expect(mockShowModal).toHaveBeenCalled();
});
});
});

View File

@ -61,7 +61,7 @@ const mockStoreWithEth = {
cachedBalances: {},
unapprovedDecryptMsgs: {},
unapprovedEncryptionPublicKeyMsgs: {},
uncofirmedTransactions: {},
unconfirmedTransactions: {},
selectedAddress: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
nativeCurrency: 'ETH',
currentCurrency: 'usd',