Skip to content

Commit dd00145

Browse files
authored
Implement routing for new confirmation components (#21970)
1 parent 2023178 commit dd00145

File tree

19 files changed

+438
-16
lines changed

19 files changed

+438
-16
lines changed

development/build/transforms/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ this.store.updateStructure({
1616
...,
1717
GasFeeController: this.gasFeeController,
1818
TokenListController: this.tokenListController,
19-
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
19+
///: BEGIN:ONLY_INCLUDE_IF(build-flask)
2020
SnapController: this.snapController,
21-
///: END:ONLY_INCLUDE_IN
21+
///: END:ONLY_INCLUDE_IF
2222
});
2323
```
2424

@@ -36,7 +36,7 @@ Note that multiple build types can be specified by separating them with
3636
commands inside the parameter parentheses:
3737

3838
```javascript
39-
///: BEGIN:ONLY_INCLUDE_IN(build-beta,build-flask)
39+
///: BEGIN:ONLY_INCLUDE_IF(build-beta,build-flask)
4040
```
4141

4242
It's critical that this transform runs before anything else processes our code.

test/e2e/tests/state-snapshots/errors-after-init-opt-in-ui-state.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"DNS": "object",
33
"activeTab": "object",
44
"appState": "object",
5+
"confirm": "object",
56
"confirmTransaction": "object",
67
"gas": { "customData": { "price": null, "limit": null } },
78
"history": { "mostRecentOverviewPage": "/" },

ui/ducks/confirm/confirm.test.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import confirmReducer, { UPDATE_CURRENT_CONFIRMATION } from './confirm';
2+
3+
describe('Confirm State', () => {
4+
const metamaskConfirmState = {
5+
currentConfirmation: undefined,
6+
};
7+
8+
it('sets currentConfirmation', () => {
9+
const currentConfirmation = {
10+
id: '123',
11+
};
12+
const state = confirmReducer(metamaskConfirmState, {
13+
type: UPDATE_CURRENT_CONFIRMATION,
14+
currentConfirmation,
15+
});
16+
17+
expect(state.currentConfirmation).toStrictEqual(currentConfirmation);
18+
});
19+
});

ui/ducks/confirm/confirm.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,38 @@
11
type ActionType = {
22
type: string;
3-
dummyValue?: string;
3+
currentConfirmation?: Record<string, unknown> | undefined;
44
};
55

66
const createActionType = (action: string): string =>
77
`metamask/confirm/${action}`;
88

9-
const UPDATE_DUMMY = createActionType('UPDATE_DUMMY');
9+
export const UPDATE_CURRENT_CONFIRMATION = createActionType(
10+
'UPDATE_CURRENT_CONFIRMATION',
11+
);
1012

1113
const initState = {
12-
dummy: undefined,
14+
currentConfirmation: undefined,
1315
};
1416

1517
export default function confirmReducer(
1618
state = initState,
1719
action: ActionType = { type: '' },
1820
) {
1921
switch (action.type) {
20-
case UPDATE_DUMMY:
22+
case UPDATE_CURRENT_CONFIRMATION:
2123
return {
22-
dummy: action.dummyValue,
24+
currentConfirmation: action.currentConfirmation,
2325
};
2426
default:
2527
return state;
2628
}
2729
}
2830

29-
export function updateDummy(dummy: string) {
31+
export function updateCurrentConfirmation(
32+
currentConfirmation: Record<string, unknown> | undefined,
33+
) {
3034
return {
31-
type: UPDATE_DUMMY,
32-
dummy,
35+
type: UPDATE_CURRENT_CONFIRMATION,
36+
currentConfirmation,
3337
};
3438
}

ui/ducks/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { combineReducers } from 'redux';
22
import { AlertTypes } from '../../shared/constants/alerts';
3+
import confirmReducer from './confirm/confirm';
34
import metamaskReducer from './metamask/metamask';
45
import localeMessagesReducer from './locale/locale';
56
import sendReducer from './send/send';
@@ -20,6 +21,7 @@ export default combineReducers({
2021
DNS: domainReducer,
2122
history: historyReducer,
2223
send: sendReducer,
24+
confirm: confirmReducer,
2325
confirmTransaction: confirmTransactionReducer,
2426
swaps: swapsReducer,
2527
gas: gasReducer,
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React from 'react';
2+
import { Provider } from 'react-redux';
3+
import { renderHook } from '@testing-library/react-hooks';
4+
import * as ConfirmDucks from '../../ducks/confirm/confirm';
5+
import configureStore from '../../store/store';
6+
import setCurrentConfirmation from './setCurrentConfirmation';
7+
8+
const mockState = {
9+
metamask: {
10+
confirm: {
11+
currentConfirmation: undefined,
12+
},
13+
},
14+
};
15+
16+
const mockCurrentConfirmation = { id: '1' };
17+
18+
jest.mock('./useCurrentConfirmation', () => () => ({
19+
currentConfirmation: mockCurrentConfirmation,
20+
}));
21+
22+
const ReduxProvider = Provider as any;
23+
24+
describe('setCurrentConfirmation', () => {
25+
it('should dispatch updateCurrentConfirmation', () => {
26+
const updateCurrentConfirmationSpy = jest.spyOn(
27+
ConfirmDucks,
28+
'updateCurrentConfirmation',
29+
);
30+
const wrapper = ({ children }) => (
31+
<ReduxProvider store={configureStore(mockState)}>
32+
{children}
33+
</ReduxProvider>
34+
);
35+
renderHook(() => setCurrentConfirmation(), { wrapper });
36+
expect(updateCurrentConfirmationSpy).toHaveBeenCalledWith(
37+
mockCurrentConfirmation,
38+
);
39+
});
40+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useEffect } from 'react';
2+
import { useDispatch } from 'react-redux';
3+
4+
import { updateCurrentConfirmation } from '../../ducks/confirm/confirm';
5+
import useCurrentConfirmation from './useCurrentConfirmation';
6+
7+
/*
8+
* This hook is called from <Confirm /> component to set current transaction.
9+
* This hook should be invoked only when we are setting or resetting the
10+
* current confirmation displayed to the user.
11+
*/
12+
const setCurrentConfirmation = () => {
13+
const dispatch = useDispatch();
14+
const { currentConfirmation } = useCurrentConfirmation();
15+
16+
useEffect(() => {
17+
if (currentConfirmation) {
18+
dispatch(updateCurrentConfirmation(currentConfirmation));
19+
}
20+
}, [currentConfirmation]);
21+
22+
useEffect(() => {
23+
return () => {
24+
dispatch(updateCurrentConfirmation(undefined));
25+
};
26+
}, []);
27+
};
28+
29+
export default setCurrentConfirmation;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { renderHookWithProvider } from '../../../test/lib/render-helpers';
2+
import syncConfirmPath from './syncConfirmPath';
3+
4+
const mockHistoryReplace = jest.fn();
5+
6+
jest.mock('react-router-dom', () => ({
7+
...jest.requireActual('react-router-dom'),
8+
useHistory: () => ({ replace: mockHistoryReplace }),
9+
}));
10+
11+
const mockState = {
12+
confirm: {
13+
currentConfirmation: {
14+
id: '1',
15+
msgParams: {},
16+
},
17+
},
18+
};
19+
20+
describe('syncConfirmPath', () => {
21+
it('should execute correctly', () => {
22+
const result = renderHookWithProvider(() => syncConfirmPath(), mockState);
23+
expect(result).toBeDefined();
24+
});
25+
26+
it('should replace history route', () => {
27+
mockHistoryReplace.mockClear();
28+
renderHookWithProvider(() => syncConfirmPath(), mockState);
29+
expect(mockHistoryReplace).toHaveBeenCalled();
30+
});
31+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useEffect } from 'react';
2+
import { useHistory, useParams } from 'react-router-dom';
3+
import { useSelector } from 'react-redux';
4+
5+
import {
6+
CONFIRM_TRANSACTION_ROUTE,
7+
SIGNATURE_REQUEST_PATH,
8+
} from '../../helpers/constants/routes';
9+
import { currentConfirmationSelector } from '../../selectors/confirm';
10+
11+
const syncConfirmPath = () => {
12+
const history = useHistory();
13+
const { id: paramsTransactionId } = useParams<{ id: string }>();
14+
15+
const currentConfirmation = useSelector(currentConfirmationSelector);
16+
17+
// Redirect below is done to keep the confirmation routes backward compatible
18+
// Currently we have only signature request,
19+
// but it will include other confirmation types in future
20+
useEffect(() => {
21+
if (!currentConfirmation) {
22+
return;
23+
}
24+
if (paramsTransactionId !== currentConfirmation.id) {
25+
history.replace(
26+
`${CONFIRM_TRANSACTION_ROUTE}/${currentConfirmation.id}/${SIGNATURE_REQUEST_PATH}`,
27+
);
28+
}
29+
}, [currentConfirmation, paramsTransactionId]);
30+
};
31+
32+
export default syncConfirmPath;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { ApprovalType } from '@metamask/controller-utils';
2+
3+
import { renderHookWithProvider } from '../../../test/lib/render-helpers';
4+
import useCurrentConfirmation from './useCurrentConfirmation';
5+
6+
const mockState = {
7+
metamask: {
8+
unapprovedPersonalMsgs: {
9+
'1': {
10+
id: '1',
11+
msgParams: {},
12+
},
13+
},
14+
pendingApprovals: {
15+
'1': {
16+
id: '1',
17+
origin: 'origin',
18+
time: Date.now(),
19+
type: ApprovalType.PersonalSign,
20+
requestData: {},
21+
requestState: null,
22+
expectsResult: false,
23+
},
24+
},
25+
},
26+
};
27+
28+
describe('useCurrentConfirmation', () => {
29+
it('should return current confirmation', () => {
30+
const { result } = renderHookWithProvider(
31+
() => useCurrentConfirmation(),
32+
mockState,
33+
);
34+
35+
expect(result.current.currentConfirmation).toBe(
36+
mockState.metamask.unapprovedPersonalMsgs['1'],
37+
);
38+
});
39+
});

0 commit comments

Comments
 (0)