Skip to content

Commit 32052e5

Browse files
feat(js/frontend): add error tracking (#814)
1 parent cd4c650 commit 32052e5

File tree

15 files changed

+127
-46
lines changed

15 files changed

+127
-46
lines changed

.github/workflows/deploy-frontend.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ jobs:
9898
VITE_VARA_ARCHIVE_NODE_ADDRESSES=${{ secrets.VITE_VARA_ARCHIVE_NODE_ADDRESSES }}
9999
VITE_ETH_BEACON_NODE_ADDRESSES=${{ secrets.VITE_ETH_BEACON_NODE_ADDRESSES }}
100100
VITE_ETH_MESSAGE_QUEUE_CONTRACT_ADDRESSES=${{ secrets.VITE_ETH_MESSAGE_QUEUE_CONTRACT_ADDRESSES }}
101+
VITE_SENTRY_DSN=${{ secrets.VITE_SENTRY_DSN }}
101102
VITE_GTM_ID=${{ secrets.VITE_GTM_ID }}
102103
103104
deploy-to-k8s:

js/frontend/.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,6 @@ VITE_VARA_ARCHIVE_NODE_ADDRESSES=
2929
VITE_ETH_BEACON_NODE_ADDRESSES=
3030
VITE_ETH_MESSAGE_QUEUE_CONTRACT_ADDRESSES=
3131

32-
# analytics
32+
# error tracking and analytics
33+
VITE_SENTRY_DSN=
3334
VITE_GTM_ID=

js/frontend/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ ARG VITE_VARA_NODE_ADDRESSES \
2929
VITE_VARA_ARCHIVE_NODE_ADDRESSES \
3030
VITE_ETH_BEACON_NODE_ADDRESSES \
3131
VITE_ETH_MESSAGE_QUEUE_CONTRACT_ADDRESSES \
32+
VITE_SENTRY_DSN \
3233
VITE_GTM_ID
3334

3435
ENV VITE_VARA_NODE_ADDRESSES=${VITE_VARA_NODE_ADDRESSES} \
@@ -46,6 +47,7 @@ ENV VITE_VARA_NODE_ADDRESSES=${VITE_VARA_NODE_ADDRESSES} \
4647
VITE_VARA_ARCHIVE_NODE_ADDRESSES=${VITE_VARA_ARCHIVE_NODE_ADDRESSES} \
4748
VITE_ETH_BEACON_NODE_ADDRESSES=${VITE_ETH_BEACON_NODE_ADDRESSES} \
4849
VITE_ETH_MESSAGE_QUEUE_CONTRACT_ADDRESSES=${VITE_ETH_MESSAGE_QUEUE_CONTRACT_ADDRESSES} \
50+
VITE_SENTRY_DSN=${VITE_SENTRY_DSN} \
4951
VITE_GTM_ID=${VITE_GTM_ID}
5052

5153

js/frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@polkadot/react-identicon": "3.16.4",
2424
"@reown/appkit": "1.8.15",
2525
"@reown/appkit-adapter-wagmi": "1.8.15",
26+
"@sentry/react": "10.40.0",
2627
"@tanstack/react-query": "5.90.12",
2728
"gear-bridge-common": "workspace:*",
2829
"graphql": "16.12.0",
Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Button } from '@gear-js/vara-ui';
2-
import { Component, ReactNode } from 'react';
2+
import { ErrorBoundary as SentryErrorBoundary, FallbackRender } from '@sentry/react';
3+
import { ComponentProps, PropsWithChildren } from 'react';
34
import { useLocation, useNavigate } from 'react-router-dom';
45

56
import { useChangeEffect } from '@/hooks';
@@ -8,56 +9,27 @@ import { Container } from '../container';
89

910
import styles from './error-boundary.module.scss';
1011

11-
type Props = {
12-
children: ReactNode;
13-
};
14-
15-
type FallbackProps = {
16-
message: string;
17-
reset: () => void;
18-
};
19-
20-
type State = {
21-
error: Error | null;
22-
};
23-
24-
function Fallback({ message, reset }: FallbackProps) {
12+
// eslint-disable-next-line @typescript-eslint/unbound-method
13+
function Fallback({ error, resetError }: ComponentProps<FallbackRender>) {
2514
const { pathname } = useLocation();
2615
const navigate = useNavigate();
2716

2817
useChangeEffect(() => {
29-
reset();
18+
resetError();
3019
}, [pathname]);
3120

3221
return (
3322
<Container>
3423
<h2 className={styles.heading}>Oops! Something went wrong:</h2>
35-
<p className={styles.error}>{message}</p>
24+
<p className={styles.error}>{error instanceof Error ? error.message : String(error)}</p>
3625

3726
<Button text="Go Back" size="small" onClick={() => navigate(-1)} />
3827
</Container>
3928
);
4029
}
4130

42-
class ErrorBoundary extends Component<Props, State> {
43-
constructor(props: Props) {
44-
super(props);
45-
this.state = { error: null };
46-
}
47-
48-
static getDerivedStateFromError(error: Error) {
49-
return { error };
50-
}
51-
52-
reset = () => {
53-
this.setState({ error: null });
54-
};
55-
56-
render() {
57-
if (!this.state.error) return this.props.children;
58-
59-
return <Fallback message={this.state.error.message} reset={this.reset} />;
60-
}
31+
function ErrorBoundary({ children }: PropsWithChildren) {
32+
return <SentryErrorBoundary fallback={Fallback}>{children}</SentryErrorBoundary>;
6133
}
6234

6335
export { ErrorBoundary };

js/frontend/src/consts/env.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const SENTRY_DSN = import.meta.env.VITE_SENTRY_DSN as string | undefined;
12
const GTM_ID = import.meta.env.VITE_GTM_ID as string | undefined;
23

3-
export { GTM_ID };
4+
export { SENTRY_DSN, GTM_ID };

js/frontend/src/consts/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ERC20_ABI, WRAPPED_ETH_ABI } from './abi';
2-
import { GTM_ID } from './env';
2+
import { SENTRY_DSN, GTM_ID } from './env';
33
import { ROUTE } from './routing';
44
import { VftProgram, WrappedVaraProgram } from './sails';
55

6-
export { ERC20_ABI, WRAPPED_ETH_ABI, ROUTE, GTM_ID, VftProgram, WrappedVaraProgram };
6+
export { ERC20_ABI, WRAPPED_ETH_ABI, ROUTE, SENTRY_DSN, GTM_ID, VftProgram, WrappedVaraProgram };

js/frontend/src/context/network-type/provider.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useApi, useAlert } from '@gear-js/react-hooks';
22
import { useAppKitNetwork } from '@reown/appkit/react';
3+
import { captureException } from '@sentry/react';
34
import { PropsWithChildren, useState, useEffect, useMemo } from 'react';
45
import { useSearchParams } from 'react-router-dom';
56
import { useChainId, useConfig, useSwitchChain } from 'wagmi';
@@ -82,6 +83,7 @@ function NetworkTypeProvider({ children }: PropsWithChildren) {
8283
switchNetwork({ endpoint: NEXT_PRESET.NODE_ADDRESS }).catch((error: Error) => {
8384
alert.error(`Failed to switch network. ${error.message}`);
8485
logger.error('Network switch', error);
86+
captureException(error, { tags: { feature: 'network-switch' } });
8587
});
8688
};
8789

js/frontend/src/features/faucet/components/get-balance-button/get-balance-button.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { HexString } from '@gear-js/api';
22
import { useAccount, useAlert, useApi } from '@gear-js/react-hooks';
33
import { Button } from '@gear-js/vara-ui';
44
import { Turnstile, TurnstileInstance } from '@marsidev/react-turnstile';
5+
import { captureException } from '@sentry/react';
56
import { useMutation } from '@tanstack/react-query';
67
import { useRef, useState } from 'react';
78

@@ -62,13 +63,19 @@ function ButtonComponent<T>({ getBalance, onSuccess, ...parameters }: Props<T>)
6263
'Your request for test tokens has been received and is being processed. The tokens will appear in your balance shortly.',
6364
);
6465
})
65-
.catch((error) => alert.error(error instanceof Error ? error.message : String(error)));
66+
.catch((error) => {
67+
alert.error(error instanceof Error ? error.message : String(error));
68+
captureException(error, { tags: { feature: 'faucet', flow: 'request' } });
69+
});
6670
};
6771

6872
const handleVerificationError = (code: string) => {
6973
settleVerification();
74+
alert.error(`Error verifying that you are a human. Please try again.`);
7075

71-
alert.error(`Error verifying that you are a human, code: ${code}. Please try again.`);
76+
const error = new Error(`Cloudflare Turnstile (human verification) error. Code: ${code}`);
77+
78+
captureException(error, { tags: { feature: 'faucet', flow: 'verification' } });
7279
};
7380

7481
return (

js/frontend/src/features/swap/components/pay-vara-fee-button/pay-vara-fee-button.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useAccount, useAlert } from '@gear-js/react-hooks';
22
import { Button } from '@gear-js/vara-ui';
33
import { WalletModal } from '@gear-js/wallet-connect';
4+
import { captureException } from '@sentry/react';
45

56
import { Tooltip } from '@/components';
67
import { useModal } from '@/hooks';
@@ -32,7 +33,10 @@ function PayVaraFeeButton({ nonce, onInBlock }: Props) {
3233
alert.success('Fee paid successfully');
3334
onInBlock();
3435
})
35-
.catch((error: Error) => alert.error(getErrorMessage(error)));
36+
.catch((error: Error) => {
37+
alert.error(getErrorMessage(error));
38+
captureException(error, { tags: { feature: 'pay-tx-fee' } });
39+
});
3640
};
3741

3842
const renderTooltipText = () => (

0 commit comments

Comments
 (0)