diff --git a/apps/scoutgame/app/(general)/notifications/page.tsx b/apps/scoutgame/app/(general)/notifications/page.tsx
index fc9903bffe..98a5d77cb0 100644
--- a/apps/scoutgame/app/(general)/notifications/page.tsx
+++ b/apps/scoutgame/app/(general)/notifications/page.tsx
@@ -2,15 +2,16 @@ import { getSession } from '@connect-shared/lib/session/getSession';
import { getNotifications } from '@packages/scoutgame/notifications/getNotifications';
import { NotificationsPage } from 'components/notifications/NotificationsPage';
+import { safeAwaitSSRData } from 'lib/utils/async';
export default async function Page() {
const { scoutId } = await getSession();
- const notifications = scoutId
- ? await getNotifications({
- userId: scoutId
- })
- : [];
+ const [, notifications = []] = await safeAwaitSSRData(
+ getNotifications({
+ userId: scoutId
+ })
+ );
return ;
}
diff --git a/apps/scoutgame/components/[username]/components/PublicScoutProfile/PublicScoutProfile.tsx b/apps/scoutgame/components/[username]/components/PublicScoutProfile/PublicScoutProfile.tsx
index 62d30112f5..16976f6cfe 100644
--- a/apps/scoutgame/components/[username]/components/PublicScoutProfile/PublicScoutProfile.tsx
+++ b/apps/scoutgame/components/[username]/components/PublicScoutProfile/PublicScoutProfile.tsx
@@ -1,23 +1,27 @@
-import { prisma } from '@charmverse/core/prisma-client';
+import 'server-only';
+import { ErrorSSRMessage } from 'components/common/ErrorSSRMessage';
+import { findScoutOrThrow } from 'lib/scouts/findScoutOrThrow';
import { getScoutedBuilders } from 'lib/scouts/getScoutedBuilders';
import { getScoutStats } from 'lib/scouts/getScoutStats';
import type { BasicUserInfo } from 'lib/users/interfaces';
-import { BasicUserInfoSelect } from 'lib/users/queries';
+import { safeAwaitSSRData } from 'lib/utils/async';
import { PublicScoutProfileContainer } from './PublicScoutProfileContainer';
export async function PublicScoutProfile({ publicUser }: { publicUser: BasicUserInfo }) {
- const [scout, { allTimePoints, seasonPoints, nftsPurchased }, scoutedBuilders] = await Promise.all([
- prisma.scout.findUniqueOrThrow({
- where: {
- id: publicUser.id
- },
- select: BasicUserInfoSelect
- }),
+ const allPromises = [
+ findScoutOrThrow(publicUser.id),
getScoutStats(publicUser.id),
getScoutedBuilders({ scoutId: publicUser.id })
- ]);
+ ] as const;
+ const [error, data] = await safeAwaitSSRData(Promise.all(allPromises));
+
+ if (error) {
+ return ;
+ }
+
+ const [scout, { allTimePoints, seasonPoints, nftsPurchased }, scoutedBuilders] = data;
return (
{
+ log.error('Error in SSR data fetching. Please refresh.');
+ }, []);
+
+ return (
+
+ {message || 'An error occured while loading your data. Please try to refresh or contact us on discord'}
+
+ );
+}
diff --git a/apps/scoutgame/components/home/components/BuildersCarousel/TodaysHotBuildersCarousel.tsx b/apps/scoutgame/components/home/components/BuildersCarousel/TodaysHotBuildersCarousel.tsx
index ed8bac2cb5..593bdf3ad0 100644
--- a/apps/scoutgame/components/home/components/BuildersCarousel/TodaysHotBuildersCarousel.tsx
+++ b/apps/scoutgame/components/home/components/BuildersCarousel/TodaysHotBuildersCarousel.tsx
@@ -1,10 +1,17 @@
'use server';
+import { ErrorSSRMessage } from 'components/common/ErrorSSRMessage';
import { getTodaysHotBuilders } from 'lib/builders/getTodaysHotBuilders';
+import { safeAwaitSSRData } from 'lib/utils/async';
import { BuildersCarousel } from './BuildersCarousel';
export async function TodaysHotBuildersCarousel() {
- const builders = await getTodaysHotBuilders();
+ const [error, builders] = await safeAwaitSSRData(getTodaysHotBuilders());
+
+ if (error) {
+ return ;
+ }
+
return ;
}
diff --git a/apps/scoutgame/components/home/components/HomePageTable/HomePageTable.tsx b/apps/scoutgame/components/home/components/HomePageTable/HomePageTable.tsx
index 82474b9f50..ee751b371d 100644
--- a/apps/scoutgame/components/home/components/HomePageTable/HomePageTable.tsx
+++ b/apps/scoutgame/components/home/components/HomePageTable/HomePageTable.tsx
@@ -1,9 +1,10 @@
-'use server';
+import 'server-only';
import { getBuilderActivities } from 'lib/builders/getBuilderActivities';
import { getLeaderboard } from 'lib/builders/getLeaderboard';
import { getTopBuilders } from 'lib/builders/getTopBuilders';
import { getTopScouts } from 'lib/scouts/getTopScouts';
+import { safeAwaitSSRData } from 'lib/utils/async';
import { ActivityTable } from './components/ActivityTable';
import { LeaderboardTable } from './components/LeaderboardTable';
@@ -12,23 +13,23 @@ import { TopScoutsTable } from './components/TopScoutsTable';
export async function HomeTab({ tab }: { tab: string }) {
if (tab === 'activity') {
- const activities = await getBuilderActivities({ limit: 100 });
+ const [, activities = []] = await safeAwaitSSRData(getBuilderActivities({ limit: 100 }));
return ;
}
if (tab === 'top-scouts') {
- const topScouts = await getTopScouts({ limit: 200 });
+ const [, topScouts = []] = await safeAwaitSSRData(getTopScouts({ limit: 200 }));
return ;
}
if (tab === 'top-builders') {
- const topBuilders = await getTopBuilders({ limit: 200 });
+ const [, topBuilders = []] = await safeAwaitSSRData(getTopBuilders({ limit: 200 }));
return ;
}
if (tab === 'leaderboard') {
- const data = await getLeaderboard({ limit: 200 });
- return ;
+ const [, leaderboard = []] = await safeAwaitSSRData(getLeaderboard({ limit: 200 }));
+ return ;
}
return null;
}
diff --git a/apps/scoutgame/components/profile/components/PointsClaimScreen/PointsClaimScreen.tsx b/apps/scoutgame/components/profile/components/PointsClaimScreen/PointsClaimScreen.tsx
index 23277e1b9a..afd4f5e1da 100644
--- a/apps/scoutgame/components/profile/components/PointsClaimScreen/PointsClaimScreen.tsx
+++ b/apps/scoutgame/components/profile/components/PointsClaimScreen/PointsClaimScreen.tsx
@@ -3,14 +3,22 @@
import { Box, Paper, Typography, Stack } from '@mui/material';
import Image from 'next/image';
+import { ErrorSSRMessage } from 'components/common/ErrorSSRMessage';
import { getClaimablePointsWithEvents } from 'lib/points/getClaimablePointsWithEvents';
+import { safeAwaitSSRData } from 'lib/utils/async';
import { BonusPartnersDisplay } from './BonusPartnersDisplay';
import { PointsClaimButton } from './PointsClaimButton';
import { QualifiedActionsTable } from './QualifiedActionsTable';
export async function PointsClaimScreen({ userId, username }: { userId: string; username: string }) {
- const { totalClaimablePoints, weeklyRewards, bonusPartners } = await getClaimablePointsWithEvents(userId);
+ const [error, data] = await safeAwaitSSRData(getClaimablePointsWithEvents(userId));
+
+ if (error) {
+ return ;
+ }
+
+ const { totalClaimablePoints, weeklyRewards, bonusPartners } = data;
if (!totalClaimablePoints) {
return (
diff --git a/apps/scoutgame/components/profile/components/ScoutProfile/ScoutProfile.tsx b/apps/scoutgame/components/profile/components/ScoutProfile/ScoutProfile.tsx
index 4108db0b30..6e867b2104 100644
--- a/apps/scoutgame/components/profile/components/ScoutProfile/ScoutProfile.tsx
+++ b/apps/scoutgame/components/profile/components/ScoutProfile/ScoutProfile.tsx
@@ -1,29 +1,25 @@
'use server';
-import { prisma } from '@charmverse/core/prisma-client';
import { Typography, Stack } from '@mui/material';
-import { currentSeason } from '@packages/scoutgame/dates';
+import { ErrorSSRMessage } from 'components/common/ErrorSSRMessage';
import { BuildersGallery } from 'components/common/Gallery/BuildersGallery';
import { getScoutedBuilders } from 'lib/scouts/getScoutedBuilders';
+import { getUserSeasonStats } from 'lib/scouts/getUserSeasonStats';
+import { safeAwaitSSRData } from 'lib/utils/async';
import { ScoutStats } from './ScoutStats';
-export async function ScoutProfile({ userId }: { userId: string }) {
- const [seasonStats, scoutedBuilders] = await Promise.all([
- prisma.userSeasonStats.findUnique({
- where: {
- userId_season: {
- userId,
- season: currentSeason
- }
- },
- select: {
- pointsEarnedAsScout: true
- }
- }),
- getScoutedBuilders({ scoutId: userId })
- ]);
+export async function ScoutProfile({ userId, isMobile }: { userId: string; isMobile?: boolean }) {
+ const [error, data] = await safeAwaitSSRData(
+ Promise.all([getUserSeasonStats(userId), getScoutedBuilders({ scoutId: userId })])
+ );
+
+ if (error) {
+ return ;
+ }
+
+ const [seasonStats, scoutedBuilders] = data;
const nftsPurchasedThisSeason = scoutedBuilders.reduce((acc, builder) => acc + (builder.nftsSoldToScout || 0), 0);
diff --git a/apps/scoutgame/components/scout/components/ScoutPageBuildersGallery.tsx b/apps/scoutgame/components/scout/components/ScoutPageBuildersGallery.tsx
index 4bdb3d5e01..61f75c88d7 100644
--- a/apps/scoutgame/components/scout/components/ScoutPageBuildersGallery.tsx
+++ b/apps/scoutgame/components/scout/components/ScoutPageBuildersGallery.tsx
@@ -2,19 +2,29 @@ import { currentSeason, getCurrentWeek } from '@packages/scoutgame/dates';
import type { BuildersSort } from 'lib/builders/getSortedBuilders';
import { getSortedBuilders } from 'lib/builders/getSortedBuilders';
+import { safeAwaitSSRData } from 'lib/utils/async';
import { BuildersGalleryContainer } from './BuildersGalleryContainer';
export const dynamic = 'force-dynamic';
export async function ScoutPageBuildersGallery({ sort, showHotIcon }: { sort: BuildersSort; showHotIcon: boolean }) {
- const { builders, nextCursor } = await getSortedBuilders({
- sort: sort as BuildersSort,
- limit: 15,
- week: getCurrentWeek(),
- season: currentSeason,
- cursor: null
- });
+ const [error, data] = await safeAwaitSSRData(
+ getSortedBuilders({
+ sort: sort as BuildersSort,
+ limit: 15,
+ week: getCurrentWeek(),
+ season: currentSeason,
+ cursor: null
+ })
+ );
+
+ if (error) {
+ return null;
+ }
+
+ const { builders, nextCursor } = data;
+
return (
= T | Promise;
+
+export async function safeAwait(
+ promise: Promise,
+ options?: { onSuccess?: (response: T) => MaybePromise; onError?: (error: Error) => MaybePromise }
+): Promise<[error: Error] | [error: null, data: T]> {
+ try {
+ const response = await promise;
+ await options?.onSuccess?.(response);
+ return [null, response];
+ } catch (_error: any) {
+ await options?.onError?.(_error);
+ return [_error];
+ }
+}
+
+export async function safeAwaitSSRData(promise: Promise) {
+ return safeAwait(promise, {
+ onError: async (err) => {
+ const session = await getSession();
+ const headersList = headers();
+ const fullUrl = headersList.get('referer') || '';
+
+ const isValidSystemError = isSystemError(err);
+
+ const errorAsSystemError = isValidSystemError ? err : new UnknownError(err.stack ?? err);
+
+ const loggedInfo = {
+ error: errorAsSystemError,
+ stack: err.stack,
+ userId: session.scoutId,
+ url: fullUrl,
+ ssr: true // Flag to identify that this error was thrown during SSR and can be looked up in DD
+ };
+
+ log.error('Server error fetching SSR data', loggedInfo);
+ }
+ });
+}
diff --git a/packages/scoutgame/src/notifications/getNotifications.ts b/packages/scoutgame/src/notifications/getNotifications.ts
index 7e14ba740c..30e5e8e36d 100644
--- a/packages/scoutgame/src/notifications/getNotifications.ts
+++ b/packages/scoutgame/src/notifications/getNotifications.ts
@@ -72,7 +72,11 @@ export type ScoutGameNotification =
| BuilderRecipientStrikeNotification
| ScoutRecipientStrikeNotification;
-export async function getNotifications({ userId }: { userId: string }): Promise {
+export async function getNotifications({ userId }: { userId?: string }): Promise {
+ if (!userId) {
+ return [];
+ }
+
if (!stringUtils.isUUID(userId)) {
throw new InvalidInputError(`userId required for notifications`);
}