Skip to content

Commit 801c9ef

Browse files
feat: improves the react connected hook when using extension & emit terminate when using extension (#1186)
* chore: improves the react connected hook when using extension * chore: fixes linting and unit tests * chore: fixes unit test to meet the new event emit of terminate * chore: fixes fetching selectedAddress to ensure refreshes dont break the flow * chore: adds unit test for useHandleTerminateEvent
1 parent 29f6598 commit 801c9ef

File tree

6 files changed

+87
-4
lines changed

6 files changed

+87
-4
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { renderHook } from '@testing-library/react-hooks';
2+
import { useHandleTerminateEvent } from './useHandleTerminateEvent';
3+
import { EventHandlerProps } from '../MetaMaskProvider';
4+
import * as loggerModule from '../utils/logger';
5+
6+
describe('useHandleTerminateEvent', () => {
7+
const spyLogger = jest.spyOn(loggerModule, 'logger');
8+
9+
const eventHandlerProps = {
10+
setConnecting: jest.fn(),
11+
setConnected: jest.fn(),
12+
setError: jest.fn(),
13+
debug: true,
14+
} as unknown as EventHandlerProps;
15+
16+
beforeEach(() => {
17+
jest.clearAllMocks();
18+
19+
eventHandlerProps.setConnecting = jest.fn();
20+
eventHandlerProps.setConnected = jest.fn();
21+
eventHandlerProps.setError = jest.fn();
22+
});
23+
24+
it('should handle the terminate event correctly', () => {
25+
const mockReason = { message: 'Terminated due to xyz', code: -32000 };
26+
27+
const { result } = renderHook(() =>
28+
useHandleTerminateEvent(eventHandlerProps),
29+
);
30+
result.current(mockReason);
31+
32+
expect(spyLogger).toHaveBeenCalledWith(
33+
"[MetaMaskProvider: useHandleTerminateEvent()] on 'terminate' event.",
34+
mockReason,
35+
);
36+
37+
expect(eventHandlerProps.setConnecting).toHaveBeenCalledWith(false);
38+
expect(eventHandlerProps.setConnected).toHaveBeenCalledWith(false);
39+
expect(eventHandlerProps.setError).toHaveBeenCalledWith(mockReason);
40+
});
41+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { EthereumRpcError } from 'eth-rpc-errors';
2+
import { useCallback } from 'react';
3+
import { EventHandlerProps } from '../MetaMaskProvider';
4+
import { logger } from '../utils/logger';
5+
6+
export const useHandleTerminateEvent = ({
7+
debug,
8+
setConnecting,
9+
setConnected,
10+
setError,
11+
}: EventHandlerProps) => {
12+
return useCallback(
13+
(reason: unknown) => {
14+
logger(
15+
`[MetaMaskProvider: useHandleTerminateEvent()] on 'terminate' event.`,
16+
reason,
17+
);
18+
19+
setConnecting(false);
20+
setConnected(false);
21+
setError(reason as EthereumRpcError<unknown>);
22+
},
23+
[debug, setConnecting, setConnected, setError],
24+
);
25+
};

packages/sdk-react/src/MetaMaskProvider.spec.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ describe('MetaMaskProvider Component', () => {
117117
});
118118

119119
expect(mockSdkOn).toHaveBeenCalledTimes(2);
120-
expect(mockProviderOn).toHaveBeenCalledTimes(6);
120+
expect(mockProviderOn).toHaveBeenCalledTimes(7);
121121

122122
expect(mockSdkOn.mock.calls).toEqual([
123123
['service_status', expect.any(Function)],
@@ -126,6 +126,7 @@ describe('MetaMaskProvider Component', () => {
126126

127127
expect(mockProviderOn.mock.calls).toEqual([
128128
['_initialized', expect.any(Function)],
129+
['terminate', expect.any(Function)],
129130
['connecting', expect.any(Function)],
130131
['connect', expect.any(Function)],
131132
['disconnect', expect.any(Function)],
@@ -144,7 +145,7 @@ describe('MetaMaskProvider Component', () => {
144145
cleanup();
145146

146147
expect(mockSdkRemoveListener).toHaveBeenCalledTimes(2);
147-
expect(mockProviderRemoveListener).toHaveBeenCalledTimes(6);
148+
expect(mockProviderRemoveListener).toHaveBeenCalledTimes(7);
148149

149150
expect(mockSdkRemoveListener.mock.calls).toEqual([
150151
['service_status', expect.any(Function)],
@@ -156,6 +157,7 @@ describe('MetaMaskProvider Component', () => {
156157
['connecting', expect.any(Function)],
157158
['connect', expect.any(Function)],
158159
['disconnect', expect.any(Function)],
160+
['terminate', expect.any(Function)],
159161
['accountsChanged', expect.any(Function)],
160162
['chainChanged', expect.any(Function)],
161163
]);

packages/sdk-react/src/MetaMaskProvider.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { useHandleInitializedEvent } from './EventsHandlers/useHandleInitialized
2323
import { useHandleOnConnectingEvent } from './EventsHandlers/useHandleOnConnectingEvent';
2424
import { useHandleProviderEvent } from './EventsHandlers/useHandleProviderEvent';
2525
import { useHandleSDKStatusEvent } from './EventsHandlers/useHandleSDKStatusEvent';
26+
import { useHandleTerminateEvent } from './EventsHandlers/useHandleTerminateEvent';
2627
import { logger } from './utils/logger';
2728

2829
export interface EventHandlerProps {
@@ -130,6 +131,8 @@ const MetaMaskProviderClient = ({
130131
const onConnect = useHandleConnectEvent(eventHandlerProps);
131132

132133
const onDisconnect = useHandleDisconnectEvent(eventHandlerProps);
134+
135+
const onTerminate = useHandleTerminateEvent(eventHandlerProps);
133136

134137
const onAccountsChanged = useHandleAccountsChangedEvent(eventHandlerProps);
135138

@@ -262,12 +265,18 @@ const MetaMaskProviderClient = ({
262265
console.warn(`[MetaMaskProviderClient] activeProvider is undefined.`);
263266
return;
264267
}
265-
setConnected(activeProvider.isConnected());
268+
269+
const isConnected = sdk.isExtensionActive()
270+
? !!activeProvider.getSelectedAddress()
271+
: activeProvider.isConnected();
272+
273+
setConnected(isConnected);
266274
setAccount(activeProvider.getSelectedAddress() || undefined);
267275
setProvider(activeProvider);
268276
setChainId(activeProvider.getChainId() || undefined);
269277

270278
activeProvider.on('_initialized', onInitialized);
279+
activeProvider.on('terminate', onTerminate);
271280
activeProvider.on('connecting', onConnecting);
272281
activeProvider.on('connect', onConnect);
273282
activeProvider.on('disconnect', onDisconnect);
@@ -296,6 +305,7 @@ const MetaMaskProviderClient = ({
296305
activeProvider.removeListener('connecting', onConnecting);
297306
activeProvider.removeListener('connect', onConnect);
298307
activeProvider.removeListener('disconnect', onDisconnect);
308+
activeProvider.removeListener('terminate', onTerminate);
299309
activeProvider.removeListener('accountsChanged', onAccountsChanged);
300310
activeProvider.removeListener('chainChanged', onChainChanged);
301311
sdk.removeListener(EventType.SERVICE_STATUS, onSDKStatusEvent);

packages/sdk/src/services/MetaMaskSDK/ConnectionManager/terminate.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ describe('terminate', () => {
9090
it('should not switch providers if extensionOnly option is true', async () => {
9191
instance.options.extensionOnly = true;
9292
await terminate(instance);
93-
expect(mockEmit).not.toHaveBeenCalled();
93+
expect(mockEmit).toHaveBeenCalledTimes(1);
9494
});
9595
});
9696

packages/sdk/src/services/MetaMaskSDK/ConnectionManager/terminate.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ export async function terminate(instance: MetaMaskSDK) {
4848
}
4949

5050
if (instance.options.extensionOnly) {
51+
instance.emit(
52+
MetaMaskSDKEvent.ProviderUpdate,
53+
PROVIDER_UPDATE_TYPE.TERMINATE,
54+
);
55+
5156
logger(
5257
`[MetaMaskSDK: terminate()] extensionOnly --- prevent switching providers`,
5358
);

0 commit comments

Comments
 (0)