Skip to content

Commit f3adb24

Browse files
committed
Enhance error handling in root.tsx ErrorBoundary with user-facing error mapping and deduplication of error descriptions
1 parent de2572f commit f3adb24

File tree

2 files changed

+62
-15
lines changed

2 files changed

+62
-15
lines changed

apps/cyberstorm-remix/app/root.tsx

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
// The styles need to be imported at the beginning, so that the layers are correctly set up
2-
// eslint-disable-next-line prettier/prettier
1+
// sort-imports-ignore
2+
import "./styles";
3+
// NOTE: The sort-imports-ignore is needed here to prevent css layers from not being loaded in the correct order, feel free to remove the ignore momentarily to sort imports
4+
35
// import { LinksFunction } from "@remix-run/react/dist/routeModules";
46
import { Provider as RadixTooltip } from "@radix-ui/react-tooltip";
57
import { captureRemixErrorBoundaryError, withSentry } from "@sentry/remix";
@@ -8,6 +10,11 @@ import {
810
type publicEnvVariablesType,
911
} from "cyberstorm/security/publicEnvVariables";
1012
import { LinkLibrary } from "cyberstorm/utils/LinkLibrary";
13+
import { NimbusAwaitErrorElement } from "cyberstorm/utils/errors/NimbusErrorBoundary";
14+
import {
15+
type UserFacingErrorPayload,
16+
parseUserFacingErrorPayload,
17+
} from "cyberstorm/utils/errors/userFacingErrorResponse";
1118
import { type ReactNode, Suspense, memo, useEffect, useRef } from "react";
1219
import {
1320
Await,
@@ -35,8 +42,6 @@ import {
3542
isRecord,
3643
} from "@thunderstore/cyberstorm";
3744
import { Toast } from "@thunderstore/cyberstorm";
38-
import "@thunderstore/cyberstorm-theme/css";
39-
import "@thunderstore/cyberstorm/css";
4045
import { type CurrentUser } from "@thunderstore/dapper";
4146
import { DapperTs } from "@thunderstore/dapper-ts";
4247
import { type RequestConfig } from "@thunderstore/thunderstore-api";
@@ -50,10 +55,7 @@ import {
5055

5156
import type { Route } from "./+types/root";
5257
import { Footer } from "./commonComponents/Footer/Footer";
53-
// Annoying prettier issue, where it wants to insert styles import here
54-
// eslint-disable-next-line prettier/prettier
5558
import { NavigationWrapper } from "./commonComponents/Navigation/NavigationWrapper";
56-
import "./styles/index.css";
5759

5860
// REMIX TODO: https://remix.run/docs/en/main/route/links
5961
// export const links: LinksFunction = () => [{ rel: "stylesheet", href: styles }];
@@ -621,18 +623,36 @@ export function ErrorBoundary() {
621623
console.log(error);
622624
}
623625
const isResponseError = isRouteErrorResponse(error);
626+
let payload: UserFacingErrorPayload | null = null;
627+
628+
if (isResponseError) {
629+
payload = parseUserFacingErrorPayload(error.data);
630+
}
631+
632+
const statusCode = payload?.status ?? (isResponseError ? error.status : 500);
633+
const headline =
634+
payload?.headline ??
635+
(isResponseError
636+
? error.statusText || `Error ${error.status}`
637+
: "Internal server error");
638+
639+
const fallbackDescription =
640+
isResponseError && typeof error.data === "string"
641+
? dedupeDescription(headline, error.data)
642+
: undefined;
643+
644+
const description = payload?.description ?? fallbackDescription;
645+
const showDefaultFlavor = !payload && !isResponseError;
624646
return (
625647
<div className="error">
626-
<div
627-
className="error__glitch"
628-
data-text={isResponseError ? error.status : 500}
629-
>
630-
<span>{isResponseError ? error.status : 500}</span>
648+
<div className="error__glitch" data-text={statusCode}>
649+
<span>{statusCode}</span>
631650
</div>
632651
<div className="error__description">
633-
{isResponseError ? error.data : "Internal server error"}
652+
<strong>{headline}</strong>
653+
{description ? <div>{description}</div> : null}
634654
</div>
635-
{!isResponseError && (
655+
{showDefaultFlavor && (
636656
<div className="error__flavor">
637657
Beep boop. Server something error happens.
638658
</div>
@@ -641,6 +661,26 @@ export function ErrorBoundary() {
641661
);
642662
}
643663

664+
function dedupeDescription(
665+
headline: string,
666+
description: string | undefined
667+
): string | undefined {
668+
if (!description) {
669+
return undefined;
670+
}
671+
672+
const trimmedDescription = description.trim();
673+
if (!trimmedDescription) {
674+
return undefined;
675+
}
676+
677+
if (trimmedDescription.toLowerCase() === headline.trim().toLowerCase()) {
678+
return undefined;
679+
}
680+
681+
return trimmedDescription;
682+
}
683+
644684
// Temporary solution for implementing ads
645685
// REMIX TODO: Move to dynamic html
646686
function AdsInit() {
@@ -739,7 +779,10 @@ function getCommunityBreadcrumb(
739779
</span>
740780
}
741781
>
742-
<Await resolve={communityPage.data.community}>
782+
<Await
783+
resolve={communityPage.data.community}
784+
errorElement={<NimbusAwaitErrorElement />}
785+
>
743786
{(resolvedValue) => {
744787
let label = undefined;
745788
let icon = undefined;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// sort-imports-ignore
2+
import "./styles/index.css";
3+
import "@thunderstore/cyberstorm-theme/css";
4+
import "@thunderstore/cyberstorm/css";

0 commit comments

Comments
 (0)