Skip to content

Commit faa950e

Browse files
committed
refactor: resolve race condition in Home navigation with v5-compat
1 parent 20d2209 commit faa950e

File tree

5 files changed

+102
-69
lines changed

5 files changed

+102
-69
lines changed

ui/components/app/srp-quiz-modal/SRPQuiz/SRPQuiz.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports, import/no-commonjs */
22
import React, { useCallback, useContext, useEffect, useState } from 'react';
33
import { useSelector } from 'react-redux';
4-
import type { NavigateFunction } from 'react-router-dom-v5-compat';
4+
import type { To } from 'react-router-dom-v5-compat';
55
import {
66
MetaMetricsEventCategory,
77
MetaMetricsEventKeyType,
@@ -64,7 +64,13 @@ export type SRPQuizProps = {
6464
isOpen: boolean;
6565
onClose: () => void;
6666
closeAfterCompleting?: boolean;
67-
navigate: NavigateFunction;
67+
navigate: {
68+
(
69+
to: To,
70+
options?: { replace?: boolean; state?: Record<string, unknown> },
71+
): void;
72+
(delta: number): void;
73+
};
6874
};
6975

7076
// TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31860

ui/components/multichain/account-details/account-details.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React, { useCallback, useContext, useState } from 'react';
33
import { useDispatch, useSelector } from 'react-redux';
44
import { KeyringObject, KeyringTypes } from '@metamask/keyring-controller';
55
import { AvatarAccountSize } from '@metamask/design-system-react';
6+
import type { To } from 'react-router-dom-v5-compat';
67
import {
78
MetaMetricsEventCategory,
89
MetaMetricsEventKeyType,
@@ -51,10 +52,13 @@ import { AccountDetailsKey } from './account-details-key';
5152

5253
type AccountDetailsProps = {
5354
address: string;
54-
navigate?: (
55-
to: string | number,
56-
options?: { replace?: boolean; state?: Record<string, unknown> },
57-
) => void;
55+
navigate?: {
56+
(
57+
to: To,
58+
options?: { replace?: boolean; state?: Record<string, unknown> },
59+
): void;
60+
(delta: number): void;
61+
};
5862
};
5963

6064
export const AccountDetails = ({ address, navigate }: AccountDetailsProps) => {

ui/pages/confirmations/confirm-transaction/confirm-transaction.component.js

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -161,30 +161,33 @@ const ConfirmTransaction = ({
161161
}, []);
162162

163163
useEffect(() => {
164-
if (
165-
paramsTransactionId &&
166-
transactionId &&
167-
prevParamsTransactionId !== paramsTransactionId
168-
) {
169-
const { txData: { txParams: { data } = {}, origin } = {} } = transaction;
170-
171-
dispatch(clearConfirmTransaction());
172-
dispatch(setTransactionToConfirm(paramsTransactionId));
173-
if (origin !== ORIGIN_METAMASK) {
174-
dispatch(getContractMethodData(data, use4ByteResolution));
175-
}
176-
} else if (prevTransactionId && !transactionId && !totalUnapproved) {
177-
dispatch(setDefaultHomeActiveTabName('activity')).then(() => {
164+
const handleNavigation = async () => {
165+
if (
166+
paramsTransactionId &&
167+
transactionId &&
168+
prevParamsTransactionId !== paramsTransactionId
169+
) {
170+
const { txData: { txParams: { data } = {}, origin } = {} } = transaction;
171+
172+
dispatch(clearConfirmTransaction());
173+
dispatch(setTransactionToConfirm(paramsTransactionId));
174+
if (origin !== ORIGIN_METAMASK) {
175+
dispatch(getContractMethodData(data, use4ByteResolution));
176+
}
177+
} else if (prevTransactionId && !transactionId && !totalUnapproved) {
178+
await dispatch(setDefaultHomeActiveTabName('activity'));
178179
navigate(DEFAULT_ROUTE, { replace: true });
179-
});
180-
} else if (
181-
prevTransactionId &&
182-
transactionId &&
183-
prevTransactionId !== transactionId &&
184-
paramsTransactionId !== transactionId
185-
) {
186-
navigate(mostRecentOverviewPage, { replace: true });
187-
}
180+
} else if (
181+
prevTransactionId &&
182+
transactionId &&
183+
prevTransactionId !== transactionId &&
184+
paramsTransactionId !== transactionId
185+
) {
186+
navigate(mostRecentOverviewPage, { replace: true });
187+
}
188+
};
189+
190+
handleNavigation();
188191
}, [
189192
dispatch,
190193
navigate,

ui/pages/home/home.component.js

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { PureComponent } from 'react';
22
import PropTypes from 'prop-types';
3-
import { Navigate, Routes, Route } from 'react-router-dom-v5-compat';
3+
import { Navigate } from 'react-router-dom-v5-compat';
44
import { Text, TextVariant, TextColor } from '@metamask/design-system-react';
55
import { COHORT_NAMES } from '@metamask/subscription-controller';
66
import {
@@ -929,18 +929,28 @@ export default class Home extends PureComponent {
929929
!showShieldEntryModal &&
930930
!showRecoveryPhrase;
931931

932+
const { location } = this.props;
933+
934+
// Handle connected routes
935+
if (location?.pathname === CONNECTED_ROUTE) {
936+
return (
937+
<ScrollContainer className="main-container main-container--has-shadow">
938+
<ConnectedSites navigate={this.props.navigate} />
939+
</ScrollContainer>
940+
);
941+
}
942+
943+
if (location?.pathname === CONNECTED_ACCOUNTS_ROUTE) {
944+
return (
945+
<ScrollContainer className="main-container main-container--has-shadow">
946+
<ConnectedAccounts navigate={this.props.navigate} />
947+
</ScrollContainer>
948+
);
949+
}
950+
951+
// Render normal home content
932952
return (
933953
<ScrollContainer className="main-container main-container--has-shadow">
934-
<Routes>
935-
<Route
936-
path={CONNECTED_ROUTE}
937-
element={<ConnectedSites navigate={this.props.navigate} />}
938-
/>
939-
<Route
940-
path={CONNECTED_ACCOUNTS_ROUTE}
941-
element={<ConnectedAccounts navigate={this.props.navigate} />}
942-
/>
943-
</Routes>
944954
<div className="home__container">
945955
{dataCollectionForMarketing === null &&
946956
participateInMetaMetrics === true

ui/pages/routes/routes.component.tsx

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
useHistory,
1212
useLocation,
1313
} from 'react-router-dom';
14+
import type { To } from 'react-router-dom-v5-compat';
1415
import IdleTimer from 'react-idle-timer';
1516
import type { ApprovalType } from '@metamask/controller-utils';
1617

@@ -169,13 +170,13 @@ import {
169170
} from './utils';
170171

171172
// V5-compat navigate function type for bridging v5 routes with v5-compat components
172-
type V5CompatNavigate = (
173-
to: string | number,
174-
options?: {
175-
replace?: boolean;
176-
state?: Record<string, unknown>;
177-
},
178-
) => void;
173+
type V5CompatNavigate = {
174+
(
175+
to: To,
176+
options?: { replace?: boolean; state?: Record<string, unknown> },
177+
): void;
178+
(delta: number): void;
179+
};
179180

180181
/**
181182
* Creates a v5-compat navigate function from v5 history
@@ -186,15 +187,18 @@ type V5CompatNavigate = (
186187
const createV5CompatNavigate = (
187188
history: RouteComponentProps['history'],
188189
): V5CompatNavigate => {
189-
return (to, options = {}) => {
190+
return ((
191+
to: To | number,
192+
options?: { replace?: boolean; state?: Record<string, unknown> },
193+
) => {
190194
if (typeof to === 'number') {
191195
history.go(to);
192-
} else if (options.replace) {
193-
history.replace(to, options.state);
196+
} else if (options?.replace) {
197+
history.replace(to as string, options.state);
194198
} else {
195-
history.push(to, options.state);
199+
history.push(to as string, options?.state);
196200
}
197-
};
201+
}) as V5CompatNavigate;
198202
};
199203

200204
/**
@@ -692,12 +696,11 @@ export default function Routes() {
692696
<Suspense fallback={null}>
693697
{/* since the loading time is less than 200ms, we decided not to show a spinner fallback or anything */}
694698
<Switch>
695-
<RouteWithLayout path={ONBOARDING_ROUTE} layout={LegacyLayout}>
696-
{createV5CompatRoute(OnboardingFlow, {
697-
includeNavigate: true,
698-
includeLocation: true,
699-
})}
700-
</RouteWithLayout>
699+
<RouteWithLayout
700+
path={ONBOARDING_ROUTE}
701+
component={OnboardingFlow}
702+
layout={LegacyLayout}
703+
/>
701704
<RouteWithLayout
702705
path={LOCK_ROUTE}
703706
component={Lock}
@@ -863,11 +866,17 @@ export default function Routes() {
863866
})}
864867
</Route>
865868
<Route path={`${CONFIRMATION_V_NEXT_ROUTE}/:id?`}>
866-
{createV5CompatRoute<{ id?: string }>(ConfirmationPage, {
867-
wrapper: AuthenticatedV5Compat,
868-
includeParams: true,
869-
paramsAsProps: false,
870-
})}
869+
{(props: RouteComponentProps<{ id?: string }>) => {
870+
const renderFn = createV5CompatRoute<{ id?: string }>(
871+
ConfirmationPage,
872+
{
873+
wrapper: AuthenticatedV5Compat,
874+
includeParams: true,
875+
paramsAsProps: false,
876+
},
877+
);
878+
return renderFn(props);
879+
}}
871880
</Route>
872881
<RouteWithLayout
873882
authenticated
@@ -1136,12 +1145,13 @@ export default function Routes() {
11361145
component={ShieldPlan}
11371146
layout={LegacyLayout}
11381147
/>
1139-
<RouteWithLayout
1140-
authenticated
1141-
path={DEFAULT_ROUTE}
1142-
component={Home}
1143-
layout={RootLayout}
1144-
/>
1148+
<RouteWithLayout path={DEFAULT_ROUTE} layout={RootLayout}>
1149+
{createV5CompatRoute(Home, {
1150+
wrapper: AuthenticatedV5Compat,
1151+
includeNavigate: true,
1152+
includeLocation: true,
1153+
})}
1154+
</RouteWithLayout>
11451155
</Switch>
11461156
</Suspense>
11471157
);

0 commit comments

Comments
 (0)