diff options
| author | Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> | 2025-07-24 14:40:22 +0300 |
|---|---|---|
| committer | Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> | 2025-07-24 14:40:22 +0300 |
| commit | b0d199936b546c75d4b19d99591237f0bf97fe55 (patch) | |
| tree | e9391880e7db2bd1ea8ff25d91aeea8dd98f186e /frontend/src | |
| parent | fix/frontend: fixed sidebar title size, removed unnecessary imports (diff) | |
| parent | feat/backend: add newrelic integration (#274) (diff) | |
| download | lphub-b0d199936b546c75d4b19d99591237f0bf97fe55.tar.gz lphub-b0d199936b546c75d4b19d99591237f0bf97fe55.tar.bz2 lphub-b0d199936b546c75d4b19d99591237f0bf97fe55.zip | |
Merge branch 'main' into css-overhaulcss-overhaul
Diffstat (limited to 'frontend/src')
| -rw-r--r-- | frontend/src/App.tsx | 11 | ||||
| -rw-r--r-- | frontend/src/api/Api.ts | 4 | ||||
| -rw-r--r-- | frontend/src/api/Maps.ts | 14 | ||||
| -rw-r--r-- | frontend/src/components/Leaderboards.tsx | 21 | ||||
| -rw-r--r-- | frontend/src/components/Summary.tsx | 36 | ||||
| -rw-r--r-- | frontend/src/components/UploadRunDialog.tsx | 106 | ||||
| -rw-r--r-- | frontend/src/pages/About.tsx | 4 | ||||
| -rw-r--r-- | frontend/src/pages/Games.tsx | 4 | ||||
| -rw-r--r-- | frontend/src/pages/Homepage.tsx | 6 | ||||
| -rw-r--r-- | frontend/src/pages/Maplist.tsx | 50 | ||||
| -rw-r--r-- | frontend/src/pages/Maps.tsx | 41 | ||||
| -rw-r--r-- | frontend/src/pages/Profile.tsx | 9 | ||||
| -rw-r--r-- | frontend/src/pages/Rankings.tsx | 30 | ||||
| -rw-r--r-- | frontend/src/pages/Rules.tsx | 4 | ||||
| -rw-r--r-- | frontend/src/pages/User.tsx | 9 | ||||
| -rw-r--r-- | frontend/src/types/Content.ts | 1 | ||||
| -rw-r--r-- | frontend/src/types/Map.ts | 1 | ||||
| -rw-r--r-- | frontend/src/types/MapNames.ts | 127 |
18 files changed, 330 insertions, 148 deletions
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e4bde75..bdd3adc 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | import React from 'react'; | 1 | import React from 'react'; |
| 2 | import { Routes, Route } from "react-router-dom"; | 2 | import { Routes, Route } from "react-router-dom"; |
| 3 | import { Helmet } from "react-helmet"; | ||
| 3 | 4 | ||
| 4 | import { UserProfile } from '@customTypes/Profile'; | 5 | import { UserProfile } from '@customTypes/Profile'; |
| 5 | import Sidebar from './components/Sidebar'; | 6 | import Sidebar from './components/Sidebar'; |
| @@ -66,14 +67,12 @@ const App: React.FC = () => { | |||
| 66 | _fetch_games(); | 67 | _fetch_games(); |
| 67 | }, []); | 68 | }, []); |
| 68 | 69 | ||
| 69 | if (!games) { | ||
| 70 | return ( | ||
| 71 | <></> | ||
| 72 | ) | ||
| 73 | }; | ||
| 74 | |||
| 75 | return ( | 70 | return ( |
| 76 | <> | 71 | <> |
| 72 | <Helmet> | ||
| 73 | <title>LPHUB</title> | ||
| 74 | <meta name="description" content="Least Portals Hub" /> | ||
| 75 | </Helmet> | ||
| 77 | <UploadRunDialog token={token} open={uploadRunDialog} onClose={(updateProfile) => { | 76 | <UploadRunDialog token={token} open={uploadRunDialog} onClose={(updateProfile) => { |
| 78 | setUploadRunDialog(false); | 77 | setUploadRunDialog(false); |
| 79 | if (updateProfile) { | 78 | if (updateProfile) { |
diff --git a/frontend/src/api/Api.ts b/frontend/src/api/Api.ts index 2e55ab4..862e688 100644 --- a/frontend/src/api/Api.ts +++ b/frontend/src/api/Api.ts | |||
| @@ -29,13 +29,13 @@ export const API = { | |||
| 29 | get_unofficial_rankings: () => get_unofficial_rankings(), | 29 | get_unofficial_rankings: () => get_unofficial_rankings(), |
| 30 | // Maps | 30 | // Maps |
| 31 | get_map_summary: (map_id: string) => get_map_summary(map_id), | 31 | get_map_summary: (map_id: string) => get_map_summary(map_id), |
| 32 | get_map_leaderboard: (map_id: string) => get_map_leaderboard(map_id), | 32 | get_map_leaderboard: (map_id: string, page: string) => get_map_leaderboard(map_id, page), |
| 33 | get_map_discussions: (map_id: string) => get_map_discussions(map_id), | 33 | get_map_discussions: (map_id: string) => get_map_discussions(map_id), |
| 34 | get_map_discussion: (map_id: string, discussion_id: number) => get_map_discussion(map_id, discussion_id), | 34 | get_map_discussion: (map_id: string, discussion_id: number) => get_map_discussion(map_id, discussion_id), |
| 35 | 35 | ||
| 36 | post_map_discussion: (token: string, map_id: string, content: MapDiscussionContent) => post_map_discussion(token, map_id, content), | 36 | post_map_discussion: (token: string, map_id: string, content: MapDiscussionContent) => post_map_discussion(token, map_id, content), |
| 37 | post_map_discussion_comment: (token: string, map_id: string, discussion_id: number, comment: string) => post_map_discussion_comment(token, map_id, discussion_id, comment), | 37 | post_map_discussion_comment: (token: string, map_id: string, discussion_id: number, comment: string) => post_map_discussion_comment(token, map_id, discussion_id, comment), |
| 38 | post_record: (token: string, run: UploadRunContent) => post_record(token, run), | 38 | post_record: (token: string, run: UploadRunContent, map_id: number) => post_record(token, run, map_id), |
| 39 | 39 | ||
| 40 | delete_map_discussion: (token: string, map_id: string, discussion_id: number) => delete_map_discussion(token, map_id, discussion_id), | 40 | delete_map_discussion: (token: string, map_id: string, discussion_id: number) => delete_map_discussion(token, map_id, discussion_id), |
| 41 | 41 | ||
diff --git a/frontend/src/api/Maps.ts b/frontend/src/api/Maps.ts index 89657b5..aa967ce 100644 --- a/frontend/src/api/Maps.ts +++ b/frontend/src/api/Maps.ts | |||
| @@ -8,8 +8,8 @@ export const get_map_summary = async (map_id: string): Promise<MapSummary> => { | |||
| 8 | return response.data.data; | 8 | return response.data.data; |
| 9 | }; | 9 | }; |
| 10 | 10 | ||
| 11 | export const get_map_leaderboard = async (map_id: string): Promise<MapLeaderboard | undefined> => { | 11 | export const get_map_leaderboard = async (map_id: string, page: string): Promise<MapLeaderboard | undefined> => { |
| 12 | const response = await axios.get(url(`maps/${map_id}/leaderboards`)); | 12 | const response = await axios.get(url(`maps/${map_id}/leaderboards?page=${page}`)); |
| 13 | if (!response.data.success) { | 13 | if (!response.data.success) { |
| 14 | return undefined; | 14 | return undefined; |
| 15 | } | 15 | } |
| @@ -73,9 +73,9 @@ export const delete_map_discussion = async (token: string, map_id: string, discu | |||
| 73 | return response.data.success; | 73 | return response.data.success; |
| 74 | }; | 74 | }; |
| 75 | 75 | ||
| 76 | export const post_record = async (token: string, run: UploadRunContent): Promise<[boolean, string]> => { | 76 | export const post_record = async (token: string, run: UploadRunContent, map_id: number): Promise<[boolean, string]> => { |
| 77 | if (run.partner_demo) { | 77 | if (run.partner_demo) { |
| 78 | const response = await axios.postForm(url(`maps/${run.map_id}/record`), { | 78 | const response = await axios.postForm(url(`maps/${map_id}/record`), { |
| 79 | "host_demo": run.host_demo, | 79 | "host_demo": run.host_demo, |
| 80 | "partner_demo": run.partner_demo, | 80 | "partner_demo": run.partner_demo, |
| 81 | }, { | 81 | }, { |
| @@ -83,16 +83,16 @@ export const post_record = async (token: string, run: UploadRunContent): Promise | |||
| 83 | "Authorization": token, | 83 | "Authorization": token, |
| 84 | } | 84 | } |
| 85 | }); | 85 | }); |
| 86 | return [ response.data.success, response.data.message ]; | 86 | return [response.data.success, response.data.message]; |
| 87 | } else { | 87 | } else { |
| 88 | const response = await axios.postForm(url(`maps/${run.map_id}/record`), { | 88 | const response = await axios.postForm(url(`maps/${map_id}/record`), { |
| 89 | "host_demo": run.host_demo, | 89 | "host_demo": run.host_demo, |
| 90 | }, { | 90 | }, { |
| 91 | headers: { | 91 | headers: { |
| 92 | "Authorization": token, | 92 | "Authorization": token, |
| 93 | } | 93 | } |
| 94 | }); | 94 | }); |
| 95 | return [ response.data.success, response.data.message ]; | 95 | return [response.data.success, response.data.message]; |
| 96 | } | 96 | } |
| 97 | } | 97 | } |
| 98 | 98 | ||
diff --git a/frontend/src/components/Leaderboards.tsx b/frontend/src/components/Leaderboards.tsx index 4a8b463..fb614fa 100644 --- a/frontend/src/components/Leaderboards.tsx +++ b/frontend/src/components/Leaderboards.tsx | |||
| @@ -1,20 +1,33 @@ | |||
| 1 | import React from 'react'; | 1 | import React from 'react'; |
| 2 | import { Link } from 'react-router-dom'; | 2 | import { Link, useNavigate } from 'react-router-dom'; |
| 3 | 3 | ||
| 4 | import { DownloadIcon, ThreedotIcon } from '@images/Images'; | 4 | import { DownloadIcon, ThreedotIcon } from '@images/Images'; |
| 5 | import { MapLeaderboard } from '@customTypes/Map'; | 5 | 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 useMessage from "@hooks/UseMessage"; | 8 | import useMessage from "@hooks/UseMessage"; |
| 8 | import "@css/Maps.css" | 9 | import "@css/Maps.css" |
| 9 | 10 | ||
| 10 | interface LeaderboardsProps { | 11 | interface LeaderboardsProps { |
| 11 | data?: MapLeaderboard; | 12 | mapID: string; |
| 12 | } | 13 | } |
| 13 | 14 | ||
| 14 | const Leaderboards: React.FC<LeaderboardsProps> = ({ data }) => { | 15 | const Leaderboards: React.FC<LeaderboardsProps> = ({ mapID }) => { |
| 16 | const navigate = useNavigate(); | ||
| 17 | const [data, setData] = React.useState<MapLeaderboard | undefined>(undefined); | ||
| 18 | const [pageNumber, setPageNumber] = React.useState<number>(1); | ||
| 19 | |||
| 20 | const _fetch_map_leaderboards = async () => { | ||
| 21 | const mapLeaderboards = await API.get_map_leaderboard(mapID, pageNumber.toString()); | ||
| 22 | setData(mapLeaderboards); | ||
| 23 | }; | ||
| 15 | 24 | ||
| 16 | const { message, MessageDialogComponent } = useMessage(); | 25 | const { message, MessageDialogComponent } = useMessage(); |
| 17 | const [pageNumber, setPageNumber] = React.useState<number>(1); | 26 | |
| 27 | React.useEffect(() => { | ||
| 28 | _fetch_map_leaderboards(); | ||
| 29 | console.log(data); | ||
| 30 | }, [pageNumber, navigate]) | ||
| 18 | 31 | ||
| 19 | if (!data) { | 32 | if (!data) { |
| 20 | return ( | 33 | return ( |
diff --git a/frontend/src/components/Summary.tsx b/frontend/src/components/Summary.tsx index 4bcaa6a..7da2f1e 100644 --- a/frontend/src/components/Summary.tsx +++ b/frontend/src/components/Summary.tsx | |||
| @@ -140,20 +140,34 @@ const Summary: React.FC<SummaryProps> = ({ selectedRun, setSelectedRun, data }) | |||
| 140 | <section id='section4' className='summary1'> | 140 | <section id='section4' className='summary1'> |
| 141 | <div id='difficulty'> | 141 | <div id='difficulty'> |
| 142 | <span>Difficulty</span> | 142 | <span>Difficulty</span> |
| 143 | {data.summary.routes[selectedRun].rating === 0 && (<span>N/A</span>)} | 143 | {data.map.difficulty <= 2 && (<span style={{ color: "lime" }}>Very easy</span>)} |
| 144 | {data.summary.routes[selectedRun].rating === 1 && (<span style={{ color: "lime" }}>Very easy</span>)} | 144 | {data.map.difficulty > 2 && data.map.difficulty <= 4 && (<span style={{ color: "green" }}>Easy</span>)} |
| 145 | {data.summary.routes[selectedRun].rating === 2 && (<span style={{ color: "green" }}>Easy</span>)} | 145 | {data.map.difficulty > 4 && data.map.difficulty <= 6 && (<span style={{ color: "yellow" }}>Medium</span>)} |
| 146 | {data.summary.routes[selectedRun].rating === 3 && (<span style={{ color: "yellow" }}>Medium</span>)} | 146 | {data.map.difficulty > 6 && data.map.difficulty <= 8 && (<span style={{ color: "orange" }}>Hard</span>)} |
| 147 | {data.summary.routes[selectedRun].rating === 4 && (<span style={{ color: "orange" }}>Hard</span>)} | 147 | {data.map.difficulty > 8 && data.map.difficulty <= 10 && (<span style={{ color: "red" }}>Very hard</span>)} |
| 148 | {data.summary.routes[selectedRun].rating === 5 && (<span style={{ color: "red" }}>Very hard</span>)} | ||
| 149 | <div> | 148 | <div> |
| 150 | {data.summary.routes[selectedRun].rating === 1 ? (<div className='difficulty-rating' style={{ backgroundColor: "lime" }}></div>) : (<div className='difficulty-rating'></div>)} | 149 | {data.map.difficulty <= 2 ? (<div className='difficulty-rating' style={{ backgroundColor: "lime" }}></div>) : (<div className='difficulty-rating'></div>)} |
| 151 | {data.summary.routes[selectedRun].rating === 2 ? (<div className='difficulty-rating' style={{ backgroundColor: "green" }}></div>) : (<div className='difficulty-rating'></div>)} | 150 | {data.map.difficulty > 2 && data.map.difficulty <= 4 ? (<div className='difficulty-rating' style={{ backgroundColor: "green" }}></div>) : (<div className='difficulty-rating'></div>)} |
| 152 | {data.summary.routes[selectedRun].rating === 3 ? (<div className='difficulty-rating' style={{ backgroundColor: "yellow" }}></div>) : (<div className='difficulty-rating'></div>)} | 151 | {data.map.difficulty > 4 && data.map.difficulty <= 6 ? (<div className='difficulty-rating' style={{ backgroundColor: "yellow" }}></div>) : (<div className='difficulty-rating'></div>)} |
| 153 | {data.summary.routes[selectedRun].rating === 4 ? (<div className='difficulty-rating' style={{ backgroundColor: "orange" }}></div>) : (<div className='difficulty-rating'></div>)} | 152 | {data.map.difficulty > 6 && data.map.difficulty <= 8 ? (<div className='difficulty-rating' style={{ backgroundColor: "orange" }}></div>) : (<div className='difficulty-rating'></div>)} |
| 154 | {data.summary.routes[selectedRun].rating === 5 ? (<div className='difficulty-rating' style={{ backgroundColor: "red" }}></div>) : (<div className='difficulty-rating'></div>)} | 153 | {data.map.difficulty > 8 && data.map.difficulty <= 10 ? (<div className='difficulty-rating' style={{ backgroundColor: "red" }}></div>) : (<div className='difficulty-rating'></div>)} |
| 155 | </div> | 154 | </div> |
| 156 | </div> | 155 | </div> |
| 156 | {/* <div id='difficulty'> | ||
| 157 | <span>Difficulty</span> | ||
| 158 | {data.summary.routes[selectedRun].rating <= 2 && (<span style={{ color: "lime" }}>Very easy</span>)} | ||
| 159 | {data.summary.routes[selectedRun].rating > 2 && data.summary.routes[selectedRun].rating <= 4 && (<span style={{ color: "green" }}>Easy</span>)} | ||
| 160 | {data.summary.routes[selectedRun].rating > 4 && data.summary.routes[selectedRun].rating <= 6 && (<span style={{ color: "yellow" }}>Medium</span>)} | ||
| 161 | {data.summary.routes[selectedRun].rating > 6 && data.summary.routes[selectedRun].rating <= 8 && (<span style={{ color: "orange" }}>Hard</span>)} | ||
| 162 | {data.summary.routes[selectedRun].rating > 8 && data.summary.routes[selectedRun].rating <= 10 && (<span style={{ color: "red" }}>Very hard</span>)} | ||
| 163 | <div> | ||
| 164 | {data.summary.routes[selectedRun].rating <= 2 ? (<div className='difficulty-rating' style={{ backgroundColor: "lime" }}></div>) : (<div className='difficulty-rating'></div>)} | ||
| 165 | {data.summary.routes[selectedRun].rating > 2 && data.summary.routes[selectedRun].rating <= 4 ? (<div className='difficulty-rating' style={{ backgroundColor: "green" }}></div>) : (<div className='difficulty-rating'></div>)} | ||
| 166 | {data.summary.routes[selectedRun].rating > 4 && data.summary.routes[selectedRun].rating <= 6 ? (<div className='difficulty-rating' style={{ backgroundColor: "yellow" }}></div>) : (<div className='difficulty-rating'></div>)} | ||
| 167 | {data.summary.routes[selectedRun].rating > 6 && data.summary.routes[selectedRun].rating <= 8 ? (<div className='difficulty-rating' style={{ backgroundColor: "orange" }}></div>) : (<div className='difficulty-rating'></div>)} | ||
| 168 | {data.summary.routes[selectedRun].rating > 8 && data.summary.routes[selectedRun].rating <= 10 ? (<div className='difficulty-rating' style={{ backgroundColor: "red" }}></div>) : (<div className='difficulty-rating'></div>)} | ||
| 169 | </div> | ||
| 170 | </div> */} | ||
| 157 | <div id='count'> | 171 | <div id='count'> |
| 158 | <span>Completion Count</span> | 172 | <span>Completion Count</span> |
| 159 | <div>{data.summary.routes[selectedRun].completion_count}</div> | 173 | <div>{data.summary.routes[selectedRun].completion_count}</div> |
diff --git a/frontend/src/components/UploadRunDialog.tsx b/frontend/src/components/UploadRunDialog.tsx index 951944b..971a747 100644 --- a/frontend/src/components/UploadRunDialog.tsx +++ b/frontend/src/components/UploadRunDialog.tsx | |||
| @@ -5,12 +5,12 @@ import { ScoreboardTempUpdate, SourceDemoParser, NetMessages } from '@nekz/sdp'; | |||
| 5 | import btn from "@css/Button.module.css"; | 5 | import btn from "@css/Button.module.css"; |
| 6 | import '@css/UploadRunDialog.css'; | 6 | import '@css/UploadRunDialog.css'; |
| 7 | import { Game } from '@customTypes/Game'; | 7 | import { Game } from '@customTypes/Game'; |
| 8 | import { Map } from '@customTypes/Map'; | ||
| 9 | import { API } from '@api/Api'; | 8 | import { API } from '@api/Api'; |
| 10 | import { useNavigate } from 'react-router-dom'; | 9 | import { useNavigate } from 'react-router-dom'; |
| 11 | import useMessage from '@hooks/UseMessage'; | 10 | import useMessage from '@hooks/UseMessage'; |
| 12 | import useConfirm from '@hooks/UseConfirm'; | 11 | import useConfirm from '@hooks/UseConfirm'; |
| 13 | import useMessageLoad from "@hooks/UseMessageLoad"; | 12 | import useMessageLoad from "@hooks/UseMessageLoad"; |
| 13 | import { MapNames } from '@customTypes/MapNames'; | ||
| 14 | 14 | ||
| 15 | interface UploadRunDialogProps { | 15 | interface UploadRunDialogProps { |
| 16 | token?: string; | 16 | token?: string; |
| @@ -28,19 +28,11 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose, | |||
| 28 | const navigate = useNavigate(); | 28 | const navigate = useNavigate(); |
| 29 | 29 | ||
| 30 | const [uploadRunContent, setUploadRunContent] = React.useState<UploadRunContent>({ | 30 | const [uploadRunContent, setUploadRunContent] = React.useState<UploadRunContent>({ |
| 31 | map_id: 0, | ||
| 32 | host_demo: null, | 31 | host_demo: null, |
| 33 | partner_demo: null, | 32 | partner_demo: null, |
| 34 | }); | 33 | }); |
| 35 | 34 | ||
| 36 | const [currentMap, setCurrentMap] = React.useState<string>(""); | ||
| 37 | |||
| 38 | const _set_current_map = (game_name: string) => { | ||
| 39 | setCurrentMap(game_name); | ||
| 40 | } | ||
| 41 | |||
| 42 | const [selectedGameID, setSelectedGameID] = React.useState<number>(0); | 35 | const [selectedGameID, setSelectedGameID] = React.useState<number>(0); |
| 43 | const [selectedGameMaps, setSelectedGameMaps] = React.useState<Map[]>([]); | ||
| 44 | const [selectedGameName, setSelectedGameName] = React.useState<string>(""); | 36 | const [selectedGameName, setSelectedGameName] = React.useState<string>(""); |
| 45 | 37 | ||
| 46 | // dropdowns | 38 | // dropdowns |
| @@ -51,6 +43,7 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose, | |||
| 51 | 43 | ||
| 52 | const [dragHightlight, setDragHighlight] = React.useState<boolean>(false); | 44 | const [dragHightlight, setDragHighlight] = React.useState<boolean>(false); |
| 53 | const [dragHightlightPartner, setDragHighlightPartner] = React.useState<boolean>(false); | 45 | const [dragHightlightPartner, setDragHighlightPartner] = React.useState<boolean>(false); |
| 46 | |||
| 54 | const fileInputRef = React.useRef<HTMLInputElement>(null); | 47 | const fileInputRef = React.useRef<HTMLInputElement>(null); |
| 55 | const fileInputRefPartner = React.useRef<HTMLInputElement>(null); | 48 | const fileInputRefPartner = React.useRef<HTMLInputElement>(null); |
| 56 | 49 | ||
| @@ -103,14 +96,6 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose, | |||
| 103 | 96 | ||
| 104 | const _handle_game_select = async (game_id: string, game_name: string) => { | 97 | const _handle_game_select = async (game_id: string, game_name: string) => { |
| 105 | setLoading(true); | 98 | setLoading(true); |
| 106 | const gameMaps = await API.get_game_maps(game_id); | ||
| 107 | setSelectedGameMaps(gameMaps); | ||
| 108 | setUploadRunContent({ | ||
| 109 | map_id: gameMaps.find((map) => !map.is_disabled)!.id, //gameMaps[0].id, | ||
| 110 | host_demo: null, | ||
| 111 | partner_demo: null, | ||
| 112 | }); | ||
| 113 | _set_current_map(gameMaps.find((map) => !map.is_disabled)!.name); | ||
| 114 | setSelectedGameID(parseInt(game_id) - 1); | 99 | setSelectedGameID(parseInt(game_id) - 1); |
| 115 | setSelectedGameName(game_name); | 100 | setSelectedGameName(game_name); |
| 116 | setLoading(false); | 101 | setLoading(false); |
| @@ -159,6 +144,20 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose, | |||
| 159 | 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 | 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.") |
| 160 | return | 145 | return |
| 161 | } | 146 | } |
| 147 | |||
| 148 | if (!demo.mapName || !MapNames[demo.mapName]) { | ||
| 149 | await message("Error", "Error while processing demo: Invalid map name.") | ||
| 150 | return | ||
| 151 | } | ||
| 152 | |||
| 153 | if (selectedGameID === 0 && MapNames[demo.mapName] > 60) { | ||
| 154 | await message("Error", "Error while processing demo: Invalid cooperative demo in singleplayer submission.") | ||
| 155 | return | ||
| 156 | } else if (selectedGameID === 1 && MapNames[demo.mapName] <= 60) { | ||
| 157 | await message("Error", "Error while processing demo: Invalid singleplayer demo in cooperative submission.") | ||
| 158 | return | ||
| 159 | } | ||
| 160 | |||
| 162 | const { portalScore, timeScore } = scoreboard.userMessage?.as<ScoreboardTempUpdate>() ?? {}; | 161 | const { portalScore, timeScore } = scoreboard.userMessage?.as<ScoreboardTempUpdate>() ?? {}; |
| 163 | 162 | ||
| 164 | const userConfirmed = await confirm("Upload Record", `Map Name: ${demo.mapName}\nPortal Count: ${portalScore}\nTicks: ${timeScore}\n\nAre you sure you want to upload this demo?`); | 163 | const userConfirmed = await confirm("Upload Record", `Map Name: ${demo.mapName}\nPortal Count: ${portalScore}\nTicks: ${timeScore}\n\nAre you sure you want to upload this demo?`); |
| @@ -168,10 +167,14 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose, | |||
| 168 | } | 167 | } |
| 169 | 168 | ||
| 170 | messageLoad("Uploading..."); | 169 | messageLoad("Uploading..."); |
| 171 | const [success, response] = await API.post_record(token, uploadRunContent); | 170 | const [success, response] = await API.post_record(token, uploadRunContent, MapNames[demo.mapName]); |
| 172 | messageLoadClose(); | 171 | messageLoadClose(); |
| 173 | await message("Upload Record", response); | 172 | await message("Upload Record", response); |
| 174 | if (success) { | 173 | if (success) { |
| 174 | setUploadRunContent({ | ||
| 175 | host_demo: null, | ||
| 176 | partner_demo: null, | ||
| 177 | }); | ||
| 175 | onClose(success); | 178 | onClose(success); |
| 176 | navigate("/profile"); | 179 | navigate("/profile"); |
| 177 | } | 180 | } |
| @@ -180,7 +183,6 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose, | |||
| 180 | 183 | ||
| 181 | React.useEffect(() => { | 184 | React.useEffect(() => { |
| 182 | if (open) { | 185 | if (open) { |
| 183 | |||
| 184 | setDragHighlightPartner(false); | 186 | setDragHighlightPartner(false); |
| 185 | setDragHighlight(false); | 187 | setDragHighlight(false); |
| 186 | _handle_game_select("1", "Portal 2 - Singleplayer"); // a different approach?. | 188 | _handle_game_select("1", "Portal 2 - Singleplayer"); // a different approach?. |
| @@ -204,37 +206,20 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose, | |||
| 204 | <div className='dropdown-cur'>{selectedGameName}</div> | 206 | <div className='dropdown-cur'>{selectedGameName}</div> |
| 205 | <i style={{ rotate: "-90deg", transform: "translate(-5px, 10px)" }} className="triangle"></i> | 207 | <i style={{ rotate: "-90deg", transform: "translate(-5px, 10px)" }} className="triangle"></i> |
| 206 | </div> | 208 | </div> |
| 207 | <div style={{top: "110px"}} className={dropdown1Vis ? "upload-run-dropdown" : "upload-run-dropdown hidden"}> | 209 | <div style={{ top: "110px" }} className={dropdown1Vis ? "upload-run-dropdown" : "upload-run-dropdown hidden"}> |
| 208 | {games.map((game) => ( | 210 | {games.map((game) => ( |
| 209 | <div onClick={() => { _handle_game_select(game.id.toString(), game.name); _handle_dropdowns(1) }} key={game.id}>{game.name}</div> | 211 | <div onClick={() => { _handle_game_select(game.id.toString(), game.name); _handle_dropdowns(1) }} key={game.id}>{game.name}</div> |
| 210 | ))} | 212 | ))} |
| 211 | </div> | 213 | </div> |
| 212 | {!loading && ( | 214 | </div> |
| 213 | <> | ||
| 214 | <div style={{ padding: "25px 0px" }}> | ||
| 215 | <h3 style={{ margin: "0px 0px" }}>Select Map</h3> | ||
| 216 | <div onClick={() => _handle_dropdowns(2)} style={{ display: "flex", alignItems: "center", cursor: "pointer", justifyContent: "space-between", margin: "10px 0px" }}> | ||
| 217 | <span style={{ userSelect: "none" }}>{currentMap}</span> | ||
| 218 | <i style={{ rotate: "-90deg", transform: "translate(-5px, 10px)" }} className="triangle"></i> | ||
| 219 | </div> | ||
| 220 | </div> | ||
| 221 | <div style={{top: "220px"}} id='dropdown2' className={dropdown2Vis ? "upload-run-dropdown" : "upload-run-dropdown hidden"}> | ||
| 222 | {selectedGameMaps && selectedGameMaps.filter(gameMap => !gameMap.is_disabled).map((gameMap) => ( | ||
| 223 | <div onClick={() => { setUploadRunContent({ ...uploadRunContent, map_id: gameMap.id }); _set_current_map(gameMap.name); _handle_dropdowns(2); }} key={gameMap.id}>{gameMap.name}</div> | ||
| 224 | ))} | ||
| 225 | </div> | ||
| 226 | </> | ||
| 227 | |||
| 228 | )} | ||
| 229 | </div> | ||
| 230 | 215 | ||
| 231 | { | 216 | { |
| 232 | !loading && | 217 | !loading && |
| 233 | ( | 218 | ( |
| 234 | <> | 219 | <> |
| 235 | 220 | ||
| 236 | <div> | 221 | <div> |
| 237 | <h3 style={{margin: "10px 0px"}}>Host Demo</h3> | 222 | <h3 style={{ margin: "10px 0px" }}>Host Demo</h3> |
| 238 | <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 | <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" : ""}`}> |
| 239 | <input ref={fileInputRef} type="file" name="host_demo" id="host_demo" accept=".dem" onChange={(e) => _handle_file_change(e.target.files, true)} /> | 224 | <input ref={fileInputRef} type="file" name="host_demo" id="host_demo" accept=".dem" onChange={(e) => _handle_file_change(e.target.files, true)} /> |
| 240 | {!uploadRunContent.host_demo ? | 225 | {!uploadRunContent.host_demo ? |
| @@ -253,38 +238,41 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose, | |||
| 253 | games[selectedGameID].is_coop && | 238 | games[selectedGameID].is_coop && |
| 254 | ( | 239 | ( |
| 255 | <> | 240 | <> |
| 256 | <div> | 241 | <div> |
| 257 | <h3 style={{margin: "10px 0px"}}>Partner Demo</h3> | 242 | <h3 style={{ margin: "10px 0px" }}>Partner Demo</h3> |
| 258 | <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 | <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" : ""}`}> |
| 259 | <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 | <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 ? |
| 260 | <div> | ||
| 261 | <span>Drag and drop</span> | ||
| 262 | <div> | 245 | <div> |
| 263 | <span style={{ fontFamily: "BarlowSemiCondensed-Regular" }}>Or click here</span><br /> | 246 | <span style={{ fontFamily: "BarlowSemiCondensed-Regular" }}>Or click here</span><br /> |
| 264 | <button className={btn.default}>Upload</button> | 247 | <button className={btn.default}>Upload</button> |
| 265 | </div> | 248 | </div> |
| 266 | </div> | 249 | : null} |
| 267 | : null} | ||
| 268 | 250 | ||
| 269 | <span className="upload-run-demo-name">{uploadRunContent.partner_demo?.name}</span> | 251 | <span className="upload-run-demo-name">{uploadRunContent.partner_demo?.name}</span> |
| 252 | </div> | ||
| 270 | </div> | 253 | </div> |
| 271 | </div> | ||
| 272 | </> | 254 | </> |
| 273 | ) | 255 | ) |
| 274 | } | 256 | } |
| 275 | </div> | 257 | </div> |
| 276 | <div className='search-container'> | 258 | <div className='search-container'> |
| 259 | |||
| 260 | </div> | ||
| 277 | 261 | ||
| 278 | </div> | ||
| 279 | |||
| 280 | </> | 262 | </> |
| 281 | ) | 263 | ) |
| 282 | } | 264 | } |
| 283 | </div> | 265 | </div> |
| 284 | <div className='upload-run-buttons-container'> | 266 | <div className='upload-run-buttons-container'> |
| 285 | <button className={`${btn.defaultWide}`} onClick={_upload_run}>Submit</button> | 267 | <button className={`${btn.defaultWide}`} onClick={_upload_run}>Submit</button> |
| 286 | <button className={`${btn.defaultWide}`} onClick={() => onClose(false)}>Cancel</button> | 268 | <button className={`${btn.defaultWide}`} onClick={() => { |
| 287 | </div> | 269 | onClose(false); |
| 270 | setUploadRunContent({ | ||
| 271 | host_demo: null, | ||
| 272 | partner_demo: null, | ||
| 273 | }); | ||
| 274 | }}>Cancel</button> | ||
| 275 | </div> | ||
| 288 | </div> | 276 | </div> |
| 289 | </div> | 277 | </div> |
| 290 | </> | 278 | </> |
diff --git a/frontend/src/pages/About.tsx b/frontend/src/pages/About.tsx index fe2e25a..b7bd534 100644 --- a/frontend/src/pages/About.tsx +++ b/frontend/src/pages/About.tsx | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | import React from 'react'; | 1 | import React from 'react'; |
| 2 | import ReactMarkdown from 'react-markdown'; | 2 | import ReactMarkdown from 'react-markdown'; |
| 3 | import { Helmet } from 'react-helmet'; | ||
| 3 | 4 | ||
| 4 | import '@css/About.css'; | 5 | import '@css/About.css'; |
| 5 | 6 | ||
| @@ -28,6 +29,9 @@ const About: React.FC = () => { | |||
| 28 | 29 | ||
| 29 | return ( | 30 | return ( |
| 30 | <main> | 31 | <main> |
| 32 | <Helmet> | ||
| 33 | <title>LPHUB | About</title> | ||
| 34 | </Helmet> | ||
| 31 | <ReactMarkdown>{aboutText}</ReactMarkdown> | 35 | <ReactMarkdown>{aboutText}</ReactMarkdown> |
| 32 | </main> | 36 | </main> |
| 33 | ); | 37 | ); |
diff --git a/frontend/src/pages/Games.tsx b/frontend/src/pages/Games.tsx index e0320af..5e0d5bf 100644 --- a/frontend/src/pages/Games.tsx +++ b/frontend/src/pages/Games.tsx | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | import React from 'react'; | 1 | import React from 'react'; |
| 2 | import { Helmet } from 'react-helmet'; | ||
| 2 | 3 | ||
| 3 | import GameEntry from '@components/GameEntry'; | 4 | import GameEntry from '@components/GameEntry'; |
| 4 | import { Game } from '@customTypes/Game'; | 5 | import { Game } from '@customTypes/Game'; |
| @@ -11,6 +12,9 @@ interface GamesProps { | |||
| 11 | const Games: React.FC<GamesProps> = ({ games }) => { | 12 | const Games: React.FC<GamesProps> = ({ games }) => { |
| 12 | return ( | 13 | return ( |
| 13 | <main> | 14 | <main> |
| 15 | <Helmet> | ||
| 16 | <title>LPHUB | Games</title> | ||
| 17 | </Helmet> | ||
| 14 | <section> | 18 | <section> |
| 15 | <div className={gamesCSS.content}> | 19 | <div className={gamesCSS.content}> |
| 16 | {games.map((game, index) => ( | 20 | {games.map((game, index) => ( |
diff --git a/frontend/src/pages/Homepage.tsx b/frontend/src/pages/Homepage.tsx index 68562b6..4f46af5 100644 --- a/frontend/src/pages/Homepage.tsx +++ b/frontend/src/pages/Homepage.tsx | |||
| @@ -1,11 +1,15 @@ | |||
| 1 | import React from 'react'; | 1 | import React from 'react'; |
| 2 | import { Helmet } from 'react-helmet'; | ||
| 2 | 3 | ||
| 3 | const Homepage: React.FC = () => { | 4 | const Homepage: React.FC = () => { |
| 4 | 5 | ||
| 5 | return ( | 6 | return ( |
| 6 | <main> | 7 | <main> |
| 8 | <Helmet> | ||
| 9 | <title>LPHUB | Homepage</title> | ||
| 10 | </Helmet> | ||
| 7 | <section> | 11 | <section> |
| 8 | <p/> | 12 | <p /> |
| 9 | <h1>Welcome to Least Portals Hub!</h1> | 13 | <h1>Welcome to Least Portals Hub!</h1> |
| 10 | <p>At the moment, LPHUB is in beta state. This means that the site has only the core functionalities enabled for providing both collaborative information and competitive leaderboards.</p> | 14 | <p>At the moment, LPHUB is in beta state. This means that the site has only the core functionalities enabled for providing both collaborative information and competitive leaderboards.</p> |
| 11 | <p>The website should feel intuitive to navigate around. For any type of feedback, reach us at LPHUB Discord server.</p> | 15 | <p>The website should feel intuitive to navigate around. For any type of feedback, reach us at LPHUB Discord server.</p> |
diff --git a/frontend/src/pages/Maplist.tsx b/frontend/src/pages/Maplist.tsx index ecea3e1..b9e17f7 100644 --- a/frontend/src/pages/Maplist.tsx +++ b/frontend/src/pages/Maplist.tsx | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | import React, { useEffect } from "react"; | 1 | import React, { useEffect } from "react"; |
| 2 | import { Link, useLocation, useNavigate, useParams } from "react-router-dom"; | 2 | import { Link, useLocation, useNavigate, useParams } from "react-router-dom"; |
| 3 | import { Helmet } from "react-helmet"; | ||
| 3 | 4 | ||
| 4 | import "@css/Maplist.css"; | 5 | import "@css/Maplist.css"; |
| 5 | import { API } from "@api/Api"; | 6 | import { API } from "@api/Api"; |
| @@ -25,9 +26,9 @@ const Maplist: React.FC = () => { | |||
| 25 | const navigate = useNavigate(); | 26 | const navigate = useNavigate(); |
| 26 | 27 | ||
| 27 | function _update_currently_selected(catNum2: number) { | 28 | function _update_currently_selected(catNum2: number) { |
| 28 | setCurrentlySelected(catNum2); | 29 | setCurrentlySelected(catNum2); |
| 29 | navigate("/games/" + game?.id + "?cat=" + catNum2); | 30 | navigate("/games/" + game?.id + "?cat=" + catNum2); |
| 30 | setHasClicked(true); | 31 | setHasClicked(true); |
| 31 | } | 32 | } |
| 32 | 33 | ||
| 33 | const _fetch_chapters = async (chapter_id: string) => { | 34 | const _fetch_chapters = async (chapter_id: string) => { |
| @@ -52,12 +53,12 @@ const Maplist: React.FC = () => { | |||
| 52 | // location query params | 53 | // location query params |
| 53 | const queryParams = new URLSearchParams(location.search); | 54 | const queryParams = new URLSearchParams(location.search); |
| 54 | if (queryParams.get("chapter")) { | 55 | if (queryParams.get("chapter")) { |
| 55 | let cat = parseFloat(queryParams.get("chapter") || ""); | 56 | let cat = parseFloat(queryParams.get("chapter") || ""); |
| 56 | if (gameId == 2) { | 57 | if (gameId == 2) { |
| 57 | cat += 10; | 58 | cat += 10; |
| 58 | } | 59 | } |
| 59 | _fetch_chapters(cat.toString()); | 60 | _fetch_chapters(cat.toString()); |
| 60 | } | 61 | } |
| 61 | 62 | ||
| 62 | const _fetch_game = async () => { | 63 | const _fetch_game = async () => { |
| 63 | const games = await API.get_games(); | 64 | const games = await API.get_games(); |
| @@ -68,7 +69,7 @@ const Maplist: React.FC = () => { | |||
| 68 | setLoad(false); | 69 | setLoad(false); |
| 69 | } | 70 | } |
| 70 | }; | 71 | }; |
| 71 | 72 | ||
| 72 | const _fetch_game_chapters = async () => { | 73 | const _fetch_game_chapters = async () => { |
| 73 | const games_chapters = await API.get_games_chapters(gameId.toString()); | 74 | const games_chapters = await API.get_games_chapters(gameId.toString()); |
| 74 | setGameChapters(games_chapters); | 75 | setGameChapters(games_chapters); |
| @@ -81,7 +82,7 @@ const Maplist: React.FC = () => { | |||
| 81 | }, []); | 82 | }, []); |
| 82 | 83 | ||
| 83 | useEffect(() => { | 84 | useEffect(() => { |
| 84 | const queryParams = new URLSearchParams(location.search); | 85 | const queryParams = new URLSearchParams(location.search); |
| 85 | if (gameChapters != undefined && !queryParams.get("chapter")) { | 86 | if (gameChapters != undefined && !queryParams.get("chapter")) { |
| 86 | _fetch_chapters(gameChapters!.chapters[0].id.toString()); | 87 | _fetch_chapters(gameChapters!.chapters[0].id.toString()); |
| 87 | } | 88 | } |
| @@ -97,6 +98,9 @@ const Maplist: React.FC = () => { | |||
| 97 | 98 | ||
| 98 | return ( | 99 | return ( |
| 99 | <main> | 100 | <main> |
| 101 | <Helmet> | ||
| 102 | <title>LPHUB | Maplist</title> | ||
| 103 | </Helmet> | ||
| 100 | <section style={{ marginTop: "20px" }}> | 104 | <section style={{ marginTop: "20px" }}> |
| 101 | <Link to="/games"> | 105 | <Link to="/games"> |
| 102 | <button className="nav-button" style={{ borderRadius: "20px" }}> | 106 | <button className="nav-button" style={{ borderRadius: "20px" }}> |
| @@ -129,7 +133,7 @@ const Maplist: React.FC = () => { | |||
| 129 | </div> | 133 | </div> |
| 130 | <div className="game-header-categories"> | 134 | <div className="game-header-categories"> |
| 131 | {game?.category_portals.map((cat, index) => ( | 135 | {game?.category_portals.map((cat, index) => ( |
| 132 | <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)}}> | 136 | <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) }}> |
| 133 | <span>{cat.category.name}</span> | 137 | <span>{cat.category.name}</span> |
| 134 | </button> | 138 | </button> |
| 135 | ))} | 139 | ))} |
| @@ -140,26 +144,26 @@ const Maplist: React.FC = () => { | |||
| 140 | <div> | 144 | <div> |
| 141 | <section className="chapter-select-container"> | 145 | <section className="chapter-select-container"> |
| 142 | <div> | 146 | <div> |
| 143 | <span style={{fontSize: "18px", transform: "translateY(5px)", display: "block", marginTop: "10px"}}>{curChapter?.chapter.name.split(" - ")[0]}</span> | 147 | <span style={{ fontSize: "18px", transform: "translateY(5px)", display: "block", marginTop: "10px" }}>{curChapter?.chapter.name.split(" - ")[0]}</span> |
| 144 | </div> | 148 | </div> |
| 145 | <div onClick={_handle_dropdown_click} className="dropdown"> | 149 | <div onClick={_handle_dropdown_click} className="dropdown"> |
| 146 | <span>{curChapter?.chapter.name.split(" - ")[1]}</span> | 150 | <span>{curChapter?.chapter.name.split(" - ")[1]}</span> |
| 147 | <i className="triangle"></i> | 151 | <i className="triangle"></i> |
| 148 | </div> | 152 | </div> |
| 149 | <div className="dropdown-elements" style={{display: dropdownActive}}> | 153 | <div className="dropdown-elements" style={{ display: dropdownActive }}> |
| 150 | {gameChapters?.chapters.map((chapter, i) => { | 154 | {gameChapters?.chapters.map((chapter, i) => { |
| 151 | return <div className="dropdown-element" onClick={() => {_fetch_chapters(chapter.id.toString()); _handle_dropdown_click()}}>{chapter.name}</div> | 155 | return <div className="dropdown-element" onClick={() => { _fetch_chapters(chapter.id.toString()); _handle_dropdown_click() }}>{chapter.name}</div> |
| 152 | }) | 156 | }) |
| 153 | 157 | ||
| 154 | } | 158 | } |
| 155 | </div> | 159 | </div> |
| 156 | </section> | 160 | </section> |
| 157 | <section className="maplist"> | 161 | <section className="maplist"> |
| 158 | {curChapter?.maps.map((map, i) => { | 162 | {curChapter?.maps.map((map, i) => { |
| 159 | return <div className="maplist-entry"> | 163 | return <div className="maplist-entry"> |
| 160 | <Link to={`/maps/${map.id}`}> | 164 | <Link to={`/maps/${map.id}`}> |
| 161 | <span>{map.name}</span> | 165 | <span>{map.name}</span> |
| 162 | <div className="map-entry-image" style={{backgroundImage: `url(${map.image})`}}> | 166 | <div className="map-entry-image" style={{ backgroundImage: `url(${map.image})` }}> |
| 163 | <div className="blur map"> | 167 | <div className="blur map"> |
| 164 | <span>{map.is_disabled ? map.category_portals[0].portal_count : map.category_portals.find( | 168 | <span>{map.is_disabled ? map.category_portals[0].portal_count : map.category_portals.find( |
| 165 | (obj) => obj.category.id === catNum + 1 | 169 | (obj) => obj.category.id === catNum + 1 |
| @@ -169,7 +173,7 @@ const Maplist: React.FC = () => { | |||
| 169 | </div> | 173 | </div> |
| 170 | <div className="difficulty-bar"> | 174 | <div className="difficulty-bar"> |
| 171 | {/* <span>Difficulty:</span> */} | 175 | {/* <span>Difficulty:</span> */} |
| 172 | <div className={map.difficulty == 0 ? "one" : map.difficulty == 1 ? "two" : map.difficulty == 2 ? "three" : map.difficulty == 3 ? "four" : map.difficulty == 4 ? "five" : "one"}> | 176 | <div className={map.difficulty <= 2 ? "one" : map.difficulty <= 4 ? "two" : map.difficulty <= 6 ? "three" : map.difficulty <= 8 ? "four" : map.difficulty <= 10 ? "five" : "one"}> |
| 173 | <div className="difficulty-point"></div> | 177 | <div className="difficulty-point"></div> |
| 174 | <div className="difficulty-point"></div> | 178 | <div className="difficulty-point"></div> |
| 175 | <div className="difficulty-point"></div> | 179 | <div className="difficulty-point"></div> |
| @@ -177,9 +181,9 @@ const Maplist: React.FC = () => { | |||
| 177 | <div className="difficulty-point"></div> | 181 | <div className="difficulty-point"></div> |
| 178 | </div> | 182 | </div> |
| 179 | </div> | 183 | </div> |
| 180 | </Link> | 184 | </Link> |
| 181 | </div> | 185 | </div> |
| 182 | })} | 186 | })} |
| 183 | </section> | 187 | </section> |
| 184 | </div> | 188 | </div> |
| 185 | </section> | 189 | </section> |
diff --git a/frontend/src/pages/Maps.tsx b/frontend/src/pages/Maps.tsx index f1daa36..fb13563 100644 --- a/frontend/src/pages/Maps.tsx +++ b/frontend/src/pages/Maps.tsx | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | import React from 'react'; | 1 | import React from 'react'; |
| 2 | import { Link, useLocation } from 'react-router-dom'; | 2 | import { Link, useLocation } from 'react-router-dom'; |
| 3 | import { Helmet } from 'react-helmet'; | ||
| 3 | 4 | ||
| 4 | import { PortalIcon, FlagIcon, ChatIcon } from '@images/Images'; | 5 | import { PortalIcon, FlagIcon, ChatIcon } from '@images/Images'; |
| 5 | import Summary from '@components/Summary'; | 6 | import Summary from '@components/Summary'; |
| @@ -35,7 +36,7 @@ const Maps: React.FC<MapProps> = ({ token, isModerator }) => { | |||
| 35 | }; | 36 | }; |
| 36 | 37 | ||
| 37 | const _fetch_map_leaderboards = async () => { | 38 | const _fetch_map_leaderboards = async () => { |
| 38 | const mapLeaderboards = await API.get_map_leaderboard(mapID); | 39 | const mapLeaderboards = await API.get_map_leaderboard(mapID, "1"); |
| 39 | setMapLeaderboardData(mapLeaderboards); | 40 | setMapLeaderboardData(mapLeaderboards); |
| 40 | }; | 41 | }; |
| 41 | 42 | ||
| @@ -53,26 +54,32 @@ const Maps: React.FC<MapProps> = ({ token, isModerator }) => { | |||
| 53 | if (!mapSummaryData) { | 54 | if (!mapSummaryData) { |
| 54 | // loading placeholder | 55 | // loading placeholder |
| 55 | return ( | 56 | return ( |
| 56 | <main> | 57 | <> |
| 57 | <section id='section1' className='summary1'> | 58 | <main> |
| 58 | <div> | 59 | <section id='section1' className='summary1'> |
| 59 | <Link to="/games"><button className='nav-button' style={{ borderRadius: "20px 20px 20px 20px" }}><i className='triangle'></i><span>Games List</span></button></Link> | 60 | <div> |
| 60 | </div> | 61 | <Link to="/games"><button className='nav-button' style={{ borderRadius: "20px 20px 20px 20px" }}><i className='triangle'></i><span>Games List</span></button></Link> |
| 61 | </section> | 62 | </div> |
| 62 | 63 | </section> | |
| 63 | <section id='section2' className='summary1'> | 64 | |
| 64 | <button className='nav-button'><img src={PortalIcon} alt="" /><span>Summary</span></button> | 65 | <section id='section2' className='summary1'> |
| 65 | <button className='nav-button'><img src={FlagIcon} alt="" /><span>Leaderboards</span></button> | 66 | <button className='nav-button'><img src={PortalIcon} alt="" /><span>Summary</span></button> |
| 66 | <button className='nav-button'><img src={ChatIcon} alt="" /><span>Discussions</span></button> | 67 | <button className='nav-button'><img src={FlagIcon} alt="" /><span>Leaderboards</span></button> |
| 67 | </section> | 68 | <button className='nav-button'><img src={ChatIcon} alt="" /><span>Discussions</span></button> |
| 68 | 69 | </section> | |
| 69 | <section id='section6' className='summary2' /> | 70 | |
| 70 | </main> | 71 | <section id='section6' className='summary2' /> |
| 72 | </main> | ||
| 73 | </> | ||
| 71 | ); | 74 | ); |
| 72 | } | 75 | } |
| 73 | 76 | ||
| 74 | return ( | 77 | return ( |
| 75 | <> | 78 | <> |
| 79 | <Helmet> | ||
| 80 | <title>LPHUB | {mapSummaryData.map.map_name}</title> | ||
| 81 | <meta name="description" content={mapSummaryData.map.map_name} /> | ||
| 82 | </Helmet> | ||
| 76 | {isModerator && <ModMenu token={token} data={mapSummaryData} selectedRun={selectedRun} mapID={mapID} />} | 83 | {isModerator && <ModMenu token={token} data={mapSummaryData} selectedRun={selectedRun} mapID={mapID} />} |
| 77 | 84 | ||
| 78 | <div id='background-image'> | 85 | <div id='background-image'> |
| @@ -94,7 +101,7 @@ const Maps: React.FC<MapProps> = ({ token, isModerator }) => { | |||
| 94 | </section> | 101 | </section> |
| 95 | 102 | ||
| 96 | {navState === 0 && <Summary selectedRun={selectedRun} setSelectedRun={setSelectedRun} data={mapSummaryData} />} | 103 | {navState === 0 && <Summary selectedRun={selectedRun} setSelectedRun={setSelectedRun} data={mapSummaryData} />} |
| 97 | {navState === 1 && <Leaderboards data={mapLeaderboardData} />} | 104 | {navState === 1 && <Leaderboards mapID={mapID} />} |
| 98 | {navState === 2 && <Discussions data={mapDiscussionsData} token={token} isModerator={isModerator} mapID={mapID} onRefresh={() => _fetch_map_discussions()} />} | 105 | {navState === 2 && <Discussions data={mapDiscussionsData} token={token} isModerator={isModerator} mapID={mapID} onRefresh={() => _fetch_map_discussions()} />} |
| 99 | </main> | 106 | </main> |
| 100 | </> | 107 | </> |
diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx index 00d8f4e..ee56999 100644 --- a/frontend/src/pages/Profile.tsx +++ b/frontend/src/pages/Profile.tsx | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | import React from 'react'; | 1 | import React from 'react'; |
| 2 | import { Link, useNavigate } from 'react-router-dom'; | 2 | import { Link, useNavigate } from 'react-router-dom'; |
| 3 | import { Helmet } from 'react-helmet'; | ||
| 3 | 4 | ||
| 4 | import { SteamIcon, TwitchIcon, YouTubeIcon, PortalIcon, FlagIcon, StatisticsIcon, SortIcon, ThreedotIcon, DownloadIcon, HistoryIcon, DeleteIcon } from '@images/Images'; | 5 | import { SteamIcon, TwitchIcon, YouTubeIcon, PortalIcon, FlagIcon, StatisticsIcon, SortIcon, ThreedotIcon, DownloadIcon, HistoryIcon, DeleteIcon } from '@images/Images'; |
| 5 | import { UserProfile } from '@customTypes/Profile'; | 6 | import { UserProfile } from '@customTypes/Profile'; |
| @@ -109,6 +110,10 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRec | |||
| 109 | 110 | ||
| 110 | return ( | 111 | return ( |
| 111 | <div style={{position: "absolute", width: "calc(100% - 50px)", left: "350px"}}> | 112 | <div style={{position: "absolute", width: "calc(100% - 50px)", left: "350px"}}> |
| 113 | <Helmet> | ||
| 114 | <title>LPHUB | {profile.user_name}</title> | ||
| 115 | <meta name="description" content={profile.user_name} /> | ||
| 116 | </Helmet> | ||
| 112 | {MessageDialogComponent} | 117 | {MessageDialogComponent} |
| 113 | {MessageDialogLoadComponent} | 118 | {MessageDialogLoadComponent} |
| 114 | {ConfirmDialogComponent} | 119 | {ConfirmDialogComponent} |
| @@ -267,7 +272,7 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRec | |||
| 267 | 272 | ||
| 268 | <span style={{ display: "grid" }}>{e.score_count}</span> | 273 | <span style={{ display: "grid" }}>{e.score_count}</span> |
| 269 | 274 | ||
| 270 | <span style={{ display: "grid" }}>{e.score_count - r.map_wr_count > 0 ? `+${e.score_count - r.map_wr_count}` : e.score_count - r.map_wr_count}</span> | 275 | <span style={{ display: "grid" }}>{e.score_count - r.map_wr_count > 0 ? `+${e.score_count - r.map_wr_count}` : `-`}</span> |
| 271 | <span style={{ display: "grid" }}>{ticks_to_time(e.score_time)}</span> | 276 | <span style={{ display: "grid" }}>{ticks_to_time(e.score_time)}</span> |
| 272 | <span> </span> | 277 | <span> </span> |
| 273 | {i === 0 ? <span>#{r.placement}</span> : <span> </span>} | 278 | {i === 0 ? <span>#{r.placement}</span> : <span> </span>} |
| @@ -313,7 +318,7 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRec | |||
| 313 | {i !== 0 ? <hr style={{ gridColumn: "1 / span 8" }} /> : ""} | 318 | {i !== 0 ? <hr style={{ gridColumn: "1 / span 8" }} /> : ""} |
| 314 | <Link to={`/maps/${r.id}`}><span>{r.name}</span></Link> | 319 | <Link to={`/maps/${r.id}`}><span>{r.name}</span></Link> |
| 315 | <span style={{ display: "grid" }}>{record!.scores[i].score_count}</span> | 320 | <span style={{ display: "grid" }}>{record!.scores[i].score_count}</span> |
| 316 | <span style={{ display: "grid" }}>{record!.scores[i].score_count - record!.map_wr_count > 0 ? `+${record!.scores[i].score_count - record!.map_wr_count}` : record!.scores[i].score_count - record!.map_wr_count}</span> | 321 | <span style={{ display: "grid" }}>{record!.scores[i].score_count - record!.map_wr_count > 0 ? `+${record!.scores[i].score_count - record!.map_wr_count}` : `-`}</span> |
| 317 | <span style={{ display: "grid" }}>{ticks_to_time(record!.scores[i].score_time)}</span> | 322 | <span style={{ display: "grid" }}>{ticks_to_time(record!.scores[i].score_time)}</span> |
| 318 | <span> </span> | 323 | <span> </span> |
| 319 | {i === 0 ? <span>#{record!.placement}</span> : <span> </span>} | 324 | {i === 0 ? <span>#{record!.placement}</span> : <span> </span>} |
diff --git a/frontend/src/pages/Rankings.tsx b/frontend/src/pages/Rankings.tsx index cdb87a8..71aa427 100644 --- a/frontend/src/pages/Rankings.tsx +++ b/frontend/src/pages/Rankings.tsx | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | import React, { useEffect } from "react"; | 1 | import React, { useEffect } from "react"; |
| 2 | import { Helmet } from "react-helmet"; | ||
| 2 | 3 | ||
| 3 | import RankingEntry from "@components/RankingEntry"; | 4 | import RankingEntry from "@components/RankingEntry"; |
| 4 | import { Ranking, SteamRanking, RankingType, SteamRankingType } from "@customTypes/Ranking"; | 5 | import { Ranking, SteamRanking, RankingType, SteamRankingType } from "@customTypes/Ranking"; |
| @@ -13,9 +14,9 @@ const Rankings: React.FC = () => { | |||
| 13 | official, | 14 | official, |
| 14 | unofficial | 15 | unofficial |
| 15 | } | 16 | } |
| 16 | const [currentRankingType, setCurrentRankingType] = React.useState<LeaderboardTypes>(LeaderboardTypes.official); | 17 | const [currentRankingType, setCurrentRankingType] = React.useState<LeaderboardTypes>(LeaderboardTypes.official); |
| 17 | 18 | ||
| 18 | const [leaderboardLoad, setLeaderboardLoad] = React.useState<boolean>(false); | 19 | const [leaderboardLoad, setLeaderboardLoad] = React.useState<boolean>(false); |
| 19 | 20 | ||
| 20 | enum RankingCategories { | 21 | enum RankingCategories { |
| 21 | rankings_overall, | 22 | rankings_overall, |
| @@ -26,7 +27,7 @@ const Rankings: React.FC = () => { | |||
| 26 | const [load, setLoad] = React.useState<boolean>(false); | 27 | const [load, setLoad] = React.useState<boolean>(false); |
| 27 | 28 | ||
| 28 | const _fetch_rankings = async () => { | 29 | const _fetch_rankings = async () => { |
| 29 | setLeaderboardLoad(false); | 30 | setLeaderboardLoad(false); |
| 30 | const rankings = await API.get_official_rankings(); | 31 | const rankings = await API.get_official_rankings(); |
| 31 | setLeaderboardData(rankings); | 32 | setLeaderboardData(rankings); |
| 32 | if (currentLeaderboardType == RankingCategories.rankings_singleplayer) { | 33 | if (currentLeaderboardType == RankingCategories.rankings_singleplayer) { |
| @@ -37,12 +38,12 @@ const Rankings: React.FC = () => { | |||
| 37 | setCurrentLeaderboard(rankings.rankings_overall) | 38 | setCurrentLeaderboard(rankings.rankings_overall) |
| 38 | } | 39 | } |
| 39 | setLoad(true); | 40 | setLoad(true); |
| 40 | setLeaderboardLoad(true); | 41 | setLeaderboardLoad(true); |
| 41 | } | 42 | } |
| 42 | 43 | ||
| 43 | const __dev_fetch_unofficial_rankings = async () => { | 44 | const __dev_fetch_unofficial_rankings = async () => { |
| 44 | try { | 45 | try { |
| 45 | setLeaderboardLoad(false); | 46 | setLeaderboardLoad(false); |
| 46 | const rankings = await API.get_unofficial_rankings(); | 47 | const rankings = await API.get_unofficial_rankings(); |
| 47 | setLeaderboardData(rankings); | 48 | setLeaderboardData(rankings); |
| 48 | if (currentLeaderboardType == RankingCategories.rankings_singleplayer) { | 49 | if (currentLeaderboardType == RankingCategories.rankings_singleplayer) { |
| @@ -53,7 +54,7 @@ const Rankings: React.FC = () => { | |||
| 53 | } else { | 54 | } else { |
| 54 | setCurrentLeaderboard(rankings.rankings_overall) | 55 | setCurrentLeaderboard(rankings.rankings_overall) |
| 55 | } | 56 | } |
| 56 | setLeaderboardLoad(true); | 57 | setLeaderboardLoad(true); |
| 57 | } catch (e) { | 58 | } catch (e) { |
| 58 | console.log(e) | 59 | console.log(e) |
| 59 | } | 60 | } |
| @@ -88,12 +89,15 @@ const Rankings: React.FC = () => { | |||
| 88 | 89 | ||
| 89 | return ( | 90 | return ( |
| 90 | <main> | 91 | <main> |
| 92 | <Helmet> | ||
| 93 | <title>LPHUB | Rankings</title> | ||
| 94 | </Helmet> | ||
| 91 | <section className="nav-container nav-1"> | 95 | <section className="nav-container nav-1"> |
| 92 | <div> | 96 | <div> |
| 93 | <button onClick={() => {_fetch_rankings(); setCurrentRankingType(LeaderboardTypes.official)}} className={`nav-1-btn ${currentRankingType == LeaderboardTypes.official ? "selected" : ""}`}> | 97 | <button onClick={() => { _fetch_rankings(); setCurrentRankingType(LeaderboardTypes.official) }} className={`nav-1-btn ${currentRankingType == LeaderboardTypes.official ? "selected" : ""}`}> |
| 94 | <span>Official (LPHUB)</span> | 98 | <span>Official (LPHUB)</span> |
| 95 | </button> | 99 | </button> |
| 96 | <button onClick={() => {__dev_fetch_unofficial_rankings(); setCurrentRankingType(LeaderboardTypes.unofficial)}} className={`nav-1-btn ${currentRankingType == LeaderboardTypes.unofficial ? "selected" : ""}`}> | 100 | <button onClick={() => { __dev_fetch_unofficial_rankings(); setCurrentRankingType(LeaderboardTypes.unofficial) }} className={`nav-1-btn ${currentRankingType == LeaderboardTypes.unofficial ? "selected" : ""}`}> |
| 97 | <span>Unofficial (Steam)</span> | 101 | <span>Unofficial (Steam)</span> |
| 98 | </button> | 102 | </button> |
| 99 | </div> | 103 | </div> |
| @@ -128,11 +132,11 @@ const Rankings: React.FC = () => { | |||
| 128 | }) | 132 | }) |
| 129 | } | 133 | } |
| 130 | 134 | ||
| 131 | {leaderboardLoad ? null : | 135 | {leaderboardLoad ? null : |
| 132 | <div style={{display: "flex", justifyContent: "center", margin: "30px 0px"}}> | 136 | <div style={{ display: "flex", justifyContent: "center", margin: "30px 0px" }}> |
| 133 | <span className="loader"></span> | 137 | <span className="loader"></span> |
| 134 | </div> | 138 | </div> |
| 135 | } | 139 | } |
| 136 | </div> | 140 | </div> |
| 137 | </section> | 141 | </section> |
| 138 | : null} | 142 | : null} |
diff --git a/frontend/src/pages/Rules.tsx b/frontend/src/pages/Rules.tsx index b5625ce..9f57b7e 100644 --- a/frontend/src/pages/Rules.tsx +++ b/frontend/src/pages/Rules.tsx | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | import React from 'react'; | 1 | import React from 'react'; |
| 2 | import ReactMarkdown from 'react-markdown'; | 2 | import ReactMarkdown from 'react-markdown'; |
| 3 | import { Helmet } from 'react-helmet'; | ||
| 3 | 4 | ||
| 4 | import '@css/Rules.css'; | 5 | import '@css/Rules.css'; |
| 5 | 6 | ||
| @@ -29,6 +30,9 @@ const Rules: React.FC = () => { | |||
| 29 | 30 | ||
| 30 | return ( | 31 | return ( |
| 31 | <main> | 32 | <main> |
| 33 | <Helmet> | ||
| 34 | <title>LPHUB | Rules</title> | ||
| 35 | </Helmet> | ||
| 32 | <ReactMarkdown>{rulesText}</ReactMarkdown> | 36 | <ReactMarkdown>{rulesText}</ReactMarkdown> |
| 33 | </main> | 37 | </main> |
| 34 | ); | 38 | ); |
diff --git a/frontend/src/pages/User.tsx b/frontend/src/pages/User.tsx index f90d1aa..d43c0c6 100644 --- a/frontend/src/pages/User.tsx +++ b/frontend/src/pages/User.tsx | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | import React from 'react'; | 1 | import React from 'react'; |
| 2 | import { Link, useLocation, useNavigate } from 'react-router-dom'; | 2 | import { Link, useLocation, useNavigate } from 'react-router-dom'; |
| 3 | import { Helmet } from 'react-helmet'; | ||
| 3 | 4 | ||
| 4 | import { SteamIcon, TwitchIcon, YouTubeIcon, PortalIcon, FlagIcon, StatisticsIcon, SortIcon, ThreedotIcon, DownloadIcon, HistoryIcon } from '@images/Images'; | 5 | import { SteamIcon, TwitchIcon, YouTubeIcon, PortalIcon, FlagIcon, StatisticsIcon, SortIcon, ThreedotIcon, DownloadIcon, HistoryIcon } from '@images/Images'; |
| 5 | import { UserProfile } from '@customTypes/Profile'; | 6 | import { UserProfile } from '@customTypes/Profile'; |
| @@ -92,6 +93,10 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => { | |||
| 92 | 93 | ||
| 93 | return ( | 94 | return ( |
| 94 | <main> | 95 | <main> |
| 96 | <Helmet> | ||
| 97 | <title>LPHUB | {user.user_name}</title> | ||
| 98 | <meta name="description" content={user.user_name} /> | ||
| 99 | </Helmet> | ||
| 95 | {MessageDialogComponent} | 100 | {MessageDialogComponent} |
| 96 | <section id='section1' className='profile'> | 101 | <section id='section1' className='profile'> |
| 97 | <div> | 102 | <div> |
| @@ -236,7 +241,7 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => { | |||
| 236 | 241 | ||
| 237 | <span style={{ display: "grid" }}>{e.score_count}</span> | 242 | <span style={{ display: "grid" }}>{e.score_count}</span> |
| 238 | 243 | ||
| 239 | <span style={{ display: "grid" }}>{e.score_count - r.map_wr_count > 0 ? `+${e.score_count - r.map_wr_count}` : e.score_count - r.map_wr_count}</span> | 244 | <span style={{ display: "grid" }}>{e.score_count - r.map_wr_count > 0 ? `+${e.score_count - r.map_wr_count}` : `-`}</span> |
| 240 | <span style={{ display: "grid" }}>{ticks_to_time(e.score_time)}</span> | 245 | <span style={{ display: "grid" }}>{ticks_to_time(e.score_time)}</span> |
| 241 | <span> </span> | 246 | <span> </span> |
| 242 | {i === 0 ? <span>#{r.placement}</span> : <span> </span>} | 247 | {i === 0 ? <span>#{r.placement}</span> : <span> </span>} |
| @@ -281,7 +286,7 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => { | |||
| 281 | {i !== 0 ? <hr style={{ gridColumn: "1 / span 8" }} /> : ""} | 286 | {i !== 0 ? <hr style={{ gridColumn: "1 / span 8" }} /> : ""} |
| 282 | <Link to={`/maps/${r.id}`}><span>{r.name}</span></Link> | 287 | <Link to={`/maps/${r.id}`}><span>{r.name}</span></Link> |
| 283 | <span style={{ display: "grid" }}>{record!.scores[i].score_count}</span> | 288 | <span style={{ display: "grid" }}>{record!.scores[i].score_count}</span> |
| 284 | <span style={{ display: "grid" }}>{record!.scores[i].score_count - record!.map_wr_count > 0 ? `+${record!.scores[i].score_count - record!.map_wr_count}` : record!.scores[i].score_count - record!.map_wr_count}</span> | 289 | <span style={{ display: "grid" }}>{record!.scores[i].score_count - record!.map_wr_count > 0 ? `+${record!.scores[i].score_count - record!.map_wr_count}` : `-`}</span> |
| 285 | <span style={{ display: "grid" }}>{ticks_to_time(record!.scores[i].score_time)}</span> | 290 | <span style={{ display: "grid" }}>{ticks_to_time(record!.scores[i].score_time)}</span> |
| 286 | <span> </span> | 291 | <span> </span> |
| 287 | {i === 0 ? <span>#{record!.placement}</span> : <span> </span>} | 292 | {i === 0 ? <span>#{record!.placement}</span> : <span> </span>} |
diff --git a/frontend/src/types/Content.ts b/frontend/src/types/Content.ts index 42a6917..775fab4 100644 --- a/frontend/src/types/Content.ts +++ b/frontend/src/types/Content.ts | |||
| @@ -18,7 +18,6 @@ export interface MapDiscussionCommentContent { | |||
| 18 | }; | 18 | }; |
| 19 | 19 | ||
| 20 | export interface UploadRunContent { | 20 | export interface UploadRunContent { |
| 21 | map_id: number; | ||
| 22 | host_demo: File | null; | 21 | host_demo: File | null; |
| 23 | partner_demo: File | null; | 22 | partner_demo: File | null; |
| 24 | }; | 23 | }; |
diff --git a/frontend/src/types/Map.ts b/frontend/src/types/Map.ts index 89c66d5..4f8eabf 100644 --- a/frontend/src/types/Map.ts +++ b/frontend/src/types/Map.ts | |||
| @@ -79,6 +79,7 @@ interface MapSummaryMap { | |||
| 79 | map_name: string; | 79 | map_name: string; |
| 80 | is_coop: boolean; | 80 | is_coop: boolean; |
| 81 | is_disabled: boolean; | 81 | is_disabled: boolean; |
| 82 | difficulty: number; | ||
| 82 | }; | 83 | }; |
| 83 | 84 | ||
| 84 | interface MapSummaryDetails { | 85 | interface MapSummaryDetails { |
diff --git a/frontend/src/types/MapNames.ts b/frontend/src/types/MapNames.ts new file mode 100644 index 0000000..b6313e7 --- /dev/null +++ b/frontend/src/types/MapNames.ts | |||
| @@ -0,0 +1,127 @@ | |||
| 1 | export const MapNames: { [key: string]: number } = { | ||
| 2 | "sp_a1_intro1": 1, | ||
| 3 | "sp_a1_intro2": 2, | ||
| 4 | "sp_a1_intro3": 3, | ||
| 5 | "sp_a1_intro4": 4, | ||
| 6 | "sp_a1_intro5": 5, | ||
| 7 | "sp_a1_intro6": 6, | ||
| 8 | "sp_a1_intro7": 7, | ||
| 9 | "sp_a1_wakeup": 8, | ||
| 10 | "sp_a2_intro": 9, | ||
| 11 | |||
| 12 | "sp_a2_laser_intro": 10, | ||
| 13 | "sp_a2_laser_stairs": 11, | ||
| 14 | "sp_a2_dual_lasers": 12, | ||
| 15 | "sp_a2_laser_over_goo": 13, | ||
| 16 | "sp_a2_catapult_intro": 14, | ||
| 17 | "sp_a2_trust_fling": 15, | ||
| 18 | "sp_a2_pit_flings": 16, | ||
| 19 | "sp_a2_fizzler_intro": 17, | ||
| 20 | |||
| 21 | "sp_a2_sphere_peek": 18, | ||
| 22 | "sp_a2_ricochet": 19, | ||
| 23 | "sp_a2_bridge_intro": 20, | ||
| 24 | "sp_a2_bridge_the_gap": 21, | ||
| 25 | "sp_a2_turret_intro": 22, | ||
| 26 | "sp_a2_laser_relays": 23, | ||
| 27 | "sp_a2_turret_blocker": 24, | ||
| 28 | "sp_a2_laser_vs_turret": 25, | ||
| 29 | "sp_a2_pull_the_rug": 26, | ||
| 30 | |||
| 31 | "sp_a2_column_blocker": 27, | ||
| 32 | "sp_a2_laser_chaining": 28, | ||
| 33 | "sp_a2_triple_laser": 29, | ||
| 34 | "sp_a2_bts1": 30, | ||
| 35 | "sp_a2_bts2": 31, | ||
| 36 | |||
| 37 | "sp_a2_bts3": 32, | ||
| 38 | "sp_a2_bts4": 33, | ||
| 39 | "sp_a2_bts5": 34, | ||
| 40 | "sp_a2_core": 35, | ||
| 41 | |||
| 42 | "sp_a3_01": 36, | ||
| 43 | "sp_a3_03": 37, | ||
| 44 | "sp_a3_jump_intro": 38, | ||
| 45 | "sp_a3_bomb_flings": 39, | ||
| 46 | "sp_a3_crazy_box": 40, | ||
| 47 | "sp_a3_transition01": 41, | ||
| 48 | |||
| 49 | "sp_a3_speed_ramp": 42, | ||
| 50 | "sp_a3_speed_flings": 43, | ||
| 51 | "sp_a3_portal_intro": 44, | ||
| 52 | "sp_a3_end": 45, | ||
| 53 | |||
| 54 | "sp_a4_intro": 46, | ||
| 55 | "sp_a4_tb_intro": 47, | ||
| 56 | "sp_a4_tb_trust_drop": 48, | ||
| 57 | "sp_a4_tb_wall_button": 49, | ||
| 58 | "sp_a4_tb_polarity": 50, | ||
| 59 | "sp_a4_tb_catch": 51, | ||
| 60 | "sp_a4_stop_the_box": 52, | ||
| 61 | "sp_a4_laser_catapult": 53, | ||
| 62 | "sp_a4_laser_platform": 54, | ||
| 63 | "sp_a4_speed_tb_catch": 55, | ||
| 64 | "sp_a4_jump_polarity": 56, | ||
| 65 | |||
| 66 | "sp_a4_finale1": 57, | ||
| 67 | "sp_a4_finale2": 58, | ||
| 68 | "sp_a4_finale3": 59, | ||
| 69 | "sp_a4_finale4": 60, | ||
| 70 | |||
| 71 | "mp_coop_start": 61, | ||
| 72 | "mp_coop_lobby_3": 62, | ||
| 73 | |||
| 74 | "mp_coop_doors": 63, | ||
| 75 | "mp_coop_race_2": 64, | ||
| 76 | "mp_coop_laser_2": 65, | ||
| 77 | "mp_coop_rat_maze": 66, | ||
| 78 | "mp_coop_laser_crusher": 67, | ||
| 79 | "mp_coop_teambts": 68, | ||
| 80 | |||
| 81 | "mp_coop_fling_3": 69, | ||
| 82 | "mp_coop_infinifling_train": 70, | ||
| 83 | "mp_coop_come_along": 71, | ||
| 84 | "mp_coop_fling_1": 72, | ||
| 85 | "mp_coop_catapult_1": 73, | ||
| 86 | "mp_coop_multifling_1": 74, | ||
| 87 | "mp_coop_fling_crushers": 75, | ||
| 88 | "mp_coop_fan": 76, | ||
| 89 | |||
| 90 | "mp_coop_wall_intro": 77, | ||
| 91 | "mp_coop_wall_2": 78, | ||
| 92 | "mp_coop_catapult_wall_intro": 79, | ||
| 93 | "mp_coop_wall_block": 80, | ||
| 94 | "mp_coop_catapult_2": 81, | ||
| 95 | "mp_coop_turret_walls": 82, | ||
| 96 | "mp_coop_turret_ball": 83, | ||
| 97 | "mp_coop_wall_5": 84, | ||
| 98 | |||
| 99 | "mp_coop_tbeam_redirect": 85, | ||
| 100 | "mp_coop_tbeam_drill": 86, | ||
| 101 | "mp_coop_tbeam_catch_grind_1": 87, | ||
| 102 | "mp_coop_tbeam_laser_1": 88, | ||
| 103 | "mp_coop_tbeam_polarity": 89, | ||
| 104 | "mp_coop_tbeam_polarity2": 90, | ||
| 105 | "mp_coop_tbeam_polarity3": 91, | ||
| 106 | "mp_coop_tbeam_maze": 92, | ||
| 107 | "mp_coop_tbeam_end": 93, | ||
| 108 | |||
| 109 | "mp_coop_paint_come_along": 94, | ||
| 110 | "mp_coop_paint_redirect": 95, | ||
| 111 | "mp_coop_paint_bridge": 96, | ||
| 112 | "mp_coop_paint_walljumps": 97, | ||
| 113 | "mp_coop_paint_speed_fling": 98, | ||
| 114 | "mp_coop_paint_red_racer": 99, | ||
| 115 | "mp_coop_paint_speed_catch": 100, | ||
| 116 | "mp_coop_paint_longjump_intro": 101, | ||
| 117 | |||
| 118 | "mp_coop_separation_1": 102, | ||
| 119 | "mp_coop_tripleaxis": 103, | ||
| 120 | "mp_coop_catapult_catch": 104, | ||
| 121 | "mp_coop_2paints_1bridge": 105, | ||
| 122 | "mp_coop_paint_conversion": 106, | ||
| 123 | "mp_coop_bridge_catch": 107, | ||
| 124 | "mp_coop_laser_tbeam": 108, | ||
| 125 | "mp_coop_paint_rat_maze": 109, | ||
| 126 | "mp_coop_paint_crazy_box": 110, | ||
| 127 | }; | ||