aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWolfboy248 <georgejvindkarlsen@gmail.com>2024-10-29 10:09:17 +0100
committerWolfboy248 <georgejvindkarlsen@gmail.com>2024-10-29 10:09:17 +0100
commit35946dbb2feb7f9d1cbab16fb0602ce0303562e9 (patch)
tree3ba530c1b3f5a1742ec41dfee5b74b43353619e2
parentMerge branch 'typescript' of https://github.com/pektezol/LeastPortalsHub into... (diff)
downloadlphub-35946dbb2feb7f9d1cbab16fb0602ce0303562e9.tar.gz
lphub-35946dbb2feb7f9d1cbab16fb0602ce0303562e9.tar.bz2
lphub-35946dbb2feb7f9d1cbab16fb0602ce0303562e9.zip
refactor: upload run dialog, useMessage update, added loader spinner
-rw-r--r--frontend/src/App.css22
-rw-r--r--frontend/src/components/Leaderboards.tsx9
-rw-r--r--frontend/src/components/UploadRunDialog.tsx56
-rw-r--r--frontend/src/css/UploadRunDialog.css10
-rw-r--r--frontend/src/hooks/UseMessage.tsx4
-rw-r--r--frontend/src/pages/Profile.tsx15
-rw-r--r--frontend/src/pages/Rankings.tsx25
7 files changed, 108 insertions, 33 deletions
diff --git a/frontend/src/App.css b/frontend/src/App.css
index b0445d8..14a9972 100644
--- a/frontend/src/App.css
+++ b/frontend/src/App.css
@@ -56,6 +56,26 @@ body {
56 } 56 }
57} 57}
58 58
59.loader {
60 width: 48px;
61 height: 48px;
62 border: 5px solid #FFF;
63 border-bottom-color: transparent;
64 border-radius: 50%;
65 display: inline-block;
66 box-sizing: border-box;
67 animation: rotation 1s linear infinite;
68 }
69
70@keyframes rotation {
71 0% {
72 transform: rotate(0deg);
73 }
74 100% {
75 transform: rotate(360deg);
76 }
77}
78
59@font-face { 79@font-face {
60 font-family: 'BarlowCondensed-Bold'; 80 font-family: 'BarlowCondensed-Bold';
61 src: local('BarlowCondensed-Bold'), url(./fonts/BarlowCondensed-Bold.ttf) format('truetype'); 81 src: local('BarlowCondensed-Bold'), url(./fonts/BarlowCondensed-Bold.ttf) format('truetype');
@@ -74,4 +94,4 @@ body {
74@font-face { 94@font-face {
75 font-family: 'BarlowSemiCondensed-SemiBold'; 95 font-family: 'BarlowSemiCondensed-SemiBold';
76 src: local('BarlowSemiCondensed-Regular'), url(./fonts/BarlowSemiCondensed-SemiBold.ttf) format('truetype'); 96 src: local('BarlowSemiCondensed-Regular'), url(./fonts/BarlowSemiCondensed-SemiBold.ttf) format('truetype');
77} \ No newline at end of file 97}
diff --git a/frontend/src/components/Leaderboards.tsx b/frontend/src/components/Leaderboards.tsx
index f86aa7b..b9e071c 100644
--- a/frontend/src/components/Leaderboards.tsx
+++ b/frontend/src/components/Leaderboards.tsx
@@ -4,6 +4,7 @@ import { Link } from 'react-router-dom';
4import { DownloadIcon, ThreedotIcon } from '../images/Images'; 4import { DownloadIcon, ThreedotIcon } from '../images/Images';
5import { MapLeaderboard } from '../types/Map'; 5import { MapLeaderboard } from '../types/Map';
6import { ticks_to_time, time_ago } from '../utils/Time'; 6import { ticks_to_time, time_ago } from '../utils/Time';
7import useMessage from "../hooks/UseMessage";
7import "../css/Maps.css" 8import "../css/Maps.css"
8 9
9interface LeaderboardsProps { 10interface LeaderboardsProps {
@@ -12,6 +13,7 @@ interface LeaderboardsProps {
12 13
13const Leaderboards: React.FC<LeaderboardsProps> = ({ data }) => { 14const Leaderboards: React.FC<LeaderboardsProps> = ({ data }) => {
14 15
16 const { message, MessageDialogComponent } = useMessage();
15 const [pageNumber, setPageNumber] = React.useState<number>(1); 17 const [pageNumber, setPageNumber] = React.useState<number>(1);
16 18
17 if (!data) { 19 if (!data) {
@@ -31,6 +33,10 @@ const Leaderboards: React.FC<LeaderboardsProps> = ({ data }) => {
31 }; 33 };
32 34
33 return ( 35 return (
36 <div>
37 <div style={{position: "absolute", width: "100%", height: "100%", top: "0px", left: "-350px"}}>
38 {MessageDialogComponent}
39 </div>
34 <section id='section6' className='summary2'> 40 <section id='section6' className='summary2'>
35 41
36 <div id='leaderboard-top' 42 <div id='leaderboard-top'
@@ -94,7 +100,7 @@ const Leaderboards: React.FC<LeaderboardsProps> = ({ data }) => {
94 ) : r.kind === "singleplayer" && ( 100 ) : r.kind === "singleplayer" && (
95 101
96 <span> 102 <span>
97 <button onClick={() => { window.alert(`Demo ID: ${r.demo_id}`) }}><img src={ThreedotIcon} alt="demo_id" /></button> 103 <button onClick={() => { message("Demo information", `Demo ID: ${r.demo_id}`) }}><img src={ThreedotIcon} alt="demo_id" /></button>
98 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${r.demo_id}`}><img src={DownloadIcon} alt="download" /></button> 104 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${r.demo_id}`}><img src={DownloadIcon} alt="download" /></button>
99 </span> 105 </span>
100 )} 106 )}
@@ -102,6 +108,7 @@ const Leaderboards: React.FC<LeaderboardsProps> = ({ data }) => {
102 ))} 108 ))}
103 </div> 109 </div>
104 </section> 110 </section>
111 </div>
105 ); 112 );
106}; 113};
107 114
diff --git a/frontend/src/components/UploadRunDialog.tsx b/frontend/src/components/UploadRunDialog.tsx
index a0d23e7..212a792 100644
--- a/frontend/src/components/UploadRunDialog.tsx
+++ b/frontend/src/components/UploadRunDialog.tsx
@@ -47,22 +47,36 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
47 const [loading, setLoading] = React.useState<boolean>(false); 47 const [loading, setLoading] = React.useState<boolean>(false);
48 48
49 const [dragHightlight, setDragHighlight] = React.useState<boolean>(false); 49 const [dragHightlight, setDragHighlight] = React.useState<boolean>(false);
50 const [dragHightlightPartner, setDragHighlightPartner] = React.useState<boolean>(false);
50 const fileInputRef = React.useRef<HTMLInputElement>(null); 51 const fileInputRef = React.useRef<HTMLInputElement>(null);
51 52 const fileInputRefPartner = React.useRef<HTMLInputElement>(null);
52 const _handle_file_click = () => { 53
53 fileInputRef.current?.click(); 54 const _handle_file_click = (host: boolean) => {
55 if (host) {
56 fileInputRef.current?.click();
57 } else {
58 fileInputRefPartner.current?.click();
59 }
54 } 60 }
55 61
56 const _handle_drag_over = (e: React.DragEvent<HTMLDivElement>) => { 62 const _handle_drag_over = (e: React.DragEvent<HTMLDivElement>, host: boolean) => {
57 e.preventDefault(); 63 e.preventDefault();
58 e.stopPropagation(); 64 e.stopPropagation();
59 setDragHighlight(true); 65 if (host) {
66 setDragHighlight(true);
67 } else {
68 setDragHighlightPartner(true);
69 }
60 } 70 }
61 71
62 const _handle_drag_leave = (e: React.DragEvent<HTMLDivElement>) => { 72 const _handle_drag_leave = (e: React.DragEvent<HTMLDivElement>, host: boolean) => {
63 e.preventDefault(); 73 e.preventDefault();
64 e.stopPropagation(); 74 e.stopPropagation();
65 setDragHighlight(false); 75 if (host) {
76 setDragHighlight(false);
77 } else {
78 setDragHighlightPartner(false);
79 }
66 } 80 }
67 81
68 const _handle_drop = (e: React.DragEvent<HTMLDivElement>, host: boolean) => { 82 const _handle_drop = (e: React.DragEvent<HTMLDivElement>, host: boolean) => {
@@ -70,8 +84,6 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
70 e.stopPropagation(); 84 e.stopPropagation();
71 setDragHighlight(true); 85 setDragHighlight(true);
72 86
73 console.log(e.dataTransfer.files);
74
75 _handle_file_change(e.dataTransfer.files, host); 87 _handle_file_change(e.dataTransfer.files, host);
76 } 88 }
77 89
@@ -102,7 +114,6 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
102 }; 114 };
103 115
104 const _handle_file_change = async (files: FileList | null, host: boolean) => { 116 const _handle_file_change = async (files: FileList | null, host: boolean) => {
105 console.log(files);
106 if (files) { 117 if (files) {
107 if (host) { 118 if (host) {
108 setUploadRunContent({ 119 setUploadRunContent({
@@ -155,13 +166,16 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
155 166
156 const [ success, response ] = await API.post_record(token, uploadRunContent); 167 const [ success, response ] = await API.post_record(token, uploadRunContent);
157 await message("Upload Record", response); 168 await message("Upload Record", response);
158 console.log("weweew")
159 onClose(success); 169 onClose(success);
170 navigate("/profile");
160 } 171 }
161 }; 172 };
162 173
163 React.useEffect(() => { 174 React.useEffect(() => {
164 if (open) { 175 if (open) {
176
177 setDragHighlightPartner(false);
178 setDragHighlight(false);
165 _handle_game_select("1", "Portal 2 - Singleplayer"); // a different approach?. 179 _handle_game_select("1", "Portal 2 - Singleplayer"); // a different approach?.
166 } 180 }
167 }, [open]); 181 }, [open]);
@@ -208,14 +222,14 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
208 </div> 222 </div>
209 </div> 223 </div>
210 <span>Host Demo</span> 224 <span>Host Demo</span>
211 <div onClick={_handle_file_click} onDragOver={(e) => {_handle_drag_over(e)}} onDrop={(e) => {_handle_drop(e, true)}} onDragLeave={(e) => {_handle_drag_leave(e)}} className={`upload-run-drag-area ${dragHightlight ? "upload-run-drag-area-highlight" : ""} ${uploadRunContent.host_demo ? "upload-run-drag-area-hidden" : ""}`}> 225 <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" : ""}`}>
212 <input ref={fileInputRef} type="file" name="host_demo" id="host_demo" accept=".dem" onChange={(e) => _handle_file_change(e.target.files, true)} /> 226 <input ref={fileInputRef} type="file" name="host_demo" id="host_demo" accept=".dem" onChange={(e) => _handle_file_change(e.target.files, true)} />
213 {!uploadRunContent.host_demo ? 227 {!uploadRunContent.host_demo ?
214 <div> 228 <div>
215 <span>Drag and drop</span> 229 <span>Drag and drop</span>
216 <div> 230 <div>
217 <span>Or click here</span> 231 <span style={{fontFamily: "BarlowSemiCondensed-Regular"}}>Or click here</span><br/>
218 <button>Upload</button> 232 <button style={{borderRadius: "24px", padding: "5px 8px", margin: "5px 0px"}}>Upload</button>
219 </div> 233 </div>
220 </div> 234 </div>
221 : null} 235 : null}
@@ -227,7 +241,19 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
227 ( 241 (
228 <> 242 <>
229 <span>Partner Demo</span> 243 <span>Partner Demo</span>
230 <input type="file" name="partner_demo" id="partner_demo" accept=".dem" onChange={(e) => _handle_file_change(e.target.files, false)} /> 244 <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" : ""}`}>
245 <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 ?
246 <div>
247 <span>Drag and drop</span>
248 <div>
249 <span>Or click here</span><br/>
250 <button>Upload</button>
251 </div>
252 </div>
253 : null}
254
255 <span>{uploadRunContent.partner_demo?.name}</span>
256 </div>
231 </> 257 </>
232 ) 258 )
233 } 259 }
diff --git a/frontend/src/css/UploadRunDialog.css b/frontend/src/css/UploadRunDialog.css
index c783d2a..737409e 100644
--- a/frontend/src/css/UploadRunDialog.css
+++ b/frontend/src/css/UploadRunDialog.css
@@ -23,6 +23,7 @@ div#upload-run{
23 outline: 8px solid #2b2e46; 23 outline: 8px solid #2b2e46;
24 border-radius: 20px; 24 border-radius: 20px;
25 font-size: 40px; 25 font-size: 40px;
26 width: 400px;
26} 27}
27 28
28#upload-run-menu-add>div, 29#upload-run-menu-add>div,
@@ -115,7 +116,7 @@ button:hover {
115 width: 100%; 116 width: 100%;
116} 117}
117 118
118#host_demo { 119#host_demo, #partner_demo {
119 background-color: rgba(0, 0, 0, 0); 120 background-color: rgba(0, 0, 0, 0);
120 display: none; 121 display: none;
121} 122}
@@ -123,18 +124,19 @@ button:hover {
123.upload-run-drag-area { 124.upload-run-drag-area {
124 border: 2px dashed grey; 125 border: 2px dashed grey;
125 border-radius: 10px; 126 border-radius: 10px;
126 height: 200px; 127 height: 150px;
127 cursor: pointer; 128 cursor: pointer;
128 width: 300px; 129 width: 360px;
129 transition: all 0.2s ease; 130 transition: all 0.2s ease;
130 text-align: center; 131 text-align: center;
131 display: flex; 132 display: flex;
132 flex-direction: column; 133 flex-direction: column;
133 align-items: center; 134 align-items: center;
134 justify-content: center; 135 justify-content: center;
136 margin: 20px 0px;
135} 137}
136 138
137.upload-run-drag-area-highlight { 139.upload-run-drag-area-highlight, .upload-run-drag-area-highlight-partner {
138 border: 2px dashed white; 140 border: 2px dashed white;
139} 141}
140 142
diff --git a/frontend/src/hooks/UseMessage.tsx b/frontend/src/hooks/UseMessage.tsx
index 602cf65..9bfb713 100644
--- a/frontend/src/hooks/UseMessage.tsx
+++ b/frontend/src/hooks/UseMessage.tsx
@@ -29,6 +29,10 @@ const useMessage = () => {
29 <MessageDialog title={title} subtitle={subtitle} onClose={handleClose}></MessageDialog> 29 <MessageDialog title={title} subtitle={subtitle} onClose={handleClose}></MessageDialog>
30 ); 30 );
31 31
32 const getDialogComponent = () => {
33 return MessageDialogComponent;
34 };
35
32 return { message, MessageDialogComponent }; 36 return { message, MessageDialogComponent };
33} 37}
34 38
diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx
index 5d1c75d..3dba3ae 100644
--- a/frontend/src/pages/Profile.tsx
+++ b/frontend/src/pages/Profile.tsx
@@ -103,10 +103,12 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRec
103 }; 103 };
104 104
105 return ( 105 return (
106 <main> 106 <div>
107 {ConfirmDialogComponent} 107 {MessageDialogComponent}
108 {MessageDialogComponent} 108 {ConfirmDialogComponent}
109 <section id='section1' className='profile'> 109
110 <main>
111 <section id='section1' className='profile'>
110 112
111 {profile.profile 113 {profile.profile
112 ? ( 114 ? (
@@ -266,7 +268,7 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRec
266 <span>{e.date.split("T")[0]}</span> 268 <span>{e.date.split("T")[0]}</span>
267 <span style={{ flexDirection: "row-reverse" }}> 269 <span style={{ flexDirection: "row-reverse" }}>
268 270
269 <button style={{ marginRight: "10px" }} onClick={() => { window.alert(`Demo ID: ${e.demo_id}`) }}><img src={ThreedotIcon} alt="demo_id" /></button> 271 <button style={{ marginRight: "10px" }} onClick={() => { message("Demo information", `Demo ID: ${e.demo_id}`) }}><img src={ThreedotIcon} alt="demo_id" /></button>
270 <button onClick={() => { _delete_submission(r.map_id, e.record_id) }}><img src={DeleteIcon}></img></button> 272 <button onClick={() => { _delete_submission(r.map_id, e.record_id) }}><img src={DeleteIcon}></img></button>
271 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${e.demo_id}`}><img src={DownloadIcon} alt="download" /></button> 273 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${e.demo_id}`}><img src={DownloadIcon} alt="download" /></button>
272 {i === 0 && r.scores.length > 1 ? <button onClick={() => { 274 {i === 0 && r.scores.length > 1 ? <button onClick={() => {
@@ -312,7 +314,7 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRec
312 <span>{record!.scores[i].date.split("T")[0]}</span> 314 <span>{record!.scores[i].date.split("T")[0]}</span>
313 <span style={{ flexDirection: "row-reverse" }}> 315 <span style={{ flexDirection: "row-reverse" }}>
314 316
315 <button onClick={() => { window.alert(`Demo ID: ${e.demo_id}`) }}><img src={ThreedotIcon} alt="demo_id" /></button> 317 <button onClick={() => { message("Demo information", `Demo ID: ${e.demo_id}`) }}><img src={ThreedotIcon} alt="demo_id" /></button>
316 <button onClick={() => { _delete_submission(r.id, e.record_id) }}><img src={DeleteIcon}></img></button> 318 <button onClick={() => { _delete_submission(r.id, e.record_id) }}><img src={DeleteIcon}></img></button>
317 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${e.demo_id}`}><img src={DownloadIcon} alt="download" /></button> 319 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${e.demo_id}`}><img src={DownloadIcon} alt="download" /></button>
318 {i === 0 && record!.scores.length > 1 ? <button onClick={() => { 320 {i === 0 && record!.scores.length > 1 ? <button onClick={() => {
@@ -333,6 +335,7 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRec
333 </div> 335 </div>
334 </section> 336 </section>
335 </main> 337 </main>
338 </div>
336 ); 339 );
337}; 340};
338 341
diff --git a/frontend/src/pages/Rankings.tsx b/frontend/src/pages/Rankings.tsx
index 9280b02..1830815 100644
--- a/frontend/src/pages/Rankings.tsx
+++ b/frontend/src/pages/Rankings.tsx
@@ -13,6 +13,9 @@ const Rankings: React.FC = () => {
13 official, 13 official,
14 unofficial 14 unofficial
15 } 15 }
16 const [currentRankingType, setCurrentRankingType] = React.useState<LeaderboardTypes>(LeaderboardTypes.official);
17
18 const [leaderboardLoad, setLeaderboardLoad] = React.useState<boolean>(false);
16 19
17 enum RankingCategories { 20 enum RankingCategories {
18 rankings_overall, 21 rankings_overall,
@@ -23,6 +26,7 @@ const Rankings: React.FC = () => {
23 const [load, setLoad] = React.useState<boolean>(false); 26 const [load, setLoad] = React.useState<boolean>(false);
24 27
25 const _fetch_rankings = async () => { 28 const _fetch_rankings = async () => {
29 setLeaderboardLoad(false);
26 const rankings = await API.get_official_rankings(); 30 const rankings = await API.get_official_rankings();
27 setLeaderboardData(rankings); 31 setLeaderboardData(rankings);
28 if (currentLeaderboardType == RankingCategories.rankings_singleplayer) { 32 if (currentLeaderboardType == RankingCategories.rankings_singleplayer) {
@@ -33,10 +37,12 @@ const Rankings: React.FC = () => {
33 setCurrentLeaderboard(rankings.rankings_overall) 37 setCurrentLeaderboard(rankings.rankings_overall)
34 } 38 }
35 setLoad(true); 39 setLoad(true);
40 setLeaderboardLoad(true);
36 } 41 }
37 42
38 const __dev_fetch_unofficial_rankings = async () => { 43 const __dev_fetch_unofficial_rankings = async () => {
39 try { 44 try {
45 setLeaderboardLoad(false);
40 const rankings = await API.get_unofficial_rankings(); 46 const rankings = await API.get_unofficial_rankings();
41 setLeaderboardData(rankings); 47 setLeaderboardData(rankings);
42 if (currentLeaderboardType == RankingCategories.rankings_singleplayer) { 48 if (currentLeaderboardType == RankingCategories.rankings_singleplayer) {
@@ -47,6 +53,7 @@ const Rankings: React.FC = () => {
47 } else { 53 } else {
48 setCurrentLeaderboard(rankings.rankings_overall) 54 setCurrentLeaderboard(rankings.rankings_overall)
49 } 55 }
56 setLeaderboardLoad(true);
50 } catch (e) { 57 } catch (e) {
51 console.log(e) 58 console.log(e)
52 } 59 }
@@ -83,23 +90,23 @@ const Rankings: React.FC = () => {
83 <main> 90 <main>
84 <section className="nav-container nav-1"> 91 <section className="nav-container nav-1">
85 <div> 92 <div>
86 <button onClick={() => _fetch_rankings()} className="nav-1-btn"> 93 <button onClick={() => {_fetch_rankings(); setCurrentRankingType(LeaderboardTypes.official)}} className={`nav-1-btn ${currentRankingType == LeaderboardTypes.official ? "selected" : ""}`}>
87 <span>Official (LPHUB)</span> 94 <span>Official (LPHUB)</span>
88 </button> 95 </button>
89 <button onClick={() => __dev_fetch_unofficial_rankings()} className="nav-1-btn"> 96 <button onClick={() => {__dev_fetch_unofficial_rankings(); setCurrentRankingType(LeaderboardTypes.unofficial)}} className={`nav-1-btn ${currentRankingType == LeaderboardTypes.unofficial ? "selected" : ""}`}>
90 <span>Unofficial (Steam)</span> 97 <span>Unofficial (Steam)</span>
91 </button> 98 </button>
92 </div> 99 </div>
93 </section> 100 </section>
94 <section className="nav-container nav-2"> 101 <section className="nav-container nav-2">
95 <div> 102 <div>
96 <button onClick={() => _set_current_leaderboard(RankingCategories.rankings_singleplayer)} className="nav-2-btn"> 103 <button onClick={() => _set_current_leaderboard(RankingCategories.rankings_singleplayer)} className={`nav-2-btn ${currentLeaderboardType == RankingCategories.rankings_singleplayer ? "selected" : ""}`}>
97 <span>Singleplayer</span> 104 <span>Singleplayer</span>
98 </button> 105 </button>
99 <button onClick={() => _set_current_leaderboard(RankingCategories.rankings_multiplayer)} className="nav-2-btn"> 106 <button onClick={() => _set_current_leaderboard(RankingCategories.rankings_multiplayer)} className={`nav-2-btn ${currentLeaderboardType == RankingCategories.rankings_multiplayer ? "selected" : ""}`}>
100 <span>Cooperative</span> 107 <span>Cooperative</span>
101 </button> 108 </button>
102 <button onClick={() => _set_current_leaderboard(RankingCategories.rankings_overall)} className="nav-2-btn"> 109 <button onClick={() => _set_current_leaderboard(RankingCategories.rankings_overall)} className={`nav-2-btn ${currentLeaderboardType == RankingCategories.rankings_overall ? "selected" : ""}`}>
103 <span>Overall</span> 110 <span>Overall</span>
104 </button> 111 </button>
105 </div> 112 </div>
@@ -116,10 +123,16 @@ const Rankings: React.FC = () => {
116 123
117 <div className="splitter"></div> 124 <div className="splitter"></div>
118 125
119 {currentLeaderboard?.map((curRankingData, i) => { 126 {leaderboardLoad && currentLeaderboard?.map((curRankingData, i) => {
120 return <RankingEntry currentLeaderboardType={currentLeaderboardType} curRankingData={curRankingData} key={i}></RankingEntry> 127 return <RankingEntry currentLeaderboardType={currentLeaderboardType} curRankingData={curRankingData} key={i}></RankingEntry>
121 }) 128 })
122 } 129 }
130
131 {leaderboardLoad ? null :
132 <div style={{display: "flex", justifyContent: "center", margin: "30px 0px"}}>
133 <span className="loader"></span>
134 </div>
135 }
123 </div> 136 </div>
124 </section> 137 </section>
125 : null} 138 : null}