diff options
| author | Wolfboy248 <georgejvindkarlsen@gmail.com> | 2024-10-29 10:09:17 +0100 |
|---|---|---|
| committer | Wolfboy248 <georgejvindkarlsen@gmail.com> | 2024-10-29 10:09:17 +0100 |
| commit | 35946dbb2feb7f9d1cbab16fb0602ce0303562e9 (patch) | |
| tree | 3ba530c1b3f5a1742ec41dfee5b74b43353619e2 | |
| parent | Merge branch 'typescript' of https://github.com/pektezol/LeastPortalsHub into... (diff) | |
| download | lphub-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.css | 22 | ||||
| -rw-r--r-- | frontend/src/components/Leaderboards.tsx | 9 | ||||
| -rw-r--r-- | frontend/src/components/UploadRunDialog.tsx | 56 | ||||
| -rw-r--r-- | frontend/src/css/UploadRunDialog.css | 10 | ||||
| -rw-r--r-- | frontend/src/hooks/UseMessage.tsx | 4 | ||||
| -rw-r--r-- | frontend/src/pages/Profile.tsx | 15 | ||||
| -rw-r--r-- | frontend/src/pages/Rankings.tsx | 25 |
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'; | |||
| 4 | import { DownloadIcon, ThreedotIcon } from '../images/Images'; | 4 | import { DownloadIcon, ThreedotIcon } from '../images/Images'; |
| 5 | import { MapLeaderboard } from '../types/Map'; | 5 | import { MapLeaderboard } from '../types/Map'; |
| 6 | import { ticks_to_time, time_ago } from '../utils/Time'; | 6 | import { ticks_to_time, time_ago } from '../utils/Time'; |
| 7 | import useMessage from "../hooks/UseMessage"; | ||
| 7 | import "../css/Maps.css" | 8 | import "../css/Maps.css" |
| 8 | 9 | ||
| 9 | interface LeaderboardsProps { | 10 | interface LeaderboardsProps { |
| @@ -12,6 +13,7 @@ interface LeaderboardsProps { | |||
| 12 | 13 | ||
| 13 | const Leaderboards: React.FC<LeaderboardsProps> = ({ data }) => { | 14 | const 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} |