Skip to content

Commit 705b777

Browse files
committed
Enhance error handling in PackageSearch component and PackageSearch route
1 parent cd40580 commit 705b777

File tree

3 files changed

+400
-187
lines changed

3 files changed

+400
-187
lines changed
Lines changed: 122 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,137 @@
11
import { useLoaderData, useOutletContext } from "react-router";
22
import { PackageSearch } from "~/commonComponents/PackageSearch/PackageSearch";
33
import { PackageOrderOptions } from "~/commonComponents/PackageSearch/components/PackageOrder";
4-
import { DapperTs } from "@thunderstore/dapper-ts";
5-
import {
6-
getPublicEnvVariables,
7-
getSessionTools,
8-
} from "cyberstorm/security/publicEnvVariables";
94
import { type OutletContextShape } from "~/root";
105
import type { Route } from "./+types/PackageSearch";
6+
import { throwUserFacingPayloadResponse } from "cyberstorm/utils/errors/userFacingErrorResponse";
7+
import { handleLoaderError } from "cyberstorm/utils/errors/handleLoaderError";
8+
import { createNotFoundMapping } from "cyberstorm/utils/errors/loaderMappings";
9+
import { NimbusDefaultRouteErrorBoundary } from "cyberstorm/utils/errors/NimbusErrorBoundary";
10+
import { getLoaderTools } from "cyberstorm/utils/getLoaderTools";
11+
import { parseIntegerSearchParam } from "cyberstorm/utils/searchParamsUtils";
12+
13+
interface PackageSearchQuery {
14+
ordering: string;
15+
page: number | undefined;
16+
search: string;
17+
includedCategories: string[] | undefined;
18+
excludedCategories: string[] | undefined;
19+
section: string;
20+
nsfw: boolean;
21+
deprecated: boolean;
22+
}
23+
24+
const communityNotFoundMappings = [
25+
createNotFoundMapping(
26+
"Community not found.",
27+
"We could not find the requested community."
28+
),
29+
];
30+
31+
function resolvePackageSearchQuery(request: Request): PackageSearchQuery {
32+
const searchParams = new URL(request.url).searchParams;
33+
const ordering = searchParams.get("ordering") ?? PackageOrderOptions.Updated;
34+
const page = parseIntegerSearchParam(searchParams.get("page"));
35+
const search = searchParams.get("search") ?? "";
36+
const included = searchParams.get("includedCategories");
37+
const excluded = searchParams.get("excludedCategories");
38+
const sectionParam = searchParams.get("section");
39+
const section = sectionParam
40+
? sectionParam === "all"
41+
? ""
42+
: sectionParam
43+
: "";
44+
const nsfw = searchParams.get("nsfw") === "true";
45+
const deprecated = searchParams.get("deprecated") === "true";
46+
47+
return {
48+
ordering,
49+
page,
50+
search,
51+
includedCategories: included?.split(",") ?? undefined,
52+
excludedCategories: excluded?.split(",") ?? undefined,
53+
section,
54+
nsfw,
55+
deprecated,
56+
};
57+
}
1158

1259
export async function loader({ params, request }: Route.LoaderArgs) {
1360
if (params.communityId) {
14-
const publicEnvVariables = getPublicEnvVariables(["VITE_API_URL"]);
15-
const dapper = new DapperTs(() => {
61+
const { dapper } = getLoaderTools();
62+
try {
63+
const query = resolvePackageSearchQuery(request);
64+
1665
return {
17-
apiHost: publicEnvVariables.VITE_API_URL,
18-
sessionId: undefined,
66+
filters: await dapper.getCommunityFilters(params.communityId),
67+
listings: await dapper.getPackageListings(
68+
{
69+
kind: "community",
70+
communityId: params.communityId,
71+
},
72+
query.ordering ?? "",
73+
query.page,
74+
query.search,
75+
query.includedCategories,
76+
query.excludedCategories,
77+
query.section,
78+
query.nsfw,
79+
query.deprecated
80+
),
1981
};
20-
});
21-
const searchParams = new URL(request.url).searchParams;
22-
const ordering =
23-
searchParams.get("ordering") ?? PackageOrderOptions.Updated;
24-
const page = searchParams.get("page");
25-
const search = searchParams.get("search");
26-
const includedCategories = searchParams.get("includedCategories");
27-
const excludedCategories = searchParams.get("excludedCategories");
28-
const section = searchParams.get("section");
29-
const nsfw = searchParams.get("nsfw");
30-
const deprecated = searchParams.get("deprecated");
31-
const filters = await dapper.getCommunityFilters(params.communityId);
32-
33-
return {
34-
filters: filters,
35-
listings: await dapper.getPackageListings(
36-
{
37-
kind: "community",
38-
communityId: params.communityId,
39-
},
40-
ordering ?? "",
41-
page === null ? undefined : Number(page),
42-
search ?? "",
43-
includedCategories?.split(",") ?? undefined,
44-
excludedCategories?.split(",") ?? undefined,
45-
section ? (section === "all" ? "" : section) : "",
46-
nsfw === "true" ? true : false,
47-
deprecated === "true" ? true : false
48-
),
49-
};
82+
} catch (error) {
83+
handleLoaderError(error, { mappings: communityNotFoundMappings });
84+
}
5085
}
51-
throw new Response("Community not found", { status: 404 });
86+
throwUserFacingPayloadResponse({
87+
headline: "Community not found.",
88+
description: "We could not find the requested community.",
89+
category: "not_found",
90+
status: 404,
91+
});
5292
}
5393

5494
export async function clientLoader({
5595
request,
5696
params,
5797
}: Route.ClientLoaderArgs) {
5898
if (params.communityId) {
59-
const tools = getSessionTools();
60-
const dapper = new DapperTs(() => {
61-
return {
62-
apiHost: tools?.getConfig().apiHost,
63-
sessionId: tools?.getConfig().sessionId,
64-
};
65-
});
66-
const searchParams = new URL(request.url).searchParams;
67-
const ordering =
68-
searchParams.get("ordering") ?? PackageOrderOptions.Updated;
69-
const page = searchParams.get("page");
70-
const search = searchParams.get("search");
71-
const includedCategories = searchParams.get("includedCategories");
72-
const excludedCategories = searchParams.get("excludedCategories");
73-
const section = searchParams.get("section");
74-
const nsfw = searchParams.get("nsfw");
75-
const deprecated = searchParams.get("deprecated");
76-
const filters = dapper.getCommunityFilters(params.communityId);
99+
const { dapper } = getLoaderTools();
100+
const query = resolvePackageSearchQuery(request);
77101
return {
78-
filters: filters,
79-
listings: dapper.getPackageListings(
80-
{
81-
kind: "community",
82-
communityId: params.communityId,
83-
},
84-
ordering ?? "",
85-
page === null ? undefined : Number(page),
86-
search ?? "",
87-
includedCategories?.split(",") ?? undefined,
88-
excludedCategories?.split(",") ?? undefined,
89-
section ? (section === "all" ? "" : section) : "",
90-
nsfw === "true" ? true : false,
91-
deprecated === "true" ? true : false
92-
),
102+
filters: dapper
103+
.getCommunityFilters(params.communityId)
104+
.catch((error) =>
105+
handleLoaderError(error, { mappings: communityNotFoundMappings })
106+
),
107+
listings: dapper
108+
.getPackageListings(
109+
{
110+
kind: "community",
111+
communityId: params.communityId,
112+
},
113+
query.ordering ?? "",
114+
query.page,
115+
query.search,
116+
query.includedCategories,
117+
query.excludedCategories,
118+
query.section,
119+
query.nsfw,
120+
query.deprecated
121+
)
122+
.catch((error) =>
123+
handleLoaderError(error, { mappings: communityNotFoundMappings })
124+
),
93125
};
94126
}
95-
throw new Response("Community not found", { status: 404 });
127+
throwUserFacingPayloadResponse({
128+
headline: "Community not found.",
129+
description: "We could not find the requested community.",
130+
category: "not_found",
131+
status: 404,
132+
});
96133
}
97134

98-
// function shouldRevalidate(arg: ShouldRevalidateFunctionArgs) {
99-
// return true; // false
100-
// }
101-
102135
export default function CommunityPackageSearch() {
103136
const { filters, listings } = useLoaderData<
104137
typeof loader | typeof clientLoader
@@ -107,14 +140,16 @@ export default function CommunityPackageSearch() {
107140
const outletContext = useOutletContext() as OutletContextShape;
108141

109142
return (
110-
<>
111-
<PackageSearch
112-
listings={listings}
113-
filters={filters}
114-
config={outletContext.requestConfig}
115-
currentUser={outletContext.currentUser}
116-
dapper={outletContext.dapper}
117-
/>
118-
</>
143+
<PackageSearch
144+
listings={listings}
145+
filters={filters}
146+
config={outletContext.requestConfig}
147+
currentUser={outletContext.currentUser}
148+
dapper={outletContext.dapper}
149+
/>
119150
);
120151
}
152+
153+
export function ErrorBoundary() {
154+
return <NimbusDefaultRouteErrorBoundary />;
155+
}

apps/cyberstorm-remix/app/commonComponents/PackageSearch/PackageSearch.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@
127127
align-self: stretch;
128128
}
129129

130+
.package-search__error {
131+
display: flex;
132+
flex-direction: column;
133+
gap: 1rem;
134+
align-items: flex-start;
135+
align-items: center;
136+
align-self: stretch;
137+
padding: 1rem;
138+
}
139+
130140
.package-search__grid {
131141
display: grid;
132142
grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));

0 commit comments

Comments
 (0)