Skip to content

Commit 751c119

Browse files
authored
enable direct routing to the send page (#15259)
1 parent d929364 commit 751c119

File tree

4 files changed

+85
-8
lines changed

4 files changed

+85
-8
lines changed

ui/ducks/send/send.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,8 +1081,10 @@ const slice = createSlice({
10811081
updateGasLimit: (state, action) => {
10821082
const draftTransaction =
10831083
state.draftTransactions[state.currentTransactionUUID];
1084-
draftTransaction.gas.gasLimit = addHexPrefix(action.payload);
1085-
slice.caseReducers.calculateGasTotal(state);
1084+
if (draftTransaction) {
1085+
draftTransaction.gas.gasLimit = addHexPrefix(action.payload);
1086+
slice.caseReducers.calculateGasTotal(state);
1087+
}
10861088
},
10871089
/**
10881090
* sets the layer 1 fees total (for a multi-layer fee network)
@@ -2334,6 +2336,19 @@ export function getCurrentDraftTransaction(state) {
23342336
return state[name].draftTransactions[getCurrentTransactionUUID(state)] ?? {};
23352337
}
23362338

2339+
/**
2340+
* Selector that returns true if a draft transaction exists.
2341+
*
2342+
* @type {Selector<boolean>}
2343+
*/
2344+
export function getDraftTransactionExists(state) {
2345+
const draftTransaction = getCurrentDraftTransaction(state);
2346+
if (Object.keys(draftTransaction).length === 0) {
2347+
return false;
2348+
}
2349+
return true;
2350+
}
2351+
23372352
// Gas selectors
23382353

23392354
/**

ui/pages/send/send-header/send-header.component.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import PageContainerHeader from '../../../components/ui/page-container/page-cont
55
import { getMostRecentOverviewPage } from '../../../ducks/history/history';
66
import { useI18nContext } from '../../../hooks/useI18nContext';
77
import {
8+
getDraftTransactionExists,
89
getSendAsset,
910
getSendStage,
1011
resetSendState,
@@ -19,15 +20,18 @@ export default function SendHeader() {
1920
const stage = useSelector(getSendStage);
2021
const asset = useSelector(getSendAsset);
2122
const t = useI18nContext();
22-
23+
const draftTransactionExists = useSelector(getDraftTransactionExists);
2324
const onClose = () => {
2425
dispatch(resetSendState());
2526
history.push(mostRecentOverviewPage);
2627
};
2728

28-
let title = asset.type === ASSET_TYPES.NATIVE ? t('send') : t('sendTokens');
29+
let title = asset?.type === ASSET_TYPES.NATIVE ? t('send') : t('sendTokens');
2930

30-
if (stage === SEND_STAGES.ADD_RECIPIENT || stage === SEND_STAGES.INACTIVE) {
31+
if (
32+
draftTransactionExists === false ||
33+
[SEND_STAGES.ADD_RECIPIENT, SEND_STAGES.INACTIVE].includes(stage)
34+
) {
3135
title = t('sendTo');
3236
} else if (stage === SEND_STAGES.EDIT) {
3337
title = t('edit');

ui/pages/send/send.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
import React, { useEffect, useCallback, useContext } from 'react';
1+
import React, { useEffect, useCallback, useContext, useRef } from 'react';
22
import { useDispatch, useSelector } from 'react-redux';
33
import { useHistory, useLocation } from 'react-router-dom';
44
import {
55
addHistoryEntry,
6+
getDraftTransactionExists,
67
getIsUsingMyAccountForRecipientSearch,
78
getRecipient,
89
getRecipientUserInput,
910
getSendStage,
1011
resetRecipientInput,
1112
resetSendState,
1213
SEND_STAGES,
14+
startNewDraftTransaction,
1315
updateRecipient,
1416
updateRecipientUserInput,
1517
} from '../../ducks/send';
@@ -18,6 +20,7 @@ import { getSendHexDataFeatureFlagState } from '../../ducks/metamask/metamask';
1820
import { showQrScanner } from '../../store/actions';
1921
import { MetaMetricsContext } from '../../contexts/metametrics';
2022
import { EVENT } from '../../../shared/constants/metametrics';
23+
import { ASSET_TYPES } from '../../../shared/constants/transaction';
2124
import SendHeader from './send-header';
2225
import AddRecipient from './send-content/add-recipient';
2326
import SendContent from './send-content';
@@ -29,6 +32,7 @@ const sendSliceIsCustomPriceExcessive = (state) =>
2932

3033
export default function SendTransactionScreen() {
3134
const history = useHistory();
35+
const startedNewDraftTransaction = useRef(false);
3236
const stage = useSelector(getSendStage);
3337
const gasIsExcessive = useSelector(sendSliceIsCustomPriceExcessive);
3438
const isUsingMyAccountsForRecipientSearch = useSelector(
@@ -37,6 +41,7 @@ export default function SendTransactionScreen() {
3741
const recipient = useSelector(getRecipient);
3842
const showHexData = useSelector(getSendHexDataFeatureFlagState);
3943
const userInput = useSelector(getRecipientUserInput);
44+
const draftTransactionExists = useSelector(getDraftTransactionExists);
4045
const location = useLocation();
4146
const trackEvent = useContext(MetaMetricsContext);
4247

@@ -46,6 +51,23 @@ export default function SendTransactionScreen() {
4651
dispatch(resetSendState());
4752
}, [dispatch]);
4853

54+
/**
55+
* It is possible to route to this page directly, either by typing in the url
56+
* or by clicking the browser back button after progressing to the confirm
57+
* screen. In the case where a draft transaction does not yet exist, this
58+
* hook is responsible for creating it. We will assume that this is a native
59+
* asset send.
60+
*/
61+
useEffect(() => {
62+
if (
63+
draftTransactionExists === false &&
64+
startedNewDraftTransaction.current === false
65+
) {
66+
startedNewDraftTransaction.current = true;
67+
dispatch(startNewDraftTransaction({ type: ASSET_TYPES.NATIVE }));
68+
}
69+
}, [draftTransactionExists, dispatch]);
70+
4971
useEffect(() => {
5072
window.addEventListener('beforeunload', cleanup);
5173
}, [cleanup]);
@@ -70,7 +92,10 @@ export default function SendTransactionScreen() {
7092

7193
let content;
7294

73-
if ([SEND_STAGES.EDIT, SEND_STAGES.DRAFT].includes(stage)) {
95+
if (
96+
draftTransactionExists &&
97+
[SEND_STAGES.EDIT, SEND_STAGES.DRAFT].includes(stage)
98+
) {
7499
content = (
75100
<>
76101
<SendContent

ui/pages/send/send.test.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import configureMockStore from 'redux-mock-store';
33
import thunk from 'redux-thunk';
44
import { useLocation } from 'react-router-dom';
5-
import { SEND_STAGES } from '../../ducks/send';
5+
import { SEND_STAGES, startNewDraftTransaction } from '../../ducks/send';
66
import { ensInitialState } from '../../ducks/ens';
77
import { renderWithProvider } from '../../../test/jest';
88
import { RINKEBY_CHAIN_ID } from '../../../shared/constants/network';
@@ -12,6 +12,20 @@ import Send from './send';
1212

1313
const middleware = [thunk];
1414

15+
jest.mock('../../ducks/send/send', () => {
16+
const original = jest.requireActual('../../ducks/send/send');
17+
return {
18+
...original,
19+
// We don't really need to start a draft transaction, and the mock store
20+
// does not update as a result of action calls so instead we just ensure
21+
// that the action WOULD be called.
22+
startNewDraftTransaction: jest.fn(() => ({
23+
type: 'TEST_START_NEW_DRAFT',
24+
payload: null,
25+
})),
26+
};
27+
});
28+
1529
jest.mock('react-router-dom', () => {
1630
const original = jest.requireActual('react-router-dom');
1731
return {
@@ -163,6 +177,25 @@ describe('Send Page', () => {
163177
const { queryByText } = renderWithProvider(<Send />, store);
164178
expect(queryByText('Next')).toBeNull();
165179
});
180+
181+
it('should render correctly even when a draftTransaction does not exist', () => {
182+
const modifiedStore = {
183+
...baseStore,
184+
send: {
185+
...baseStore.send,
186+
currentTransactionUUID: null,
187+
},
188+
};
189+
const store = configureMockStore(middleware)(modifiedStore);
190+
const { getByPlaceholderText } = renderWithProvider(<Send />, store);
191+
// Ensure that the send flow renders on the add recipient screen when
192+
// there is no draft transaction.
193+
expect(
194+
getByPlaceholderText('Search, public address (0x), or ENS'),
195+
).toBeTruthy();
196+
// Ensure we start a new draft transaction when its missing.
197+
expect(startNewDraftTransaction).toHaveBeenCalledTimes(1);
198+
});
166199
});
167200

168201
describe('Send and Edit Flow (draft)', () => {

0 commit comments

Comments
 (0)