aboutsummaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
authorFifthWit <fifthwitbusiness@gmail.com>2025-01-30 10:41:25 -0600
committerFifthWit <fifthwitbusiness@gmail.com>2025-01-30 10:41:25 -0600
commit66b46f2f372594e08e3675478455157d8553fc73 (patch)
tree5bc6e31269106a9e0c1d5e0efd39301939658d35 /frontend
parentmissing useEffect dependancies (diff)
downloadlphub-66b46f2f372594e08e3675478455157d8553fc73.tar.gz
lphub-66b46f2f372594e08e3675478455157d8553fc73.tar.bz2
lphub-66b46f2f372594e08e3675478455157d8553fc73.zip
missing useEffect dependancies
Diffstat (limited to 'frontend')
-rw-r--r--frontend/src/components/ModMenu.tsx466
1 files changed, 313 insertions, 153 deletions
diff --git a/frontend/src/components/ModMenu.tsx b/frontend/src/components/ModMenu.tsx
index 925b8a8..f765cd8 100644
--- a/frontend/src/components/ModMenu.tsx
+++ b/frontend/src/components/ModMenu.tsx
@@ -1,12 +1,12 @@
1import React from 'react'; 1import React from "react";
2import ReactMarkdown from 'react-markdown'; 2import ReactMarkdown from "react-markdown";
3import { useNavigate } from 'react-router-dom'; 3import { useNavigate } from "react-router-dom";
4 4
5import { MapSummary } from '@customTypes/Map'; 5import { MapSummary } from "@customTypes/Map";
6import { ModMenuContent } from '@customTypes/Content'; 6import { ModMenuContent } from "@customTypes/Content";
7import { API } from '@api/Api'; 7import { API } from "@api/Api";
8import "@css/ModMenu.css" 8import "@css/ModMenu.css";
9import useConfirm from '@hooks/UseConfirm'; 9import useConfirm from "@hooks/UseConfirm";
10 10
11interface ModMenuProps { 11interface ModMenuProps {
12 token?: string; 12 token?: string;
@@ -15,8 +15,12 @@ interface ModMenuProps {
15 mapID: string; 15 mapID: string;
16} 16}
17 17
18const ModMenu: React.FC<ModMenuProps> = ({ token, data, selectedRun, mapID }) => { 18const ModMenu: React.FC<ModMenuProps> = ({
19 19 token,
20 data,
21 selectedRun,
22 mapID,
23}) => {
20 const { confirm, ConfirmDialogComponent } = useConfirm(); 24 const { confirm, ConfirmDialogComponent } = useConfirm();
21 25
22 const [menu, setMenu] = React.useState<number>(0); 26 const [menu, setMenu] = React.useState<number>(0);
@@ -40,7 +44,7 @@ const ModMenu: React.FC<ModMenuProps> = ({ token, data, selectedRun, mapID }) =>
40 function compressImage(file: File): Promise<string> { 44 function compressImage(file: File): Promise<string> {
41 const reader = new FileReader(); 45 const reader = new FileReader();
42 reader.readAsDataURL(file); 46 reader.readAsDataURL(file);
43 return new Promise(resolve => { 47 return new Promise((resolve) => {
44 reader.onload = () => { 48 reader.onload = () => {
45 const img = new Image(); 49 const img = new Image();
46 if (typeof reader.result === "string") { 50 if (typeof reader.result === "string") {
@@ -55,74 +59,99 @@ const ModMenu: React.FC<ModMenuProps> = ({ token, data, selectedRun, mapID }) =>
55 width *= 320 / height; 59 width *= 320 / height;
56 height = 320; 60 height = 320;
57 } 61 }
58 const canvas = document.createElement('canvas'); 62 const canvas = document.createElement("canvas");
59 canvas.width = width; 63 canvas.width = width;
60 canvas.height = height; 64 canvas.height = height;
61 canvas.getContext('2d')!.drawImage(img, 0, 0, width, height); 65 canvas.getContext("2d")!.drawImage(img, 0, 0, width, height);
62 resolve(canvas.toDataURL(file.type, 0.6)); 66 resolve(canvas.toDataURL(file.type, 0.6));
63 }; 67 };
64 } 68 }
65 }; 69 };
66 }); 70 });
67 }; 71 }
68 72
69 const _edit_map_summary_image = async () => { 73 const _edit_map_summary_image = async () => {
70 if (await confirm("Edit Map Summary Image", "Are you sure you want to submit this to the database?")) { 74 if (
75 await confirm(
76 "Edit Map Summary Image",
77 "Are you sure you want to submit this to the database?"
78 )
79 ) {
71 if (token) { 80 if (token) {
72 const success = await API.put_map_image(token, mapID, image); 81 const success = await API.put_map_image(token, mapID, image);
73 if (success) { 82 if (success) {
74 navigate(0); 83 navigate(0);
75 } else { 84 } else {
76 alert("Error. Check logs.") 85 alert("Error. Check logs.");
77 } 86 }
78 } 87 }
79 } 88 }
80 }; 89 };
81 90
82 const _edit_map_summary_route = async () => { 91 const _edit_map_summary_route = async () => {
83 if (await confirm("Edit Map Summary Route", "Are you sure you want to submit this to the database?")) { 92 if (
93 await confirm(
94 "Edit Map Summary Route",
95 "Are you sure you want to submit this to the database?"
96 )
97 ) {
84 if (token) { 98 if (token) {
85 routeContent.date += "T00:00:00Z"; 99 routeContent.date += "T00:00:00Z";
86 const success = await API.put_map_summary(token, mapID, routeContent); 100 const success = await API.put_map_summary(token, mapID, routeContent);
87 if (success) { 101 if (success) {
88 navigate(0); 102 navigate(0);
89 } else { 103 } else {
90 alert("Error. Check logs.") 104 alert("Error. Check logs.");
91 } 105 }
92 } 106 }
93 } 107 }
94 }; 108 };
95 109
96 const _create_map_summary_route = async () => { 110 const _create_map_summary_route = async () => {
97 if (await confirm("Create Map Summary Route", "Are you sure you want to submit this to the database?")) { 111 if (
112 await confirm(
113 "Create Map Summary Route",
114 "Are you sure you want to submit this to the database?"
115 )
116 ) {
98 if (token) { 117 if (token) {
99 routeContent.date += "T00:00:00Z"; 118 routeContent.date += "T00:00:00Z";
100 const success = await API.post_map_summary(token, mapID, routeContent); 119 const success = await API.post_map_summary(token, mapID, routeContent);
101 if (success) { 120 if (success) {
102 navigate(0); 121 navigate(0);
103 } else { 122 } else {
104 alert("Error. Check logs.") 123 alert("Error. Check logs.");
105 } 124 }
106 } 125 }
107 } 126 }
108 }; 127 };
109 128
110 const _delete_map_summary_route = async () => { 129 const _delete_map_summary_route = async () => {
111 if (await confirm("Delete Map Summary Route", `Are you sure you want to submit this to the database?\n 130 if (
112 ${data.summary.routes[selectedRun].category.name}\n${data.summary.routes[selectedRun].history.score_count} portals\n${data.summary.routes[selectedRun].history.runner_name}`)) { 131 await confirm(
132 "Delete Map Summary Route",
133 `Are you sure you want to submit this to the database?\n
134 ${data.summary.routes[selectedRun].category.name}\n${data.summary.routes[selectedRun].history.score_count} portals\n${data.summary.routes[selectedRun].history.runner_name}`
135 )
136 ) {
113 if (token) { 137 if (token) {
114 const success = await API.delete_map_summary(token, mapID, data.summary.routes[selectedRun].route_id); 138 const success = await API.delete_map_summary(
139 token,
140 mapID,
141 data.summary.routes[selectedRun].route_id
142 );
115 if (success) { 143 if (success) {
116 navigate(0); 144 navigate(0);
117 } else { 145 } else {
118 alert("Error. Check logs.") 146 alert("Error. Check logs.");
119 } 147 }
120 } 148 }
121 } 149 }
122 }; 150 };
123 151
124 React.useEffect(() => { 152 React.useEffect(() => {
125 if (menu === 3) { // add route 153 if (menu === 3) {
154 // add route
126 setRouteContent({ 155 setRouteContent({
127 id: 0, 156 id: 0,
128 name: "", 157 name: "",
@@ -134,7 +163,8 @@ const ModMenu: React.FC<ModMenuProps> = ({ token, data, selectedRun, mapID }) =>
134 }); 163 });
135 setMd("No description available."); 164 setMd("No description available.");
136 } 165 }
137 if (menu === 2) { // edit route 166 if (menu === 2) {
167 // edit route
138 setRouteContent({ 168 setRouteContent({
139 id: data.summary.routes[selectedRun].route_id, 169 id: data.summary.routes[selectedRun].route_id,
140 name: data.summary.routes[selectedRun].history.runner_name, 170 name: data.summary.routes[selectedRun].history.runner_name,
@@ -146,207 +176,337 @@ const ModMenu: React.FC<ModMenuProps> = ({ token, data, selectedRun, mapID }) =>
146 }); 176 });
147 setMd(data.summary.routes[selectedRun].description); 177 setMd(data.summary.routes[selectedRun].description);
148 } 178 }
149 }, [menu]); 179 }, [menu, data.summary.routes, selectedRun]);
150 180
151 React.useEffect(() => { 181 React.useEffect(() => {
152 const modview = document.querySelector("div#modview") as HTMLElement 182 const modview = document.querySelector("div#modview") as HTMLElement;
153 if (modview) { 183 if (modview) {
154 showButton ? modview.style.transform = "translateY(-68%)" 184 showButton
155 : modview.style.transform = "translateY(0%)" 185 ? (modview.style.transform = "translateY(-68%)")
186 : (modview.style.transform = "translateY(0%)");
156 } 187 }
157 188
158 const modview_block = document.querySelector("#modview_block") as HTMLElement 189 const modview_block = document.querySelector(
190 "#modview_block"
191 ) as HTMLElement;
159 if (modview_block) { 192 if (modview_block) {
160 showButton ? modview_block.style.display = "none" : modview_block.style.display = "block" 193 showButton
194 ? (modview_block.style.display = "none")
195 : (modview_block.style.display = "block");
161 } 196 }
162 }, [showButton]) 197 }, [showButton]);
163 198
164 return ( 199 return (
165 <> 200 <>
166 {ConfirmDialogComponent} 201 {ConfirmDialogComponent}
167 <div id="modview_block" /> 202 <div id="modview_block" />
168 <div id='modview'> 203 <div id="modview">
169 <div> 204 <div>
170 <button onClick={() => setMenu(1)}>Edit Image</button> 205 <button onClick={() => setMenu(1)}>Edit Image</button>
171 <button onClick={() => setMenu(2)}>Edit Selected Route</button> 206 <button onClick={() => setMenu(2)}>Edit Selected Route</button>
172 <button onClick={() => setMenu(3)}>Add New Route</button> 207 <button onClick={() => setMenu(3)}>Add New Route</button>
173 <button onClick={() => _delete_map_summary_route()}>Delete Selected Route</button> 208 <button onClick={() => _delete_map_summary_route()}>
209 Delete Selected Route
210 </button>
174 </div> 211 </div>
175 <div> 212 <div>
176 {showButton ? ( 213 {showButton ? (
177 <button onClick={() => setShowButton(false)}>Show</button> 214 <button onClick={() => setShowButton(false)}>Show</button>
178 ) : ( 215 ) : (
179 <button onClick={() => { setShowButton(true); setMenu(0); }}>Hide</button> 216 <button
217 onClick={() => {
218 setShowButton(true);
219 setMenu(0);
220 }}
221 >
222 Hide
223 </button>
180 )} 224 )}
181 </div> 225 </div>
182 </div><div id='modview-menu'> 226 </div>
183 {// Edit Image 227 <div id="modview-menu">
228 {
229 // Edit Image
184 menu === 1 && ( 230 menu === 1 && (
185 <div id='modview-menu-image'> 231 <div id="modview-menu-image">
186 <div> 232 <div>
187 <span>Current Image:</span> 233 <span>Current Image:</span>
188 <img src={data.map.image} alt="missing" /> 234 <img src={data.map.image} alt="missing" />
189 </div> 235 </div>
190 236
191 <div> 237 <div>
192 <span>New Image: 238 <span>
193 <input type="file" accept='image/*' onChange={e => { 239 New Image:
194 if (e.target.files) { 240 <input
195 compressImage(e.target.files[0]) 241 type="file"
196 .then(d => setImage(d)); 242 accept="image/*"
197 } 243 onChange={(e) => {
198 }} /></span> 244 if (e.target.files) {
199 {image ? (<button onClick={() => _edit_map_summary_image()}>upload</button>) : <span></span>} 245 compressImage(e.target.files[0]).then((d) =>
200 <img src={image} alt="" id='modview-menu-image-file' /> 246 setImage(d)
201 247 );
248 }
249 }}
250 />
251 </span>
252 {image ? (
253 <button onClick={() => _edit_map_summary_image()}>
254 upload
255 </button>
256 ) : (
257 <span></span>
258 )}
259 <img src={image} alt="" id="modview-menu-image-file" />
202 </div> 260 </div>
203 </div> 261 </div>
204 )} 262 )
263 }
205 264
206 {// Edit Route 265 {
266 // Edit Route
207 menu === 2 && ( 267 menu === 2 && (
208 <div id='modview-menu-edit'> 268 <div id="modview-menu-edit">
209 <div id='modview-route-id'> 269 <div id="modview-route-id">
210 <span>Route ID:</span> 270 <span>Route ID:</span>
211 <input type="number" value={routeContent.id} disabled /> 271 <input type="number" value={routeContent.id} disabled />
212 </div> 272 </div>
213 <div id='modview-route-name'> 273 <div id="modview-route-name">
214 <span>Runner Name:</span> 274 <span>Runner Name:</span>
215 <input type="text" value={routeContent.name} onChange={(e) => { 275 <input
216 setRouteContent({ 276 type="text"
217 ...routeContent, 277 value={routeContent.name}
218 name: e.target.value, 278 onChange={(e) => {
219 }); 279 setRouteContent({
220 }} /> 280 ...routeContent,
281 name: e.target.value,
282 });
283 }}
284 />
221 </div> 285 </div>
222 <div id='modview-route-score'> 286 <div id="modview-route-score">
223 <span>Score:</span> 287 <span>Score:</span>
224 <input type="number" value={routeContent.score} onChange={(e) => { 288 <input
225 setRouteContent({ 289 type="number"
226 ...routeContent, 290 value={routeContent.score}
227 score: parseInt(e.target.value), 291 onChange={(e) => {
228 }); 292 setRouteContent({
229 }} /> 293 ...routeContent,
294 score: parseInt(e.target.value),
295 });
296 }}
297 />
230 </div> 298 </div>
231 <div id='modview-route-date'> 299 <div id="modview-route-date">
232 <span>Date:</span> 300 <span>Date:</span>
233 <input type="date" value={routeContent.date} onChange={(e) => { 301 <input
234 setRouteContent({ 302 type="date"
235 ...routeContent, 303 value={routeContent.date}
236 date: e.target.value, 304 onChange={(e) => {
237 }); 305 setRouteContent({
238 }} /> 306 ...routeContent,
307 date: e.target.value,
308 });
309 }}
310 />
239 </div> 311 </div>
240 <div id='modview-route-showcase'> 312 <div id="modview-route-showcase">
241 <span>Showcase Video:</span> 313 <span>Showcase Video:</span>
242 <input type="text" value={routeContent.showcase} onChange={(e) => { 314 <input
243 setRouteContent({ 315 type="text"
244 ...routeContent, 316 value={routeContent.showcase}
245 showcase: e.target.value, 317 onChange={(e) => {
246 }); 318 setRouteContent({
247 }} /> 319 ...routeContent,
320 showcase: e.target.value,
321 });
322 }}
323 />
248 </div> 324 </div>
249 <div id='modview-route-description' style={{ height: "180px", gridColumn: "1 / span 5" }}> 325 <div
326 id="modview-route-description"
327 style={{ height: "180px", gridColumn: "1 / span 5" }}
328 >
250 <span>Description:</span> 329 <span>Description:</span>
251 <textarea value={routeContent.description} onChange={(e) => { 330 <textarea
252 setRouteContent({ 331 value={routeContent.description}
253 ...routeContent, 332 onChange={(e) => {
254 description: e.target.value, 333 setRouteContent({
255 }); 334 ...routeContent,
256 setMd(routeContent.description); 335 description: e.target.value,
257 }} /> 336 });
337 setMd(routeContent.description);
338 }}
339 />
258 </div> 340 </div>
259 <button style={{ gridColumn: "2 / span 3", height: "40px" }} onClick={_edit_map_summary_route}>Apply</button> 341 <button
342 style={{ gridColumn: "2 / span 3", height: "40px" }}
343 onClick={_edit_map_summary_route}
344 >
345 Apply
346 </button>
260 347
261 <div id='modview-md'> 348 <div id="modview-md">
262 <span>Markdown Preview</span> 349 <span>Markdown Preview</span>
263 <span><a href="https://commonmark.org/help/" rel="noreferrer" target='_blank'>Documentation</a></span> 350 <span>
264 <span><a href="https://remarkjs.github.io/react-markdown/" rel="noreferrer" target='_blank'>Demo</a></span> 351 <a
352 href="https://commonmark.org/help/"
353 rel="noreferrer"
354 target="_blank"
355 >
356 Documentation
357 </a>
358 </span>
359 <span>
360 <a
361 href="https://remarkjs.github.io/react-markdown/"
362 rel="noreferrer"
363 target="_blank"
364 >
365 Demo
366 </a>
367 </span>
265 <p> 368 <p>
266 <ReactMarkdown>{md} 369 <ReactMarkdown>{md}</ReactMarkdown>
267 </ReactMarkdown>
268 </p> 370 </p>
269 </div> 371 </div>
270 </div> 372 </div>
271 )} 373 )
374 }
272 375
273 {// Add Route 376 {
377 // Add Route
274 menu === 3 && ( 378 menu === 3 && (
275 <div id='modview-menu-add'> 379 <div id="modview-menu-add">
276 <div id='modview-route-category'> 380 <div id="modview-route-category">
277 <span>Category:</span> 381 <span>Category:</span>
278 <select onChange={(e) => { 382 <select
279 setRouteContent({ 383 onChange={(e) => {
280 ...routeContent, 384 setRouteContent({
281 category_id: parseInt(e.target.value), 385 ...routeContent,
282 }); 386 category_id: parseInt(e.target.value),
283 }}> 387 });
284 <option value="1" key="1">CM</option> 388 }}
285 <option value="2" key="2">No SLA</option> 389 >
286 {data.map.game_name === "Portal 2 - Cooperative" ? "" : ( 390 <option value="1" key="1">
287 <option value="3" key="3">Inbounds SLA</option>)} 391 CM
288 <option value="4" key="4">Any%</option> 392 </option>
393 <option value="2" key="2">
394 No SLA
395 </option>
396 {data.map.game_name === "Portal 2 - Cooperative" ? (
397 ""
398 ) : (
399 <option value="3" key="3">
400 Inbounds SLA
401 </option>
402 )}
403 <option value="4" key="4">
404 Any%
405 </option>
289 </select> 406 </select>
290 </div> 407 </div>
291 <div id='modview-route-name'> 408 <div id="modview-route-name">
292 <span>Runner Name:</span> 409 <span>Runner Name:</span>
293 <input type="text" value={routeContent.name} onChange={(e) => { 410 <input
294 setRouteContent({ 411 type="text"
295 ...routeContent, 412 value={routeContent.name}
296 name: e.target.value, 413 onChange={(e) => {
297 }); 414 setRouteContent({
298 }} /> 415 ...routeContent,
416 name: e.target.value,
417 });
418 }}
419 />
299 </div> 420 </div>
300 <div id='modview-route-score'> 421 <div id="modview-route-score">
301 <span>Score:</span> 422 <span>Score:</span>
302 <input type="number" value={routeContent.score} onChange={(e) => { 423 <input
303 setRouteContent({ 424 type="number"
304 ...routeContent, 425 value={routeContent.score}
305 score: parseInt(e.target.value), 426 onChange={(e) => {
306 }); 427 setRouteContent({
307 }} /> 428 ...routeContent,
429 score: parseInt(e.target.value),
430 });
431 }}
432 />
308 </div> 433 </div>
309 <div id='modview-route-date'> 434 <div id="modview-route-date">
310 <span>Date:</span> 435 <span>Date:</span>
311 <input type="date" value={routeContent.date} onChange={(e) => { 436 <input
312 setRouteContent({ 437 type="date"
313 ...routeContent, 438 value={routeContent.date}
314 date: e.target.value, 439 onChange={(e) => {
315 }); 440 setRouteContent({
316 }} /> 441 ...routeContent,
442 date: e.target.value,
443 });
444 }}
445 />
317 </div> 446 </div>
318 <div id='modview-route-showcase'> 447 <div id="modview-route-showcase">
319 <span>Showcase Video:</span> 448 <span>Showcase Video:</span>
320 <input type="text" value={routeContent.showcase} onChange={(e) => { 449 <input
321 setRouteContent({ 450 type="text"
322 ...routeContent, 451 value={routeContent.showcase}
323 showcase: e.target.value, 452 onChange={(e) => {
324 }); 453 setRouteContent({
325 }} /> 454 ...routeContent,
455 showcase: e.target.value,
456 });
457 }}
458 />
326 </div> 459 </div>
327 <div id='modview-route-description' style={{ height: "180px", gridColumn: "1 / span 5" }}> 460 <div
461 id="modview-route-description"
462 style={{ height: "180px", gridColumn: "1 / span 5" }}
463 >
328 <span>Description:</span> 464 <span>Description:</span>
329 <textarea value={routeContent.description} onChange={(e) => { 465 <textarea
330 setRouteContent({ 466 value={routeContent.description}
331 ...routeContent, 467 onChange={(e) => {
332 description: e.target.value, 468 setRouteContent({
333 }); 469 ...routeContent,
334 setMd(routeContent.description); 470 description: e.target.value,
335 }} /> 471 });
472 setMd(routeContent.description);
473 }}
474 />
336 </div> 475 </div>
337 <button style={{ gridColumn: "2 / span 3", height: "40px" }} onClick={_create_map_summary_route}>Apply</button> 476 <button
477 style={{ gridColumn: "2 / span 3", height: "40px" }}
478 onClick={_create_map_summary_route}
479 >
480 Apply
481 </button>
338 482
339 <div id='modview-md'> 483 <div id="modview-md">
340 <span>Markdown preview</span> 484 <span>Markdown preview</span>
341 <span><a href="https://commonmark.org/help/" rel="noreferrer" target='_blank'>documentation</a></span> 485 <span>
342 <span><a href="https://remarkjs.github.io/react-markdown/" rel="noreferrer" target='_blank'>demo</a></span> 486 <a
487 href="https://commonmark.org/help/"
488 rel="noreferrer"
489 target="_blank"
490 >
491 documentation
492 </a>
493 </span>
494 <span>
495 <a
496 href="https://remarkjs.github.io/react-markdown/"
497 rel="noreferrer"
498 target="_blank"
499 >
500 demo
501 </a>
502 </span>
343 <p> 503 <p>
344 <ReactMarkdown>{md} 504 <ReactMarkdown>{md}</ReactMarkdown>
345 </ReactMarkdown>
346 </p> 505 </p>
347 </div> 506 </div>
348 </div> 507 </div>
349 )} 508 )
509 }
350 </div> 510 </div>
351 </> 511 </>
352 ); 512 );