Skip to content

Commit 4bfcd17

Browse files
committed
Enhance Upload page functionality and UI
- Implemented community and category selection using NewSelectSearch component. - Added meta tags for improved SEO and page description. - Integrated data loading for communities via loader functions. - Updated state management for selected communities and categories. - Improved user experience with dynamic category loading based on selected communities. - Enhanced UI elements for clarity and usability.
1 parent f8fbdf0 commit 4bfcd17

File tree

2 files changed

+261
-108
lines changed

2 files changed

+261
-108
lines changed

Diff for: apps/cyberstorm-remix/app/upload/upload.tsx

+181-48
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ import {
66
// Select,
77
NewSelectSearch,
88
Switch,
9+
NewSelectOption,
910
} from "@thunderstore/cyberstorm";
1011
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
1112
import { PageHeader } from "../commonComponents/PageHeader/PageHeader";
1213
import { DnDFileInput } from "@thunderstore/react-dnd";
13-
import { useCallback, useState } from "react";
14+
import { useCallback, useEffect, useState } from "react";
1415
import { initMultipartUpload, IUploadHandle } from "@thunderstore/ts-uploader";
1516
import { useUploadProgress } from "@thunderstore/ts-uploader-react";
1617
// import { useOutletContext } from "@remix-run/react";
@@ -19,15 +20,90 @@ import { useSession } from "@thunderstore/ts-api-react";
1920
import { faFileZip, faTreasureChest } from "@fortawesome/pro-solid-svg-icons";
2021
import { UserMedia } from "@thunderstore/ts-uploader/src/client/types";
2122
import { DapperTs } from "@thunderstore/dapper-ts";
23+
import { MetaFunction } from "@remix-run/node";
24+
import { useLoaderData } from "@remix-run/react";
25+
26+
interface CommunityOption {
27+
value: string;
28+
label: string;
29+
}
30+
31+
interface CategoryOption {
32+
value: string;
33+
label: string;
34+
}
35+
36+
export const meta: MetaFunction = () => {
37+
return [
38+
{ title: "Upload | Thunderstore" },
39+
{
40+
name: "description",
41+
content: "Upload a package to Thunderstore",
42+
},
43+
];
44+
};
45+
46+
export async function loader() {
47+
const dapper = new DapperTs(() => {
48+
return {
49+
apiHost: process.env.PUBLIC_API_URL,
50+
sessionId: undefined,
51+
};
52+
});
53+
return await dapper.getCommunities();
54+
}
55+
56+
export async function clientLoader() {
57+
const dapper = window.Dapper;
58+
return await dapper.getCommunities();
59+
}
2260

2361
export default function Upload() {
62+
const uploadData = useLoaderData<typeof loader | typeof clientLoader>();
63+
64+
const communityOptions: CommunityOption[] = [];
65+
const [categoryOptions, setCategoryOptions] = useState<
66+
{ communityId: string; categories: CategoryOption[] }[]
67+
>([]);
68+
69+
for (const community of uploadData.results) {
70+
communityOptions.push({
71+
value: community.identifier,
72+
label: community.name,
73+
});
74+
}
75+
2476
// const outletContext = useOutletContext() as OutletContextShape;
2577
const session = useSession();
2678

2779
const [NSFW, setNSFW] = useState<boolean>(false);
2880
const [team, setTeam] = useState<string>();
29-
const [communities, setCommunities] = useState<string[]>();
30-
const [categories, setCategories] = useState<string[]>();
81+
const [selectedCommunities, setSelectedCommunities] = useState<
82+
NewSelectOption[]
83+
>([]);
84+
const [selectedCategories, setSelectedCategories] = useState<
85+
{ communityId: string; categoryId: string }[]
86+
>([]);
87+
88+
const handleCategoryChange = useCallback(
89+
(
90+
val: NewSelectOption[] | undefined,
91+
categories: CategoryOption[],
92+
communityId: string
93+
) => {
94+
setSelectedCategories((prev) => {
95+
const filtered = prev.filter((cat) => cat.communityId !== communityId);
96+
if (val) {
97+
return [
98+
...filtered,
99+
...val.map((v) => ({ communityId, categoryId: v.value })),
100+
];
101+
}
102+
return filtered;
103+
});
104+
},
105+
[]
106+
);
31107

32108
const [file, setFile] = useState<File | null>(null);
33109
const [handle, setHandle] = useState<IUploadHandle>();
@@ -61,17 +137,43 @@ export default function Upload() {
61137

62138
const submit = useCallback(() => {
63139
const config = session.getConfig();
64-
// const currentUser = session.getSessionCurrentUser();
65140
const dapper = new DapperTs(() => config);
66141
dapper.postPackageSubmissionMetadata(
67142
team ?? "",
68-
categories ? categories : [""],
69-
communities ? communities : [""],
143+
selectedCategories.map((cat) => cat.categoryId),
144+
selectedCommunities.map(
145+
(community) => (community as NewSelectOption).value
146+
),
70147
NSFW,
71148
usermedia?.uuid ?? "",
72149
[] // TODO: wth are community categories??
73150
);
74-
}, [usermedia, NSFW, team, communities, categories, session]);
151+
}, [usermedia, NSFW, team, selectedCommunities, selectedCategories, session]);
152+
153+
useEffect(() => {
154+
if (selectedCommunities) {
155+
for (const community of selectedCommunities) {
156+
// Skip if we already have categories for this community
157+
if (
158+
categoryOptions.some((opt) => opt.communityId === community.value)
159+
) {
160+
continue;
161+
}
162+
window.Dapper.getCommunityFilters(community.value).then((filters) => {
163+
setCategoryOptions((prev) => [
164+
...prev,
165+
{
166+
communityId: community.value,
167+
categories: filters.package_categories.map((cat) => ({
168+
value: cat.id,
169+
label: cat.name,
170+
})),
171+
},
172+
]);
173+
});
174+
}
175+
}
176+
}, [selectedCommunities]);
75177

76178
return (
77179
<div className="container container--y container--full layout__content">
@@ -170,24 +272,23 @@ export default function Upload() {
170272
<NewSelectSearch
171273
placeholder="Select communities"
172274
multiple
173-
options={[
174-
{ value: "community1", label: "Community 1" },
175-
{ value: "community2", label: "Community 2" },
176-
{ value: "community3", label: "Community 3" },
177-
{ value: "community4", label: "Community 4" },
178-
{ value: "community5", label: "Community 5" },
179-
{ value: "community6", label: "Community 6" },
180-
{ value: "community7", label: "Community 7" },
181-
{ value: "community8", label: "Community 8" },
182-
{ value: "community9", label: "Community 9" },
183-
{ value: "community10", label: "Community 10" },
184-
]}
185-
onChange={(val) => setCommunities(val ? [val.value] : [])}
186-
value={
187-
communities?.[0]
188-
? { value: communities[0], label: communities[0] }
189-
: undefined
190-
}
275+
options={communityOptions}
276+
onChange={(val) => {
277+
if (val) {
278+
const newCommunities = Array.isArray(val) ? val : [val];
279+
setSelectedCommunities(newCommunities);
280+
// Remove categories for communities that are no longer selected
281+
setSelectedCategories((prev) =>
282+
prev.filter((cat) =>
283+
newCommunities.some((c) => c.value === cat.communityId)
284+
)
285+
);
286+
} else {
287+
setSelectedCommunities([]);
288+
setSelectedCategories([]);
289+
}
290+
}}
291+
value={selectedCommunities}
191292
/>
192293
</div>
193294
</div>
@@ -200,35 +301,56 @@ export default function Upload() {
200301
</p>
201302
</div>
202303
<div className="upload__content">
203-
<NewSelectSearch
204-
placeholder="Select categories"
205-
multiple
206-
options={[
207-
{ value: "cat1", label: "Cat 1" },
208-
{ value: "cat2", label: "Cat 2" },
209-
{ value: "cat3", label: "Cat 3" },
210-
{ value: "cat4", label: "Cat 4" },
211-
{ value: "cat5", label: "Cat 5" },
212-
{ value: "cat6", label: "Cat 6" },
213-
{ value: "cat7", label: "Cat 7" },
214-
{ value: "cat8", label: "Cat 8" },
215-
]}
216-
onChange={(val) => setCategories(val ? [val.value] : [])}
217-
value={
218-
categories?.[0]
219-
? { value: categories[0], label: categories[0] }
220-
: undefined
221-
}
222-
/>
304+
{selectedCommunities.map((community) => {
305+
const communityData = uploadData.results.find(
306+
(c) => c.identifier === community.value
307+
);
308+
const categories =
309+
categoryOptions.find((c) => c.communityId === community.value)
310+
?.categories || [];
311+
312+
return (
313+
<div key={community.value} className="upload__category-select">
314+
<p className="upload__category-label">
315+
{communityData?.name} categories
316+
</p>
317+
<NewSelectSearch
318+
placeholder="Select categories"
319+
multiple
320+
options={categories}
321+
onChange={(val) => {
322+
handleCategoryChange(
323+
val ? (Array.isArray(val) ? val : [val]) : undefined,
324+
categories,
325+
community.value
326+
);
327+
}}
328+
value={selectedCategories
329+
.filter((cat) => cat.communityId === community.value)
330+
.map((cat) => ({
331+
value: cat.categoryId,
332+
label:
333+
categories.find((c) => c.value === cat.categoryId)
334+
?.label || "",
335+
}))}
336+
/>
337+
</div>
338+
);
339+
})}
340+
{(!selectedCommunities || selectedCommunities.length === 0) && (
341+
<p className="upload__category-placeholder">
342+
Select a community to choose categories
343+
</p>
344+
)}
223345
</div>
224346
</div>
225347
<div className="upload__divider" />
226348
<div className="container container--x container--full upload__row">
227349
<div className="upload__meta">
228350
<p className="upload__title">Contains NSFW content</p>
229351
<p className="upload__description">
230-
Select if your package contains NSFW material. An "NSWF" -tag will
231-
be applied to your package.
352+
Select if your package contains NSFW material. An
353+
&ldquo;NSFW&rdquo; -tag will be applied to your package.
232354
</p>
233355
</div>
234356
<div className="upload__content">
@@ -245,7 +367,8 @@ export default function Upload() {
245367
<div className="upload__meta">
246368
<p className="upload__title">Submit</p>
247369
<p className="upload__description">
248-
Double-check your selections and hit "Submit" when you're ready!
370+
Double-check your selections and hit &ldquo;Submit&rdquo; when
371+
you&rsquo;re ready!
249372
</p>
250373
</div>
251374
<div className="upload__content">
@@ -296,6 +419,16 @@ export default function Upload() {
296419
<p>Upload success: {isDone.toString()}</p>
297420
</div> */}
298421
</section>
422+
<span>usermedia: {usermedia?.uuid}</span>
423+
<span>NSFW: {NSFW}</span>
424+
<span>team: {team}</span>
425+
<span>
426+
selectedCommunities: {selectedCommunities.map((c) => c.value)}
427+
</span>
428+
<span>
429+
selectedCategories:{" "}
430+
{selectedCategories.map((c) => `${c.communityId}-${c.categoryId} `)}
431+
</span>
299432
</div>
300433
);
301434
}

0 commit comments

Comments
 (0)