aboutsummaryrefslogtreecommitdiff
path: root/frontend/src
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src')
-rw-r--r--frontend/src/App.tsx2
-rw-r--r--frontend/src/api/Api.tsx4
-rw-r--r--frontend/src/api/Maps.tsx26
-rw-r--r--frontend/src/components/UploadRunDialog.tsx70
-rw-r--r--frontend/src/types/Content.tsx1
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';
3import { get_user, get_profile, post_profile } from './User'; 3import { get_user, get_profile, post_profile } from './User';
4import { get_games, get_chapters, get_games_chapters, get_game_maps, get_search } from './Games'; 4import { get_games, get_chapters, get_games_chapters, get_game_maps, get_search } from './Games';
5import { get_official_rankings, get_unofficial_rankings } from './Rankings'; 5import { get_official_rankings, get_unofficial_rankings } from './Rankings';
6import { get_map_summary, get_map_leaderboard, get_map_discussions, get_map_discussion, post_map_discussion, post_map_discussion_comment, delete_map_discussion } from './Maps'; 6import { 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';
7import { delete_map_summary, post_map_summary, put_map_image, put_map_summary } from './Mod'; 7import { delete_map_summary, post_map_summary, put_map_image, put_map_summary } from './Mod';
8import { 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 @@
1import axios from "axios"; 1import axios from "axios";
2import { url } from "./Api"; 2import { url } from "./Api";
3import { MapDiscussionContent } from "../types/Content"; 3import { MapDiscussionContent, UploadRunContent } from "../types/Content";
4import { MapSummary, MapLeaderboard, MapDiscussions, MapDiscussion } from "../types/Map"; 4import { MapSummary, MapLeaderboard, MapDiscussions, MapDiscussion } from "../types/Map";
5 5
6export const get_map_summary = async (map_id: string): Promise<MapSummary> => { 6export const get_map_summary = async (map_id: string): Promise<MapSummary> => {
@@ -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
78export 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
4import '../css/UploadRunDialog.css'; 4import '../css/UploadRunDialog.css';
5import { Game } from '../types/Game'; 5import { Game } from '../types/Game';
6import Games from '../pages/Games';
7import { Map } from '../types/Map'; 6import { Map } from '../types/Map';
8import { API } from '../api/Api'; 7import { API } from '../api/Api';
8import { useNavigate } from 'react-router-dom';
9 9
10interface UploadRunDialogProps { 10interface 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
17const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ open, onClose, mapID, games }) => { 17const 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};