aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/components/UploadRunDialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/components/UploadRunDialog.tsx')
-rw-r--r--frontend/src/components/UploadRunDialog.tsx375
1 files changed, 257 insertions, 118 deletions
diff --git a/frontend/src/components/UploadRunDialog.tsx b/frontend/src/components/UploadRunDialog.tsx
index c02fdb8..0034019 100644
--- a/frontend/src/components/UploadRunDialog.tsx
+++ b/frontend/src/components/UploadRunDialog.tsx
@@ -1,15 +1,14 @@
1import React from 'react'; 1import React from "react";
2import { UploadRunContent } from '@customTypes/Content'; 2import { UploadRunContent } from "@customTypes/Content";
3import { ScoreboardTempUpdate, SourceDemoParser, NetMessages } from '@nekz/sdp'; 3import { ScoreboardTempUpdate, SourceDemoParser, NetMessages } from "@nekz/sdp";
4 4
5import '@css/UploadRunDialog.css'; 5import { Game } from "@customTypes/Game";
6import { Game } from '@customTypes/Game'; 6import { API } from "@api/Api";
7import { API } from '@api/Api'; 7import { useNavigate } from "react-router-dom";
8import { useNavigate } from 'react-router-dom'; 8import useMessage from "@hooks/UseMessage";
9import useMessage from '@hooks/UseMessage'; 9import useConfirm from "@hooks/UseConfirm";
10import useConfirm from '@hooks/UseConfirm';
11import useMessageLoad from "@hooks/UseMessageLoad"; 10import useMessageLoad from "@hooks/UseMessageLoad";
12import { MapNames } from '@customTypes/MapNames'; 11import { MapNames } from "@customTypes/MapNames";
13 12
14interface UploadRunDialogProps { 13interface UploadRunDialogProps {
15 token?: string; 14 token?: string;
@@ -18,18 +17,24 @@ interface UploadRunDialogProps {
18 games: Game[]; 17 games: Game[];
19} 18}
20 19
21const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose, games }) => { 20const UploadRunDialog: React.FC<UploadRunDialogProps> = ({
22 21 token,
22 open,
23 onClose,
24 games,
25}) => {
23 const { message, MessageDialogComponent } = useMessage(); 26 const { message, MessageDialogComponent } = useMessage();
24 const { confirm, ConfirmDialogComponent } = useConfirm(); 27 const { confirm, ConfirmDialogComponent } = useConfirm();
25 const { messageLoad, messageLoadClose, MessageDialogLoadComponent } = useMessageLoad(); 28 const { messageLoad, messageLoadClose, MessageDialogLoadComponent } =
29 useMessageLoad();
26 30
27 const navigate = useNavigate(); 31 const navigate = useNavigate();
28 32
29 const [uploadRunContent, setUploadRunContent] = React.useState<UploadRunContent>({ 33 const [uploadRunContent, setUploadRunContent] =
30 host_demo: null, 34 React.useState<UploadRunContent>({
31 partner_demo: null, 35 host_demo: null,
32 }); 36 partner_demo: null,
37 });
33 38
34 const [selectedGameID, setSelectedGameID] = React.useState<number>(0); 39 const [selectedGameID, setSelectedGameID] = React.useState<number>(0);
35 const [selectedGameName, setSelectedGameName] = React.useState<string>(""); 40 const [selectedGameName, setSelectedGameName] = React.useState<string>("");
@@ -41,7 +46,8 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
41 const [loading, setLoading] = React.useState<boolean>(false); 46 const [loading, setLoading] = React.useState<boolean>(false);
42 47
43 const [dragHightlight, setDragHighlight] = React.useState<boolean>(false); 48 const [dragHightlight, setDragHighlight] = React.useState<boolean>(false);
44 const [dragHightlightPartner, setDragHighlightPartner] = React.useState<boolean>(false); 49 const [dragHightlightPartner, setDragHighlightPartner] =
50 React.useState<boolean>(false);
45 51
46 const fileInputRef = React.useRef<HTMLInputElement>(null); 52 const fileInputRef = React.useRef<HTMLInputElement>(null);
47 const fileInputRefPartner = React.useRef<HTMLInputElement>(null); 53 const fileInputRefPartner = React.useRef<HTMLInputElement>(null);
@@ -52,9 +58,12 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
52 } else { 58 } else {
53 fileInputRefPartner.current?.click(); 59 fileInputRefPartner.current?.click();
54 } 60 }
55 } 61 };
56 62
57 const _handle_drag_over = (e: React.DragEvent<HTMLDivElement>, host: boolean) => { 63 const _handle_drag_over = (
64 e: React.DragEvent<HTMLDivElement>,
65 host: boolean
66 ) => {
58 e.preventDefault(); 67 e.preventDefault();
59 e.stopPropagation(); 68 e.stopPropagation();
60 if (host) { 69 if (host) {
@@ -62,9 +71,12 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
62 } else { 71 } else {
63 setDragHighlightPartner(true); 72 setDragHighlightPartner(true);
64 } 73 }
65 } 74 };
66 75
67 const _handle_drag_leave = (e: React.DragEvent<HTMLDivElement>, host: boolean) => { 76 const _handle_drag_leave = (
77 e: React.DragEvent<HTMLDivElement>,
78 host: boolean
79 ) => {
68 e.preventDefault(); 80 e.preventDefault();
69 e.stopPropagation(); 81 e.stopPropagation();
70 if (host) { 82 if (host) {
@@ -72,7 +84,7 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
72 } else { 84 } else {
73 setDragHighlightPartner(false); 85 setDragHighlightPartner(false);
74 } 86 }
75 } 87 };
76 88
77 const _handle_drop = (e: React.DragEvent<HTMLDivElement>, host: boolean) => { 89 const _handle_drop = (e: React.DragEvent<HTMLDivElement>, host: boolean) => {
78 e.preventDefault(); 90 e.preventDefault();
@@ -80,18 +92,18 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
80 setDragHighlight(true); 92 setDragHighlight(true);
81 93
82 _handle_file_change(e.dataTransfer.files, host); 94 _handle_file_change(e.dataTransfer.files, host);
83 } 95 };
84 96
85 const _handle_dropdowns = (dropdown: number) => { 97 const _handle_dropdowns = (dropdown: number) => {
86 setDropdown1Vis(false); 98 setDropdown1Vis(false);
87 setDropdown2Vis(false); 99 setDropdown2Vis(false);
88 if (dropdown == 1) { 100 if (dropdown === 1) {
89 setDropdown1Vis(!dropdown1Vis); 101 setDropdown1Vis(!dropdown1Vis);
90 } else if (dropdown == 2) { 102 } else if (dropdown === 2) {
91 setDropdown2Vis(!dropdown2Vis); 103 setDropdown2Vis(!dropdown2Vis);
92 document.querySelector("#dropdown2")?.scrollTo(0, 0); 104 document.querySelector("#dropdown2")?.scrollTo(0, 0);
93 } 105 }
94 } 106 };
95 107
96 const _handle_game_select = async (game_id: string, game_name: string) => { 108 const _handle_game_select = async (game_id: string, game_name: string) => {
97 setLoading(true); 109 setLoading(true);
@@ -120,53 +132,76 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
120 if (token) { 132 if (token) {
121 if (games[selectedGameID].is_coop) { 133 if (games[selectedGameID].is_coop) {
122 if (uploadRunContent.host_demo === null) { 134 if (uploadRunContent.host_demo === null) {
123 await message("Error", "You must select a host demo to upload.") 135 await message("Error", "You must select a host demo to upload.");
124 return 136 return;
125 } else if (uploadRunContent.partner_demo === null) { 137 } else if (uploadRunContent.partner_demo === null) {
126 await message("Error", "You must select a partner demo to upload.") 138 await message("Error", "You must select a partner demo to upload.");
127 return 139 return;
128 } 140 }
129 } else { 141 } else {
130 if (uploadRunContent.host_demo === null) { 142 if (uploadRunContent.host_demo === null) {
131 await message("Error", "You must select a demo to upload.") 143 await message("Error", "You must select a demo to upload.");
132 return 144 return;
133 } 145 }
134 } 146 }
135 const demo = SourceDemoParser.default() 147 const demo = SourceDemoParser.default()
136 .setOptions({ packets: true, header: true }) 148 .setOptions({ packets: true, header: true })
137 .parse(await uploadRunContent.host_demo.arrayBuffer()); 149 .parse(await uploadRunContent.host_demo.arrayBuffer());
138 const scoreboard = demo.findPacket<NetMessages.SvcUserMessage>((msg) => { 150 const scoreboard = demo.findPacket<NetMessages.SvcUserMessage>(msg => {
139 return msg instanceof NetMessages.SvcUserMessage && msg.userMessage instanceof ScoreboardTempUpdate; 151 return (
140 }) 152 msg instanceof NetMessages.SvcUserMessage &&
153 msg.userMessage instanceof ScoreboardTempUpdate
154 );
155 });
141 156
142 if (!scoreboard) { 157 if (!scoreboard) {
143 await message("Error", "Error while processing demo: Unable to get scoreboard result. Either there is a demo that is corrupt or haven't been recorded in challenge mode.") 158 await message(
144 return 159 "Error",
160 "Error while processing demo: Unable to get scoreboard result. Either there is a demo that is corrupt or haven't been recorded in challenge mode."
161 );
162 return;
145 } 163 }
146 164
147 if (!demo.mapName || !MapNames[demo.mapName]) { 165 if (!demo.mapName || !MapNames[demo.mapName]) {
148 await message("Error", "Error while processing demo: Invalid map name.") 166 await message(
149 return 167 "Error",
168 "Error while processing demo: Invalid map name."
169 );
170 return;
150 } 171 }
151 172
152 if (selectedGameID === 0 && MapNames[demo.mapName] > 60) { 173 if (selectedGameID === 0 && MapNames[demo.mapName] > 60) {
153 await message("Error", "Error while processing demo: Invalid cooperative demo in singleplayer submission.") 174 await message(
154 return 175 "Error",
176 "Error while processing demo: Invalid cooperative demo in singleplayer submission."
177 );
178 return;
155 } else if (selectedGameID === 1 && MapNames[demo.mapName] <= 60) { 179 } else if (selectedGameID === 1 && MapNames[demo.mapName] <= 60) {
156 await message("Error", "Error while processing demo: Invalid singleplayer demo in cooperative submission.") 180 await message(
157 return 181 "Error",
182 "Error while processing demo: Invalid singleplayer demo in cooperative submission."
183 );
184 return;
158 } 185 }
159 186
160 const { portalScore, timeScore } = scoreboard.userMessage?.as<ScoreboardTempUpdate>() ?? {}; 187 const { portalScore, timeScore } =
188 scoreboard.userMessage?.as<ScoreboardTempUpdate>() ?? {};
161 189
162 const userConfirmed = await confirm("Upload Record", `Map Name: ${demo.mapName}\nPortal Count: ${portalScore}\nTicks: ${timeScore}\n\nAre you sure you want to upload this demo?`); 190 const userConfirmed = await confirm(
191 "Upload Record",
192 `Map Name: ${demo.mapName}\nPortal Count: ${portalScore}\nTicks: ${timeScore}\n\nAre you sure you want to upload this demo?`
193 );
163 194
164 if (!userConfirmed) { 195 if (!userConfirmed) {
165 return; 196 return;
166 } 197 }
167 198
168 messageLoad("Uploading..."); 199 messageLoad("Uploading...");
169 const [success, response] = await API.post_record(token, uploadRunContent, MapNames[demo.mapName]); 200 const [success, response] = await API.post_record(
201 token,
202 uploadRunContent,
203 MapNames[demo.mapName]
204 );
170 messageLoadClose(); 205 messageLoadClose();
171 await message("Upload Record", response); 206 await message("Upload Record", response);
172 if (success) { 207 if (success) {
@@ -196,84 +231,191 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
196 {MessageDialogLoadComponent} 231 {MessageDialogLoadComponent}
197 {ConfirmDialogComponent} 232 {ConfirmDialogComponent}
198 233
199 <div id='upload-run-menu'> 234 <div id="upload-run-menu">
200 <div id='upload-run-menu-add'> 235 <div id="upload-run-menu-add">
201 <div id='upload-run-route-category'> 236 <div id="upload-run-route-category">
202 <div style={{ padding: "15px 0px" }} className='upload-run-dropdown-container upload-run-item'> 237 <div
238 style={{ padding: "15px 0px" }}
239 className="upload-run-dropdown-container upload-run-item"
240 >
203 <h3 style={{ margin: "0px 0px" }}>Select Game</h3> 241 <h3 style={{ margin: "0px 0px" }}>Select Game</h3>
204 <div onClick={() => _handle_dropdowns(1)} style={{ display: "flex", alignItems: "center", cursor: "pointer", justifyContent: "space-between", margin: "10px 0px" }}> 242 <div
205 <div className='dropdown-cur'>{selectedGameName}</div> 243 onClick={() => _handle_dropdowns(1)}
206 <i style={{ rotate: "-90deg", transform: "translate(-5px, 10px)" }} className="triangle"></i> 244 style={{
245 display: "flex",
246 alignItems: "center",
247 cursor: "pointer",
248 justifyContent: "space-between",
249 margin: "10px 0px",
250 }}
251 >
252 <div className="dropdown-cur">{selectedGameName}</div>
253 <i
254 style={{
255 rotate: "-90deg",
256 transform: "translate(-5px, 10px)",
257 }}
258 className="triangle"
259 ></i>
207 </div> 260 </div>
208 <div style={{ top: "110px" }} className={dropdown1Vis ? "upload-run-dropdown" : "upload-run-dropdown hidden"}> 261 <div
209 {games.map((game) => ( 262 style={{ top: "110px" }}
210 <div onClick={() => { _handle_game_select(game.id.toString(), game.name); _handle_dropdowns(1) }} key={game.id}>{game.name}</div> 263 className={
264 dropdown1Vis
265 ? "upload-run-dropdown"
266 : "upload-run-dropdown hidden"
267 }
268 >
269 {games.map(game => (
270 <div
271 onClick={() => {
272 _handle_game_select(game.id.toString(), game.name);
273 _handle_dropdowns(1);
274 }}
275 key={game.id}
276 >
277 {game.name}
278 </div>
211 ))} 279 ))}
212 </div> 280 </div>
213 </div> 281 </div>
214 282
215 { 283 {!loading && (
216 !loading && 284 <>
217 ( 285 <div>
218 <> 286 <h3 style={{ margin: "10px 0px" }}>Host Demo</h3>
219 287 <div
220 <div> 288 onClick={() => {
221 <h3 style={{ margin: "10px 0px" }}>Host Demo</h3> 289 _handle_file_click(true);
222 <div onClick={() => { _handle_file_click(true) }} onDragOver={(e) => { _handle_drag_over(e, true) }} onDrop={(e) => { _handle_drop(e, true) }} onDragLeave={(e) => { _handle_drag_leave(e, true) }} className={`upload-run-drag-area ${dragHightlight ? "upload-run-drag-area-highlight" : ""} ${uploadRunContent.host_demo ? "upload-run-drag-area-hidden" : ""}`}> 290 }}
223 <input ref={fileInputRef} type="file" name="host_demo" id="host_demo" accept=".dem" onChange={(e) => _handle_file_change(e.target.files, true)} /> 291 onDragOver={e => {
224 {!uploadRunContent.host_demo ? 292 _handle_drag_over(e, true);
293 }}
294 onDrop={e => {
295 _handle_drop(e, true);
296 }}
297 onDragLeave={e => {
298 _handle_drag_leave(e, true);
299 }}
300 className={`upload-run-drag-area ${dragHightlight ? "upload-run-drag-area-highlight" : ""} ${uploadRunContent.host_demo ? "upload-run-drag-area-hidden" : ""}`}
301 >
302 <input
303 ref={fileInputRef}
304 type="file"
305 name="host_demo"
306 id="host_demo"
307 accept=".dem"
308 onChange={e =>
309 _handle_file_change(e.target.files, true)
310 }
311 />
312 {!uploadRunContent.host_demo ? (
313 <div>
314 <span>Drag and drop</span>
225 <div> 315 <div>
226 <span>Drag and drop</span> 316 <span
227 <div> 317 style={{
228 <span style={{ fontFamily: "BarlowSemiCondensed-Regular" }}>Or click here</span><br /> 318 fontFamily: "BarlowSemiCondensed-Regular",
229 <button style={{ borderRadius: "24px", padding: "5px 8px", margin: "5px 0px" }}>Upload</button> 319 }}
230 </div> 320 >
321 Or click here
322 </span>
323 <br />
324 <button
325 style={{
326 borderRadius: "24px",
327 padding: "5px 8px",
328 margin: "5px 0px",
329 }}
330 >
331 Upload
332 </button>
231 </div> 333 </div>
232 : null} 334 </div>
233 335 ) : null}
234 <span className="upload-run-demo-name">{uploadRunContent.host_demo?.name}</span>
235 </div>
236 {
237 games[selectedGameID].is_coop &&
238 (
239 <>
240 <div>
241 <h3 style={{ margin: "10px 0px" }}>Partner Demo</h3>
242 <div onClick={() => { _handle_file_click(false) }} onDragOver={(e) => { _handle_drag_over(e, false) }} onDrop={(e) => { _handle_drop(e, false) }} onDragLeave={(e) => { _handle_drag_leave(e, false) }} className={`upload-run-drag-area ${dragHightlightPartner ? "upload-run-drag-area-highlight-partner" : ""} ${uploadRunContent.partner_demo ? "upload-run-drag-area-hidden" : ""}`}>
243 <input ref={fileInputRefPartner} type="file" name="partner_demo" id="partner_demo" accept=".dem" onChange={(e) => _handle_file_change(e.target.files, false)} /> {!uploadRunContent.partner_demo ?
244 <div>
245 <span>Drag and drop</span>
246 <div>
247 <span style={{ fontFamily: "BarlowSemiCondensed-Regular" }}>Or click here</span><br />
248 <button style={{ borderRadius: "24px", padding: "5px 8px", margin: "5px 0px" }}>Upload</button>
249 </div>
250 </div>
251 : null}
252
253 <span className="upload-run-demo-name">{uploadRunContent.partner_demo?.name}</span>
254 </div>
255 </div>
256 </>
257 )
258 }
259 </div>
260 <div className='search-container'>
261 336
337 <span className="upload-run-demo-name">
338 {uploadRunContent.host_demo?.name}
339 </span>
262 </div> 340 </div>
263 341 {games[selectedGameID].is_coop && (
264 </> 342 <>
265 ) 343 <div>
266 } 344 <h3 style={{ margin: "10px 0px" }}>Partner Demo</h3>
345 <div
346 onClick={() => {
347 _handle_file_click(false);
348 }}
349 onDragOver={e => {
350 _handle_drag_over(e, false);
351 }}
352 onDrop={e => {
353 _handle_drop(e, false);
354 }}
355 onDragLeave={e => {
356 _handle_drag_leave(e, false);
357 }}
358 className={`upload-run-drag-area ${dragHightlightPartner ? "upload-run-drag-area-highlight-partner" : ""} ${uploadRunContent.partner_demo ? "upload-run-drag-area-hidden" : ""}`}
359 >
360 <input
361 ref={fileInputRefPartner}
362 type="file"
363 name="partner_demo"
364 id="partner_demo"
365 accept=".dem"
366 onChange={e =>
367 _handle_file_change(e.target.files, false)
368 }
369 />{" "}
370 {!uploadRunContent.partner_demo ? (
371 <div>
372 <span>Drag and drop</span>
373 <div>
374 <span
375 style={{
376 fontFamily: "BarlowSemiCondensed-Regular",
377 }}
378 >
379 Or click here
380 </span>
381 <br />
382 <button
383 style={{
384 borderRadius: "24px",
385 padding: "5px 8px",
386 margin: "5px 0px",
387 }}
388 >
389 Upload
390 </button>
391 </div>
392 </div>
393 ) : null}
394 <span className="upload-run-demo-name">
395 {uploadRunContent.partner_demo?.name}
396 </span>
397 </div>
398 </div>
399 </>
400 )}
401 </div>
402 <div className="search-container"></div>
403 </>
404 )}
267 </div> 405 </div>
268 <div className='upload-run-buttons-container'> 406 <div className="upload-run-buttons-container">
269 <button onClick={_upload_run}>Submit</button> 407 <button onClick={_upload_run}>Submit</button>
270 <button onClick={() => { 408 <button
271 onClose(false); 409 onClick={() => {
272 setUploadRunContent({ 410 onClose(false);
273 host_demo: null, 411 setUploadRunContent({
274 partner_demo: null, 412 host_demo: null,
275 }); 413 partner_demo: null,
276 }}>Cancel</button> 414 });
415 }}
416 >
417 Cancel
418 </button>
277 </div> 419 </div>
278 </div> 420 </div>
279 </div> 421 </div>
@@ -281,10 +423,7 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
281 ); 423 );
282 } 424 }
283 425
284 return ( 426 return <></>;
285 <></>
286 );
287
288}; 427};
289 428
290export default UploadRunDialog; 429export default UploadRunDialog;