aboutsummaryrefslogtreecommitdiff
path: root/frontend/src
diff options
context:
space:
mode:
authorArda Serdar Pektezol <1669855+pektezol@users.noreply.github.com>2025-10-26 13:51:31 +0400
committerGitHub <noreply@github.com>2025-10-26 12:51:31 +0300
commit6a631744d9c80ff9a1a9f4b278bc0337ecdb494e (patch)
tree3acf1f505751d997b1b7482823eacabcb79a03f3 /frontend/src
parentfix/frontend: tablet view logout button (#286) (diff)
downloadlphub-6a631744d9c80ff9a1a9f4b278bc0337ecdb494e.tar.gz
lphub-6a631744d9c80ff9a1a9f4b278bc0337ecdb494e.tar.bz2
lphub-6a631744d9c80ff9a1a9f4b278bc0337ecdb494e.zip
chore/frontend: semicolon linting (#287)
Diffstat (limited to 'frontend/src')
-rw-r--r--frontend/src/App.tsx44
-rw-r--r--frontend/src/api/Api.ts2
-rw-r--r--frontend/src/api/Auth.ts2
-rw-r--r--frontend/src/api/Games.ts8
-rw-r--r--frontend/src/api/Maps.ts4
-rw-r--r--frontend/src/components/ConfirmDialog.tsx4
-rw-r--r--frontend/src/components/Discussions.tsx2
-rw-r--r--frontend/src/components/GameCategory.tsx6
-rw-r--r--frontend/src/components/GameEntry.tsx4
-rw-r--r--frontend/src/components/Leaderboards.tsx8
-rw-r--r--frontend/src/components/MapEntry.tsx4
-rw-r--r--frontend/src/components/MessageDialog.tsx6
-rw-r--r--frontend/src/components/MessageDialogLoad.tsx6
-rw-r--r--frontend/src/components/ModMenu.tsx20
-rw-r--r--frontend/src/components/RankingEntry.tsx6
-rw-r--r--frontend/src/components/Sidebar.tsx90
-rw-r--r--frontend/src/components/Summary.tsx10
-rw-r--r--frontend/src/components/UploadRunDialog.tsx46
-rw-r--r--frontend/src/hooks/UseConfirm.tsx6
-rw-r--r--frontend/src/hooks/UseMessage.tsx2
-rw-r--r--frontend/src/hooks/UseMessageLoad.tsx2
-rw-r--r--frontend/src/images/Images.tsx4
-rw-r--r--frontend/src/pages/Games.tsx4
-rw-r--r--frontend/src/pages/Maplist.tsx14
-rw-r--r--frontend/src/pages/Profile.tsx22
-rw-r--r--frontend/src/pages/Rankings.tsx34
-rw-r--r--frontend/src/pages/User.tsx14
-rw-r--r--frontend/src/utils/Time.ts8
28 files changed, 211 insertions, 171 deletions
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index e7f225c..6ed103e 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -52,8 +52,8 @@ const App: React.FC = () => {
52 setIsModerator(false); 52 setIsModerator(false);
53 } else { 53 } else {
54 setProfile({} as UserProfile); // placeholder before we call actual user profile 54 setProfile({} as UserProfile); // placeholder before we call actual user profile
55 _set_profile(get_user_id_from_token(token)) 55 _set_profile(get_user_id_from_token(token));
56 const modStatus = get_user_mod_from_token(token) 56 const modStatus = get_user_mod_from_token(token);
57 if (modStatus) { 57 if (modStatus) {
58 setIsModerator(true); 58 setIsModerator(true);
59 } else { 59 } else {
@@ -65,6 +65,46 @@ const App: React.FC = () => {
65 React.useEffect(() => { 65 React.useEffect(() => {
66 _fetch_token(); 66 _fetch_token();
67 _fetch_games(); 67 _fetch_games();
68 if (import.meta.env.DEV) {
69 setProfile({
70 profile: true,
71 steam_id: "76561234567890123",
72 user_name: "test",
73 avatar_link: "",
74 country_code: "XD",
75 titles: [],
76 links: {
77 "p2sr": "",
78 "steam": "",
79 "twitch": "",
80 "youtube": "",
81 },
82 rankings: {
83 "cooperative": {
84 "completion_count": 0,
85 "completion_total": 0,
86 "rank": 0,
87 },
88 "singleplayer": {
89 "completion_count": 0,
90 "completion_total": 0,
91 "rank": 0,
92 },
93 "overall": {
94 "completion_count": 0,
95 "completion_total": 0,
96 "rank": 0,
97 },
98 },
99 records: [],
100 pagination: {
101 "current_page": 0,
102 "page_size": 0,
103 "total_pages": 0,
104 "total_records": 0,
105 },
106 } as UserProfile);
107 };
68 }, []); 108 }, []);
69 109
70 return ( 110 return (
diff --git a/frontend/src/api/Api.ts b/frontend/src/api/Api.ts
index 19ece86..dd5076a 100644
--- a/frontend/src/api/Api.ts
+++ b/frontend/src/api/Api.ts
@@ -51,7 +51,7 @@ export const API = {
51 51
52const BASE_API_URL: string = import.meta.env.DEV 52const BASE_API_URL: string = import.meta.env.DEV
53 ? "https://lp.portal2.sr/api/v1/" 53 ? "https://lp.portal2.sr/api/v1/"
54 : "/api/v1/" 54 : "/api/v1/";
55 55
56export function url(path: string): string { 56export function url(path: string): string {
57 return BASE_API_URL + path; 57 return BASE_API_URL + path;
diff --git a/frontend/src/api/Auth.ts b/frontend/src/api/Auth.ts
index 56e0f6a..426bea5 100644
--- a/frontend/src/api/Auth.ts
+++ b/frontend/src/api/Auth.ts
@@ -2,7 +2,7 @@ import axios from "axios";
2import { url } from "@api/Api"; 2import { url } from "@api/Api";
3 3
4export const get_token = async (): Promise<string | undefined> => { 4export const get_token = async (): Promise<string | undefined> => {
5 const response = await axios.get(url("token")) 5 const response = await axios.get(url("token"));
6 if (!response.data.success) { 6 if (!response.data.success) {
7 return undefined; 7 return undefined;
8 } 8 }
diff --git a/frontend/src/api/Games.ts b/frontend/src/api/Games.ts
index 98c65e7..a66902d 100644
--- a/frontend/src/api/Games.ts
+++ b/frontend/src/api/Games.ts
@@ -6,14 +6,14 @@ import { Map } from "@customTypes/Map";
6import { Search } from "@customTypes/Search"; 6import { Search } from "@customTypes/Search";
7 7
8export const get_games = async (): Promise<Game[]> => { 8export const get_games = async (): Promise<Game[]> => {
9 const response = await axios.get(url("games")) 9 const response = await axios.get(url("games"));
10 return response.data.data; 10 return response.data.data;
11}; 11};
12 12
13export const get_chapters = async (chapter_id: string): Promise<GameChapter> => { 13export const get_chapters = async (chapter_id: string): Promise<GameChapter> => {
14 const response = await axios.get(url(`chapters/${chapter_id}`)); 14 const response = await axios.get(url(`chapters/${chapter_id}`));
15 return response.data.data; 15 return response.data.data;
16} 16};
17 17
18export const get_games_chapters = async (game_id: string): Promise<GamesChapters> => { 18export const get_games_chapters = async (game_id: string): Promise<GamesChapters> => {
19 const response = await axios.get(url(`games/${game_id}`)); 19 const response = await axios.get(url(`games/${game_id}`));
@@ -21,11 +21,11 @@ export const get_games_chapters = async (game_id: string): Promise<GamesChapters
21}; 21};
22 22
23export const get_game_maps = async (game_id: string): Promise<Map[]> => { 23export const get_game_maps = async (game_id: string): Promise<Map[]> => {
24 const response = await axios.get(url(`games/${game_id}/maps`)) 24 const response = await axios.get(url(`games/${game_id}/maps`));
25 return response.data.data.maps; 25 return response.data.data.maps;
26}; 26};
27 27
28export const get_search = async (q: string): Promise<Search> => { 28export const get_search = async (q: string): Promise<Search> => {
29 const response = await axios.get(url(`search?q=${q}`)) 29 const response = await axios.get(url(`search?q=${q}`));
30 return response.data.data; 30 return response.data.data;
31}; 31};
diff --git a/frontend/src/api/Maps.ts b/frontend/src/api/Maps.ts
index 8d29f84..7cbd70c 100644
--- a/frontend/src/api/Maps.ts
+++ b/frontend/src/api/Maps.ts
@@ -4,7 +4,7 @@ import { MapDiscussionContent, UploadRunContent } from "@customTypes/Content";
4import { MapSummary, MapLeaderboard, MapDiscussions, MapDiscussion } from "@customTypes/Map"; 4import { MapSummary, MapLeaderboard, MapDiscussions, MapDiscussion } from "@customTypes/Map";
5 5
6export const get_map_summary = async (map_id: string): Promise<MapSummary> => { 6export const get_map_summary = async (map_id: string): Promise<MapSummary> => {
7 const response = await axios.get(url(`maps/${map_id}/summary`)) 7 const response = await axios.get(url(`maps/${map_id}/summary`));
8 return response.data.data; 8 return response.data.data;
9}; 9};
10 10
@@ -94,7 +94,7 @@ export const post_record = async (token: string, run: UploadRunContent, map_id:
94 }); 94 });
95 return [response.data.success, response.data.message]; 95 return [response.data.success, response.data.message];
96 } 96 }
97} 97};
98 98
99export const delete_map_record = async (token: string, map_id: number, record_id: number): Promise<boolean> => { 99export const delete_map_record = async (token: string, map_id: number, record_id: number): Promise<boolean> => {
100 const response = await axios.delete(url(`maps/${map_id}/record/${record_id}`), { 100 const response = await axios.delete(url(`maps/${map_id}/record/${record_id}`), {
diff --git a/frontend/src/components/ConfirmDialog.tsx b/frontend/src/components/ConfirmDialog.tsx
index d8784d2..dbfbc62 100644
--- a/frontend/src/components/ConfirmDialog.tsx
+++ b/frontend/src/components/ConfirmDialog.tsx
@@ -1,6 +1,6 @@
1import React from "react"; 1import React from "react";
2 2
3import "@css/Dialog.css" 3import "@css/Dialog.css";
4 4
5interface ConfirmDialogProps { 5interface ConfirmDialogProps {
6 title: string; 6 title: string;
@@ -25,7 +25,7 @@ const ConfirmDialog: React.FC<ConfirmDialogProps> = ({ title, subtitle, onConfir
25 </div> 25 </div>
26 </div> 26 </div>
27 </div> 27 </div>
28 ) 28 );
29}; 29};
30 30
31export default ConfirmDialog; 31export default ConfirmDialog;
diff --git a/frontend/src/components/Discussions.tsx b/frontend/src/components/Discussions.tsx
index 0c37355..51cf06a 100644
--- a/frontend/src/components/Discussions.tsx
+++ b/frontend/src/components/Discussions.tsx
@@ -4,7 +4,7 @@ import { MapDiscussion, MapDiscussions, MapDiscussionsDetail } from "@customType
4import { MapDiscussionContent } from "@customTypes/Content"; 4import { MapDiscussionContent } from "@customTypes/Content";
5import { time_ago } from "@utils/Time"; 5import { time_ago } from "@utils/Time";
6import { API } from "@api/Api"; 6import { API } from "@api/Api";
7import "@css/Maps.css" 7import "@css/Maps.css";
8import { Link } from "react-router-dom"; 8import { Link } from "react-router-dom";
9import useConfirm from "@hooks/UseConfirm"; 9import useConfirm from "@hooks/UseConfirm";
10 10
diff --git a/frontend/src/components/GameCategory.tsx b/frontend/src/components/GameCategory.tsx
index 0d76d3e..79bb3fa 100644
--- a/frontend/src/components/GameCategory.tsx
+++ b/frontend/src/components/GameCategory.tsx
@@ -2,7 +2,7 @@ import React from "react";
2import { Link } from "react-router-dom"; 2import { Link } from "react-router-dom";
3 3
4import { Game, GameCategoryPortals } from "@customTypes/Game"; 4import { Game, GameCategoryPortals } from "@customTypes/Game";
5import "@css/Games.css" 5import "@css/Games.css";
6 6
7interface GameCategoryProps { 7interface GameCategoryProps {
8 game: Game; 8 game: Game;
@@ -18,7 +18,7 @@ const GameCategory: React.FC<GameCategoryProps> = ({cat, game}) => {
18 <span className='games-page-item-body-item-num'>{cat.portal_count}</span> 18 <span className='games-page-item-body-item-num'>{cat.portal_count}</span>
19 </div> 19 </div>
20 </Link> 20 </Link>
21 ) 21 );
22} 22};
23 23
24export default GameCategory; 24export default GameCategory;
diff --git a/frontend/src/components/GameEntry.tsx b/frontend/src/components/GameEntry.tsx
index 454efb1..deeb0ed 100644
--- a/frontend/src/components/GameEntry.tsx
+++ b/frontend/src/components/GameEntry.tsx
@@ -2,7 +2,7 @@ import React from "react";
2import { Link } from "react-router-dom"; 2import { Link } from "react-router-dom";
3 3
4import { Game, GameCategoryPortals } from "@customTypes/Game"; 4import { Game, GameCategoryPortals } from "@customTypes/Game";
5import "@css/Games.css" 5import "@css/Games.css";
6 6
7import GameCategory from "@components/GameCategory"; 7import GameCategory from "@components/GameCategory";
8 8
@@ -25,7 +25,7 @@ const GameEntry: React.FC<GameEntryProps> = ({ game }) => {
25 </div> 25 </div>
26 <div id={game.id as any as string} className='games-page-item-body'> 26 <div id={game.id as any as string} className='games-page-item-body'>
27 {catInfo.map((cat, index) => { 27 {catInfo.map((cat, index) => {
28 return <GameCategory cat={cat} game={game} key={index}></GameCategory> 28 return <GameCategory cat={cat} game={game} key={index}></GameCategory>;
29 })} 29 })}
30 </div> 30 </div>
31 </div></Link> 31 </div></Link>
diff --git a/frontend/src/components/Leaderboards.tsx b/frontend/src/components/Leaderboards.tsx
index 1a4aa48..073c21b 100644
--- a/frontend/src/components/Leaderboards.tsx
+++ b/frontend/src/components/Leaderboards.tsx
@@ -6,7 +6,7 @@ import { MapLeaderboard } from "@customTypes/Map";
6import { ticks_to_time, time_ago } from "@utils/Time"; 6import { ticks_to_time, time_ago } from "@utils/Time";
7import { API } from "@api/Api"; 7import { API } from "@api/Api";
8import useMessage from "@hooks/UseMessage"; 8import useMessage from "@hooks/UseMessage";
9import "@css/Maps.css" 9import "@css/Maps.css";
10 10
11interface LeaderboardsProps { 11interface LeaderboardsProps {
12 mapID: string; 12 mapID: string;
@@ -26,7 +26,7 @@ const Leaderboards: React.FC<LeaderboardsProps> = ({ mapID }) => {
26 26
27 React.useEffect(() => { 27 React.useEffect(() => {
28 _fetch_map_leaderboards(); 28 _fetch_map_leaderboards();
29 }, [pageNumber, navigate]) 29 }, [pageNumber, navigate]);
30 30
31 if (!data) { 31 if (!data) {
32 return ( 32 return (
@@ -103,14 +103,14 @@ const Leaderboards: React.FC<LeaderboardsProps> = ({ mapID }) => {
103 103
104 {r.kind === "multiplayer" ? ( 104 {r.kind === "multiplayer" ? (
105 <span> 105 <span>
106 <button onClick={() => { message("Demo Information", `Host Demo ID: ${r.host_demo_id} \nParnter Demo ID: ${r.partner_demo_id}`) }}><img src={ThreedotIcon} alt="demo_id" /></button> 106 <button onClick={() => { message("Demo Information", `Host Demo ID: ${r.host_demo_id} \nParnter Demo ID: ${r.partner_demo_id}`); }}><img src={ThreedotIcon} alt="demo_id" /></button>
107 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${r.partner_demo_id}`}><img src={DownloadIcon} alt="download" style={{ filter: "hue-rotate(160deg) contrast(60%) saturate(1000%)" }} /></button> 107 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${r.partner_demo_id}`}><img src={DownloadIcon} alt="download" style={{ filter: "hue-rotate(160deg) contrast(60%) saturate(1000%)" }} /></button>
108 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${r.host_demo_id}`}><img src={DownloadIcon} alt="download" style={{ filter: "hue-rotate(300deg) contrast(60%) saturate(1000%)" }} /></button> 108 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${r.host_demo_id}`}><img src={DownloadIcon} alt="download" style={{ filter: "hue-rotate(300deg) contrast(60%) saturate(1000%)" }} /></button>
109 </span> 109 </span>
110 ) : r.kind === "singleplayer" && ( 110 ) : r.kind === "singleplayer" && (
111 111
112 <span> 112 <span>
113 <button onClick={() => { message("Demo Information", `Demo ID: ${r.demo_id}`) }}><img src={ThreedotIcon} alt="demo_id" /></button> 113 <button onClick={() => { message("Demo Information", `Demo ID: ${r.demo_id}`); }}><img src={ThreedotIcon} alt="demo_id" /></button>
114 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${r.demo_id}`}><img src={DownloadIcon} alt="download" /></button> 114 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${r.demo_id}`}><img src={DownloadIcon} alt="download" /></button>
115 </span> 115 </span>
116 )} 116 )}
diff --git a/frontend/src/components/MapEntry.tsx b/frontend/src/components/MapEntry.tsx
index 88137d8..5c58401 100644
--- a/frontend/src/components/MapEntry.tsx
+++ b/frontend/src/components/MapEntry.tsx
@@ -6,7 +6,7 @@ const MapEntry: React.FC = () => {
6 <div> 6 <div>
7 7
8 </div> 8 </div>
9 ) 9 );
10} 10};
11 11
12export default MapEntry; 12export default MapEntry;
diff --git a/frontend/src/components/MessageDialog.tsx b/frontend/src/components/MessageDialog.tsx
index ae95f8d..e731c73 100644
--- a/frontend/src/components/MessageDialog.tsx
+++ b/frontend/src/components/MessageDialog.tsx
@@ -1,6 +1,6 @@
1import React from "react"; 1import React from "react";
2 2
3import "@css/Dialog.css" 3import "@css/Dialog.css";
4 4
5interface MessageDialogProps { 5interface MessageDialogProps {
6 title: string; 6 title: string;
@@ -23,7 +23,7 @@ const MessageDialog: React.FC<MessageDialogProps> = ({ title, subtitle, onClose
23 </div> 23 </div>
24 </div> 24 </div>
25 </div> 25 </div>
26 ) 26 );
27} 27};
28 28
29export default MessageDialog; 29export default MessageDialog;
diff --git a/frontend/src/components/MessageDialogLoad.tsx b/frontend/src/components/MessageDialogLoad.tsx
index 000a2ab..b3726fb 100644
--- a/frontend/src/components/MessageDialogLoad.tsx
+++ b/frontend/src/components/MessageDialogLoad.tsx
@@ -1,6 +1,6 @@
1import React from "react"; 1import React from "react";
2 2
3import "@css/Dialog.css" 3import "@css/Dialog.css";
4 4
5interface MessageDialogLoadProps { 5interface MessageDialogLoadProps {
6 title: string; 6 title: string;
@@ -23,7 +23,7 @@ const MessageDialogLoad: React.FC<MessageDialogLoadProps> = ({ title, onClose })
23 </div> 23 </div>
24 </div> 24 </div>
25 </div> 25 </div>
26 ) 26 );
27} 27};
28 28
29export default MessageDialogLoad; 29export default MessageDialogLoad;
diff --git a/frontend/src/components/ModMenu.tsx b/frontend/src/components/ModMenu.tsx
index 19ce0ce..e7ef25c 100644
--- a/frontend/src/components/ModMenu.tsx
+++ b/frontend/src/components/ModMenu.tsx
@@ -5,7 +5,7 @@ import { useNavigate } from "react-router-dom";
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 {
@@ -73,7 +73,7 @@ const ModMenu: React.FC<ModMenuProps> = ({ token, data, selectedRun, mapID }) =>
73 if (success) { 73 if (success) {
74 navigate(0); 74 navigate(0);
75 } else { 75 } else {
76 alert("Error. Check logs.") 76 alert("Error. Check logs.");
77 } 77 }
78 } 78 }
79 } 79 }
@@ -87,7 +87,7 @@ const ModMenu: React.FC<ModMenuProps> = ({ token, data, selectedRun, mapID }) =>
87 if (success) { 87 if (success) {
88 navigate(0); 88 navigate(0);
89 } else { 89 } else {
90 alert("Error. Check logs.") 90 alert("Error. Check logs.");
91 } 91 }
92 } 92 }
93 } 93 }
@@ -101,7 +101,7 @@ const ModMenu: React.FC<ModMenuProps> = ({ token, data, selectedRun, mapID }) =>
101 if (success) { 101 if (success) {
102 navigate(0); 102 navigate(0);
103 } else { 103 } else {
104 alert("Error. Check logs.") 104 alert("Error. Check logs.");
105 } 105 }
106 } 106 }
107 } 107 }
@@ -115,7 +115,7 @@ const ModMenu: React.FC<ModMenuProps> = ({ token, data, selectedRun, mapID }) =>
115 if (success) { 115 if (success) {
116 navigate(0); 116 navigate(0);
117 } else { 117 } else {
118 alert("Error. Check logs.") 118 alert("Error. Check logs.");
119 } 119 }
120 } 120 }
121 } 121 }
@@ -149,17 +149,17 @@ const ModMenu: React.FC<ModMenuProps> = ({ token, data, selectedRun, mapID }) =>
149 }, [menu]); 149 }, [menu]);
150 150
151 React.useEffect(() => { 151 React.useEffect(() => {
152 const modview = document.querySelector("div#modview") as HTMLElement 152 const modview = document.querySelector("div#modview") as HTMLElement;
153 if (modview) { 153 if (modview) {
154 showButton ? modview.style.transform = "translateY(-68%)" 154 showButton ? modview.style.transform = "translateY(-68%)"
155 : modview.style.transform = "translateY(0%)" 155 : modview.style.transform = "translateY(0%)";
156 } 156 }
157 157
158 const modview_block = document.querySelector("#modview_block") as HTMLElement 158 const modview_block = document.querySelector("#modview_block") as HTMLElement;
159 if (modview_block) { 159 if (modview_block) {
160 showButton ? modview_block.style.display = "none" : modview_block.style.display = "block" 160 showButton ? modview_block.style.display = "none" : modview_block.style.display = "block";
161 } 161 }
162 }, [showButton]) 162 }, [showButton]);
163 163
164 return ( 164 return (
165 <> 165 <>
diff --git a/frontend/src/components/RankingEntry.tsx b/frontend/src/components/RankingEntry.tsx
index 9ad9e1c..8db753a 100644
--- a/frontend/src/components/RankingEntry.tsx
+++ b/frontend/src/components/RankingEntry.tsx
@@ -26,7 +26,7 @@ const RankingEntry: React.FC<RankingEntryProps> = (prop) => {
26 </div> 26 </div>
27 <span>{prop.curRankingData.total_score}</span> 27 <span>{prop.curRankingData.total_score}</span>
28 </div> 28 </div>
29 ) 29 );
30 } else { 30 } else {
31 return ( 31 return (
32 <div className='leaderboard-entry'> 32 <div className='leaderboard-entry'>
@@ -39,8 +39,8 @@ const RankingEntry: React.FC<RankingEntryProps> = (prop) => {
39 </div> 39 </div>
40 <span>{prop.currentLeaderboardType == RankingCategories.rankings_singleplayer ? prop.curRankingData.sp_score : prop.currentLeaderboardType == RankingCategories.rankings_multiplayer ? prop.curRankingData.mp_score : prop.curRankingData.overall_score}</span> 40 <span>{prop.currentLeaderboardType == RankingCategories.rankings_singleplayer ? prop.curRankingData.sp_score : prop.currentLeaderboardType == RankingCategories.rankings_multiplayer ? prop.curRankingData.mp_score : prop.curRankingData.overall_score}</span>
41 </div> 41 </div>
42 ) 42 );
43 } 43 }
44} 44};
45 45
46export default RankingEntry; 46export default RankingEntry;
diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx
index 0a7efa8..8e2573c 100644
--- a/frontend/src/components/Sidebar.tsx
+++ b/frontend/src/components/Sidebar.tsx
@@ -28,19 +28,19 @@ const Sidebar: React.FC<SidebarProps> = ({ setToken, profile, setProfile, onUplo
28 28
29 const handle_sidebar_click = (clicked_sidebar_idx: number) => { 29 const handle_sidebar_click = (clicked_sidebar_idx: number) => {
30 const btn = document.querySelectorAll("button.sidebar-button"); 30 const btn = document.querySelectorAll("button.sidebar-button");
31 if (isSidebarOpen) { setSidebarOpen(false); _handle_sidebar_hide() } 31 if (isSidebarOpen) { setSidebarOpen(false); _handle_sidebar_hide(); }
32 // clusterfuck 32 // clusterfuck
33 btn.forEach((e, i) => { 33 btn.forEach((e, i) => {
34 btn[i].classList.remove("sidebar-button-selected") 34 btn[i].classList.remove("sidebar-button-selected");
35 btn[i].classList.add("sidebar-button-deselected") 35 btn[i].classList.add("sidebar-button-deselected");
36 }) 36 });
37 btn[clicked_sidebar_idx].classList.add("sidebar-button-selected") 37 btn[clicked_sidebar_idx].classList.add("sidebar-button-selected");
38 btn[clicked_sidebar_idx].classList.remove("sidebar-button-deselected") 38 btn[clicked_sidebar_idx].classList.remove("sidebar-button-deselected");
39 }; 39 };
40 40
41 const _handle_sidebar_hide = () => { 41 const _handle_sidebar_hide = () => {
42 const btn = document.querySelectorAll("button.sidebar-button") as NodeListOf<HTMLElement> 42 const btn = document.querySelectorAll("button.sidebar-button") as NodeListOf<HTMLElement>;
43 const span = document.querySelectorAll("button.sidebar-button>span") as NodeListOf<HTMLElement> 43 const span = document.querySelectorAll("button.sidebar-button>span") as NodeListOf<HTMLElement>;
44 const side = document.querySelector("#sidebar-list") as HTMLElement; 44 const side = document.querySelector("#sidebar-list") as HTMLElement;
45 const searchbar = document.querySelector("#searchbar") as HTMLInputElement; 45 const searchbar = document.querySelector("#searchbar") as HTMLInputElement;
46 const uploadRunBtn = document.querySelector("#upload-run") as HTMLInputElement; 46 const uploadRunBtn = document.querySelector("#upload-run") as HTMLInputElement;
@@ -49,42 +49,42 @@ const Sidebar: React.FC<SidebarProps> = ({ setToken, profile, setProfile, onUplo
49 if (isSidebarOpen) { 49 if (isSidebarOpen) {
50 if (profile) { 50 if (profile) {
51 const login = document.querySelectorAll(".login>button")[1] as HTMLElement; 51 const login = document.querySelectorAll(".login>button")[1] as HTMLElement;
52 login.style.opacity = "1" 52 login.style.opacity = "1";
53 uploadRunBtn.style.width = "310px" 53 uploadRunBtn.style.width = "310px";
54 uploadRunBtn.style.padding = "0.4em 0 0 11px" 54 uploadRunBtn.style.padding = "0.4em 0 0 11px";
55 uploadRunSpan.style.opacity = "0" 55 uploadRunSpan.style.opacity = "0";
56 setTimeout(() => { 56 setTimeout(() => {
57 uploadRunSpan.style.opacity = "1" 57 uploadRunSpan.style.opacity = "1";
58 }, 100) 58 }, 100);
59 } 59 }
60 setSidebarOpen(false); 60 setSidebarOpen(false);
61 side.style.width = "320px" 61 side.style.width = "320px";
62 btn.forEach((e, i) => { 62 btn.forEach((e, i) => {
63 e.style.width = (window.innerWidth > 1024) ? "310px" : "265px" 63 e.style.width = (window.innerWidth > 1024) ? "310px" : "265px";
64 e.style.padding = "0.4em 0 0 11px" 64 e.style.padding = "0.4em 0 0 11px";
65 setTimeout(() => { 65 setTimeout(() => {
66 span[i].style.opacity = "1" 66 span[i].style.opacity = "1";
67 }, 100) 67 }, 100);
68 }); 68 });
69 side.style.zIndex = "2" 69 side.style.zIndex = "2";
70 } else { 70 } else {
71 if (profile) { 71 if (profile) {
72 const login = document.querySelectorAll(".login>button")[1] as HTMLElement; 72 const login = document.querySelectorAll(".login>button")[1] as HTMLElement;
73 login.style.opacity = "0" 73 login.style.opacity = "0";
74 uploadRunBtn.style.width = "40px" 74 uploadRunBtn.style.width = "40px";
75 uploadRunBtn.style.padding = "0.4em 0 0 5px" 75 uploadRunBtn.style.padding = "0.4em 0 0 5px";
76 uploadRunSpan.style.opacity = "0" 76 uploadRunSpan.style.opacity = "0";
77 } 77 }
78 setSidebarOpen(true); 78 setSidebarOpen(true);
79 side.style.width = "40px"; 79 side.style.width = "40px";
80 searchbar.focus(); 80 searchbar.focus();
81 btn.forEach((e, i) => { 81 btn.forEach((e, i) => {
82 e.style.width = "40px" 82 e.style.width = "40px";
83 e.style.padding = "0.4em 0 0 5px" 83 e.style.padding = "0.4em 0 0 5px";
84 span[i].style.opacity = "0" 84 span[i].style.opacity = "0";
85 }) 85 });
86 setTimeout(() => { 86 setTimeout(() => {
87 side.style.zIndex = "0" 87 side.style.zIndex = "0";
88 }, 300); 88 }, 300);
89 } 89 }
90 }; 90 };
@@ -94,7 +94,7 @@ const Sidebar: React.FC<SidebarProps> = ({ setToken, profile, setProfile, onUplo
94 setIsMobileSearchOpen(!isMobileSearchOpen); 94 setIsMobileSearchOpen(!isMobileSearchOpen);
95 } else { 95 } else {
96 if (!isSidebarLocked) { 96 if (!isSidebarLocked) {
97 _handle_sidebar_hide() 97 _handle_sidebar_hide();
98 setIsSidebarLocked(true); 98 setIsSidebarLocked(true);
99 setTimeout(() => setIsSidebarLocked(false), 300); 99 setTimeout(() => setIsSidebarLocked(false), 300);
100 } 100 }
@@ -124,14 +124,14 @@ const Sidebar: React.FC<SidebarProps> = ({ setToken, profile, setProfile, onUplo
124 }; 124 };
125 125
126 React.useEffect(() => { 126 React.useEffect(() => {
127 if (path === "/") { handle_sidebar_click(1) } 127 if (path === "/") { handle_sidebar_click(1); }
128 else if (path.includes("games")) { handle_sidebar_click(2) } 128 else if (path.includes("games")) { handle_sidebar_click(2); }
129 else if (path.includes("rankings")) { handle_sidebar_click(3) } 129 else if (path.includes("rankings")) { handle_sidebar_click(3); }
130 // else if (path.includes("news")) { handle_sidebar_click(4) } 130 // else if (path.includes("news")) { handle_sidebar_click(4) }
131 // else if (path.includes("scorelog")) { handle_sidebar_click(5) } 131 // else if (path.includes("scorelog")) { handle_sidebar_click(5) }
132 else if (path.includes("profile")) { handle_sidebar_click(4) } 132 else if (path.includes("profile")) { handle_sidebar_click(4); }
133 else if (path.includes("rules")) { handle_sidebar_click(5) } 133 else if (path.includes("rules")) { handle_sidebar_click(5); }
134 else if (path.includes("about")) { handle_sidebar_click(6) } 134 else if (path.includes("about")) { handle_sidebar_click(6); }
135 }, [path]); 135 }, [path]);
136 136
137 return ( 137 return (
@@ -225,15 +225,15 @@ const Sidebar: React.FC<SidebarProps> = ({ setToken, profile, setProfile, onUplo
225 </Link> 225 </Link>
226 ))} 226 ))}
227 {searchData?.players.map((q, index) => 227 {searchData?.players.map((q, index) =>
228 ( 228 (
229 <Link to={ 229 <Link to={
230 profile && q.steam_id === profile.steam_id ? "/profile" : 230 profile && q.steam_id === profile.steam_id ? "/profile" :
231 `/users/${q.steam_id}` 231 `/users/${q.steam_id}`
232 } className='search-player' key={index} onClick={_close_mobile_search_and_menu}> 232 } className='search-player' key={index} onClick={_close_mobile_search_and_menu}>
233 <img src={q.avatar_link} alt='pfp'></img> 233 <img src={q.avatar_link} alt='pfp'></img>
234 <span style={{ fontSize: `${36 - q.user_name.length * 0.8}px` }}>{q.user_name}</span> 234 <span style={{ fontSize: `${36 - q.user_name.length * 0.8}px` }}>{q.user_name}</span>
235 </Link> 235 </Link>
236 ))} 236 ))}
237 237
238 </div> 238 </div>
239 </div> 239 </div>
diff --git a/frontend/src/components/Summary.tsx b/frontend/src/components/Summary.tsx
index 922960b..ee50ce8 100644
--- a/frontend/src/components/Summary.tsx
+++ b/frontend/src/components/Summary.tsx
@@ -2,7 +2,7 @@ import React from "react";
2import ReactMarkdown from "react-markdown"; 2import ReactMarkdown from "react-markdown";
3 3
4import { MapSummary } from "@customTypes/Map"; 4import { MapSummary } from "@customTypes/Map";
5import "@css/Maps.css" 5import "@css/Maps.css";
6 6
7interface SummaryProps { 7interface SummaryProps {
8 selectedRun: number 8 selectedRun: number
@@ -18,11 +18,11 @@ const Summary: React.FC<SummaryProps> = ({ selectedRun, setSelectedRun, data })
18 function _select_run(idx: number, category_id: number) { 18 function _select_run(idx: number, category_id: number) {
19 const r = document.querySelectorAll("button.record"); 19 const r = document.querySelectorAll("button.record");
20 r.forEach(e => (e as HTMLElement).style.backgroundColor = "#2b2e46"); 20 r.forEach(e => (e as HTMLElement).style.backgroundColor = "#2b2e46");
21 (r[idx] as HTMLElement).style.backgroundColor = "#161723" 21 (r[idx] as HTMLElement).style.backgroundColor = "#161723";
22 22
23 23
24 if (data && data.summary.routes.length !== 0) { 24 if (data && data.summary.routes.length !== 0) {
25 idx += data.summary.routes.filter(e => e.category.id < category_id).length // lethimcook 25 idx += data.summary.routes.filter(e => e.category.id < category_id).length; // lethimcook
26 setSelectedRun(idx); 26 setSelectedRun(idx);
27 } 27 }
28 }; 28 };
@@ -34,7 +34,7 @@ const Summary: React.FC<SummaryProps> = ({ selectedRun, setSelectedRun, data })
34 34
35 function _category_change() { 35 function _category_change() {
36 const btn = document.querySelectorAll("#section3 #category span button"); 36 const btn = document.querySelectorAll("#section3 #category span button");
37 btn.forEach((e) => { (e as HTMLElement).style.backgroundColor = "#2b2e46" }); 37 btn.forEach((e) => { (e as HTMLElement).style.backgroundColor = "#2b2e46"; });
38 // heavenly father forgive me for i have sinned. TODO: fix this bullshit with dynamic categories 38 // heavenly father forgive me for i have sinned. TODO: fix this bullshit with dynamic categories
39 const idx = selectedCategory === 1 ? 0 : data.map.is_coop ? selectedCategory - 3 : selectedCategory - 1; 39 const idx = selectedCategory === 1 ? 0 : data.map.is_coop ? selectedCategory - 3 : selectedCategory - 1;
40 (btn[idx] as HTMLElement).style.backgroundColor = "#202232"; 40 (btn[idx] as HTMLElement).style.backgroundColor = "#202232";
@@ -42,7 +42,7 @@ const Summary: React.FC<SummaryProps> = ({ selectedRun, setSelectedRun, data })
42 42
43 function _history_change() { 43 function _history_change() {
44 const btn = document.querySelectorAll("#section3 #history span button"); 44 const btn = document.querySelectorAll("#section3 #history span button");
45 btn.forEach((e) => { (e as HTMLElement).style.backgroundColor = "#2b2e46" }); 45 btn.forEach((e) => { (e as HTMLElement).style.backgroundColor = "#2b2e46"; });
46 (historySelected ? btn[1] as HTMLElement : btn[0] as HTMLElement).style.backgroundColor = "#202232"; 46 (historySelected ? btn[1] as HTMLElement : btn[0] as HTMLElement).style.backgroundColor = "#202232";
47 }; 47 };
48 48
diff --git a/frontend/src/components/UploadRunDialog.tsx b/frontend/src/components/UploadRunDialog.tsx
index dd609a1..445fe7c 100644
--- a/frontend/src/components/UploadRunDialog.tsx
+++ b/frontend/src/components/UploadRunDialog.tsx
@@ -52,7 +52,7 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
52 } else { 52 } else {
53 fileInputRefPartner.current?.click(); 53 fileInputRefPartner.current?.click();
54 } 54 }
55 } 55 };
56 56
57 const _handle_drag_over = (e: React.DragEvent<HTMLDivElement>, host: boolean) => { 57 const _handle_drag_over = (e: React.DragEvent<HTMLDivElement>, host: boolean) => {
58 e.preventDefault(); 58 e.preventDefault();
@@ -62,7 +62,7 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
62 } else { 62 } else {
63 setDragHighlightPartner(true); 63 setDragHighlightPartner(true);
64 } 64 }
65 } 65 };
66 66
67 const _handle_drag_leave = (e: React.DragEvent<HTMLDivElement>, host: boolean) => { 67 const _handle_drag_leave = (e: React.DragEvent<HTMLDivElement>, host: boolean) => {
68 e.preventDefault(); 68 e.preventDefault();
@@ -72,7 +72,7 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
72 } else { 72 } else {
73 setDragHighlightPartner(false); 73 setDragHighlightPartner(false);
74 } 74 }
75 } 75 };
76 76
77 const _handle_drop = (e: React.DragEvent<HTMLDivElement>, host: boolean) => { 77 const _handle_drop = (e: React.DragEvent<HTMLDivElement>, host: boolean) => {
78 e.preventDefault(); 78 e.preventDefault();
@@ -80,7 +80,7 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
80 setDragHighlight(true); 80 setDragHighlight(true);
81 81
82 _handle_file_change(e.dataTransfer.files, host); 82 _handle_file_change(e.dataTransfer.files, host);
83 } 83 };
84 84
85 const _handle_dropdowns = (dropdown: number) => { 85 const _handle_dropdowns = (dropdown: number) => {
86 setDropdown1Vis(false); 86 setDropdown1Vis(false);
@@ -91,7 +91,7 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
91 setDropdown2Vis(!dropdown2Vis); 91 setDropdown2Vis(!dropdown2Vis);
92 document.querySelector("#dropdown2")?.scrollTo(0, 0); 92 document.querySelector("#dropdown2")?.scrollTo(0, 0);
93 } 93 }
94 } 94 };
95 95
96 const _handle_game_select = async (game_id: string, game_name: string) => { 96 const _handle_game_select = async (game_id: string, game_name: string) => {
97 setLoading(true); 97 setLoading(true);
@@ -120,16 +120,16 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
120 if (token) { 120 if (token) {
121 if (games[selectedGameID].is_coop) { 121 if (games[selectedGameID].is_coop) {
122 if (uploadRunContent.host_demo === null) { 122 if (uploadRunContent.host_demo === null) {
123 await message("Error", "You must select a host demo to upload.") 123 await message("Error", "You must select a host demo to upload.");
124 return 124 return;
125 } else if (uploadRunContent.partner_demo === null) { 125 } else if (uploadRunContent.partner_demo === null) {
126 await message("Error", "You must select a partner demo to upload.") 126 await message("Error", "You must select a partner demo to upload.");
127 return 127 return;
128 } 128 }
129 } else { 129 } else {
130 if (uploadRunContent.host_demo === null) { 130 if (uploadRunContent.host_demo === null) {
131 await message("Error", "You must select a demo to upload.") 131 await message("Error", "You must select a demo to upload.");
132 return 132 return;
133 } 133 }
134 } 134 }
135 const demo = SourceDemoParser.default() 135 const demo = SourceDemoParser.default()
@@ -137,24 +137,24 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
137 .parse(await uploadRunContent.host_demo.arrayBuffer()); 137 .parse(await uploadRunContent.host_demo.arrayBuffer());
138 const scoreboard = demo.findPacket<NetMessages.SvcUserMessage>((msg) => { 138 const scoreboard = demo.findPacket<NetMessages.SvcUserMessage>((msg) => {
139 return msg instanceof NetMessages.SvcUserMessage && msg.userMessage instanceof ScoreboardTempUpdate; 139 return msg instanceof NetMessages.SvcUserMessage && msg.userMessage instanceof ScoreboardTempUpdate;
140 }) 140 });
141 141
142 if (!scoreboard) { 142 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.") 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.");
144 return 144 return;
145 } 145 }
146 146
147 if (!demo.mapName || !MapNames[demo.mapName]) { 147 if (!demo.mapName || !MapNames[demo.mapName]) {
148 await message("Error", "Error while processing demo: Invalid map name.") 148 await message("Error", "Error while processing demo: Invalid map name.");
149 return 149 return;
150 } 150 }
151 151
152 if (selectedGameID === 0 && MapNames[demo.mapName] > 60) { 152 if (selectedGameID === 0 && MapNames[demo.mapName] > 60) {
153 await message("Error", "Error while processing demo: Invalid cooperative demo in singleplayer submission.") 153 await message("Error", "Error while processing demo: Invalid cooperative demo in singleplayer submission.");
154 return 154 return;
155 } else if (selectedGameID === 1 && MapNames[demo.mapName] <= 60) { 155 } else if (selectedGameID === 1 && MapNames[demo.mapName] <= 60) {
156 await message("Error", "Error while processing demo: Invalid singleplayer demo in cooperative submission.") 156 await message("Error", "Error while processing demo: Invalid singleplayer demo in cooperative submission.");
157 return 157 return;
158 } 158 }
159 159
160 const { portalScore, timeScore } = scoreboard.userMessage?.as<ScoreboardTempUpdate>() ?? {}; 160 const { portalScore, timeScore } = scoreboard.userMessage?.as<ScoreboardTempUpdate>() ?? {};
@@ -207,7 +207,7 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
207 </div> 207 </div>
208 <div style={{ top: "110px" }} className={dropdown1Vis ? "upload-run-dropdown" : "upload-run-dropdown hidden"}> 208 <div style={{ top: "110px" }} className={dropdown1Vis ? "upload-run-dropdown" : "upload-run-dropdown hidden"}>
209 {games.map((game) => ( 209 {games.map((game) => (
210 <div onClick={() => { _handle_game_select(game.id.toString(), game.name); _handle_dropdowns(1) }} key={game.id}>{game.name}</div> 210 <div onClick={() => { _handle_game_select(game.id.toString(), game.name); _handle_dropdowns(1); }} key={game.id}>{game.name}</div>
211 ))} 211 ))}
212 </div> 212 </div>
213 </div> 213 </div>
@@ -219,7 +219,7 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
219 219
220 <div> 220 <div>
221 <h3 style={{ margin: "10px 0px" }}>Host Demo</h3> 221 <h3 style={{ margin: "10px 0px" }}>Host Demo</h3>
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" : ""}`}> 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" : ""}`}>
223 <input ref={fileInputRef} type="file" name="host_demo" id="host_demo" accept=".dem" onChange={(e) => _handle_file_change(e.target.files, true)} /> 223 <input ref={fileInputRef} type="file" name="host_demo" id="host_demo" accept=".dem" onChange={(e) => _handle_file_change(e.target.files, true)} />
224 {!uploadRunContent.host_demo ? 224 {!uploadRunContent.host_demo ?
225 <div> 225 <div>
@@ -239,7 +239,7 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
239 <> 239 <>
240 <div> 240 <div>
241 <h3 style={{ margin: "10px 0px" }}>Partner Demo</h3> 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" : ""}`}> 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 ? 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> 244 <div>
245 <span>Drag and drop</span> 245 <span>Drag and drop</span>
diff --git a/frontend/src/hooks/UseConfirm.tsx b/frontend/src/hooks/UseConfirm.tsx
index 593427e..a7e6bef 100644
--- a/frontend/src/hooks/UseConfirm.tsx
+++ b/frontend/src/hooks/UseConfirm.tsx
@@ -21,20 +21,20 @@ const useConfirm = () => {
21 if (resolvePromise) { 21 if (resolvePromise) {
22 resolvePromise(true); 22 resolvePromise(true);
23 } 23 }
24 } 24 };
25 25
26 const handleCancel = () => { 26 const handleCancel = () => {
27 setIsOpen(false); 27 setIsOpen(false);
28 if (resolvePromise) { 28 if (resolvePromise) {
29 resolvePromise(false); 29 resolvePromise(false);
30 } 30 }
31 } 31 };
32 32
33 const ConfirmDialogComponent = isOpen && ( 33 const ConfirmDialogComponent = isOpen && (
34 <ConfirmDialog title={title} subtitle={subtitle} onConfirm={handleConfirm} onCancel={handleCancel}></ConfirmDialog> 34 <ConfirmDialog title={title} subtitle={subtitle} onConfirm={handleConfirm} onCancel={handleCancel}></ConfirmDialog>
35 ); 35 );
36 36
37 return { confirm, ConfirmDialogComponent }; 37 return { confirm, ConfirmDialogComponent };
38} 38};
39 39
40export default useConfirm; 40export default useConfirm;
diff --git a/frontend/src/hooks/UseMessage.tsx b/frontend/src/hooks/UseMessage.tsx
index 22a5168..a75b688 100644
--- a/frontend/src/hooks/UseMessage.tsx
+++ b/frontend/src/hooks/UseMessage.tsx
@@ -32,6 +32,6 @@ const useMessage = () => {
32 ); 32 );
33 33
34 return { message, MessageDialogComponent }; 34 return { message, MessageDialogComponent };
35} 35};
36 36
37export default useMessage; 37export default useMessage;
diff --git a/frontend/src/hooks/UseMessageLoad.tsx b/frontend/src/hooks/UseMessageLoad.tsx
index eb42a45..332c2f5 100644
--- a/frontend/src/hooks/UseMessageLoad.tsx
+++ b/frontend/src/hooks/UseMessageLoad.tsx
@@ -30,6 +30,6 @@ const useMessageLoad = () => {
30 ); 30 );
31 31
32 return { messageLoad, messageLoadClose, MessageDialogLoadComponent }; 32 return { messageLoad, messageLoadClose, MessageDialogLoadComponent };
33} 33};
34 34
35export default useMessageLoad; 35export default useMessageLoad;
diff --git a/frontend/src/images/Images.tsx b/frontend/src/images/Images.tsx
index c4511ef..940e036 100644
--- a/frontend/src/images/Images.tsx
+++ b/frontend/src/images/Images.tsx
@@ -1,5 +1,5 @@
1import logo from "./png/logo.png" 1import logo from "./png/logo.png";
2import login from "./png/login.png" 2import login from "./png/login.png";
3import img1 from "./png/1.png"; 3import img1 from "./png/1.png";
4import img2 from "./png/2.png"; 4import img2 from "./png/2.png";
5import img3 from "./png/3.png"; 5import img3 from "./png/3.png";
diff --git a/frontend/src/pages/Games.tsx b/frontend/src/pages/Games.tsx
index 909ea20..f4b5463 100644
--- a/frontend/src/pages/Games.tsx
+++ b/frontend/src/pages/Games.tsx
@@ -3,7 +3,7 @@ import { Helmet } from "react-helmet";
3 3
4import GameEntry from "@components/GameEntry"; 4import GameEntry from "@components/GameEntry";
5import { Game } from "@customTypes/Game"; 5import { Game } from "@customTypes/Game";
6import "@css/Maps.css" 6import "@css/Maps.css";
7 7
8interface GamesProps { 8interface GamesProps {
9 games: Game[]; 9 games: Game[];
@@ -16,7 +16,7 @@ const Games: React.FC<GamesProps> = ({ games }) => {
16 loaders.forEach((loader) => { 16 loaders.forEach((loader) => {
17 (loader as HTMLElement).style.display = "none"; 17 (loader as HTMLElement).style.display = "none";
18 }); 18 });
19 } 19 };
20 20
21 React.useEffect(() => { 21 React.useEffect(() => {
22 document.querySelectorAll(".games-page-item-body").forEach((game, index) => { 22 document.querySelectorAll(".games-page-item-body").forEach((game, index) => {
diff --git a/frontend/src/pages/Maplist.tsx b/frontend/src/pages/Maplist.tsx
index bda24cd..dc655ca 100644
--- a/frontend/src/pages/Maplist.tsx
+++ b/frontend/src/pages/Maplist.tsx
@@ -34,7 +34,7 @@ const Maplist: React.FC = () => {
34 const _fetch_chapters = async (chapter_id: string) => { 34 const _fetch_chapters = async (chapter_id: string) => {
35 const chapters = await API.get_chapters(chapter_id); 35 const chapters = await API.get_chapters(chapter_id);
36 setCurChapter(chapters); 36 setCurChapter(chapters);
37 } 37 };
38 38
39 const _handle_dropdown_click = () => { 39 const _handle_dropdown_click = () => {
40 if (dropdownActive == "none") { 40 if (dropdownActive == "none") {
@@ -42,7 +42,7 @@ const Maplist: React.FC = () => {
42 } else { 42 } else {
43 setDropdownActive("none"); 43 setDropdownActive("none");
44 } 44 }
45 } 45 };
46 46
47 // im sorry but im too lazy to fix this right now 47 // im sorry but im too lazy to fix this right now
48 useEffect(() => { 48 useEffect(() => {
@@ -73,7 +73,7 @@ const Maplist: React.FC = () => {
73 const games_chapters = await API.get_games_chapters(gameId.toString()); 73 const games_chapters = await API.get_games_chapters(gameId.toString());
74 setGameChapters(games_chapters); 74 setGameChapters(games_chapters);
75 setNumChapters(games_chapters.chapters.length); 75 setNumChapters(games_chapters.chapters.length);
76 } 76 };
77 77
78 setLoad(true); 78 setLoad(true);
79 _fetch_game(); 79 _fetch_game();
@@ -85,7 +85,7 @@ const Maplist: React.FC = () => {
85 if (gameChapters != undefined && !queryParams.get("chapter")) { 85 if (gameChapters != undefined && !queryParams.get("chapter")) {
86 _fetch_chapters(gameChapters!.chapters[0].id.toString()); 86 _fetch_chapters(gameChapters!.chapters[0].id.toString());
87 } 87 }
88 }, [gameChapters]) 88 }, [gameChapters]);
89 89
90 90
91 91
@@ -124,7 +124,7 @@ const Maplist: React.FC = () => {
124 </div> 124 </div>
125 <div className="game-header-categories"> 125 <div className="game-header-categories">
126 {game?.category_portals.map((cat, index) => ( 126 {game?.category_portals.map((cat, index) => (
127 <button key={index} className={currentlySelected == cat.category.id || cat.category.id - 1 == catNum && !hasClicked ? "game-cat-button selected" : "game-cat-button"} onClick={() => { setCatNum(cat.category.id - 1); _update_currently_selected(cat.category.id) }}> 127 <button key={index} className={currentlySelected == cat.category.id || cat.category.id - 1 == catNum && !hasClicked ? "game-cat-button selected" : "game-cat-button"} onClick={() => { setCatNum(cat.category.id - 1); _update_currently_selected(cat.category.id); }}>
128 <span>{cat.category.name}</span> 128 <span>{cat.category.name}</span>
129 </button> 129 </button>
130 ))} 130 ))}
@@ -143,7 +143,7 @@ const Maplist: React.FC = () => {
143 </div> 143 </div>
144 <div className="dropdown-elements" style={{ display: dropdownActive }}> 144 <div className="dropdown-elements" style={{ display: dropdownActive }}>
145 {gameChapters?.chapters.map((chapter, i) => { 145 {gameChapters?.chapters.map((chapter, i) => {
146 return <div className="dropdown-element" onClick={() => { _fetch_chapters(chapter.id.toString()); _handle_dropdown_click() }}>{chapter.name}</div> 146 return <div className="dropdown-element" onClick={() => { _fetch_chapters(chapter.id.toString()); _handle_dropdown_click(); }}>{chapter.name}</div>;
147 }) 147 })
148 148
149 } 149 }
@@ -173,7 +173,7 @@ const Maplist: React.FC = () => {
173 </div> 173 </div>
174 </div> 174 </div>
175 </Link> 175 </Link>
176 </div> 176 </div>;
177 })} 177 })}
178 </section> 178 </section>
179 </div> 179 </div>
diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx
index e7b8325..8b8ce3e 100644
--- a/frontend/src/pages/Profile.tsx
+++ b/frontend/src/pages/Profile.tsx
@@ -28,8 +28,8 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRec
28 const [pageNumber, setPageNumber] = React.useState(1); 28 const [pageNumber, setPageNumber] = React.useState(1);
29 const [pageMax, setPageMax] = React.useState(0); 29 const [pageMax, setPageMax] = React.useState(0);
30 30
31 const [game, setGame] = React.useState("0") 31 const [game, setGame] = React.useState("0");
32 const [chapter, setChapter] = React.useState("0") 32 const [chapter, setChapter] = React.useState("0");
33 const [chapterData, setChapterData] = React.useState<GameChapters | null>(null); 33 const [chapterData, setChapterData] = React.useState<GameChapters | null>(null);
34 const [maps, setMaps] = React.useState<Map[]>([]); 34 const [maps, setMaps] = React.useState<Map[]>([]);
35 35
@@ -100,7 +100,7 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRec
100 if (profile && game !== "0") { 100 if (profile && game !== "0") {
101 _get_game_maps(); 101 _get_game_maps();
102 } 102 }
103 }, [profile, game, chapter, chapterData]) 103 }, [profile, game, chapter, chapterData]);
104 104
105 if (!profile) { 105 if (!profile) {
106 return ( 106 return (
@@ -279,14 +279,14 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRec
279 <span>{e.date.split("T")[0]}</span> 279 <span>{e.date.split("T")[0]}</span>
280 <span style={{ flexDirection: "row-reverse" }}> 280 <span style={{ flexDirection: "row-reverse" }}>
281 281
282 <button style={{ marginRight: "10px" }} onClick={() => { message("Demo Information", `Demo ID: ${e.demo_id}`) }}><img src={ThreedotIcon} alt="demo_id" /></button> 282 <button style={{ marginRight: "10px" }} onClick={() => { message("Demo Information", `Demo ID: ${e.demo_id}`); }}><img src={ThreedotIcon} alt="demo_id" /></button>
283 <button onClick={() => { _delete_submission(r.map_id, e.record_id) }}><img src={DeleteIcon}></img></button> 283 <button onClick={() => { _delete_submission(r.map_id, e.record_id); }}><img src={DeleteIcon}></img></button>
284 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${e.demo_id}`}><img src={DownloadIcon} alt="download" /></button> 284 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${e.demo_id}`}><img src={DownloadIcon} alt="download" /></button>
285 {i === 0 && r.scores.length > 1 ? <button onClick={() => { 285 {i === 0 && r.scores.length > 1 ? <button onClick={() => {
286 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "44px" || 286 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "44px" ||
287 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "" ? 287 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "" ?
288 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = `${r.scores.length * 46}px` : 288 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = `${r.scores.length * 46}px` :
289 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = "44px" 289 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = "44px";
290 } 290 }
291 }><img src={HistoryIcon} alt="history" /></button> : ""} 291 }><img src={HistoryIcon} alt="history" /></button> : ""}
292 292
@@ -325,14 +325,14 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRec
325 <span>{record!.scores[i].date.split("T")[0]}</span> 325 <span>{record!.scores[i].date.split("T")[0]}</span>
326 <span style={{ flexDirection: "row-reverse" }}> 326 <span style={{ flexDirection: "row-reverse" }}>
327 327
328 <button onClick={() => { message("Demo Information", `Demo ID: ${e.demo_id}`) }}><img src={ThreedotIcon} alt="demo_id" /></button> 328 <button onClick={() => { message("Demo Information", `Demo ID: ${e.demo_id}`); }}><img src={ThreedotIcon} alt="demo_id" /></button>
329 <button onClick={() => { _delete_submission(r.id, e.record_id) }}><img src={DeleteIcon}></img></button> 329 <button onClick={() => { _delete_submission(r.id, e.record_id); }}><img src={DeleteIcon}></img></button>
330 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${e.demo_id}`}><img src={DownloadIcon} alt="download" /></button> 330 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${e.demo_id}`}><img src={DownloadIcon} alt="download" /></button>
331 {i === 0 && record!.scores.length > 1 ? <button onClick={() => { 331 {i === 0 && record!.scores.length > 1 ? <button onClick={() => {
332 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "44px" || 332 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "44px" ||
333 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "" ? 333 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "" ?
334 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = `${record!.scores.length * 46}px` : 334 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = `${record!.scores.length * 46}px` :
335 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = "44px" 335 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = "44px";
336 } 336 }
337 }><img src={HistoryIcon} alt="history" /></button> : ""} 337 }><img src={HistoryIcon} alt="history" /></button> : ""}
338 338
@@ -340,8 +340,8 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRec
340 </>))} 340 </>))}
341 </button> 341 </button>
342 342
343 ) 343 );
344 } else { return null } 344 } else { return null; }
345 }) : (<>{console.warn(maps)}</>)} 345 }) : (<>{console.warn(maps)}</>)}
346 </div> 346 </div>
347 </section> 347 </section>
diff --git a/frontend/src/pages/Rankings.tsx b/frontend/src/pages/Rankings.tsx
index 4a96e26..4693117 100644
--- a/frontend/src/pages/Rankings.tsx
+++ b/frontend/src/pages/Rankings.tsx
@@ -31,15 +31,15 @@ const Rankings: React.FC = () => {
31 const rankings = await API.get_official_rankings(); 31 const rankings = await API.get_official_rankings();
32 setLeaderboardData(rankings); 32 setLeaderboardData(rankings);
33 if (currentLeaderboardType == RankingCategories.rankings_singleplayer) { 33 if (currentLeaderboardType == RankingCategories.rankings_singleplayer) {
34 setCurrentLeaderboard(rankings.rankings_singleplayer) 34 setCurrentLeaderboard(rankings.rankings_singleplayer);
35 } else if (currentLeaderboardType == RankingCategories.rankings_multiplayer) { 35 } else if (currentLeaderboardType == RankingCategories.rankings_multiplayer) {
36 setCurrentLeaderboard(rankings.rankings_multiplayer) 36 setCurrentLeaderboard(rankings.rankings_multiplayer);
37 } else { 37 } else {
38 setCurrentLeaderboard(rankings.rankings_overall) 38 setCurrentLeaderboard(rankings.rankings_overall);
39 } 39 }
40 setLoad(true); 40 setLoad(true);
41 setLeaderboardLoad(true); 41 setLeaderboardLoad(true);
42 } 42 };
43 43
44 const __dev_fetch_unofficial_rankings = async () => { 44 const __dev_fetch_unofficial_rankings = async () => {
45 try { 45 try {
@@ -47,17 +47,17 @@ const Rankings: React.FC = () => {
47 const rankings = await API.get_unofficial_rankings(); 47 const rankings = await API.get_unofficial_rankings();
48 setLeaderboardData(rankings); 48 setLeaderboardData(rankings);
49 if (currentLeaderboardType == RankingCategories.rankings_singleplayer) { 49 if (currentLeaderboardType == RankingCategories.rankings_singleplayer) {
50 setCurrentLeaderboard(rankings.rankings_singleplayer) 50 setCurrentLeaderboard(rankings.rankings_singleplayer);
51 } else if (currentLeaderboardType == RankingCategories.rankings_multiplayer) { 51 } else if (currentLeaderboardType == RankingCategories.rankings_multiplayer) {
52 setCurrentLeaderboard(rankings.rankings_multiplayer) 52 setCurrentLeaderboard(rankings.rankings_multiplayer);
53 } else { 53 } else {
54 setCurrentLeaderboard(rankings.rankings_overall) 54 setCurrentLeaderboard(rankings.rankings_overall);
55 } 55 }
56 setLeaderboardLoad(true); 56 setLeaderboardLoad(true);
57 } catch (e) { 57 } catch (e) {
58 console.log(e) 58 console.log(e);
59 } 59 }
60 } 60 };
61 61
62 const _set_current_leaderboard = (ranking_cat: RankingCategories) => { 62 const _set_current_leaderboard = (ranking_cat: RankingCategories) => {
63 if (ranking_cat == RankingCategories.rankings_singleplayer) { 63 if (ranking_cat == RankingCategories.rankings_singleplayer) {
@@ -69,7 +69,7 @@ const Rankings: React.FC = () => {
69 } 69 }
70 70
71 setCurrentLeaderboardType(ranking_cat); 71 setCurrentLeaderboardType(ranking_cat);
72 } 72 };
73 73
74 const _set_leaderboard_type = (leaderboard_type: LeaderboardTypes) => { 74 const _set_leaderboard_type = (leaderboard_type: LeaderboardTypes) => {
75 if (leaderboard_type == LeaderboardTypes.official) { 75 if (leaderboard_type == LeaderboardTypes.official) {
@@ -77,14 +77,14 @@ const Rankings: React.FC = () => {
77 } else { 77 } else {
78 78
79 } 79 }
80 } 80 };
81 81
82 useEffect(() => { 82 useEffect(() => {
83 _fetch_rankings(); 83 _fetch_rankings();
84 if (load) { 84 if (load) {
85 _set_current_leaderboard(RankingCategories.rankings_singleplayer); 85 _set_current_leaderboard(RankingCategories.rankings_singleplayer);
86 } 86 }
87 }, [load]) 87 }, [load]);
88 88
89 return ( 89 return (
90 <main> 90 <main>
@@ -93,10 +93,10 @@ const Rankings: React.FC = () => {
93 </Helmet> 93 </Helmet>
94 <section className="nav-container nav-1"> 94 <section className="nav-container nav-1">
95 <div> 95 <div>
96 <button onClick={() => { _fetch_rankings(); setCurrentRankingType(LeaderboardTypes.official) }} className={`nav-1-btn ${currentRankingType == LeaderboardTypes.official ? "selected" : ""}`}> 96 <button onClick={() => { _fetch_rankings(); setCurrentRankingType(LeaderboardTypes.official); }} className={`nav-1-btn ${currentRankingType == LeaderboardTypes.official ? "selected" : ""}`}>
97 <span>Official (LPHUB)</span> 97 <span>Official (LPHUB)</span>
98 </button> 98 </button>
99 <button onClick={() => { __dev_fetch_unofficial_rankings(); setCurrentRankingType(LeaderboardTypes.unofficial) }} className={`nav-1-btn ${currentRankingType == LeaderboardTypes.unofficial ? "selected" : ""}`}> 99 <button onClick={() => { __dev_fetch_unofficial_rankings(); setCurrentRankingType(LeaderboardTypes.unofficial); }} className={`nav-1-btn ${currentRankingType == LeaderboardTypes.unofficial ? "selected" : ""}`}>
100 <span>Unofficial (Steam)</span> 100 <span>Unofficial (Steam)</span>
101 </button> 101 </button>
102 </div> 102 </div>
@@ -127,7 +127,7 @@ const Rankings: React.FC = () => {
127 <div className="splitter"></div> 127 <div className="splitter"></div>
128 128
129 {leaderboardLoad && currentLeaderboard?.map((curRankingData, i) => { 129 {leaderboardLoad && currentLeaderboard?.map((curRankingData, i) => {
130 return <RankingEntry currentLeaderboardType={currentLeaderboardType} curRankingData={curRankingData} key={i}></RankingEntry> 130 return <RankingEntry currentLeaderboardType={currentLeaderboardType} curRankingData={curRankingData} key={i}></RankingEntry>;
131 }) 131 })
132 } 132 }
133 133
@@ -140,7 +140,7 @@ const Rankings: React.FC = () => {
140 </section> 140 </section>
141 : null} 141 : null}
142 </main> 142 </main>
143 ) 143 );
144} 144};
145 145
146export default Rankings; 146export default Rankings;
diff --git a/frontend/src/pages/User.tsx b/frontend/src/pages/User.tsx
index 33be1f0..236d5f9 100644
--- a/frontend/src/pages/User.tsx
+++ b/frontend/src/pages/User.tsx
@@ -83,7 +83,7 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
83 if (user && game !== "0") { 83 if (user && game !== "0") {
84 _get_game_maps(); 84 _get_game_maps();
85 } 85 }
86 }, [user, game, chapter, location]) 86 }, [user, game, chapter, location]);
87 87
88 if (!user) { 88 if (!user) {
89 return ( 89 return (
@@ -248,13 +248,13 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
248 <span>{e.date.split("T")[0]}</span> 248 <span>{e.date.split("T")[0]}</span>
249 <span style={{ flexDirection: "row-reverse" }}> 249 <span style={{ flexDirection: "row-reverse" }}>
250 250
251 <button onClick={() => { message("Demo Information", `Demo ID: ${e.demo_id}`) }}><img src={ThreedotIcon} alt="demo_id" /></button> 251 <button onClick={() => { message("Demo Information", `Demo ID: ${e.demo_id}`); }}><img src={ThreedotIcon} alt="demo_id" /></button>
252 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${e.demo_id}`}><img src={DownloadIcon} alt="download" /></button> 252 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${e.demo_id}`}><img src={DownloadIcon} alt="download" /></button>
253 {i === 0 && r.scores.length > 1 ? <button onClick={() => { 253 {i === 0 && r.scores.length > 1 ? <button onClick={() => {
254 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "44px" || 254 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "44px" ||
255 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "" ? 255 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "" ?
256 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = `${r.scores.length * 46}px` : 256 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = `${r.scores.length * 46}px` :
257 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = "44px" 257 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = "44px";
258 } 258 }
259 }><img src={HistoryIcon} alt="history" /></button> : ""} 259 }><img src={HistoryIcon} alt="history" /></button> : ""}
260 260
@@ -293,13 +293,13 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
293 <span>{record!.scores[i].date.split("T")[0]}</span> 293 <span>{record!.scores[i].date.split("T")[0]}</span>
294 <span style={{ flexDirection: "row-reverse" }}> 294 <span style={{ flexDirection: "row-reverse" }}>
295 295
296 <button onClick={() => { message("Demo Information", `Demo ID: ${e.demo_id}`) }}><img src={ThreedotIcon} alt="demo_id" /></button> 296 <button onClick={() => { message("Demo Information", `Demo ID: ${e.demo_id}`); }}><img src={ThreedotIcon} alt="demo_id" /></button>
297 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${e.demo_id}`}><img src={DownloadIcon} alt="download" /></button> 297 <button onClick={() => window.location.href = `/api/v1/demos?uuid=${e.demo_id}`}><img src={DownloadIcon} alt="download" /></button>
298 {i === 0 && record!.scores.length > 1 ? <button onClick={() => { 298 {i === 0 && record!.scores.length > 1 ? <button onClick={() => {
299 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "44px" || 299 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "44px" ||
300 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "" ? 300 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "" ?
301 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = `${record!.scores.length * 46}px` : 301 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = `${record!.scores.length * 46}px` :
302 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = "44px" 302 (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height = "44px";
303 } 303 }
304 }><img src={HistoryIcon} alt="history" /></button> : ""} 304 }><img src={HistoryIcon} alt="history" /></button> : ""}
305 305
@@ -307,8 +307,8 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
307 </>))} 307 </>))}
308 </button> 308 </button>
309 309
310 ) 310 );
311 } else { return null } 311 } else { return null; }
312 }) : (<>{console.warn(maps)}</>)} 312 }) : (<>{console.warn(maps)}</>)}
313 </div> 313 </div>
314 </section> 314 </section>
diff --git a/frontend/src/utils/Time.ts b/frontend/src/utils/Time.ts
index 9844e0c..5e3d99b 100644
--- a/frontend/src/utils/Time.ts
+++ b/frontend/src/utils/Time.ts
@@ -30,11 +30,11 @@ export function time_ago(date: Date) {
30}; 30};
31 31
32export function ticks_to_time(ticks: number) { 32export function ticks_to_time(ticks: number) {
33 let seconds = Math.floor(ticks / 60) 33 let seconds = Math.floor(ticks / 60);
34 let minutes = Math.floor(seconds / 60) 34 let minutes = Math.floor(seconds / 60);
35 const hours = Math.floor(minutes / 60) 35 const hours = Math.floor(minutes / 60);
36 36
37 const milliseconds = Math.floor((ticks % 60) * 1000 / 60) 37 const milliseconds = Math.floor((ticks % 60) * 1000 / 60);
38 seconds = seconds % 60; 38 seconds = seconds % 60;
39 minutes = minutes % 60; 39 minutes = minutes % 60;
40 40