@@ -139,7 +146,7 @@ export function ThirdwebLoginContent({
- >
+
)
}
diff --git a/src/components/app/pageDonate/donateButton.tsx b/src/components/app/pageDonate/donateButton.tsx
index fa55db9c8..cd05fd02f 100644
--- a/src/components/app/pageDonate/donateButton.tsx
+++ b/src/components/app/pageDonate/donateButton.tsx
@@ -4,6 +4,7 @@ import React from 'react'
import { actionCreateCoinbaseCommerceCharge } from '@/actions/actionCreateCoinbaseCommerceCharge'
import { Button } from '@/components/ui/button'
import { openWindow } from '@/utils/shared/openWindow'
+import { ErrorBoundary } from '@/utils/web/errorBoundary'
import { triggerServerActionForForm } from '@/utils/web/formUtils'
import { toastGenericError } from '@/utils/web/toastUtils'
@@ -36,9 +37,16 @@ export function DonateButton() {
}
return (
-
+
+
+
)
}
diff --git a/src/components/app/pageHome/delayedRecentActivity.tsx b/src/components/app/pageHome/delayedRecentActivity.tsx
index 2d1cc4a63..7a126d17e 100644
--- a/src/components/app/pageHome/delayedRecentActivity.tsx
+++ b/src/components/app/pageHome/delayedRecentActivity.tsx
@@ -1,4 +1,5 @@
'use client'
+
import { useRef } from 'react'
import { TabsContent } from '@radix-ui/react-tabs'
import { useInView } from 'framer-motion'
@@ -14,6 +15,7 @@ import { useApiRecentActivity } from '@/hooks/useApiRecentActivity'
import { useIntlUrls } from '@/hooks/useIntlUrls'
import { useIsMobile } from '@/hooks/useIsMobile'
import { SupportedLocale } from '@/intl/locales'
+import { ErrorBoundary } from '@/utils/web/errorBoundary'
export function DelayedRecentActivityWithMap(props: {
actions: PublicRecentActivity
@@ -41,12 +43,26 @@ export function DelayedRecentActivityWithMap(props: {
) : (
-
+
+
+
)
}
diff --git a/src/components/app/userActionGridCTAs/components/userActionGridCTA.tsx b/src/components/app/userActionGridCTAs/components/userActionGridCTA.tsx
index f310a40e6..8ccb2d551 100644
--- a/src/components/app/userActionGridCTAs/components/userActionGridCTA.tsx
+++ b/src/components/app/userActionGridCTAs/components/userActionGridCTA.tsx
@@ -1,42 +1,98 @@
+import { Fragment } from 'react'
+
import { UserActionCard } from '@/components/app/userActionGridCTAs/components/userActionCard'
import { UserActionGridCampaignsDialog } from '@/components/app/userActionGridCTAs/components/userActionGridCampaignsDialog'
import type { UserActionCardProps as UserActionGridCTAProps } from '@/components/app/userActionGridCTAs/types'
+import { ErrorBoundary } from '@/utils/web/errorBoundary'
export function UserActionGridCTA(props: UserActionGridCTAProps) {
+ const firstCampaign = props.campaigns[0]
+
+ // If there is only one campaign, clicking the CTA will trigger the WrapperComponent for that campaign.
+ const shouldUseFirstCampaignWrapperComponent = props.campaignsLength === 1
+
if (props.link) {
// If the link property is present, the CTA will function as a link, even if there are multiple campaigns.
const LinkComponent = props.link
+
return (
-
-
-
+
+
+
+
+
)
}
- // If there is only one campaign, clicking the CTA will trigger the WrapperComponent for that campaign.
- const shouldUseFirstCampaignWrapperComponent = props.campaignsLength === 1
-
if (shouldUseFirstCampaignWrapperComponent) {
- const WrapperComponent = props.campaigns[0].WrapperComponent
-
- if (!WrapperComponent) {
- return
- }
+ const WrapperComponent = firstCampaign.WrapperComponent ?? Fragment
return (
-
-
-
+
+
+
+
+
)
}
return (
-
-
-
+
+
+
+
)
}
diff --git a/src/components/app/userActionGridCTAs/hooks/useGridCTAs.ts b/src/components/app/userActionGridCTAs/hooks/useGridCTAs.ts
index 6fd678509..fc84c492a 100644
--- a/src/components/app/userActionGridCTAs/hooks/useGridCTAs.ts
+++ b/src/components/app/userActionGridCTAs/hooks/useGridCTAs.ts
@@ -21,7 +21,7 @@ export function useGridCTAs({
const locale = useLocale()
const isProfilePage = pathname?.includes(getIntlUrls(locale).profile())
- const performeduserActionObj = performedUserActionTypes.length
+ const performedUserActionObj = performedUserActionTypes.length
? performedUserActionTypes.reduce(
(acc, performedUserAction) => {
acc[`${performedUserAction.actionType}-${performedUserAction.campaignName}`] =
@@ -47,12 +47,12 @@ export function useGridCTAs({
* If we are on the profile page, we want to show all the CTAs, including
* those with campaigns inactive if the user has already performed them.
*/
- return campaign.isCampaignActive || (isProfilePage && !!performeduserActionObj[key])
+ return campaign.isCampaignActive || (isProfilePage && !!performedUserActionObj[key])
})
return { ...cta, campaigns: filteredCampaigns }
})
.filter(cta => cta.campaigns.length > 0)
- return { ctas: filteredInactiveCampaigns, performeduserActionObj }
+ return { ctas: filteredInactiveCampaigns, performedUserActionObj }
}
diff --git a/src/components/app/userActionGridCTAs/hooks/useOrderedCTAs.ts b/src/components/app/userActionGridCTAs/hooks/useOrderedCTAs.ts
index 7e13bc518..e05c3763c 100644
--- a/src/components/app/userActionGridCTAs/hooks/useOrderedCTAs.ts
+++ b/src/components/app/userActionGridCTAs/hooks/useOrderedCTAs.ts
@@ -27,7 +27,7 @@ export function useOrderedCTAs({
performedUserActionTypes,
excludeUserActionTypes,
}: UseOrderedCTAsProps) {
- const performeduserActionObj = useMemo(() => {
+ const performedUserActionObj = useMemo(() => {
return performedUserActionTypes.length
? performedUserActionTypes.reduce(
(acc, performedUserAction) => {
@@ -64,7 +64,7 @@ export function useOrderedCTAs({
const completedActions = cta.campaigns
.map(campaign => {
const key = `${campaign.actionType}-${campaign.campaignName}`
- if (!performeduserActionObj[key]) return
+ if (!performedUserActionObj[key]) return
return key
})
@@ -76,7 +76,7 @@ export function useOrderedCTAs({
})
return uniqBy([...incompleteCTAs, ...completeCTAs], cta => `${cta.title}-${cta.description}`)
- }, [filteredInactiveCampaigns, performeduserActionObj])
+ }, [filteredInactiveCampaigns, performedUserActionObj])
- return { orderedCTAs, performeduserActionObj }
+ return { orderedCTAs, performedUserActionObj }
}
diff --git a/src/components/app/userActionGridCTAs/index.tsx b/src/components/app/userActionGridCTAs/index.tsx
index e64a7fbec..75eaf5bf6 100644
--- a/src/components/app/userActionGridCTAs/index.tsx
+++ b/src/components/app/userActionGridCTAs/index.tsx
@@ -19,7 +19,7 @@ export function UserActionGridCTAs({
const { data } = useApiResponseForUserPerformedUserActionTypes()
const performedUserActionTypes = data?.performedUserActionTypes ?? []
- const { ctas, performeduserActionObj } = useGridCTAs({
+ const { ctas, performedUserActionObj } = useGridCTAs({
excludeUserActionTypes,
performedUserActionTypes,
})
@@ -29,11 +29,11 @@ export function UserActionGridCTAs({
{ctas.map(cta => {
const completedCampaigns = cta.campaigns.reduce((acc, campaign) => {
const key = `${campaign.actionType}-${campaign.campaignName}`
- return performeduserActionObj[key] ? acc + 1 : acc
+ return performedUserActionObj[key] ? acc + 1 : acc
}, 0)
const filteredCampaigns = cta.campaigns.filter(campaign => {
const key = `${campaign.actionType}-${campaign.campaignName}`
- return campaign.isCampaignActive || !!performeduserActionObj[key]
+ return campaign.isCampaignActive || !!performedUserActionObj[key]
})
return (
@@ -51,7 +51,7 @@ export function UserActionGridCTAs({
key={cta.title + cta.description}
link={cta.link}
mobileCTADescription={cta.mobileCTADescription}
- performedUserActions={performeduserActionObj}
+ performedUserActions={performedUserActionObj}
title={cta.title}
/>
)
diff --git a/src/components/app/userActionGridCTAs/successScreenCTAS.tsx b/src/components/app/userActionGridCTAs/successScreenCTAS.tsx
index cf09fe7b0..15e7a5532 100644
--- a/src/components/app/userActionGridCTAs/successScreenCTAS.tsx
+++ b/src/components/app/userActionGridCTAs/successScreenCTAS.tsx
@@ -17,7 +17,7 @@ export function SuccessScreenCTAS({
excludeUserActionTypes,
performedUserActionTypes,
}: SuccessScreenCTASProps) {
- const { orderedCTAs, performeduserActionObj } = useOrderedCTAs({
+ const { orderedCTAs, performedUserActionObj } = useOrderedCTAs({
performedUserActionTypes,
excludeUserActionTypes,
})
@@ -27,11 +27,11 @@ export function SuccessScreenCTAS({
{orderedCTAs.map(cta => {
const completedCampaigns = cta.campaigns.reduce((acc, campaign) => {
const key = `${campaign.actionType}-${campaign.campaignName}`
- return performeduserActionObj[key] ? acc + 1 : acc
+ return performedUserActionObj[key] ? acc + 1 : acc
}, 0)
const filteredCampaigns = cta.campaigns.filter(campaign => {
const key = `${campaign.actionType}-${campaign.campaignName}`
- return campaign.isCampaignActive || !!performeduserActionObj[key]
+ return campaign.isCampaignActive || !!performedUserActionObj[key]
})
return (
@@ -49,7 +49,7 @@ export function SuccessScreenCTAS({
key={cta.title + cta.description}
link={cta.link}
mobileCTADescription={cta.mobileCTADescription}
- performedUserActions={performeduserActionObj}
+ performedUserActions={performedUserActionObj}
title={cta.title}
/>
)
diff --git a/src/utils/web/errorBoundary/index.tsx b/src/utils/web/errorBoundary/index.tsx
new file mode 100644
index 000000000..b8b007a59
--- /dev/null
+++ b/src/utils/web/errorBoundary/index.tsx
@@ -0,0 +1,47 @@
+'use client'
+
+import { ReactNode } from 'react'
+import * as Sentry from '@sentry/react'
+import { Extras, Primitive } from '@sentry/types'
+
+import { getUserSessionIdOnClient } from '@/utils/web/clientUserSessionId'
+
+interface ErrorBoundaryProps extends Sentry.ErrorBoundaryProps {
+ children: ReactNode
+ tags?: { [key: string]: Primitive }
+ extras?: Extras
+ severityLevel?: Sentry.SeverityLevel
+}
+
+export function ErrorBoundary({ children, tags, extras, severityLevel }: ErrorBoundaryProps) {
+ return (
+
{
+ const sessionId = getUserSessionIdOnClient()
+
+ if (sessionId) {
+ scope.setUser({
+ id: sessionId,
+ idType: 'session',
+ })
+ }
+
+ if (tags) {
+ scope.setTags(tags)
+ }
+
+ if (extras) {
+ scope.setExtras(extras)
+ }
+
+ if (severityLevel) {
+ scope.setLevel(severityLevel)
+ }
+
+ return scope
+ }}
+ >
+ {children}
+
+ )
+}