diff options
Diffstat (limited to 'frontend/src')
| -rw-r--r-- | frontend/src/App.tsx | 2 | ||||
| -rw-r--r-- | frontend/src/api/Api.tsx | 4 | ||||
| -rw-r--r-- | frontend/src/api/Maps.tsx | 26 | ||||
| -rw-r--r-- | frontend/src/components/UploadRunDialog.tsx | 70 | ||||
| -rw-r--r-- | frontend/src/types/Content.tsx | 1 |
5 files changed, 90 insertions, 13 deletions
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 3a7fa18..c34cbcf 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx | |||
| @@ -75,7 +75,7 @@ const App: React.FC = () => { | |||
| 75 | 75 | ||
| 76 | return ( | 76 | return ( |
| 77 | <> | 77 | <> |
| 78 | <UploadRunDialog open={uploadRunDialog} onClose={() => setUploadRunDialog(false)} mapID={uploadRunDialogMapID} games={games} /> | 78 | <UploadRunDialog token={token} open={uploadRunDialog} onClose={() => setUploadRunDialog(false)} games={games} /> |
| 79 | <Sidebar setToken={setToken} profile={profile} setProfile={setProfile} onUploadRun={() => setUploadRunDialog(true)} /> | 79 | <Sidebar setToken={setToken} profile={profile} setProfile={setProfile} onUploadRun={() => setUploadRunDialog(true)} /> |
| 80 | <Routes> | 80 | <Routes> |
| 81 | <Route path="/" element={<Homepage />} /> | 81 | <Route path="/" element={<Homepage />} /> |
diff --git a/frontend/src/api/Api.tsx b/frontend/src/api/Api.tsx index 6731cb3..0f0c4d3 100644 --- a/frontend/src/api/Api.tsx +++ b/frontend/src/api/Api.tsx | |||
| @@ -3,8 +3,9 @@ import { delete_token, get_token } from './Auth'; | |||
| 3 | import { get_user, get_profile, post_profile } from './User'; | 3 | import { get_user, get_profile, post_profile } from './User'; |
| 4 | import { get_games, get_chapters, get_games_chapters, get_game_maps, get_search } from './Games'; | 4 | import { get_games, get_chapters, get_games_chapters, get_game_maps, get_search } from './Games'; |
| 5 | import { get_official_rankings, get_unofficial_rankings } from './Rankings'; | 5 | import { get_official_rankings, get_unofficial_rankings } from './Rankings'; |
| 6 | import { get_map_summary, get_map_leaderboard, get_map_discussions, get_map_discussion, post_map_discussion, post_map_discussion_comment, delete_map_discussion } from './Maps'; | 6 | import { get_map_summary, get_map_leaderboard, get_map_discussions, get_map_discussion, post_map_discussion, post_map_discussion_comment, delete_map_discussion, post_record } from './Maps'; |
| 7 | import { delete_map_summary, post_map_summary, put_map_image, put_map_summary } from './Mod'; | 7 | import { delete_map_summary, post_map_summary, put_map_image, put_map_summary } from './Mod'; |
| 8 | import { UploadRunContent } from '../types/Content'; | ||
| 8 | 9 | ||
| 9 | // add new api call function entries here | 10 | // add new api call function entries here |
| 10 | // example usage: API.get_games(); | 11 | // example usage: API.get_games(); |
| @@ -34,6 +35,7 @@ export const API = { | |||
| 34 | 35 | ||
| 35 | 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), |
| 36 | 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), | ||
| 37 | 39 | ||
| 38 | 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), |
| 39 | // Mod | 41 | // Mod |
diff --git a/frontend/src/api/Maps.tsx b/frontend/src/api/Maps.tsx index fbad78c..6bdc3e6 100644 --- a/frontend/src/api/Maps.tsx +++ b/frontend/src/api/Maps.tsx | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | import axios from "axios"; | 1 | import axios from "axios"; |
| 2 | import { url } from "./Api"; | 2 | import { url } from "./Api"; |
| 3 | import { MapDiscussionContent } from "../types/Content"; | 3 | import { MapDiscussionContent, UploadRunContent } from "../types/Content"; |
| 4 | import { MapSummary, MapLeaderboard, MapDiscussions, MapDiscussion } from "../types/Map"; | 4 | import { MapSummary, MapLeaderboard, MapDiscussions, MapDiscussion } from "../types/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> => { |
| @@ -74,3 +74,27 @@ export const delete_map_discussion = async (token: string, map_id: string, discu | |||
| 74 | }); | 74 | }); |
| 75 | return response.data.success; | 75 | return response.data.success; |
| 76 | }; | 76 | }; |
| 77 | |||
| 78 | export const post_record = async (token: string, run: UploadRunContent): Promise<[string]> => { | ||
| 79 | if (run.partner_demo && run.partner_id) { | ||
| 80 | const response = await axios.postForm(url(`maps/${run.map_id}/record`), { | ||
| 81 | "host_demo": run.host_demo, | ||
| 82 | "partner_demo": run.partner_demo, | ||
| 83 | "partner_id": run.partner_id, | ||
| 84 | }, { | ||
| 85 | headers: { | ||
| 86 | "Authorization": token, | ||
| 87 | } | ||
| 88 | }); | ||
| 89 | return response.data.message; | ||
| 90 | } else { | ||
| 91 | const response = await axios.postForm(url(`maps/${run.map_id}/record`), { | ||
| 92 | "host_demo": run.host_demo, | ||
| 93 | }, { | ||
| 94 | headers: { | ||
| 95 | "Authorization": token, | ||
| 96 | } | ||
| 97 | }); | ||
| 98 | return response.data.message; | ||
| 99 | } | ||
| 100 | }; | ||
diff --git a/frontend/src/components/UploadRunDialog.tsx b/frontend/src/components/UploadRunDialog.tsx index fb146ba..08d4108 100644 --- a/frontend/src/components/UploadRunDialog.tsx +++ b/frontend/src/components/UploadRunDialog.tsx | |||
| @@ -3,25 +3,26 @@ import { UploadRunContent } from '../types/Content'; | |||
| 3 | 3 | ||
| 4 | import '../css/UploadRunDialog.css'; | 4 | import '../css/UploadRunDialog.css'; |
| 5 | import { Game } from '../types/Game'; | 5 | import { Game } from '../types/Game'; |
| 6 | import Games from '../pages/Games'; | ||
| 7 | import { Map } from '../types/Map'; | 6 | import { Map } from '../types/Map'; |
| 8 | import { API } from '../api/Api'; | 7 | import { API } from '../api/Api'; |
| 8 | import { useNavigate } from 'react-router-dom'; | ||
| 9 | 9 | ||
| 10 | interface UploadRunDialogProps { | 10 | interface UploadRunDialogProps { |
| 11 | token?: string; | ||
| 11 | open: boolean; | 12 | open: boolean; |
| 12 | onClose: () => void; | 13 | onClose: () => void; |
| 13 | mapID?: number; | ||
| 14 | games: Game[]; | 14 | games: Game[]; |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ open, onClose, mapID, games }) => { | 17 | const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose, games }) => { |
| 18 | |||
| 19 | const navigate = useNavigate(); | ||
| 18 | 20 | ||
| 19 | const [uploadRunContent, setUploadRunContent] = React.useState<UploadRunContent>({ | 21 | const [uploadRunContent, setUploadRunContent] = React.useState<UploadRunContent>({ |
| 20 | map_id: 0, | 22 | map_id: 0, |
| 21 | host_demo: null, | 23 | host_demo: null, |
| 22 | partner_demo: null, | 24 | partner_demo: null, |
| 23 | partner_id: undefined, | 25 | partner_id: undefined, |
| 24 | is_partner_orange: undefined, | ||
| 25 | }); | 26 | }); |
| 26 | 27 | ||
| 27 | const [currentMap, setCurrentMap] = React.useState<string>(""); | 28 | const [currentMap, setCurrentMap] = React.useState<string>(""); |
| @@ -56,8 +57,10 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ open, onClose, mapID, | |||
| 56 | const gameMaps = await API.get_game_maps(game_id); | 57 | const gameMaps = await API.get_game_maps(game_id); |
| 57 | setSelectedGameMaps(gameMaps); | 58 | setSelectedGameMaps(gameMaps); |
| 58 | setUploadRunContent({ | 59 | setUploadRunContent({ |
| 59 | ...uploadRunContent, | ||
| 60 | map_id: gameMaps[0].id, | 60 | map_id: gameMaps[0].id, |
| 61 | host_demo: null, | ||
| 62 | partner_demo: null, | ||
| 63 | partner_id: undefined, | ||
| 61 | }); | 64 | }); |
| 62 | _set_current_map(gameMaps[0].name); | 65 | _set_current_map(gameMaps[0].name); |
| 63 | setSelectedGameID(parseInt(game_id) - 1); | 66 | setSelectedGameID(parseInt(game_id) - 1); |
| @@ -65,6 +68,50 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ open, onClose, mapID, | |||
| 65 | setLoading(false); | 68 | setLoading(false); |
| 66 | }; | 69 | }; |
| 67 | 70 | ||
| 71 | const _handle_file_change = async (e: React.ChangeEvent<HTMLInputElement>, host: boolean) => { | ||
| 72 | if (e.target.files) { | ||
| 73 | if (host) { | ||
| 74 | setUploadRunContent({ | ||
| 75 | ...uploadRunContent, | ||
| 76 | host_demo: e.target.files[0], | ||
| 77 | }); | ||
| 78 | } else { | ||
| 79 | setUploadRunContent({ | ||
| 80 | ...uploadRunContent, | ||
| 81 | partner_demo: e.target.files[0], | ||
| 82 | }); | ||
| 83 | } | ||
| 84 | } | ||
| 85 | }; | ||
| 86 | |||
| 87 | const _upload_run = async () => { | ||
| 88 | if (token) { | ||
| 89 | if (games[selectedGameID].is_coop) { | ||
| 90 | if (uploadRunContent.host_demo === null) { | ||
| 91 | alert("You must select a host demo to upload.") | ||
| 92 | return | ||
| 93 | } else if (uploadRunContent.partner_demo === null) { | ||
| 94 | alert("You must select a partner demo to upload.") | ||
| 95 | return | ||
| 96 | } else if (uploadRunContent.partner_id === undefined) { | ||
| 97 | alert("You must specify your partner.") | ||
| 98 | return | ||
| 99 | } | ||
| 100 | } else { | ||
| 101 | if (uploadRunContent.host_demo === null) { | ||
| 102 | alert("You must select a demo to upload.") | ||
| 103 | return | ||
| 104 | } | ||
| 105 | } | ||
| 106 | if (window.confirm("Are you sure you want to submit this run to LPHUB?")) { | ||
| 107 | const message = await API.post_record(token, uploadRunContent); | ||
| 108 | alert(message); | ||
| 109 | navigate(0); | ||
| 110 | onClose(); | ||
| 111 | } | ||
| 112 | } | ||
| 113 | }; | ||
| 114 | |||
| 68 | React.useEffect(() => { | 115 | React.useEffect(() => { |
| 69 | if (open) { | 116 | if (open) { |
| 70 | _handle_game_select("1", "Portal 2 - Singleplayer"); // a different approach?. | 117 | _handle_game_select("1", "Portal 2 - Singleplayer"); // a different approach?. |
| @@ -105,23 +152,28 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ open, onClose, mapID, | |||
| 105 | <div> | 152 | <div> |
| 106 | <div id='dropdown2' className={dropdown2Vis ? "upload-run-dropdown" : "upload-run-dropdown hidden"}> | 153 | <div id='dropdown2' className={dropdown2Vis ? "upload-run-dropdown" : "upload-run-dropdown hidden"}> |
| 107 | {selectedGameMaps && selectedGameMaps.map((gameMap) => ( | 154 | {selectedGameMaps && selectedGameMaps.map((gameMap) => ( |
| 108 | <div onClick={() => { setUploadRunContent({...uploadRunContent, map_id: parseInt(gameMap.name)}); _set_current_map(gameMap.name); _handle_dropdowns(2); }} key={gameMap.id}>{gameMap.name}</div> | 155 | <div onClick={() => { setUploadRunContent({...uploadRunContent, map_id: gameMap.id}); _set_current_map(gameMap.name); _handle_dropdowns(2); }} key={gameMap.id}>{gameMap.name}</div> |
| 109 | ))} | 156 | ))} |
| 110 | </div> | 157 | </div> |
| 111 | </div> | 158 | </div> |
| 112 | <span>Host Demo</span> | 159 | <span>Host Demo</span> |
| 113 | <input type="file" name="host_demo" id="host_demo" accept=".dem" /> | 160 | <input type="file" name="host_demo" id="host_demo" accept=".dem" onChange={(e) => _handle_file_change(e, true)} /> |
| 114 | { | 161 | { |
| 115 | games[selectedGameID].is_coop && | 162 | games[selectedGameID].is_coop && |
| 116 | ( | 163 | ( |
| 117 | <> | 164 | <> |
| 118 | <span>Partner Demo</span> | 165 | <span>Partner Demo</span> |
| 119 | <input type="file" name="partner_demo" id="partner_demo" accept=".dem" /> | 166 | <input type="file" name="partner_demo" id="partner_demo" accept=".dem" onChange={(e) => _handle_file_change(e, false)} /> |
| 167 | <span>Partner ID</span> | ||
| 168 | <input type="text" name="partner_id" id="partner_id" onChange={(e) => setUploadRunContent({ | ||
| 169 | ...uploadRunContent, | ||
| 170 | partner_id: e.target.value, | ||
| 171 | })} /> | ||
| 120 | </> | 172 | </> |
| 121 | ) | 173 | ) |
| 122 | } | 174 | } |
| 123 | <div className='upload-run-buttons-container'> | 175 | <div className='upload-run-buttons-container'> |
| 124 | <button onClick={() => onClose()}>Submit</button> | 176 | <button onClick={_upload_run}>Submit</button> |
| 125 | <button onClick={() => onClose()}>Cancel</button> | 177 | <button onClick={() => onClose()}>Cancel</button> |
| 126 | </div> | 178 | </div> |
| 127 | </div> | 179 | </div> |
diff --git a/frontend/src/types/Content.tsx b/frontend/src/types/Content.tsx index 5733f1f..e6e0cb1 100644 --- a/frontend/src/types/Content.tsx +++ b/frontend/src/types/Content.tsx | |||
| @@ -22,5 +22,4 @@ export interface UploadRunContent { | |||
| 22 | host_demo: File | null; | 22 | host_demo: File | null; |
| 23 | partner_demo: File | null; | 23 | partner_demo: File | null; |
| 24 | partner_id?: string; | 24 | partner_id?: string; |
| 25 | is_partner_orange?: boolean; | ||
| 26 | }; | 25 | }; |