diff options
Diffstat (limited to '')
29 files changed, 212 insertions, 171 deletions
diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs index 4667d92..d3c22e8 100644 --- a/frontend/eslint.config.mjs +++ b/frontend/eslint.config.mjs | |||
| @@ -16,6 +16,7 @@ export default [ | |||
| 16 | 'no-empty': 'warn', | 16 | 'no-empty': 'warn', |
| 17 | 'indent': ['warn', 2], | 17 | 'indent': ['warn', 2], |
| 18 | 'quotes': ['warn', 'double'], | 18 | 'quotes': ['warn', 'double'], |
| 19 | 'semi': ['warn', 'always'], | ||
| 19 | '@typescript-eslint/no-explicit-any': 'warn', | 20 | '@typescript-eslint/no-explicit-any': 'warn', |
| 20 | '@typescript-eslint/no-unused-vars': 'warn', | 21 | '@typescript-eslint/no-unused-vars': 'warn', |
| 21 | '@typescript-eslint/no-unused-expressions': 'warn', | 22 | '@typescript-eslint/no-unused-expressions': 'warn', |
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 | ||
| 52 | const BASE_API_URL: string = import.meta.env.DEV | 52 | const 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 | ||
| 56 | export function url(path: string): string { | 56 | export 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"; | |||
| 2 | import { url } from "@api/Api"; | 2 | import { url } from "@api/Api"; |
| 3 | 3 | ||
| 4 | export const get_token = async (): Promise<string | undefined> => { | 4 | export 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"; | |||
| 6 | import { Search } from "@customTypes/Search"; | 6 | import { Search } from "@customTypes/Search"; |
| 7 | 7 | ||
| 8 | export const get_games = async (): Promise<Game[]> => { | 8 | export 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 | ||
| 13 | export const get_chapters = async (chapter_id: string): Promise<GameChapter> => { | 13 | export 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 | ||
| 18 | export const get_games_chapters = async (game_id: string): Promise<GamesChapters> => { | 18 | export 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 | ||
| 23 | export const get_game_maps = async (game_id: string): Promise<Map[]> => { | 23 | export 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 | ||
| 28 | export const get_search = async (q: string): Promise<Search> => { | 28 | export 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"; | |||
| 4 | import { MapSummary, MapLeaderboard, MapDiscussions, MapDiscussion } from "@customTypes/Map"; | 4 | import { MapSummary, MapLeaderboard, MapDiscussions, MapDiscussion } from "@customTypes/Map"; |
| 5 | 5 | ||
| 6 | export const get_map_summary = async (map_id: string): Promise<MapSummary> => { | 6 | export 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 | ||
| 99 | export const delete_map_record = async (token: string, map_id: number, record_id: number): Promise<boolean> => { | 99 | export 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 @@ | |||
| 1 | import React from "react"; | 1 | import React from "react"; |
| 2 | 2 | ||
| 3 | import "@css/Dialog.css" | 3 | import "@css/Dialog.css"; |
| 4 | 4 | ||
| 5 | interface ConfirmDialogProps { | 5 | interface 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 | ||
| 31 | export default ConfirmDialog; | 31 | export 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 | |||
| 4 | import { MapDiscussionContent } from "@customTypes/Content"; | 4 | import { MapDiscussionContent } from "@customTypes/Content"; |
| 5 | import { time_ago } from "@utils/Time"; | 5 | import { time_ago } from "@utils/Time"; |
| 6 | import { API } from "@api/Api"; | 6 | import { API } from "@api/Api"; |
| 7 | import "@css/Maps.css" | 7 | import "@css/Maps.css"; |
| 8 | import { Link } from "react-router-dom"; | 8 | import { Link } from "react-router-dom"; |
| 9 | import useConfirm from "@hooks/UseConfirm"; | 9 | import 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"; | |||
| 2 | import { Link } from "react-router-dom"; | 2 | import { Link } from "react-router-dom"; |
| 3 | 3 | ||
| 4 | import { Game, GameCategoryPortals } from "@customTypes/Game"; | 4 | import { Game, GameCategoryPortals } from "@customTypes/Game"; |
| 5 | import "@css/Games.css" | 5 | import "@css/Games.css"; |
| 6 | 6 | ||
| 7 | interface GameCategoryProps { | 7 | interface 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 | ||
| 24 | export default GameCategory; | 24 | export 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"; | |||
| 2 | import { Link } from "react-router-dom"; | 2 | import { Link } from "react-router-dom"; |
| 3 | 3 | ||
| 4 | import { Game, GameCategoryPortals } from "@customTypes/Game"; | 4 | import { Game, GameCategoryPortals } from "@customTypes/Game"; |
| 5 | import "@css/Games.css" | 5 | import "@css/Games.css"; |
| 6 | 6 | ||
| 7 | import GameCategory from "@components/GameCategory"; | 7 | import 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"; | |||
| 6 | import { ticks_to_time, time_ago } from "@utils/Time"; | 6 | import { ticks_to_time, time_ago } from "@utils/Time"; |
| 7 | import { API } from "@api/Api"; | 7 | import { API } from "@api/Api"; |
| 8 | import useMessage from "@hooks/UseMessage"; | 8 | import useMessage from "@hooks/UseMessage"; |
| 9 | import "@css/Maps.css" | 9 | import "@css/Maps.css"; |
| 10 | 10 | ||
| 11 | interface LeaderboardsProps { | 11 | interface 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 | ||
| 12 | export default MapEntry; | 12 | export 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 @@ | |||
| 1 | import React from "react"; | 1 | import React from "react"; |
| 2 | 2 | ||
| 3 | import "@css/Dialog.css" | 3 | import "@css/Dialog.css"; |
| 4 | 4 | ||
| 5 | interface MessageDialogProps { | 5 | interface 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 | ||
| 29 | export default MessageDialog; | 29 | export 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 @@ | |||
| 1 | import React from "react"; | 1 | import React from "react"; |
| 2 | 2 | ||
| 3 | import "@css/Dialog.css" | 3 | import "@css/Dialog.css"; |
| 4 | 4 | ||
| 5 | interface MessageDialogLoadProps { | 5 | interface 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 | ||
| 29 | export default MessageDialogLoad; | 29 | export 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"; | |||
| 5 | import { MapSummary } from "@customTypes/Map"; | 5 | import { MapSummary } from "@customTypes/Map"; |
| 6 | import { ModMenuContent } from "@customTypes/Content"; | 6 | import { ModMenuContent } from "@customTypes/Content"; |
| 7 | import { API } from "@api/Api"; | 7 | import { API } from "@api/Api"; |
| 8 | import "@css/ModMenu.css" | 8 | import "@css/ModMenu.css"; |
| 9 | import useConfirm from "@hooks/UseConfirm"; | 9 | import useConfirm from "@hooks/UseConfirm"; |
| 10 | 10 | ||
| 11 | interface ModMenuProps { | 11 | interface 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 | ||
| 46 | export default RankingEntry; | 46 | export 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"; | |||
| 2 | import ReactMarkdown from "react-markdown"; | 2 | import ReactMarkdown from "react-markdown"; |
| 3 | 3 | ||
| 4 | import { MapSummary } from "@customTypes/Map"; | 4 | import { MapSummary } from "@customTypes/Map"; |
| 5 | import "@css/Maps.css" | 5 | import "@css/Maps.css"; |
| 6 | 6 | ||
| 7 | interface SummaryProps { | 7 | interface 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 | ||
| 40 | export default useConfirm; | 40 | export 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 | ||
| 37 | export default useMessage; | 37 | export 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 | ||
| 35 | export default useMessageLoad; | 35 | export 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 @@ | |||
| 1 | import logo from "./png/logo.png" | 1 | import logo from "./png/logo.png"; |
| 2 | import login from "./png/login.png" | 2 | import login from "./png/login.png"; |
| 3 | import img1 from "./png/1.png"; | 3 | import img1 from "./png/1.png"; |
| 4 | import img2 from "./png/2.png"; | 4 | import img2 from "./png/2.png"; |
| 5 | import img3 from "./png/3.png"; | 5 | import 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 | ||
| 4 | import GameEntry from "@components/GameEntry"; | 4 | import GameEntry from "@components/GameEntry"; |
| 5 | import { Game } from "@customTypes/Game"; | 5 | import { Game } from "@customTypes/Game"; |
| 6 | import "@css/Maps.css" | 6 | import "@css/Maps.css"; |
| 7 | 7 | ||
| 8 | interface GamesProps { | 8 | interface 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 | ||
| 146 | export default Rankings; | 146 | export 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 | ||
| 32 | export function ticks_to_time(ticks: number) { | 32 | export 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 | ||