enable direct routing to the send page (#15259)

feature/default_network_editable
Brad Decker 2 years ago committed by Dan J Miller
parent 810c00c108
commit 024a62f401
  1. 19
      ui/ducks/send/send.js
  2. 10
      ui/pages/send/send-header/send-header.component.js
  3. 29
      ui/pages/send/send.js
  4. 35
      ui/pages/send/send.test.js

@ -1079,8 +1079,10 @@ const slice = createSlice({
updateGasLimit: (state, action) => {
const draftTransaction =
state.draftTransactions[state.currentTransactionUUID];
draftTransaction.gas.gasLimit = addHexPrefix(action.payload);
slice.caseReducers.calculateGasTotal(state);
if (draftTransaction) {
draftTransaction.gas.gasLimit = addHexPrefix(action.payload);
slice.caseReducers.calculateGasTotal(state);
}
},
/**
* sets the layer 1 fees total (for a multi-layer fee network)
@ -2346,6 +2348,19 @@ export function getCurrentDraftTransaction(state) {
return state[name].draftTransactions[getCurrentTransactionUUID(state)] ?? {};
}
/**
* Selector that returns true if a draft transaction exists.
*
* @type {Selector<boolean>}
*/
export function getDraftTransactionExists(state) {
const draftTransaction = getCurrentDraftTransaction(state);
if (Object.keys(draftTransaction).length === 0) {
return false;
}
return true;
}
// Gas selectors
/**

@ -5,6 +5,7 @@ import PageContainerHeader from '../../../components/ui/page-container/page-cont
import { getMostRecentOverviewPage } from '../../../ducks/history/history';
import { useI18nContext } from '../../../hooks/useI18nContext';
import {
getDraftTransactionExists,
getSendAsset,
getSendStage,
resetSendState,
@ -19,15 +20,18 @@ export default function SendHeader() {
const stage = useSelector(getSendStage);
const asset = useSelector(getSendAsset);
const t = useI18nContext();
const draftTransactionExists = useSelector(getDraftTransactionExists);
const onClose = () => {
dispatch(resetSendState());
history.push(mostRecentOverviewPage);
};
let title = asset.type === ASSET_TYPES.NATIVE ? t('send') : t('sendTokens');
let title = asset?.type === ASSET_TYPES.NATIVE ? t('send') : t('sendTokens');
if (stage === SEND_STAGES.ADD_RECIPIENT || stage === SEND_STAGES.INACTIVE) {
if (
draftTransactionExists === false ||
[SEND_STAGES.ADD_RECIPIENT, SEND_STAGES.INACTIVE].includes(stage)
) {
title = t('sendTo');
} else if (stage === SEND_STAGES.EDIT) {
title = t('edit');

@ -1,8 +1,9 @@
import React, { useEffect, useCallback, useContext } from 'react';
import React, { useEffect, useCallback, useContext, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory, useLocation } from 'react-router-dom';
import {
addHistoryEntry,
getDraftTransactionExists,
getIsUsingMyAccountForRecipientSearch,
getRecipient,
getRecipientUserInput,
@ -10,6 +11,7 @@ import {
resetRecipientInput,
resetSendState,
SEND_STAGES,
startNewDraftTransaction,
updateRecipient,
updateRecipientUserInput,
} from '../../ducks/send';
@ -18,6 +20,7 @@ import { getSendHexDataFeatureFlagState } from '../../ducks/metamask/metamask';
import { showQrScanner } from '../../store/actions';
import { MetaMetricsContext } from '../../contexts/metametrics';
import { EVENT } from '../../../shared/constants/metametrics';
import { ASSET_TYPES } from '../../../shared/constants/transaction';
import SendHeader from './send-header';
import AddRecipient from './send-content/add-recipient';
import SendContent from './send-content';
@ -29,6 +32,7 @@ const sendSliceIsCustomPriceExcessive = (state) =>
export default function SendTransactionScreen() {
const history = useHistory();
const startedNewDraftTransaction = useRef(false);
const stage = useSelector(getSendStage);
const gasIsExcessive = useSelector(sendSliceIsCustomPriceExcessive);
const isUsingMyAccountsForRecipientSearch = useSelector(
@ -37,6 +41,7 @@ export default function SendTransactionScreen() {
const recipient = useSelector(getRecipient);
const showHexData = useSelector(getSendHexDataFeatureFlagState);
const userInput = useSelector(getRecipientUserInput);
const draftTransactionExists = useSelector(getDraftTransactionExists);
const location = useLocation();
const trackEvent = useContext(MetaMetricsContext);
@ -46,6 +51,23 @@ export default function SendTransactionScreen() {
dispatch(resetSendState());
}, [dispatch]);
/**
* It is possible to route to this page directly, either by typing in the url
* or by clicking the browser back button after progressing to the confirm
* screen. In the case where a draft transaction does not yet exist, this
* hook is responsible for creating it. We will assume that this is a native
* asset send.
*/
useEffect(() => {
if (
draftTransactionExists === false &&
startedNewDraftTransaction.current === false
) {
startedNewDraftTransaction.current = true;
dispatch(startNewDraftTransaction({ type: ASSET_TYPES.NATIVE }));
}
}, [draftTransactionExists, dispatch]);
useEffect(() => {
window.addEventListener('beforeunload', cleanup);
}, [cleanup]);
@ -70,7 +92,10 @@ export default function SendTransactionScreen() {
let content;
if ([SEND_STAGES.EDIT, SEND_STAGES.DRAFT].includes(stage)) {
if (
draftTransactionExists &&
[SEND_STAGES.EDIT, SEND_STAGES.DRAFT].includes(stage)
) {
content = (
<>
<SendContent

@ -3,7 +3,7 @@ import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { useLocation } from 'react-router-dom';
import { SEND_STAGES } from '../../ducks/send';
import { SEND_STAGES, startNewDraftTransaction } from '../../ducks/send';
import { ensInitialState } from '../../ducks/ens';
import { renderWithProvider } from '../../../test/jest';
import { RINKEBY_CHAIN_ID } from '../../../shared/constants/network';
@ -13,6 +13,20 @@ import Send from './send';
const middleware = [thunk];
jest.mock('../../ducks/send/send', () => {
const original = jest.requireActual('../../ducks/send/send');
return {
...original,
// We don't really need to start a draft transaction, and the mock store
// does not update as a result of action calls so instead we just ensure
// that the action WOULD be called.
startNewDraftTransaction: jest.fn(() => ({
type: 'TEST_START_NEW_DRAFT',
payload: null,
})),
};
});
jest.mock('react-router-dom', () => {
const original = jest.requireActual('react-router-dom');
return {
@ -160,6 +174,25 @@ describe('Send Page', () => {
const { queryByText } = renderWithProvider(<Send />, store);
expect(queryByText('Next')).toBeNull();
});
it('should render correctly even when a draftTransaction does not exist', () => {
const modifiedStore = {
...baseStore,
send: {
...baseStore.send,
currentTransactionUUID: null,
},
};
const store = configureMockStore(middleware)(modifiedStore);
const { getByPlaceholderText } = renderWithProvider(<Send />, store);
// Ensure that the send flow renders on the add recipient screen when
// there is no draft transaction.
expect(
getByPlaceholderText('Search, public address (0x), or ENS'),
).toBeTruthy();
// Ensure we start a new draft transaction when its missing.
expect(startNewDraftTransaction).toHaveBeenCalledTimes(1);
});
});
describe('Send and Edit Flow (draft)', () => {

Loading…
Cancel
Save