Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(sentry): don't report AxiosError: Network Error #11721

Merged
merged 36 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
5adfb0e
added a try catch around the infura calls so Sentry does not pick up …
Daniel-Cross Oct 9, 2024
661cdbc
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 10, 2024
bfa33d4
removed unused useCallback
Daniel-Cross Oct 10, 2024
cb6d002
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 10, 2024
5384c97
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 11, 2024
df0bf97
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 14, 2024
ca19834
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 14, 2024
92c9724
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 14, 2024
4604866
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 14, 2024
5daf66a
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 16, 2024
62f9327
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 16, 2024
63b1217
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 17, 2024
f263828
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 17, 2024
b5effef
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 18, 2024
25f31db
added event tracking for dropped connections
Daniel-Cross Oct 18, 2024
a29eab4
fixed import address
Daniel-Cross Oct 18, 2024
ff7bab7
revert project file
Daniel-Cross Oct 18, 2024
e90b5b2
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 18, 2024
0eedc6a
added new line to bottom of file
Daniel-Cross Oct 18, 2024
d9cc1f9
added tests
Daniel-Cross Oct 18, 2024
c53d232
added types
Daniel-Cross Oct 18, 2024
8075d63
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 18, 2024
be0f062
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 19, 2024
4388657
updated wrong test
Daniel-Cross Oct 19, 2024
1b146ea
added new test
Daniel-Cross Oct 20, 2024
4f54785
passed in correct state
Daniel-Cross Oct 20, 2024
a0098b7
added function back into navigation
Daniel-Cross Oct 20, 2024
acacf29
removed unused mocks
Daniel-Cross Oct 20, 2024
1d29786
moved useMetrics inside component
Daniel-Cross Oct 21, 2024
017c480
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 21, 2024
f4425ec
added missing state and navigation
Daniel-Cross Oct 21, 2024
2dd0c6a
pulled function from component and wrote tests
Daniel-Cross Oct 21, 2024
856771f
removed unnecessary prop
Daniel-Cross Oct 21, 2024
9427fc2
added skip check for any
Daniel-Cross Oct 21, 2024
473050b
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 21, 2024
0d4d5d0
Merge branch 'main' into 10778-sentry-axioserror-network-error
Daniel-Cross Oct 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 4 additions & 18 deletions app/components/Nav/Main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ import { createStackNavigator } from '@react-navigation/stack';
import ReviewModal from '../../UI/ReviewModal';
import { useTheme } from '../../../util/theme';
import RootRPCMethodsUI from './RootRPCMethodsUI';
import { colors as importedColors } from '../../../styles/common';
import {
ToastContext,
ToastVariants,
Expand Down Expand Up @@ -82,6 +81,7 @@ import {
stopIncomingTransactionPolling,
} from '../../../util/transaction-controller';
import isNetworkUiRedesignEnabled from '../../../util/networks/isNetworkUiRedesignEnabled';
import { useConnectionHandler } from '../../../util/navigation/useConnectionHandler';

const Stack = createStackNavigator();

Expand All @@ -99,7 +99,6 @@ const createStyles = (colors) =>
});

const Main = (props) => {
const [connected, setConnected] = useState(true);
const [forceReload, setForceReload] = useState(false);
const [showRemindLaterModal, setShowRemindLaterModal] = useState(false);
const [skipCheckbox, setSkipCheckbox] = useState(false);
Expand All @@ -110,6 +109,8 @@ const Main = (props) => {
const locale = useRef(I18n.locale);
const removeConnectionStatusListener = useRef();

const { connectionChangeHandler } = useConnectionHandler(props.navigation);

const removeNotVisibleNotifications = props.removeNotVisibleNotifications;
useNotificationHandler(props.navigation);
useEnableAutomaticSecurityChecks();
Expand All @@ -133,21 +134,6 @@ const Main = (props) => {
}
}, [props.showIncomingTransactionsNetworks, props.chainId]);

const connectionChangeHandler = useCallback(
(state) => {
if (!state) return;
const { isConnected } = state;
// Show the modal once the status changes to offline
if (connected && isConnected === false) {
props.navigation.navigate('OfflineModeView');
}
if (connected !== isConnected && isConnected !== null) {
setConnected(isConnected);
}
},
[connected, setConnected, props.navigation],
);

const checkInfuraAvailability = useCallback(async () => {
if (props.providerType !== 'rpc') {
try {
Expand Down Expand Up @@ -336,7 +322,7 @@ const Main = (props) => {
removeConnectionStatusListener.current();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [connectionChangeHandler]);

const termsOfUse = useCallback(async () => {
if (props.navigation) {
Expand Down
137 changes: 137 additions & 0 deletions app/components/Nav/Main/useConnectionHandler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { renderHook, act } from '@testing-library/react-hooks';
import { useConnectionHandler } from '../../../util/navigation/useConnectionHandler';
import { MetaMetricsEvents } from '../../../components/hooks/useMetrics';

const mockTrackEvent = jest.fn();

jest.mock('../../../components/hooks/useMetrics', () => ({
useMetrics: () => ({
trackEvent: mockTrackEvent,
}),
MetaMetricsEvents: {
CONNECTION_DROPPED: 'CONNECTION_DROPPED',
CONNECTION_RESTORED: 'CONNECTION_RESTORED',
},
}));

describe('useConnectionHandler', () => {
const mockNavigation = { navigate: jest.fn() };

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

afterEach(() => {
jest.useRealTimers();
});

it('should not navigate to OfflineModeView immediately when connection is lost', () => {
const { result } = renderHook(() => useConnectionHandler(mockNavigation));

act(() => {
result.current.connectionChangeHandler({ isConnected: false });
});

expect(mockNavigation.navigate).not.toHaveBeenCalled();
expect(mockTrackEvent).toHaveBeenCalledWith(
MetaMetricsEvents.CONNECTION_DROPPED,
);
});

it('should navigate to OfflineModeView after sustained offline period', () => {
const { result } = renderHook(() => useConnectionHandler(mockNavigation));

act(() => {
result.current.connectionChangeHandler({ isConnected: false });
});

jest.advanceTimersByTime(3000);

expect(mockNavigation.navigate).toHaveBeenCalledWith('OfflineModeView');
expect(mockTrackEvent).toHaveBeenCalledWith(
MetaMetricsEvents.CONNECTION_DROPPED,
);
});

it('should not navigate to OfflineModeView if connection is restored within timeout', () => {
const { result } = renderHook(() => useConnectionHandler(mockNavigation));

act(() => {
result.current.connectionChangeHandler({ isConnected: false });
});

expect(mockTrackEvent).toHaveBeenCalledWith(
MetaMetricsEvents.CONNECTION_DROPPED,
);

jest.advanceTimersByTime(1000);

act(() => {
result.current.connectionChangeHandler({ isConnected: true });
});

jest.advanceTimersByTime(2000);

expect(mockNavigation.navigate).not.toHaveBeenCalled();
expect(mockTrackEvent).toHaveBeenCalledTimes(2);
expect(mockTrackEvent).toHaveBeenNthCalledWith(
2,
MetaMetricsEvents.CONNECTION_RESTORED,
);
});

it('should do nothing if state is null', () => {
const { result } = renderHook(() => useConnectionHandler(mockNavigation));

act(() => {
result.current.connectionChangeHandler(null);
});

expect(mockNavigation.navigate).not.toHaveBeenCalled();
expect(mockTrackEvent).not.toHaveBeenCalled();
});

it('should not track events if connection state does not change', () => {
const { result } = renderHook(() => useConnectionHandler(mockNavigation));

act(() => {
result.current.connectionChangeHandler({ isConnected: true });
});

expect(mockTrackEvent).not.toHaveBeenCalled();

act(() => {
result.current.connectionChangeHandler({ isConnected: true });
});

expect(mockTrackEvent).not.toHaveBeenCalled();
});

it('should clear timeout if connection is restored before navigation', () => {
const { result } = renderHook(() => useConnectionHandler(mockNavigation));

act(() => {
result.current.connectionChangeHandler({ isConnected: false });
});

expect(mockTrackEvent).toHaveBeenCalledWith(
MetaMetricsEvents.CONNECTION_DROPPED,
);

jest.advanceTimersByTime(2000);

act(() => {
result.current.connectionChangeHandler({ isConnected: true });
});

expect(mockTrackEvent).toHaveBeenCalledWith(
MetaMetricsEvents.CONNECTION_RESTORED,
);

jest.advanceTimersByTime(1000);

expect(mockNavigation.navigate).not.toHaveBeenCalled();
expect(mockTrackEvent).toHaveBeenCalledTimes(2);
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-require-imports */
/* eslint-disable import/no-commonjs */
/* eslint-disable @typescript-eslint/no-var-requires */
import React, { useRef, useCallback } from 'react';
import React, { useRef } from 'react';
import BottomSheet, {
BottomSheetRef,
} from '../../../component-library/components/BottomSheets/BottomSheet';
Expand Down Expand Up @@ -35,31 +35,29 @@ const NFTAutoDetectionModal = () => {
const chainId = useSelector(selectChainId);
const displayNftMedia = useSelector(selectDisplayNftMedia);
const { trackEvent } = useMetrics();
const enableNftDetectionAndDismissModal = useCallback(
(value: boolean) => {
Daniel-Cross marked this conversation as resolved.
Show resolved Hide resolved
if (value) {
const { PreferencesController } = Engine.context;
if (!displayNftMedia) {
PreferencesController.setDisplayNftMedia(true);
}
PreferencesController.setUseNftDetection(true);
trackEvent(MetaMetricsEvents.NFT_AUTO_DETECTION_MODAL_ENABLE, {
chainId,
});
} else {
trackEvent(MetaMetricsEvents.NFT_AUTO_DETECTION_MODAL_DISABLE, {
chainId,
});
}

if (sheetRef?.current) {
sheetRef.current.onCloseBottomSheet();
} else {
navigation.goBack();
const enableNftDetectionAndDismissModal = (value: boolean) => {
if (value) {
const { PreferencesController } = Engine.context;
if (!displayNftMedia) {
PreferencesController.setDisplayNftMedia(true);
}
},
[displayNftMedia, trackEvent, chainId, navigation],
);
PreferencesController.setUseNftDetection(true);
trackEvent(MetaMetricsEvents.NFT_AUTO_DETECTION_MODAL_ENABLE, {
chainId,
});
} else {
trackEvent(MetaMetricsEvents.NFT_AUTO_DETECTION_MODAL_DISABLE, {
chainId,
});
}

if (sheetRef?.current) {
sheetRef.current.onCloseBottomSheet();
} else {
navigation.goBack();
}
};

return (
<BottomSheet ref={sheetRef}>
Expand Down
8 changes: 8 additions & 0 deletions app/core/Analytics/MetaMetrics.events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,10 @@ enum EVENT_NAME {

// network
MULTI_RPC_MIGRATION_MODAL_ACCEPTED = 'multi_rpc_migration_modal_accepted',

// Connection
CONNECTION_DROPPED = 'Connection dropped',
CONNECTION_RESTORED = 'Connection restored',
}

enum ACTIONS {
Expand Down Expand Up @@ -854,6 +858,10 @@ const events = {
),
PRIMARY_CURRENCY_TOGGLE: generateOpt(EVENT_NAME.PRIMARY_CURRENCY_TOGGLE),
LOGIN_DOWNLOAD_LOGS: generateOpt(EVENT_NAME.LOGIN_DOWNLOAD_LOGS),

// Connection
CONNECTION_DROPPED: generateOpt(EVENT_NAME.CONNECTION_DROPPED),
CONNECTION_RESTORED: generateOpt(EVENT_NAME.CONNECTION_RESTORED),
};

/**
Expand Down
41 changes: 41 additions & 0 deletions app/util/navigation/useConnectionHandler.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useRef, useCallback } from 'react';
import {
useMetrics,
MetaMetricsEvents,
} from '../../components/hooks/useMetrics';

// TODO: Replace "any" with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const useConnectionHandler = (navigation: any) => {
const connectedRef = useRef(true);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);

const { trackEvent } = useMetrics();

const connectionChangeHandler = useCallback(
(state: { isConnected: boolean } | null) => {
if (!state) return;
const { isConnected } = state;

if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}

if (connectedRef.current !== isConnected) {
if (isConnected === false) {
trackEvent(MetaMetricsEvents.CONNECTION_DROPPED);
timeoutRef.current = setTimeout(() => {
navigation.navigate('OfflineModeView');
}, 3000);
} else {
trackEvent(MetaMetricsEvents.CONNECTION_RESTORED);
}
connectedRef.current = isConnected;
}
},
[navigation, trackEvent],
);

return { connectionChangeHandler };
};
Loading