@@ -6,11 +6,12 @@ import {
6
6
// Select,
7
7
NewSelectSearch ,
8
8
Switch ,
9
+ NewSelectOption ,
9
10
} from "@thunderstore/cyberstorm" ;
10
11
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" ;
11
12
import { PageHeader } from "../commonComponents/PageHeader/PageHeader" ;
12
13
import { DnDFileInput } from "@thunderstore/react-dnd" ;
13
- import { useCallback , useState } from "react" ;
14
+ import { useCallback , useEffect , useState } from "react" ;
14
15
import { initMultipartUpload , IUploadHandle } from "@thunderstore/ts-uploader" ;
15
16
import { useUploadProgress } from "@thunderstore/ts-uploader-react" ;
16
17
// import { useOutletContext } from "@remix-run/react";
@@ -19,15 +20,90 @@ import { useSession } from "@thunderstore/ts-api-react";
19
20
import { faFileZip , faTreasureChest } from "@fortawesome/pro-solid-svg-icons" ;
20
21
import { UserMedia } from "@thunderstore/ts-uploader/src/client/types" ;
21
22
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
+ }
22
60
23
61
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
+
24
76
// const outletContext = useOutletContext() as OutletContextShape;
25
77
const session = useSession ( ) ;
26
78
27
79
const [ NSFW , setNSFW ] = useState < boolean > ( false ) ;
28
80
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
+ ) ;
31
107
32
108
const [ file , setFile ] = useState < File | null > ( null ) ;
33
109
const [ handle , setHandle ] = useState < IUploadHandle > ( ) ;
@@ -61,17 +137,43 @@ export default function Upload() {
61
137
62
138
const submit = useCallback ( ( ) => {
63
139
const config = session . getConfig ( ) ;
64
- // const currentUser = session.getSessionCurrentUser();
65
140
const dapper = new DapperTs ( ( ) => config ) ;
66
141
dapper . postPackageSubmissionMetadata (
67
142
team ?? "" ,
68
- categories ? categories : [ "" ] ,
69
- communities ? communities : [ "" ] ,
143
+ selectedCategories . map ( ( cat ) => cat . categoryId ) ,
144
+ selectedCommunities . map (
145
+ ( community ) => ( community as NewSelectOption ) . value
146
+ ) ,
70
147
NSFW ,
71
148
usermedia ?. uuid ?? "" ,
72
149
[ ] // TODO: wth are community categories??
73
150
) ;
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 ] ) ;
75
177
76
178
return (
77
179
< div className = "container container--y container--full layout__content" >
@@ -170,24 +272,23 @@ export default function Upload() {
170
272
< NewSelectSearch
171
273
placeholder = "Select communities"
172
274
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 }
191
292
/>
192
293
</ div >
193
294
</ div >
@@ -200,35 +301,56 @@ export default function Upload() {
200
301
</ p >
201
302
</ div >
202
303
< 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
+ ) }
223
345
</ div >
224
346
</ div >
225
347
< div className = "upload__divider" />
226
348
< div className = "container container--x container--full upload__row" >
227
349
< div className = "upload__meta" >
228
350
< p className = "upload__title" > Contains NSFW content</ p >
229
351
< 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
+ “NSFW” -tag will be applied to your package.
232
354
</ p >
233
355
</ div >
234
356
< div className = "upload__content" >
@@ -245,7 +367,8 @@ export default function Upload() {
245
367
< div className = "upload__meta" >
246
368
< p className = "upload__title" > Submit</ p >
247
369
< p className = "upload__description" >
248
- Double-check your selections and hit "Submit" when you're ready!
370
+ Double-check your selections and hit “Submit” when
371
+ you’re ready!
249
372
</ p >
250
373
</ div >
251
374
< div className = "upload__content" >
@@ -296,6 +419,16 @@ export default function Upload() {
296
419
<p>Upload success: {isDone.toString()}</p>
297
420
</div> */ }
298
421
</ 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 >
299
432
</ div >
300
433
) ;
301
434
}
0 commit comments