Skip to content

Commit

Permalink
feat: improves the react connected hook when using extension & emit t…
Browse files Browse the repository at this point in the history
…erminate 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
  • Loading branch information
christopherferreira9 authored Jan 13, 2025
1 parent 29f6598 commit 801c9ef
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { renderHook } from '@testing-library/react-hooks';
import { useHandleTerminateEvent } from './useHandleTerminateEvent';
import { EventHandlerProps } from '../MetaMaskProvider';
import * as loggerModule from '../utils/logger';

describe('useHandleTerminateEvent', () => {
const spyLogger = jest.spyOn(loggerModule, 'logger');

const eventHandlerProps = {
setConnecting: jest.fn(),
setConnected: jest.fn(),
setError: jest.fn(),
debug: true,
} as unknown as EventHandlerProps;

beforeEach(() => {
jest.clearAllMocks();

eventHandlerProps.setConnecting = jest.fn();
eventHandlerProps.setConnected = jest.fn();
eventHandlerProps.setError = jest.fn();
});

it('should handle the terminate event correctly', () => {
const mockReason = { message: 'Terminated due to xyz', code: -32000 };

const { result } = renderHook(() =>
useHandleTerminateEvent(eventHandlerProps),
);
result.current(mockReason);

expect(spyLogger).toHaveBeenCalledWith(
"[MetaMaskProvider: useHandleTerminateEvent()] on 'terminate' event.",
mockReason,
);

expect(eventHandlerProps.setConnecting).toHaveBeenCalledWith(false);
expect(eventHandlerProps.setConnected).toHaveBeenCalledWith(false);
expect(eventHandlerProps.setError).toHaveBeenCalledWith(mockReason);
});
});
25 changes: 25 additions & 0 deletions packages/sdk-react/src/EventsHandlers/useHandleTerminateEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { EthereumRpcError } from 'eth-rpc-errors';
import { useCallback } from 'react';
import { EventHandlerProps } from '../MetaMaskProvider';
import { logger } from '../utils/logger';

export const useHandleTerminateEvent = ({
debug,
setConnecting,
setConnected,
setError,
}: EventHandlerProps) => {
return useCallback(
(reason: unknown) => {
logger(
`[MetaMaskProvider: useHandleTerminateEvent()] on 'terminate' event.`,
reason,
);

setConnecting(false);
setConnected(false);
setError(reason as EthereumRpcError<unknown>);
},
[debug, setConnecting, setConnected, setError],
);
};
6 changes: 4 additions & 2 deletions packages/sdk-react/src/MetaMaskProvider.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ describe('MetaMaskProvider Component', () => {
});

expect(mockSdkOn).toHaveBeenCalledTimes(2);
expect(mockProviderOn).toHaveBeenCalledTimes(6);
expect(mockProviderOn).toHaveBeenCalledTimes(7);

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

expect(mockProviderOn.mock.calls).toEqual([
['_initialized', expect.any(Function)],
['terminate', expect.any(Function)],
['connecting', expect.any(Function)],
['connect', expect.any(Function)],
['disconnect', expect.any(Function)],
Expand All @@ -144,7 +145,7 @@ describe('MetaMaskProvider Component', () => {
cleanup();

expect(mockSdkRemoveListener).toHaveBeenCalledTimes(2);
expect(mockProviderRemoveListener).toHaveBeenCalledTimes(6);
expect(mockProviderRemoveListener).toHaveBeenCalledTimes(7);

expect(mockSdkRemoveListener.mock.calls).toEqual([
['service_status', expect.any(Function)],
Expand All @@ -156,6 +157,7 @@ describe('MetaMaskProvider Component', () => {
['connecting', expect.any(Function)],
['connect', expect.any(Function)],
['disconnect', expect.any(Function)],
['terminate', expect.any(Function)],
['accountsChanged', expect.any(Function)],
['chainChanged', expect.any(Function)],
]);
Expand Down
12 changes: 11 additions & 1 deletion packages/sdk-react/src/MetaMaskProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { useHandleInitializedEvent } from './EventsHandlers/useHandleInitialized
import { useHandleOnConnectingEvent } from './EventsHandlers/useHandleOnConnectingEvent';
import { useHandleProviderEvent } from './EventsHandlers/useHandleProviderEvent';
import { useHandleSDKStatusEvent } from './EventsHandlers/useHandleSDKStatusEvent';
import { useHandleTerminateEvent } from './EventsHandlers/useHandleTerminateEvent';
import { logger } from './utils/logger';

export interface EventHandlerProps {
Expand Down Expand Up @@ -130,6 +131,8 @@ const MetaMaskProviderClient = ({
const onConnect = useHandleConnectEvent(eventHandlerProps);

const onDisconnect = useHandleDisconnectEvent(eventHandlerProps);

const onTerminate = useHandleTerminateEvent(eventHandlerProps);

const onAccountsChanged = useHandleAccountsChangedEvent(eventHandlerProps);

Expand Down Expand Up @@ -262,12 +265,18 @@ const MetaMaskProviderClient = ({
console.warn(`[MetaMaskProviderClient] activeProvider is undefined.`);
return;
}
setConnected(activeProvider.isConnected());

const isConnected = sdk.isExtensionActive()
? !!activeProvider.getSelectedAddress()
: activeProvider.isConnected();

setConnected(isConnected);
setAccount(activeProvider.getSelectedAddress() || undefined);
setProvider(activeProvider);
setChainId(activeProvider.getChainId() || undefined);

activeProvider.on('_initialized', onInitialized);
activeProvider.on('terminate', onTerminate);
activeProvider.on('connecting', onConnecting);
activeProvider.on('connect', onConnect);
activeProvider.on('disconnect', onDisconnect);
Expand Down Expand Up @@ -296,6 +305,7 @@ const MetaMaskProviderClient = ({
activeProvider.removeListener('connecting', onConnecting);
activeProvider.removeListener('connect', onConnect);
activeProvider.removeListener('disconnect', onDisconnect);
activeProvider.removeListener('terminate', onTerminate);
activeProvider.removeListener('accountsChanged', onAccountsChanged);
activeProvider.removeListener('chainChanged', onChainChanged);
sdk.removeListener(EventType.SERVICE_STATUS, onSDKStatusEvent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ describe('terminate', () => {
it('should not switch providers if extensionOnly option is true', async () => {
instance.options.extensionOnly = true;
await terminate(instance);
expect(mockEmit).not.toHaveBeenCalled();
expect(mockEmit).toHaveBeenCalledTimes(1);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export async function terminate(instance: MetaMaskSDK) {
}

if (instance.options.extensionOnly) {
instance.emit(
MetaMaskSDKEvent.ProviderUpdate,
PROVIDER_UPDATE_TYPE.TERMINATE,
);

logger(
`[MetaMaskSDK: terminate()] extensionOnly --- prevent switching providers`,
);
Expand Down

0 comments on commit 801c9ef

Please sign in to comment.