aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/pages
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/pages')
-rw-r--r--frontend/src/pages/About.tsx16
-rw-r--r--frontend/src/pages/Games.tsx18
-rw-r--r--frontend/src/pages/Homepage.tsx4
-rw-r--r--frontend/src/pages/Maplist.tsx64
-rw-r--r--frontend/src/pages/Maps.tsx36
-rw-r--r--frontend/src/pages/Profile.tsx200
-rw-r--r--frontend/src/pages/Rankings.tsx28
-rw-r--r--frontend/src/pages/Rules.tsx16
-rw-r--r--frontend/src/pages/User.tsx184
9 files changed, 283 insertions, 283 deletions
diff --git a/frontend/src/pages/About.tsx b/frontend/src/pages/About.tsx
index a5d34f6..5a69bfe 100644
--- a/frontend/src/pages/About.tsx
+++ b/frontend/src/pages/About.tsx
@@ -1,25 +1,25 @@
1import React from 'react'; 1import React from "react";
2import ReactMarkdown from 'react-markdown'; 2import ReactMarkdown from "react-markdown";
3import { Helmet } from 'react-helmet'; 3import { Helmet } from "react-helmet";
4 4
5import '@css/About.css'; 5import "@css/About.css";
6 6
7const About: React.FC = () => { 7const About: React.FC = () => {
8 const [aboutText, setAboutText] = React.useState<string>(''); 8 const [aboutText, setAboutText] = React.useState<string>("");
9 9
10 React.useEffect(() => { 10 React.useEffect(() => {
11 const fetchReadme = async () => { 11 const fetchReadme = async () => {
12 try { 12 try {
13 const response = await fetch( 13 const response = await fetch(
14 'https://raw.githubusercontent.com/pektezol/lphub/main/README.md' 14 "https://raw.githubusercontent.com/pektezol/lphub/main/README.md"
15 ); 15 );
16 if (!response.ok) { 16 if (!response.ok) {
17 throw new Error('Failed to fetch README'); 17 throw new Error("Failed to fetch README");
18 } 18 }
19 const readmeText = await response.text(); 19 const readmeText = await response.text();
20 setAboutText(readmeText); 20 setAboutText(readmeText);
21 } catch (error) { 21 } catch (error) {
22 console.error('Error fetching README:', error); 22 console.error("Error fetching README:", error);
23 } 23 }
24 }; 24 };
25 fetchReadme(); 25 fetchReadme();
diff --git a/frontend/src/pages/Games.tsx b/frontend/src/pages/Games.tsx
index ae0a2d6..d7dacde 100644
--- a/frontend/src/pages/Games.tsx
+++ b/frontend/src/pages/Games.tsx
@@ -1,9 +1,9 @@
1import React from 'react'; 1import React from "react";
2import { Helmet } from 'react-helmet'; 2import { 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[];
@@ -11,17 +11,17 @@ interface GamesProps {
11 11
12const Games: React.FC<GamesProps> = ({ games }) => { 12const Games: React.FC<GamesProps> = ({ games }) => {
13 const _page_load = () => { 13 const _page_load = () => {
14 const loaders = document.querySelectorAll('.loader'); 14 const loaders = document.querySelectorAll(".loader");
15 loaders.forEach(loader => { 15 loaders.forEach(loader => {
16 (loader as HTMLElement).style.display = 'none'; 16 (loader as HTMLElement).style.display = "none";
17 }); 17 });
18 }; 18 };
19 19
20 React.useEffect(() => { 20 React.useEffect(() => {
21 document 21 document
22 .querySelectorAll('.games-page-item-body') 22 .querySelectorAll(".games-page-item-body")
23 .forEach((game, index) => { 23 .forEach((game, index) => {
24 game.innerHTML = ''; 24 game.innerHTML = "";
25 }); 25 });
26 _page_load(); 26 _page_load();
27 }, []); 27 }, []);
diff --git a/frontend/src/pages/Homepage.tsx b/frontend/src/pages/Homepage.tsx
index 859af52..f0c5821 100644
--- a/frontend/src/pages/Homepage.tsx
+++ b/frontend/src/pages/Homepage.tsx
@@ -1,5 +1,5 @@
1import React from 'react'; 1import React from "react";
2import { Helmet } from 'react-helmet'; 2import { Helmet } from "react-helmet";
3 3
4const Homepage: React.FC = () => { 4const Homepage: React.FC = () => {
5 return ( 5 return (
diff --git a/frontend/src/pages/Maplist.tsx b/frontend/src/pages/Maplist.tsx
index 372b800..a7242ef 100644
--- a/frontend/src/pages/Maplist.tsx
+++ b/frontend/src/pages/Maplist.tsx
@@ -1,11 +1,11 @@
1import React, { useEffect } from 'react'; 1import React, { useEffect } from "react";
2import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'; 2import { Link, useLocation, useNavigate, useParams } from "react-router-dom";
3import { Helmet } from 'react-helmet'; 3import { Helmet } from "react-helmet";
4 4
5import '@css/Maplist.css'; 5import "@css/Maplist.css";
6import { API } from '@api/Api'; 6import { API } from "@api/Api";
7import { Game } from '@customTypes/Game'; 7import { Game } from "@customTypes/Game";
8import { GameChapter, GamesChapters } from '@customTypes/Chapters'; 8import { GameChapter, GamesChapters } from "@customTypes/Chapters";
9 9
10const Maplist: React.FC = () => { 10const Maplist: React.FC = () => {
11 const [game, setGame] = React.useState<Game | null>(null); 11 const [game, setGame] = React.useState<Game | null>(null);
@@ -18,7 +18,7 @@ const Maplist: React.FC = () => {
18 const [curChapter, setCurChapter] = React.useState<GameChapter>(); 18 const [curChapter, setCurChapter] = React.useState<GameChapter>();
19 const [numChapters, setNumChapters] = React.useState<number>(0); 19 const [numChapters, setNumChapters] = React.useState<number>(0);
20 20
21 const [dropdownActive, setDropdownActive] = React.useState('none'); 21 const [dropdownActive, setDropdownActive] = React.useState("none");
22 22
23 const params = useParams<{ id: string; chapter: string }>(); 23 const params = useParams<{ id: string; chapter: string }>();
24 const location = useLocation(); 24 const location = useLocation();
@@ -26,7 +26,7 @@ const Maplist: React.FC = () => {
26 26
27 function _update_currently_selected(catNum2: number) { 27 function _update_currently_selected(catNum2: number) {
28 setCurrentlySelected(catNum2); 28 setCurrentlySelected(catNum2);
29 navigate('/games/' + game?.id + '?cat=' + catNum2); 29 navigate("/games/" + game?.id + "?cat=" + catNum2);
30 setHasClicked(true); 30 setHasClicked(true);
31 } 31 }
32 32
@@ -36,23 +36,23 @@ const Maplist: React.FC = () => {
36 }; 36 };
37 37
38 const _handle_dropdown_click = () => { 38 const _handle_dropdown_click = () => {
39 if (dropdownActive === 'none') { 39 if (dropdownActive === "none") {
40 setDropdownActive('block'); 40 setDropdownActive("block");
41 } else { 41 } else {
42 setDropdownActive('none'); 42 setDropdownActive("none");
43 } 43 }
44 }; 44 };
45 45
46 // im sorry but im too lazy to fix this right now 46 // im sorry but im too lazy to fix this right now
47 useEffect(() => { 47 useEffect(() => {
48 // gameID 48 // gameID
49 const gameId = parseFloat(params.id || ''); 49 const gameId = parseFloat(params.id || "");
50 setId(gameId); 50 setId(gameId);
51 51
52 // location query params 52 // location query params
53 const queryParams = new URLSearchParams(location.search); 53 const queryParams = new URLSearchParams(location.search);
54 if (queryParams.get('chapter')) { 54 if (queryParams.get("chapter")) {
55 let cat = parseFloat(queryParams.get('chapter') || ''); 55 let cat = parseFloat(queryParams.get("chapter") || "");
56 if (gameId === 2) { 56 if (gameId === 2) {
57 cat += 10; 57 cat += 10;
58 } 58 }
@@ -82,7 +82,7 @@ const Maplist: React.FC = () => {
82 82
83 useEffect(() => { 83 useEffect(() => {
84 const queryParams = new URLSearchParams(location.search); 84 const queryParams = new URLSearchParams(location.search);
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, location.search]); 88 }, [gameChapters, location.search]);
@@ -92,9 +92,9 @@ const Maplist: React.FC = () => {
92 <Helmet> 92 <Helmet>
93 <title>LPHUB | Maplist</title> 93 <title>LPHUB | Maplist</title>
94 </Helmet> 94 </Helmet>
95 <section style={{ marginTop: '20px' }}> 95 <section style={{ marginTop: "20px" }}>
96 <Link to="/games"> 96 <Link to="/games">
97 <button className="nav-button" style={{ borderRadius: '20px' }}> 97 <button className="nav-button" style={{ borderRadius: "20px" }}>
98 <i className="triangle"></i> 98 <i className="triangle"></i>
99 <span>Games List</span> 99 <span>Games List</span>
100 </button> 100 </button>
@@ -127,8 +127,8 @@ const Maplist: React.FC = () => {
127 className={ 127 className={
128 currentlySelected === cat.category.id || 128 currentlySelected === cat.category.id ||
129 (cat.category.id - 1 === catNum && !hasClicked) 129 (cat.category.id - 1 === catNum && !hasClicked)
130 ? 'game-cat-button selected' 130 ? "game-cat-button selected"
131 : 'game-cat-button' 131 : "game-cat-button"
132 } 132 }
133 onClick={() => { 133 onClick={() => {
134 setCatNum(cat.category.id - 1); 134 setCatNum(cat.category.id - 1);
@@ -147,17 +147,17 @@ const Maplist: React.FC = () => {
147 <div> 147 <div>
148 <span 148 <span
149 style={{ 149 style={{
150 fontSize: '18px', 150 fontSize: "18px",
151 transform: 'translateY(5px)', 151 transform: "translateY(5px)",
152 display: 'block', 152 display: "block",
153 marginTop: '10px', 153 marginTop: "10px",
154 }} 154 }}
155 > 155 >
156 {curChapter?.chapter.name.split(' - ')[0]} 156 {curChapter?.chapter.name.split(" - ")[0]}
157 </span> 157 </span>
158 </div> 158 </div>
159 <div onClick={_handle_dropdown_click} className="dropdown"> 159 <div onClick={_handle_dropdown_click} className="dropdown">
160 <span>{curChapter?.chapter.name.split(' - ')[1]}</span> 160 <span>{curChapter?.chapter.name.split(" - ")[1]}</span>
161 <i className="triangle"></i> 161 <i className="triangle"></i>
162 </div> 162 </div>
163 <div 163 <div
@@ -205,16 +205,16 @@ const Maplist: React.FC = () => {
205 <div 205 <div
206 className={ 206 className={
207 map.difficulty === 0 207 map.difficulty === 0
208 ? 'one' 208 ? "one"
209 : map.difficulty === 1 209 : map.difficulty === 1
210 ? 'two' 210 ? "two"
211 : map.difficulty === 2 211 : map.difficulty === 2
212 ? 'three' 212 ? "three"
213 : map.difficulty === 3 213 : map.difficulty === 3
214 ? 'four' 214 ? "four"
215 : map.difficulty === 4 215 : map.difficulty === 4
216 ? 'five' 216 ? "five"
217 : 'one' 217 : "one"
218 } 218 }
219 > 219 >
220 <div className="difficulty-point"></div> 220 <div className="difficulty-point"></div>
diff --git a/frontend/src/pages/Maps.tsx b/frontend/src/pages/Maps.tsx
index 33bd05b..fbdb8f3 100644
--- a/frontend/src/pages/Maps.tsx
+++ b/frontend/src/pages/Maps.tsx
@@ -1,15 +1,15 @@
1import React from 'react'; 1import React from "react";
2import { Link, useLocation } from 'react-router-dom'; 2import { Link, useLocation } from "react-router-dom";
3import { Helmet } from 'react-helmet'; 3import { Helmet } from "react-helmet";
4 4
5import { PortalIcon, FlagIcon, ChatIcon } from '@images/Images'; 5import { PortalIcon, FlagIcon, ChatIcon } from "@images/Images";
6import Summary from '@components/Summary'; 6import Summary from "@components/Summary";
7import Leaderboards from '@components/Leaderboards'; 7import Leaderboards from "@components/Leaderboards";
8import Discussions from '@components/Discussions'; 8import Discussions from "@components/Discussions";
9import ModMenu from '@components/ModMenu'; 9import ModMenu from "@components/ModMenu";
10import { MapDiscussions, MapLeaderboard, MapSummary } from '@customTypes/Map'; 10import { MapDiscussions, MapLeaderboard, MapSummary } from "@customTypes/Map";
11import { API } from '@api/Api'; 11import { API } from "@api/Api";
12import '@css/Maps.css'; 12import "@css/Maps.css";
13 13
14interface MapProps { 14interface MapProps {
15 token?: string; 15 token?: string;
@@ -33,7 +33,7 @@ const Maps: React.FC<MapProps> = ({ token, isModerator }) => {
33 33
34 const location = useLocation(); 34 const location = useLocation();
35 35
36 const mapID = location.pathname.split('/')[2]; 36 const mapID = location.pathname.split("/")[2];
37 37
38 const _fetch_map_summary = React.useCallback(async () => { 38 const _fetch_map_summary = React.useCallback(async () => {
39 const mapSummary = await API.get_map_summary(mapID); 39 const mapSummary = await API.get_map_summary(mapID);
@@ -41,7 +41,7 @@ const Maps: React.FC<MapProps> = ({ token, isModerator }) => {
41 }, [mapID]); 41 }, [mapID]);
42 42
43 const _fetch_map_leaderboards = React.useCallback(async () => { 43 const _fetch_map_leaderboards = React.useCallback(async () => {
44 const mapLeaderboards = await API.get_map_leaderboard(mapID, '1'); 44 const mapLeaderboards = await API.get_map_leaderboard(mapID, "1");
45 setMapLeaderboardData(mapLeaderboards); 45 setMapLeaderboardData(mapLeaderboards);
46 }, [mapID]); 46 }, [mapID]);
47 47
@@ -71,7 +71,7 @@ const Maps: React.FC<MapProps> = ({ token, isModerator }) => {
71 <Link to="/games"> 71 <Link to="/games">
72 <button 72 <button
73 className="nav-button" 73 className="nav-button"
74 style={{ borderRadius: '20px 20px 20px 20px' }} 74 style={{ borderRadius: "20px 20px 20px 20px" }}
75 > 75 >
76 <i className="triangle"></i> 76 <i className="triangle"></i>
77 <span>Games List</span> 77 <span>Games List</span>
@@ -125,18 +125,18 @@ const Maps: React.FC<MapProps> = ({ token, isModerator }) => {
125 <Link to="/games"> 125 <Link to="/games">
126 <button 126 <button
127 className="nav-button" 127 className="nav-button"
128 style={{ borderRadius: '20px 0px 0px 20px' }} 128 style={{ borderRadius: "20px 0px 0px 20px" }}
129 > 129 >
130 <i className="triangle"></i> 130 <i className="triangle"></i>
131 <span>Games List</span> 131 <span>Games List</span>
132 </button> 132 </button>
133 </Link> 133 </Link>
134 <Link 134 <Link
135 to={`/games/${mapSummaryData.map.is_coop ? '2' : '1'}?chapter=${mapSummaryData.map.chapter_name.split(' ')[1]}`} 135 to={`/games/${mapSummaryData.map.is_coop ? "2" : "1"}?chapter=${mapSummaryData.map.chapter_name.split(" ")[1]}`}
136 > 136 >
137 <button 137 <button
138 className="nav-button" 138 className="nav-button"
139 style={{ borderRadius: '0px 20px 20px 0px', marginLeft: '2px' }} 139 style={{ borderRadius: "0px 20px 20px 0px", marginLeft: "2px" }}
140 > 140 >
141 <i className="triangle"></i> 141 <i className="triangle"></i>
142 <span>{mapSummaryData.map.chapter_name}</span> 142 <span>{mapSummaryData.map.chapter_name}</span>
diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx
index 8acf746..e2d6000 100644
--- a/frontend/src/pages/Profile.tsx
+++ b/frontend/src/pages/Profile.tsx
@@ -1,6 +1,6 @@
1import React from 'react'; 1import React from "react";
2import { Link, useNavigate } from 'react-router-dom'; 2import { Link, useNavigate } from "react-router-dom";
3import { Helmet } from 'react-helmet'; 3import { Helmet } from "react-helmet";
4 4
5import { 5import {
6 SteamIcon, 6 SteamIcon,
@@ -14,16 +14,16 @@ import {
14 DownloadIcon, 14 DownloadIcon,
15 HistoryIcon, 15 HistoryIcon,
16 DeleteIcon, 16 DeleteIcon,
17} from '@images/Images'; 17} from "@images/Images";
18import { UserProfile } from '@customTypes/Profile'; 18import { UserProfile } from "@customTypes/Profile";
19import { Game, GameChapters } from '@customTypes/Game'; 19import { Game, GameChapters } from "@customTypes/Game";
20import { Map } from '@customTypes/Map'; 20import { Map } from "@customTypes/Map";
21import { ticks_to_time } from '@utils/Time'; 21import { ticks_to_time } from "@utils/Time";
22import '@css/Profile.css'; 22import "@css/Profile.css";
23import { API } from '@api/Api'; 23import { API } from "@api/Api";
24import useConfirm from '@hooks/UseConfirm'; 24import useConfirm from "@hooks/UseConfirm";
25import useMessage from '@hooks/UseMessage'; 25import useMessage from "@hooks/UseMessage";
26import useMessageLoad from '@hooks/UseMessageLoad'; 26import useMessageLoad from "@hooks/UseMessageLoad";
27 27
28interface ProfileProps { 28interface ProfileProps {
29 profile?: UserProfile; 29 profile?: UserProfile;
@@ -46,8 +46,8 @@ const Profile: React.FC<ProfileProps> = ({
46 const [pageNumber, setPageNumber] = React.useState(1); 46 const [pageNumber, setPageNumber] = React.useState(1);
47 const [pageMax, setPageMax] = React.useState(0); 47 const [pageMax, setPageMax] = React.useState(0);
48 48
49 const [game, setGame] = React.useState('0'); 49 const [game, setGame] = React.useState("0");
50 const [chapter, setChapter] = React.useState('0'); 50 const [chapter, setChapter] = React.useState("0");
51 const [chapterData, setChapterData] = React.useState<GameChapters | null>( 51 const [chapterData, setChapterData] = React.useState<GameChapters | null>(
52 null 52 null
53 ); 53 );
@@ -62,17 +62,17 @@ const Profile: React.FC<ProfileProps> = ({
62 }; 62 };
63 63
64 const _get_game_chapters = React.useCallback(async () => { 64 const _get_game_chapters = React.useCallback(async () => {
65 if (game && game !== '0') { 65 if (game && game !== "0") {
66 const gameChapters = await API.get_games_chapters(game); 66 const gameChapters = await API.get_games_chapters(game);
67 setChapterData(gameChapters); 67 setChapterData(gameChapters);
68 } else if (game && game === '0') { 68 } else if (game && game === "0") {
69 setPageMax(Math.ceil(profile!.records.length / 20)); 69 setPageMax(Math.ceil(profile!.records.length / 20));
70 setPageNumber(1); 70 setPageNumber(1);
71 } 71 }
72 }, [game, profile]); 72 }, [game, profile]);
73 73
74 const _get_game_maps = React.useCallback(async () => { 74 const _get_game_maps = React.useCallback(async () => {
75 if (chapter === '0') { 75 if (chapter === "0") {
76 const gameMaps = await API.get_game_maps(game); 76 const gameMaps = await API.get_game_maps(game);
77 setMaps(gameMaps); 77 setMaps(gameMaps);
78 setPageMax(Math.ceil(gameMaps.length / 20)); 78 setPageMax(Math.ceil(gameMaps.length / 20));
@@ -87,29 +87,29 @@ const Profile: React.FC<ProfileProps> = ({
87 87
88 const _delete_submission = async (map_id: number, record_id: number) => { 88 const _delete_submission = async (map_id: number, record_id: number) => {
89 const userConfirmed = await confirm( 89 const userConfirmed = await confirm(
90 'Delete Record', 90 "Delete Record",
91 'Are you sure you want to delete this record?' 91 "Are you sure you want to delete this record?"
92 ); 92 );
93 93
94 if (!userConfirmed) { 94 if (!userConfirmed) {
95 return; 95 return;
96 } 96 }
97 97
98 messageLoad('Deleting...'); 98 messageLoad("Deleting...");
99 99
100 const api_success = await API.delete_map_record(token!, map_id, record_id); 100 const api_success = await API.delete_map_record(token!, map_id, record_id);
101 messageLoadClose(); 101 messageLoadClose();
102 if (api_success) { 102 if (api_success) {
103 await message('Delete Record', 'Successfully deleted record.'); 103 await message("Delete Record", "Successfully deleted record.");
104 onDeleteRecord(); 104 onDeleteRecord();
105 } else { 105 } else {
106 await message('Delete Record', 'Could not delete record.'); 106 await message("Delete Record", "Could not delete record.");
107 } 107 }
108 }; 108 };
109 109
110 React.useEffect(() => { 110 React.useEffect(() => {
111 if (!profile) { 111 if (!profile) {
112 navigate('/'); 112 navigate("/");
113 } 113 }
114 }, [profile, navigate]); 114 }, [profile, navigate]);
115 115
@@ -120,7 +120,7 @@ const Profile: React.FC<ProfileProps> = ({
120 }, [profile, game, _get_game_chapters]); 120 }, [profile, game, _get_game_chapters]);
121 121
122 React.useEffect(() => { 122 React.useEffect(() => {
123 if (profile && game !== '0') { 123 if (profile && game !== "0") {
124 _get_game_maps(); 124 _get_game_maps();
125 } 125 }
126 }, [profile, game, chapter, chapterData, _get_game_maps]); 126 }, [profile, game, chapter, chapterData, _get_game_maps]);
@@ -156,8 +156,8 @@ const Profile: React.FC<ProfileProps> = ({
156 <div> 156 <div>
157 <div>{profile.user_name}</div> 157 <div>{profile.user_name}</div>
158 <div> 158 <div>
159 {profile.country_code === 'XX' ? ( 159 {profile.country_code === "XX" ? (
160 '' 160 ""
161 ) : ( 161 ) : (
162 <img 162 <img
163 src={`https://flagcdn.com/w80/${profile.country_code.toLowerCase()}.jpg`} 163 src={`https://flagcdn.com/w80/${profile.country_code.toLowerCase()}.jpg`}
@@ -177,32 +177,32 @@ const Profile: React.FC<ProfileProps> = ({
177 </div> 177 </div>
178 </div> 178 </div>
179 <div> 179 <div>
180 {profile.links.steam === '-' ? ( 180 {profile.links.steam === "-" ? (
181 '' 181 ""
182 ) : ( 182 ) : (
183 <a href={profile.links.steam}> 183 <a href={profile.links.steam}>
184 <img src={SteamIcon} alt="Steam" /> 184 <img src={SteamIcon} alt="Steam" />
185 </a> 185 </a>
186 )} 186 )}
187 {profile.links.twitch === '-' ? ( 187 {profile.links.twitch === "-" ? (
188 '' 188 ""
189 ) : ( 189 ) : (
190 <a href={profile.links.twitch}> 190 <a href={profile.links.twitch}>
191 <img src={TwitchIcon} alt="Twitch" /> 191 <img src={TwitchIcon} alt="Twitch" />
192 </a> 192 </a>
193 )} 193 )}
194 {profile.links.youtube === '-' ? ( 194 {profile.links.youtube === "-" ? (
195 '' 195 ""
196 ) : ( 196 ) : (
197 <a href={profile.links.youtube}> 197 <a href={profile.links.youtube}>
198 <img src={YouTubeIcon} alt="Youtube" /> 198 <img src={YouTubeIcon} alt="Youtube" />
199 </a> 199 </a>
200 )} 200 )}
201 {profile.links.p2sr === '-' ? ( 201 {profile.links.p2sr === "-" ? (
202 '' 202 ""
203 ) : ( 203 ) : (
204 <a href={profile.links.p2sr}> 204 <a href={profile.links.p2sr}>
205 <img src={PortalIcon} alt="P2SR" style={{ padding: '0' }} /> 205 <img src={PortalIcon} alt="P2SR" style={{ padding: "0" }} />
206 </a> 206 </a>
207 )} 207 )}
208 </div> 208 </div>
@@ -212,8 +212,8 @@ const Profile: React.FC<ProfileProps> = ({
212 <span>Overall</span> 212 <span>Overall</span>
213 <span> 213 <span>
214 {profile.rankings.overall.rank === 0 214 {profile.rankings.overall.rank === 0
215 ? 'N/A ' 215 ? "N/A "
216 : '#' + profile.rankings.overall.rank + ' '} 216 : "#" + profile.rankings.overall.rank + " "}
217 <span> 217 <span>
218 ({profile.rankings.overall.completion_count}/ 218 ({profile.rankings.overall.completion_count}/
219 {profile.rankings.overall.completion_total}) 219 {profile.rankings.overall.completion_total})
@@ -224,8 +224,8 @@ const Profile: React.FC<ProfileProps> = ({
224 <span>Singleplayer</span> 224 <span>Singleplayer</span>
225 <span> 225 <span>
226 {profile.rankings.singleplayer.rank === 0 226 {profile.rankings.singleplayer.rank === 0
227 ? 'N/A ' 227 ? "N/A "
228 : '#' + profile.rankings.singleplayer.rank + ' '} 228 : "#" + profile.rankings.singleplayer.rank + " "}
229 <span> 229 <span>
230 ({profile.rankings.singleplayer.completion_count}/ 230 ({profile.rankings.singleplayer.completion_count}/
231 {profile.rankings.singleplayer.completion_total}) 231 {profile.rankings.singleplayer.completion_total})
@@ -236,8 +236,8 @@ const Profile: React.FC<ProfileProps> = ({
236 <span>Cooperative</span> 236 <span>Cooperative</span>
237 <span> 237 <span>
238 {profile.rankings.cooperative.rank === 0 238 {profile.rankings.cooperative.rank === 0
239 ? 'N/A ' 239 ? "N/A "
240 : '#' + profile.rankings.cooperative.rank + ' '} 240 : "#" + profile.rankings.cooperative.rank + " "}
241 <span> 241 <span>
242 ({profile.rankings.cooperative.completion_count}/ 242 ({profile.rankings.cooperative.completion_count}/
243 {profile.rankings.cooperative.completion_total}) 243 {profile.rankings.cooperative.completion_total})
@@ -267,15 +267,15 @@ const Profile: React.FC<ProfileProps> = ({
267 id="select-game" 267 id="select-game"
268 onChange={() => { 268 onChange={() => {
269 setGame( 269 setGame(
270 (document.querySelector('#select-game') as HTMLInputElement) 270 (document.querySelector("#select-game") as HTMLInputElement)
271 .value 271 .value
272 ); 272 );
273 setChapter('0'); 273 setChapter("0");
274 const chapterSelect = document.querySelector( 274 const chapterSelect = document.querySelector(
275 '#select-chapter' 275 "#select-chapter"
276 ) as HTMLSelectElement; 276 ) as HTMLSelectElement;
277 if (chapterSelect) { 277 if (chapterSelect) {
278 chapterSelect.value = '0'; 278 chapterSelect.value = "0";
279 } 279 }
280 }} 280 }}
281 > 281 >
@@ -290,7 +290,7 @@ const Profile: React.FC<ProfileProps> = ({
290 </select> 290 </select>
291 )} 291 )}
292 292
293 {game === '0' ? ( 293 {game === "0" ? (
294 <select disabled> 294 <select disabled>
295 <option>All Chapters</option> 295 <option>All Chapters</option>
296 </select> 296 </select>
@@ -303,7 +303,7 @@ const Profile: React.FC<ProfileProps> = ({
303 setChapter( 303 setChapter(
304 ( 304 (
305 document.querySelector( 305 document.querySelector(
306 '#select-chapter' 306 "#select-chapter"
307 ) as HTMLInputElement 307 ) as HTMLInputElement
308 ).value 308 ).value
309 ) 309 )
@@ -327,15 +327,15 @@ const Profile: React.FC<ProfileProps> = ({
327 <span>Map Name</span> 327 <span>Map Name</span>
328 <img src={SortIcon} alt="" /> 328 <img src={SortIcon} alt="" />
329 </span> 329 </span>
330 <span style={{ justifyContent: 'center' }}> 330 <span style={{ justifyContent: "center" }}>
331 <span>Portals</span> 331 <span>Portals</span>
332 <img src={SortIcon} alt="" /> 332 <img src={SortIcon} alt="" />
333 </span> 333 </span>
334 <span style={{ justifyContent: 'center' }}> 334 <span style={{ justifyContent: "center" }}>
335 <span>WRΔ </span> 335 <span>WRΔ </span>
336 <img src={SortIcon} alt="" /> 336 <img src={SortIcon} alt="" />
337 </span> 337 </span>
338 <span style={{ justifyContent: 'center' }}> 338 <span style={{ justifyContent: "center" }}>
339 <span>Time</span> 339 <span>Time</span>
340 <img src={SortIcon} alt="" /> 340 <img src={SortIcon} alt="" />
341 </span> 341 </span>
@@ -355,18 +355,18 @@ const Profile: React.FC<ProfileProps> = ({
355 if (pageNumber !== 1) { 355 if (pageNumber !== 1) {
356 setPageNumber(prevPageNumber => prevPageNumber - 1); 356 setPageNumber(prevPageNumber => prevPageNumber - 1);
357 const records = document.querySelectorAll( 357 const records = document.querySelectorAll(
358 '.profileboard-record' 358 ".profileboard-record"
359 ); 359 );
360 records.forEach(r => { 360 records.forEach(r => {
361 (r as HTMLInputElement).style.height = '44px'; 361 (r as HTMLInputElement).style.height = "44px";
362 }); 362 });
363 } 363 }
364 }} 364 }}
365 > 365 >
366 <i 366 <i
367 className="triangle" 367 className="triangle"
368 style={{ position: 'relative', left: '-5px' }} 368 style={{ position: "relative", left: "-5px" }}
369 ></i>{' '} 369 ></i>{" "}
370 </button> 370 </button>
371 <span> 371 <span>
372 {pageNumber}/{pageMax} 372 {pageNumber}/{pageMax}
@@ -376,10 +376,10 @@ const Profile: React.FC<ProfileProps> = ({
376 if (pageNumber !== pageMax) { 376 if (pageNumber !== pageMax) {
377 setPageNumber(prevPageNumber => prevPageNumber + 1); 377 setPageNumber(prevPageNumber => prevPageNumber + 1);
378 const records = document.querySelectorAll( 378 const records = document.querySelectorAll(
379 '.profileboard-record' 379 ".profileboard-record"
380 ); 380 );
381 records.forEach(r => { 381 records.forEach(r => {
382 (r as HTMLInputElement).style.height = '44px'; 382 (r as HTMLInputElement).style.height = "44px";
383 }); 383 });
384 } 384 }
385 }} 385 }}
@@ -387,18 +387,18 @@ const Profile: React.FC<ProfileProps> = ({
387 <i 387 <i
388 className="triangle" 388 className="triangle"
389 style={{ 389 style={{
390 position: 'relative', 390 position: "relative",
391 left: '5px', 391 left: "5px",
392 transform: 'rotate(180deg)', 392 transform: "rotate(180deg)",
393 }} 393 }}
394 ></i>{' '} 394 ></i>{" "}
395 </button> 395 </button>
396 </div> 396 </div>
397 </div> 397 </div>
398 </div> 398 </div>
399 <hr /> 399 <hr />
400 <div id="profileboard-records"> 400 <div id="profileboard-records">
401 {game === '0' ? ( 401 {game === "0" ? (
402 profile.records 402 profile.records
403 .sort((a, b) => a.map_id - b.map_id) 403 .sort((a, b) => a.map_id - b.map_id)
404 .map((r, index) => 404 .map((r, index) =>
@@ -407,25 +407,25 @@ const Profile: React.FC<ProfileProps> = ({
407 {r.scores.map((e, i) => ( 407 {r.scores.map((e, i) => (
408 <> 408 <>
409 {i !== 0 ? ( 409 {i !== 0 ? (
410 <hr style={{ gridColumn: '1 / span 8' }} /> 410 <hr style={{ gridColumn: "1 / span 8" }} />
411 ) : ( 411 ) : (
412 '' 412 ""
413 )} 413 )}
414 414
415 <Link to={`/maps/${r.map_id}`}> 415 <Link to={`/maps/${r.map_id}`}>
416 <span>{r.map_name}</span> 416 <span>{r.map_name}</span>
417 </Link> 417 </Link>
418 418
419 <span style={{ display: 'grid' }}> 419 <span style={{ display: "grid" }}>
420 {e.score_count} 420 {e.score_count}
421 </span> 421 </span>
422 422
423 <span style={{ display: 'grid' }}> 423 <span style={{ display: "grid" }}>
424 {e.score_count - r.map_wr_count > 0 424 {e.score_count - r.map_wr_count > 0
425 ? `+${e.score_count - r.map_wr_count}` 425 ? `+${e.score_count - r.map_wr_count}`
426 : `-`} 426 : `-`}
427 </span> 427 </span>
428 <span style={{ display: 'grid' }}> 428 <span style={{ display: "grid" }}>
429 {ticks_to_time(e.score_time)} 429 {ticks_to_time(e.score_time)}
430 </span> 430 </span>
431 <span> </span> 431 <span> </span>
@@ -434,13 +434,13 @@ const Profile: React.FC<ProfileProps> = ({
434 ) : ( 434 ) : (
435 <span> </span> 435 <span> </span>
436 )} 436 )}
437 <span>{e.date.split('T')[0]}</span> 437 <span>{e.date.split("T")[0]}</span>
438 <span style={{ flexDirection: 'row-reverse' }}> 438 <span style={{ flexDirection: "row-reverse" }}>
439 <button 439 <button
440 style={{ marginRight: '10px' }} 440 style={{ marginRight: "10px" }}
441 onClick={() => { 441 onClick={() => {
442 message( 442 message(
443 'Demo Information', 443 "Demo Information",
444 `Demo ID: ${e.demo_id}` 444 `Demo ID: ${e.demo_id}`
445 ); 445 );
446 }} 446 }}
@@ -466,38 +466,38 @@ const Profile: React.FC<ProfileProps> = ({
466 onClick={() => { 466 onClick={() => {
467 ( 467 (
468 document.querySelectorAll( 468 document.querySelectorAll(
469 '.profileboard-record' 469 ".profileboard-record"
470 )[index % 20] as HTMLInputElement 470 )[index % 20] as HTMLInputElement
471 ).style.height === '44px' || 471 ).style.height === "44px" ||
472 ( 472 (
473 document.querySelectorAll( 473 document.querySelectorAll(
474 '.profileboard-record' 474 ".profileboard-record"
475 )[index % 20] as HTMLInputElement 475 )[index % 20] as HTMLInputElement
476 ).style.height === '' 476 ).style.height === ""
477 ? (( 477 ? ((
478 document.querySelectorAll( 478 document.querySelectorAll(
479 '.profileboard-record' 479 ".profileboard-record"
480 )[index % 20] as HTMLInputElement 480 )[index % 20] as HTMLInputElement
481 ).style.height = 481 ).style.height =
482 `${r.scores.length * 46}px`) 482 `${r.scores.length * 46}px`)
483 : (( 483 : ((
484 document.querySelectorAll( 484 document.querySelectorAll(
485 '.profileboard-record' 485 ".profileboard-record"
486 )[index % 20] as HTMLInputElement 486 )[index % 20] as HTMLInputElement
487 ).style.height = '44px'); 487 ).style.height = "44px");
488 }} 488 }}
489 > 489 >
490 <img src={HistoryIcon} alt="history" /> 490 <img src={HistoryIcon} alt="history" />
491 </button> 491 </button>
492 ) : ( 492 ) : (
493 '' 493 ""
494 )} 494 )}
495 </span> 495 </span>
496 </> 496 </>
497 ))} 497 ))}
498 </button> 498 </button>
499 ) : ( 499 ) : (
500 '' 500 ""
501 ) 501 )
502 ) 502 )
503 ) : maps ? ( 503 ) : maps ? (
@@ -511,42 +511,42 @@ const Profile: React.FC<ProfileProps> = ({
511 <button 511 <button
512 className="profileboard-record" 512 className="profileboard-record"
513 key={index} 513 key={index}
514 style={{ backgroundColor: '#1b1b20' }} 514 style={{ backgroundColor: "#1b1b20" }}
515 > 515 >
516 <Link to={`/maps/${r.id}`}> 516 <Link to={`/maps/${r.id}`}>
517 <span>{r.name}</span> 517 <span>{r.name}</span>
518 </Link> 518 </Link>
519 <span style={{ display: 'grid' }}>N/A</span> 519 <span style={{ display: "grid" }}>N/A</span>
520 <span style={{ display: 'grid' }}>N/A</span> 520 <span style={{ display: "grid" }}>N/A</span>
521 <span>N/A</span> 521 <span>N/A</span>
522 <span> </span> 522 <span> </span>
523 <span>N/A</span> 523 <span>N/A</span>
524 <span>N/A</span> 524 <span>N/A</span>
525 <span style={{ flexDirection: 'row-reverse' }}></span> 525 <span style={{ flexDirection: "row-reverse" }}></span>
526 </button> 526 </button>
527 ) : ( 527 ) : (
528 <button className="profileboard-record" key={index}> 528 <button className="profileboard-record" key={index}>
529 {record.scores.map((e, i) => ( 529 {record.scores.map((e, i) => (
530 <> 530 <>
531 {i !== 0 ? ( 531 {i !== 0 ? (
532 <hr style={{ gridColumn: '1 / span 8' }} /> 532 <hr style={{ gridColumn: "1 / span 8" }} />
533 ) : ( 533 ) : (
534 '' 534 ""
535 )} 535 )}
536 <Link to={`/maps/${r.id}`}> 536 <Link to={`/maps/${r.id}`}>
537 <span>{r.name}</span> 537 <span>{r.name}</span>
538 </Link> 538 </Link>
539 <span style={{ display: 'grid' }}> 539 <span style={{ display: "grid" }}>
540 {record!.scores[i].score_count} 540 {record!.scores[i].score_count}
541 </span> 541 </span>
542 <span style={{ display: 'grid' }}> 542 <span style={{ display: "grid" }}>
543 {record!.scores[i].score_count - 543 {record!.scores[i].score_count -
544 record!.map_wr_count > 544 record!.map_wr_count >
545 0 545 0
546 ? `+${record!.scores[i].score_count - record!.map_wr_count}` 546 ? `+${record!.scores[i].score_count - record!.map_wr_count}`
547 : `-`} 547 : `-`}
548 </span> 548 </span>
549 <span style={{ display: 'grid' }}> 549 <span style={{ display: "grid" }}>
550 {ticks_to_time(record!.scores[i].score_time)} 550 {ticks_to_time(record!.scores[i].score_time)}
551 </span> 551 </span>
552 <span> </span> 552 <span> </span>
@@ -555,12 +555,12 @@ const Profile: React.FC<ProfileProps> = ({
555 ) : ( 555 ) : (
556 <span> </span> 556 <span> </span>
557 )} 557 )}
558 <span>{record!.scores[i].date.split('T')[0]}</span> 558 <span>{record!.scores[i].date.split("T")[0]}</span>
559 <span style={{ flexDirection: 'row-reverse' }}> 559 <span style={{ flexDirection: "row-reverse" }}>
560 <button 560 <button
561 onClick={() => { 561 onClick={() => {
562 message( 562 message(
563 'Demo Information', 563 "Demo Information",
564 `Demo ID: ${e.demo_id}` 564 `Demo ID: ${e.demo_id}`
565 ); 565 );
566 }} 566 }}
@@ -586,31 +586,31 @@ const Profile: React.FC<ProfileProps> = ({
586 onClick={() => { 586 onClick={() => {
587 ( 587 (
588 document.querySelectorAll( 588 document.querySelectorAll(
589 '.profileboard-record' 589 ".profileboard-record"
590 )[index % 20] as HTMLInputElement 590 )[index % 20] as HTMLInputElement
591 ).style.height === '44px' || 591 ).style.height === "44px" ||
592 ( 592 (
593 document.querySelectorAll( 593 document.querySelectorAll(
594 '.profileboard-record' 594 ".profileboard-record"
595 )[index % 20] as HTMLInputElement 595 )[index % 20] as HTMLInputElement
596 ).style.height === '' 596 ).style.height === ""
597 ? (( 597 ? ((
598 document.querySelectorAll( 598 document.querySelectorAll(
599 '.profileboard-record' 599 ".profileboard-record"
600 )[index % 20] as HTMLInputElement 600 )[index % 20] as HTMLInputElement
601 ).style.height = 601 ).style.height =
602 `${record!.scores.length * 46}px`) 602 `${record!.scores.length * 46}px`)
603 : (( 603 : ((
604 document.querySelectorAll( 604 document.querySelectorAll(
605 '.profileboard-record' 605 ".profileboard-record"
606 )[index % 20] as HTMLInputElement 606 )[index % 20] as HTMLInputElement
607 ).style.height = '44px'); 607 ).style.height = "44px");
608 }} 608 }}
609 > 609 >
610 <img src={HistoryIcon} alt="history" /> 610 <img src={HistoryIcon} alt="history" />
611 </button> 611 </button>
612 ) : ( 612 ) : (
613 '' 613 ""
614 )} 614 )}
615 </span> 615 </span>
616 </> 616 </>
diff --git a/frontend/src/pages/Rankings.tsx b/frontend/src/pages/Rankings.tsx
index ab82931..275f9d0 100644
--- a/frontend/src/pages/Rankings.tsx
+++ b/frontend/src/pages/Rankings.tsx
@@ -1,16 +1,16 @@
1import React, { useEffect } from 'react'; 1import React, { useEffect } from "react";
2import { Helmet } from 'react-helmet'; 2import { Helmet } from "react-helmet";
3 3
4import RankingEntry from '@components/RankingEntry'; 4import RankingEntry from "@components/RankingEntry";
5import { 5import {
6 Ranking, 6 Ranking,
7 SteamRanking, 7 SteamRanking,
8 RankingType, 8 RankingType,
9 SteamRankingType, 9 SteamRankingType,
10} from '@customTypes/Ranking'; 10} from "@customTypes/Ranking";
11import { API } from '@api/Api'; 11import { API } from "@api/Api";
12 12
13import '@css/Rankings.css'; 13import "@css/Rankings.css";
14 14
15const Rankings: React.FC = () => { 15const Rankings: React.FC = () => {
16 const [leaderboardData, setLeaderboardData] = React.useState< 16 const [leaderboardData, setLeaderboardData] = React.useState<
@@ -122,7 +122,7 @@ const Rankings: React.FC = () => {
122 _fetch_rankings(); 122 _fetch_rankings();
123 setCurrentRankingType(LeaderboardTypes.official); 123 setCurrentRankingType(LeaderboardTypes.official);
124 }} 124 }}
125 className={`nav-1-btn ${currentRankingType === LeaderboardTypes.official ? 'selected' : ''}`} 125 className={`nav-1-btn ${currentRankingType === LeaderboardTypes.official ? "selected" : ""}`}
126 > 126 >
127 <span>Official (LPHUB)</span> 127 <span>Official (LPHUB)</span>
128 </button> 128 </button>
@@ -131,7 +131,7 @@ const Rankings: React.FC = () => {
131 __dev_fetch_unofficial_rankings(); 131 __dev_fetch_unofficial_rankings();
132 setCurrentRankingType(LeaderboardTypes.unofficial); 132 setCurrentRankingType(LeaderboardTypes.unofficial);
133 }} 133 }}
134 className={`nav-1-btn ${currentRankingType === LeaderboardTypes.unofficial ? 'selected' : ''}`} 134 className={`nav-1-btn ${currentRankingType === LeaderboardTypes.unofficial ? "selected" : ""}`}
135 > 135 >
136 <span>Unofficial (Steam)</span> 136 <span>Unofficial (Steam)</span>
137 </button> 137 </button>
@@ -143,7 +143,7 @@ const Rankings: React.FC = () => {
143 onClick={() => 143 onClick={() =>
144 _set_current_leaderboard(RankingCategories.rankings_singleplayer) 144 _set_current_leaderboard(RankingCategories.rankings_singleplayer)
145 } 145 }
146 className={`nav-2-btn ${currentLeaderboardType === RankingCategories.rankings_singleplayer ? 'selected' : ''}`} 146 className={`nav-2-btn ${currentLeaderboardType === RankingCategories.rankings_singleplayer ? "selected" : ""}`}
147 > 147 >
148 <span>Singleplayer</span> 148 <span>Singleplayer</span>
149 </button> 149 </button>
@@ -151,7 +151,7 @@ const Rankings: React.FC = () => {
151 onClick={() => 151 onClick={() =>
152 _set_current_leaderboard(RankingCategories.rankings_multiplayer) 152 _set_current_leaderboard(RankingCategories.rankings_multiplayer)
153 } 153 }
154 className={`nav-2-btn ${currentLeaderboardType === RankingCategories.rankings_multiplayer ? 'selected' : ''}`} 154 className={`nav-2-btn ${currentLeaderboardType === RankingCategories.rankings_multiplayer ? "selected" : ""}`}
155 > 155 >
156 <span>Cooperative</span> 156 <span>Cooperative</span>
157 </button> 157 </button>
@@ -159,7 +159,7 @@ const Rankings: React.FC = () => {
159 onClick={() => 159 onClick={() =>
160 _set_current_leaderboard(RankingCategories.rankings_overall) 160 _set_current_leaderboard(RankingCategories.rankings_overall)
161 } 161 }
162 className={`nav-2-btn ${currentLeaderboardType === RankingCategories.rankings_overall ? 'selected' : ''}`} 162 className={`nav-2-btn ${currentLeaderboardType === RankingCategories.rankings_overall ? "selected" : ""}`}
163 > 163 >
164 <span>Overall</span> 164 <span>Overall</span>
165 </button> 165 </button>
@@ -191,9 +191,9 @@ const Rankings: React.FC = () => {
191 {leaderboardLoad ? null : ( 191 {leaderboardLoad ? null : (
192 <div 192 <div
193 style={{ 193 style={{
194 display: 'flex', 194 display: "flex",
195 justifyContent: 'center', 195 justifyContent: "center",
196 margin: '30px 0px', 196 margin: "30px 0px",
197 }} 197 }}
198 > 198 >
199 <span className="loader"></span> 199 <span className="loader"></span>
diff --git a/frontend/src/pages/Rules.tsx b/frontend/src/pages/Rules.tsx
index 7a774bc..91027a0 100644
--- a/frontend/src/pages/Rules.tsx
+++ b/frontend/src/pages/Rules.tsx
@@ -1,25 +1,25 @@
1import React from 'react'; 1import React from "react";
2import ReactMarkdown from 'react-markdown'; 2import ReactMarkdown from "react-markdown";
3import { Helmet } from 'react-helmet'; 3import { Helmet } from "react-helmet";
4 4
5import '@css/Rules.css'; 5import "@css/Rules.css";
6 6
7const Rules: React.FC = () => { 7const Rules: React.FC = () => {
8 const [rulesText, setRulesText] = React.useState<string>(''); 8 const [rulesText, setRulesText] = React.useState<string>("");
9 9
10 React.useEffect(() => { 10 React.useEffect(() => {
11 const fetchRules = async () => { 11 const fetchRules = async () => {
12 try { 12 try {
13 const response = await fetch( 13 const response = await fetch(
14 'https://raw.githubusercontent.com/pektezol/lphub/main/RULES.md' 14 "https://raw.githubusercontent.com/pektezol/lphub/main/RULES.md"
15 ); 15 );
16 if (!response.ok) { 16 if (!response.ok) {
17 throw new Error('Failed to fetch README'); 17 throw new Error("Failed to fetch README");
18 } 18 }
19 const rulesText = await response.text(); 19 const rulesText = await response.text();
20 setRulesText(rulesText); 20 setRulesText(rulesText);
21 } catch (error) { 21 } catch (error) {
22 console.error('Error fetching Rules:', error); 22 console.error("Error fetching Rules:", error);
23 } 23 }
24 // setRulesText(rulesText) 24 // setRulesText(rulesText)
25 }; 25 };
diff --git a/frontend/src/pages/User.tsx b/frontend/src/pages/User.tsx
index 29d0041..0198034 100644
--- a/frontend/src/pages/User.tsx
+++ b/frontend/src/pages/User.tsx
@@ -1,6 +1,6 @@
1import React from 'react'; 1import React from "react";
2import { Link, useLocation, useNavigate } from 'react-router-dom'; 2import { Link, useLocation, useNavigate } from "react-router-dom";
3import { Helmet } from 'react-helmet'; 3import { Helmet } from "react-helmet";
4 4
5import { 5import {
6 SteamIcon, 6 SteamIcon,
@@ -13,14 +13,14 @@ import {
13 ThreedotIcon, 13 ThreedotIcon,
14 DownloadIcon, 14 DownloadIcon,
15 HistoryIcon, 15 HistoryIcon,
16} from '@images/Images'; 16} from "@images/Images";
17import { UserProfile } from '@customTypes/Profile'; 17import { UserProfile } from "@customTypes/Profile";
18import { Game, GameChapters } from '@customTypes/Game'; 18import { Game, GameChapters } from "@customTypes/Game";
19import { Map } from '@customTypes/Map'; 19import { Map } from "@customTypes/Map";
20import { API } from '@api/Api'; 20import { API } from "@api/Api";
21import { ticks_to_time } from '@utils/Time'; 21import { ticks_to_time } from "@utils/Time";
22import '@css/Profile.css'; 22import "@css/Profile.css";
23import useMessage from '@hooks/UseMessage'; 23import useMessage from "@hooks/UseMessage";
24 24
25interface UserProps { 25interface UserProps {
26 profile?: UserProfile; 26 profile?: UserProfile;
@@ -37,8 +37,8 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
37 const [pageNumber, setPageNumber] = React.useState(1); 37 const [pageNumber, setPageNumber] = React.useState(1);
38 const [pageMax, setPageMax] = React.useState(0); 38 const [pageMax, setPageMax] = React.useState(0);
39 39
40 const [game, setGame] = React.useState('0'); 40 const [game, setGame] = React.useState("0");
41 const [chapter, setChapter] = React.useState('0'); 41 const [chapter, setChapter] = React.useState("0");
42 const [chapterData, setChapterData] = React.useState<GameChapters | null>( 42 const [chapterData, setChapterData] = React.useState<GameChapters | null>(
43 null 43 null
44 ); 44 );
@@ -48,9 +48,9 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
48 const navigate = useNavigate(); 48 const navigate = useNavigate();
49 49
50 const _fetch_user = React.useCallback(async () => { 50 const _fetch_user = React.useCallback(async () => {
51 const userID = location.pathname.split('/')[2]; 51 const userID = location.pathname.split("/")[2];
52 if (token && profile && profile.profile && profile.steam_id === userID) { 52 if (token && profile && profile.profile && profile.steam_id === userID) {
53 navigate('/profile'); 53 navigate("/profile");
54 return; 54 return;
55 } 55 }
56 const userData = await API.get_user(userID); 56 const userData = await API.get_user(userID);
@@ -58,7 +58,7 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
58 }, [location.pathname, token, profile, navigate]); 58 }, [location.pathname, token, profile, navigate]);
59 59
60 const _get_game_chapters = React.useCallback(async () => { 60 const _get_game_chapters = React.useCallback(async () => {
61 if (game !== '0') { 61 if (game !== "0") {
62 const gameChapters = await API.get_games_chapters(game); 62 const gameChapters = await API.get_games_chapters(game);
63 setChapterData(gameChapters); 63 setChapterData(gameChapters);
64 } else { 64 } else {
@@ -68,7 +68,7 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
68 }, [game, user]); 68 }, [game, user]);
69 69
70 const _get_game_maps = React.useCallback(async () => { 70 const _get_game_maps = React.useCallback(async () => {
71 if (chapter === '0') { 71 if (chapter === "0") {
72 const gameMaps = await API.get_game_maps(game); 72 const gameMaps = await API.get_game_maps(game);
73 setMaps(gameMaps); 73 setMaps(gameMaps);
74 setPageMax(Math.ceil(gameMaps.length / 20)); 74 setPageMax(Math.ceil(gameMaps.length / 20));
@@ -92,7 +92,7 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
92 }, [user, game, location, _get_game_chapters]); 92 }, [user, game, location, _get_game_chapters]);
93 93
94 React.useEffect(() => { 94 React.useEffect(() => {
95 if (user && game !== '0') { 95 if (user && game !== "0") {
96 _get_game_maps(); 96 _get_game_maps();
97 } 97 }
98 }, [user, game, chapter, location, _get_game_maps]); 98 }, [user, game, chapter, location, _get_game_maps]);
@@ -116,8 +116,8 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
116 <div> 116 <div>
117 <div>{user.user_name}</div> 117 <div>{user.user_name}</div>
118 <div> 118 <div>
119 {user.country_code === 'XX' ? ( 119 {user.country_code === "XX" ? (
120 '' 120 ""
121 ) : ( 121 ) : (
122 <img 122 <img
123 src={`https://flagcdn.com/w80/${user.country_code.toLowerCase()}.jpg`} 123 src={`https://flagcdn.com/w80/${user.country_code.toLowerCase()}.jpg`}
@@ -137,32 +137,32 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
137 </div> 137 </div>
138 </div> 138 </div>
139 <div> 139 <div>
140 {user.links.steam === '-' ? ( 140 {user.links.steam === "-" ? (
141 '' 141 ""
142 ) : ( 142 ) : (
143 <a href={user.links.steam}> 143 <a href={user.links.steam}>
144 <img src={SteamIcon} alt="Steam" /> 144 <img src={SteamIcon} alt="Steam" />
145 </a> 145 </a>
146 )} 146 )}
147 {user.links.twitch === '-' ? ( 147 {user.links.twitch === "-" ? (
148 '' 148 ""
149 ) : ( 149 ) : (
150 <a href={user.links.twitch}> 150 <a href={user.links.twitch}>
151 <img src={TwitchIcon} alt="Twitch" /> 151 <img src={TwitchIcon} alt="Twitch" />
152 </a> 152 </a>
153 )} 153 )}
154 {user.links.youtube === '-' ? ( 154 {user.links.youtube === "-" ? (
155 '' 155 ""
156 ) : ( 156 ) : (
157 <a href={user.links.youtube}> 157 <a href={user.links.youtube}>
158 <img src={YouTubeIcon} alt="Youtube" /> 158 <img src={YouTubeIcon} alt="Youtube" />
159 </a> 159 </a>
160 )} 160 )}
161 {user.links.p2sr === '-' ? ( 161 {user.links.p2sr === "-" ? (
162 '' 162 ""
163 ) : ( 163 ) : (
164 <a href={user.links.p2sr}> 164 <a href={user.links.p2sr}>
165 <img src={PortalIcon} alt="P2SR" style={{ padding: '0' }} /> 165 <img src={PortalIcon} alt="P2SR" style={{ padding: "0" }} />
166 </a> 166 </a>
167 )} 167 )}
168 </div> 168 </div>
@@ -172,8 +172,8 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
172 <span>Overall</span> 172 <span>Overall</span>
173 <span> 173 <span>
174 {user.rankings.overall.rank === 0 174 {user.rankings.overall.rank === 0
175 ? 'N/A ' 175 ? "N/A "
176 : '#' + user.rankings.overall.rank + ' '} 176 : "#" + user.rankings.overall.rank + " "}
177 <span> 177 <span>
178 ({user.rankings.overall.completion_count}/ 178 ({user.rankings.overall.completion_count}/
179 {user.rankings.overall.completion_total}) 179 {user.rankings.overall.completion_total})
@@ -184,8 +184,8 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
184 <span>Singleplayer</span> 184 <span>Singleplayer</span>
185 <span> 185 <span>
186 {user.rankings.singleplayer.rank === 0 186 {user.rankings.singleplayer.rank === 0
187 ? 'N/A ' 187 ? "N/A "
188 : '#' + user.rankings.singleplayer.rank + ' '} 188 : "#" + user.rankings.singleplayer.rank + " "}
189 <span> 189 <span>
190 ({user.rankings.singleplayer.completion_count}/ 190 ({user.rankings.singleplayer.completion_count}/
191 {user.rankings.singleplayer.completion_total}) 191 {user.rankings.singleplayer.completion_total})
@@ -196,8 +196,8 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
196 <span>Cooperative</span> 196 <span>Cooperative</span>
197 <span> 197 <span>
198 {user.rankings.cooperative.rank === 0 198 {user.rankings.cooperative.rank === 0
199 ? 'N/A ' 199 ? "N/A "
200 : '#' + user.rankings.cooperative.rank + ' '} 200 : "#" + user.rankings.cooperative.rank + " "}
201 <span> 201 <span>
202 ({user.rankings.cooperative.completion_count}/ 202 ({user.rankings.cooperative.completion_count}/
203 {user.rankings.cooperative.completion_total}) 203 {user.rankings.cooperative.completion_total})
@@ -227,15 +227,15 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
227 id="select-game" 227 id="select-game"
228 onChange={() => { 228 onChange={() => {
229 setGame( 229 setGame(
230 (document.querySelector('#select-game') as HTMLInputElement) 230 (document.querySelector("#select-game") as HTMLInputElement)
231 .value 231 .value
232 ); 232 );
233 setChapter('0'); 233 setChapter("0");
234 const chapterSelect = document.querySelector( 234 const chapterSelect = document.querySelector(
235 '#select-chapter' 235 "#select-chapter"
236 ) as HTMLSelectElement; 236 ) as HTMLSelectElement;
237 if (chapterSelect) { 237 if (chapterSelect) {
238 chapterSelect.value = '0'; 238 chapterSelect.value = "0";
239 } 239 }
240 }} 240 }}
241 > 241 >
@@ -250,7 +250,7 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
250 </select> 250 </select>
251 )} 251 )}
252 252
253 {game === '0' ? ( 253 {game === "0" ? (
254 <select disabled> 254 <select disabled>
255 <option>All Chapters</option> 255 <option>All Chapters</option>
256 </select> 256 </select>
@@ -263,7 +263,7 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
263 setChapter( 263 setChapter(
264 ( 264 (
265 document.querySelector( 265 document.querySelector(
266 '#select-chapter' 266 "#select-chapter"
267 ) as HTMLInputElement 267 ) as HTMLInputElement
268 ).value 268 ).value
269 ) 269 )
@@ -287,15 +287,15 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
287 <span>Map Name</span> 287 <span>Map Name</span>
288 <img src={SortIcon} alt="" /> 288 <img src={SortIcon} alt="" />
289 </span> 289 </span>
290 <span style={{ justifyContent: 'center' }}> 290 <span style={{ justifyContent: "center" }}>
291 <span>Portals</span> 291 <span>Portals</span>
292 <img src={SortIcon} alt="" /> 292 <img src={SortIcon} alt="" />
293 </span> 293 </span>
294 <span style={{ justifyContent: 'center' }}> 294 <span style={{ justifyContent: "center" }}>
295 <span>WRΔ </span> 295 <span>WRΔ </span>
296 <img src={SortIcon} alt="" /> 296 <img src={SortIcon} alt="" />
297 </span> 297 </span>
298 <span style={{ justifyContent: 'center' }}> 298 <span style={{ justifyContent: "center" }}>
299 <span>Time</span> 299 <span>Time</span>
300 <img src={SortIcon} alt="" /> 300 <img src={SortIcon} alt="" />
301 </span> 301 </span>
@@ -315,18 +315,18 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
315 if (pageNumber !== 1) { 315 if (pageNumber !== 1) {
316 setPageNumber(prevPageNumber => prevPageNumber - 1); 316 setPageNumber(prevPageNumber => prevPageNumber - 1);
317 const records = document.querySelectorAll( 317 const records = document.querySelectorAll(
318 '.profileboard-record' 318 ".profileboard-record"
319 ); 319 );
320 records.forEach(r => { 320 records.forEach(r => {
321 (r as HTMLInputElement).style.height = '44px'; 321 (r as HTMLInputElement).style.height = "44px";
322 }); 322 });
323 } 323 }
324 }} 324 }}
325 > 325 >
326 <i 326 <i
327 className="triangle" 327 className="triangle"
328 style={{ position: 'relative', left: '-5px' }} 328 style={{ position: "relative", left: "-5px" }}
329 ></i>{' '} 329 ></i>{" "}
330 </button> 330 </button>
331 <span> 331 <span>
332 {pageNumber}/{pageMax} 332 {pageNumber}/{pageMax}
@@ -336,10 +336,10 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
336 if (pageNumber !== pageMax) { 336 if (pageNumber !== pageMax) {
337 setPageNumber(prevPageNumber => prevPageNumber + 1); 337 setPageNumber(prevPageNumber => prevPageNumber + 1);
338 const records = document.querySelectorAll( 338 const records = document.querySelectorAll(
339 '.profileboard-record' 339 ".profileboard-record"
340 ); 340 );
341 records.forEach(r => { 341 records.forEach(r => {
342 (r as HTMLInputElement).style.height = '44px'; 342 (r as HTMLInputElement).style.height = "44px";
343 }); 343 });
344 } 344 }
345 }} 345 }}
@@ -347,18 +347,18 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
347 <i 347 <i
348 className="triangle" 348 className="triangle"
349 style={{ 349 style={{
350 position: 'relative', 350 position: "relative",
351 left: '5px', 351 left: "5px",
352 transform: 'rotate(180deg)', 352 transform: "rotate(180deg)",
353 }} 353 }}
354 ></i>{' '} 354 ></i>{" "}
355 </button> 355 </button>
356 </div> 356 </div>
357 </div> 357 </div>
358 </div> 358 </div>
359 <hr /> 359 <hr />
360 <div id="profileboard-records"> 360 <div id="profileboard-records">
361 {game === '0' ? ( 361 {game === "0" ? (
362 user.records 362 user.records
363 .sort((a, b) => a.map_id - b.map_id) 363 .sort((a, b) => a.map_id - b.map_id)
364 .map((r, index) => 364 .map((r, index) =>
@@ -367,33 +367,33 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
367 {r.scores.map((e, i) => ( 367 {r.scores.map((e, i) => (
368 <> 368 <>
369 {i !== 0 ? ( 369 {i !== 0 ? (
370 <hr style={{ gridColumn: '1 / span 8' }} /> 370 <hr style={{ gridColumn: "1 / span 8" }} />
371 ) : ( 371 ) : (
372 '' 372 ""
373 )} 373 )}
374 374
375 <Link to={`/maps/${r.map_id}`}> 375 <Link to={`/maps/${r.map_id}`}>
376 <span>{r.map_name}</span> 376 <span>{r.map_name}</span>
377 </Link> 377 </Link>
378 378
379 <span style={{ display: 'grid' }}>{e.score_count}</span> 379 <span style={{ display: "grid" }}>{e.score_count}</span>
380 380
381 <span style={{ display: 'grid' }}> 381 <span style={{ display: "grid" }}>
382 {e.score_count - r.map_wr_count > 0 382 {e.score_count - r.map_wr_count > 0
383 ? `+${e.score_count - r.map_wr_count}` 383 ? `+${e.score_count - r.map_wr_count}`
384 : `-`} 384 : `-`}
385 </span> 385 </span>
386 <span style={{ display: 'grid' }}> 386 <span style={{ display: "grid" }}>
387 {ticks_to_time(e.score_time)} 387 {ticks_to_time(e.score_time)}
388 </span> 388 </span>
389 <span> </span> 389 <span> </span>
390 {i === 0 ? <span>#{r.placement}</span> : <span> </span>} 390 {i === 0 ? <span>#{r.placement}</span> : <span> </span>}
391 <span>{e.date.split('T')[0]}</span> 391 <span>{e.date.split("T")[0]}</span>
392 <span style={{ flexDirection: 'row-reverse' }}> 392 <span style={{ flexDirection: "row-reverse" }}>
393 <button 393 <button
394 onClick={() => { 394 onClick={() => {
395 message( 395 message(
396 'Demo Information', 396 "Demo Information",
397 `Demo ID: ${e.demo_id}` 397 `Demo ID: ${e.demo_id}`
398 ); 398 );
399 }} 399 }}
@@ -412,38 +412,38 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
412 onClick={() => { 412 onClick={() => {
413 ( 413 (
414 document.querySelectorAll( 414 document.querySelectorAll(
415 '.profileboard-record' 415 ".profileboard-record"
416 )[index % 20] as HTMLInputElement 416 )[index % 20] as HTMLInputElement
417 ).style.height === '44px' || 417 ).style.height === "44px" ||
418 ( 418 (
419 document.querySelectorAll( 419 document.querySelectorAll(
420 '.profileboard-record' 420 ".profileboard-record"
421 )[index % 20] as HTMLInputElement 421 )[index % 20] as HTMLInputElement
422 ).style.height === '' 422 ).style.height === ""
423 ? (( 423 ? ((
424 document.querySelectorAll( 424 document.querySelectorAll(
425 '.profileboard-record' 425 ".profileboard-record"
426 )[index % 20] as HTMLInputElement 426 )[index % 20] as HTMLInputElement
427 ).style.height = 427 ).style.height =
428 `${r.scores.length * 46}px`) 428 `${r.scores.length * 46}px`)
429 : (( 429 : ((
430 document.querySelectorAll( 430 document.querySelectorAll(
431 '.profileboard-record' 431 ".profileboard-record"
432 )[index % 20] as HTMLInputElement 432 )[index % 20] as HTMLInputElement
433 ).style.height = '44px'); 433 ).style.height = "44px");
434 }} 434 }}
435 > 435 >
436 <img src={HistoryIcon} alt="history" /> 436 <img src={HistoryIcon} alt="history" />
437 </button> 437 </button>
438 ) : ( 438 ) : (
439 '' 439 ""
440 )} 440 )}
441 </span> 441 </span>
442 </> 442 </>
443 ))} 443 ))}
444 </button> 444 </button>
445 ) : ( 445 ) : (
446 '' 446 ""
447 ) 447 )
448 ) 448 )
449 ) : maps ? ( 449 ) : maps ? (
@@ -457,42 +457,42 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
457 <button 457 <button
458 className="profileboard-record" 458 className="profileboard-record"
459 key={index} 459 key={index}
460 style={{ backgroundColor: '#1b1b20' }} 460 style={{ backgroundColor: "#1b1b20" }}
461 > 461 >
462 <Link to={`/maps/${r.id}`}> 462 <Link to={`/maps/${r.id}`}>
463 <span>{r.name}</span> 463 <span>{r.name}</span>
464 </Link> 464 </Link>
465 <span style={{ display: 'grid' }}>N/A</span> 465 <span style={{ display: "grid" }}>N/A</span>
466 <span style={{ display: 'grid' }}>N/A</span> 466 <span style={{ display: "grid" }}>N/A</span>
467 <span>N/A</span> 467 <span>N/A</span>
468 <span> </span> 468 <span> </span>
469 <span>N/A</span> 469 <span>N/A</span>
470 <span>N/A</span> 470 <span>N/A</span>
471 <span style={{ flexDirection: 'row-reverse' }}></span> 471 <span style={{ flexDirection: "row-reverse" }}></span>
472 </button> 472 </button>
473 ) : ( 473 ) : (
474 <button className="profileboard-record" key={index}> 474 <button className="profileboard-record" key={index}>
475 {record.scores.map((e, i) => ( 475 {record.scores.map((e, i) => (
476 <> 476 <>
477 {i !== 0 ? ( 477 {i !== 0 ? (
478 <hr style={{ gridColumn: '1 / span 8' }} /> 478 <hr style={{ gridColumn: "1 / span 8" }} />
479 ) : ( 479 ) : (
480 '' 480 ""
481 )} 481 )}
482 <Link to={`/maps/${r.id}`}> 482 <Link to={`/maps/${r.id}`}>
483 <span>{r.name}</span> 483 <span>{r.name}</span>
484 </Link> 484 </Link>
485 <span style={{ display: 'grid' }}> 485 <span style={{ display: "grid" }}>
486 {record!.scores[i].score_count} 486 {record!.scores[i].score_count}
487 </span> 487 </span>
488 <span style={{ display: 'grid' }}> 488 <span style={{ display: "grid" }}>
489 {record!.scores[i].score_count - 489 {record!.scores[i].score_count -
490 record!.map_wr_count > 490 record!.map_wr_count >
491 0 491 0
492 ? `+${record!.scores[i].score_count - record!.map_wr_count}` 492 ? `+${record!.scores[i].score_count - record!.map_wr_count}`
493 : `-`} 493 : `-`}
494 </span> 494 </span>
495 <span style={{ display: 'grid' }}> 495 <span style={{ display: "grid" }}>
496 {ticks_to_time(record!.scores[i].score_time)} 496 {ticks_to_time(record!.scores[i].score_time)}
497 </span> 497 </span>
498 <span> </span> 498 <span> </span>
@@ -501,12 +501,12 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
501 ) : ( 501 ) : (
502 <span> </span> 502 <span> </span>
503 )} 503 )}
504 <span>{record!.scores[i].date.split('T')[0]}</span> 504 <span>{record!.scores[i].date.split("T")[0]}</span>
505 <span style={{ flexDirection: 'row-reverse' }}> 505 <span style={{ flexDirection: "row-reverse" }}>
506 <button 506 <button
507 onClick={() => { 507 onClick={() => {
508 message( 508 message(
509 'Demo Information', 509 "Demo Information",
510 `Demo ID: ${e.demo_id}` 510 `Demo ID: ${e.demo_id}`
511 ); 511 );
512 }} 512 }}
@@ -525,31 +525,31 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => {
525 onClick={() => { 525 onClick={() => {
526 ( 526 (
527 document.querySelectorAll( 527 document.querySelectorAll(
528 '.profileboard-record' 528 ".profileboard-record"
529 )[index % 20] as HTMLInputElement 529 )[index % 20] as HTMLInputElement
530 ).style.height === '44px' || 530 ).style.height === "44px" ||
531 ( 531 (
532 document.querySelectorAll( 532 document.querySelectorAll(
533 '.profileboard-record' 533 ".profileboard-record"
534 )[index % 20] as HTMLInputElement 534 )[index % 20] as HTMLInputElement
535 ).style.height === '' 535 ).style.height === ""
536 ? (( 536 ? ((
537 document.querySelectorAll( 537 document.querySelectorAll(
538 '.profileboard-record' 538 ".profileboard-record"
539 )[index % 20] as HTMLInputElement 539 )[index % 20] as HTMLInputElement
540 ).style.height = 540 ).style.height =
541 `${record!.scores.length * 46}px`) 541 `${record!.scores.length * 46}px`)
542 : (( 542 : ((
543 document.querySelectorAll( 543 document.querySelectorAll(
544 '.profileboard-record' 544 ".profileboard-record"
545 )[index % 20] as HTMLInputElement 545 )[index % 20] as HTMLInputElement
546 ).style.height = '44px'); 546 ).style.height = "44px");
547 }} 547 }}
548 > 548 >
549 <img src={HistoryIcon} alt="history" /> 549 <img src={HistoryIcon} alt="history" />
550 </button> 550 </button>
551 ) : ( 551 ) : (
552 '' 552 ""
553 )} 553 )}
554 </span> 554 </span>
555 </> 555 </>