aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backend/go.mod1
-rw-r--r--backend/go.sum2
-rw-r--r--backend/handlers/user.go40
-rw-r--r--backend/main.go28
-rw-r--r--frontend/package-lock.json6
-rw-r--r--frontend/package.json1
-rw-r--r--frontend/src/components/UploadRunDialog.tsx99
7 files changed, 106 insertions, 71 deletions
diff --git a/backend/go.mod b/backend/go.mod
index f6eef48..ae50685 100644
--- a/backend/go.mod
+++ b/backend/go.mod
@@ -8,6 +8,7 @@ require (
8) 8)
9 9
10require ( 10require (
11 github.com/gin-contrib/cors v1.7.2
11 github.com/golang-jwt/jwt/v4 v4.5.0 12 github.com/golang-jwt/jwt/v4 v4.5.0
12 github.com/google/uuid v1.6.0 13 github.com/google/uuid v1.6.0
13 github.com/pektezol/steam_go v1.1.2 14 github.com/pektezol/steam_go v1.1.2
diff --git a/backend/go.sum b/backend/go.sum
index 10504e4..f117b31 100644
--- a/backend/go.sum
+++ b/backend/go.sum
@@ -31,6 +31,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
31github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 31github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
32github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= 32github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
33github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= 33github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
34github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw=
35github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E=
34github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= 36github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
35github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= 37github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
36github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 38github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
diff --git a/backend/handlers/user.go b/backend/handlers/user.go
index 908063f..17a7819 100644
--- a/backend/handlers/user.go
+++ b/backend/handlers/user.go
@@ -301,10 +301,12 @@ func Profile(c *gin.Context) {
301 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 301 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
302 return 302 return
303 } 303 }
304 sql = `WITH best_scores AS (SELECT sp.user_id, sp.map_id, MIN(sp.score_count) AS best_score_count, MIN(sp.score_time) AS best_score_time 304 sql = `WITH best_scores AS (WITH RankedScores AS (SELECT sp.user_id, sp.map_id, sp.score_count, sp.score_time, ROW_NUMBER() OVER (
305 FROM records_sp sp WHERE sp.is_deleted = false GROUP BY sp.user_id, sp.map_id) 305 PARTITION BY sp.user_id, sp.map_id ORDER BY sp.score_count ASC, sp.score_time ASC) AS rank FROM records_sp sp WHERE sp.is_deleted = false)
306 SELECT (SELECT COUNT(*) + 1 FROM best_scores AS inner_scores WHERE inner_scores.map_id = bs.map_id AND (inner_scores.best_score_count < bs.best_score_count OR (inner_scores.best_score_count = bs.best_score_count AND inner_scores.best_score_time < bs.best_score_time))) AS placement 306 SELECT user_id, map_id, score_count AS best_score_count, score_time AS best_score_time FROM RankedScores WHERE rank = 1)
307 FROM best_scores AS bs WHERE bs.user_id = $1 ORDER BY map_id, placement` 307 SELECT (SELECT COUNT(*) + 1 FROM best_scores AS inner_scores WHERE inner_scores.map_id = bs.map_id
308 AND (inner_scores.best_score_count < bs.best_score_count OR (inner_scores.best_score_count = bs.best_score_count
309 AND inner_scores.best_score_time < bs.best_score_time))) AS placement FROM best_scores AS bs WHERE bs.user_id = $1 ORDER BY map_id, placement`
308 placementsRows, err := database.DB.Query(sql, user.(models.User).SteamID) 310 placementsRows, err := database.DB.Query(sql, user.(models.User).SteamID)
309 if err != nil { 311 if err != nil {
310 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 312 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
@@ -355,10 +357,12 @@ func Profile(c *gin.Context) {
355 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 357 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
356 return 358 return
357 } 359 }
358 sql = `WITH best_scores AS (SELECT mp.host_id, mp.partner_id, mp.map_id, MIN(mp.score_count) AS best_score_count, MIN(mp.score_time) AS best_score_time 360 sql = `WITH best_scores AS (WITH RankedScores AS (SELECT mp.host_id, mp.partner_id, mp.map_id, mp.score_count, mp.score_time, ROW_NUMBER() OVER (
359 FROM records_mp mp WHERE mp.is_deleted = false GROUP BY mp.host_id, mp.partner_id, mp.map_id) 361 PARTITION BY mp.host_id, mp.partner_id, mp.map_id ORDER BY mp.score_count ASC, mp.score_time ASC) AS rank FROM records_mp mp WHERE mp.is_deleted = false)
360 SELECT (SELECT COUNT(*) + 1 FROM best_scores AS inner_scores WHERE inner_scores.map_id = bs.map_id AND (inner_scores.best_score_count < bs.best_score_count OR (inner_scores.best_score_count = bs.best_score_count AND inner_scores.best_score_time < bs.best_score_time))) AS placement 362 SELECT host_id, partner_id, map_id, score_count AS best_score_count, score_time AS best_score_time FROM RankedScores WHERE rank = 1)
361 FROM best_scores AS bs WHERE bs.host_id = $1 or bs.partner_id = $1 ORDER BY map_id, placement` 363 SELECT (SELECT COUNT(*) + 1 FROM best_scores AS inner_scores WHERE inner_scores.map_id = bs.map_id
364 AND (inner_scores.best_score_count < bs.best_score_count OR (inner_scores.best_score_count = bs.best_score_count
365 AND inner_scores.best_score_time < bs.best_score_time))) AS placement FROM best_scores AS bs WHERE bs.host_id = $1 or bs.partner_id = $1 ORDER BY map_id, placement`
362 placementsRows, err = database.DB.Query(sql, user.(models.User).SteamID) 366 placementsRows, err = database.DB.Query(sql, user.(models.User).SteamID)
363 if err != nil { 367 if err != nil {
364 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 368 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
@@ -675,10 +679,12 @@ func FetchUser(c *gin.Context) {
675 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 679 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
676 return 680 return
677 } 681 }
678 sql = `WITH best_scores AS (SELECT sp.user_id, sp.map_id, MIN(sp.score_count) AS best_score_count, MIN(sp.score_time) AS best_score_time 682 sql = `WITH best_scores AS (WITH RankedScores AS (SELECT sp.user_id, sp.map_id, sp.score_count, sp.score_time, ROW_NUMBER() OVER (
679 FROM records_sp sp WHERE sp.is_deleted = false GROUP BY sp.user_id, sp.map_id) 683 PARTITION BY sp.user_id, sp.map_id ORDER BY sp.score_count ASC, sp.score_time ASC) AS rank FROM records_sp sp WHERE sp.is_deleted = false)
680 SELECT (SELECT COUNT(*) + 1 FROM best_scores AS inner_scores WHERE inner_scores.map_id = bs.map_id AND (inner_scores.best_score_count < bs.best_score_count OR (inner_scores.best_score_count = bs.best_score_count AND inner_scores.best_score_time < bs.best_score_time))) AS placement 684 SELECT user_id, map_id, score_count AS best_score_count, score_time AS best_score_time FROM RankedScores WHERE rank = 1)
681 FROM best_scores AS bs WHERE bs.user_id = $1 ORDER BY map_id, placement` 685 SELECT (SELECT COUNT(*) + 1 FROM best_scores AS inner_scores WHERE inner_scores.map_id = bs.map_id
686 AND (inner_scores.best_score_count < bs.best_score_count OR (inner_scores.best_score_count = bs.best_score_count
687 AND inner_scores.best_score_time < bs.best_score_time))) AS placement FROM best_scores AS bs WHERE bs.user_id = $1 ORDER BY map_id, placement`
682 placementsRows, err := database.DB.Query(sql, user.SteamID) 688 placementsRows, err := database.DB.Query(sql, user.SteamID)
683 if err != nil { 689 if err != nil {
684 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 690 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
@@ -729,10 +735,12 @@ func FetchUser(c *gin.Context) {
729 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 735 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
730 return 736 return
731 } 737 }
732 sql = `WITH best_scores AS (SELECT mp.host_id, mp.partner_id, mp.map_id, MIN(mp.score_count) AS best_score_count, MIN(mp.score_time) AS best_score_time 738 sql = `WITH best_scores AS (WITH RankedScores AS (SELECT mp.host_id, mp.partner_id, mp.map_id, mp.score_count, mp.score_time, ROW_NUMBER() OVER (
733 FROM records_mp mp WHERE mp.is_deleted = false GROUP BY mp.host_id, mp.partner_id, mp.map_id) 739 PARTITION BY mp.host_id, mp.partner_id, mp.map_id ORDER BY mp.score_count ASC, mp.score_time ASC) AS rank FROM records_mp mp WHERE mp.is_deleted = false)
734 SELECT (SELECT COUNT(*) + 1 FROM best_scores AS inner_scores WHERE inner_scores.map_id = bs.map_id AND (inner_scores.best_score_count < bs.best_score_count OR (inner_scores.best_score_count = bs.best_score_count AND inner_scores.best_score_time < bs.best_score_time))) AS placement 740 SELECT host_id, partner_id, map_id, score_count AS best_score_count, score_time AS best_score_time FROM RankedScores WHERE rank = 1)
735 FROM best_scores AS bs WHERE bs.host_id = $1 or bs.partner_id = $1 ORDER BY map_id, placement` 741 SELECT (SELECT COUNT(*) + 1 FROM best_scores AS inner_scores WHERE inner_scores.map_id = bs.map_id
742 AND (inner_scores.best_score_count < bs.best_score_count OR (inner_scores.best_score_count = bs.best_score_count
743 AND inner_scores.best_score_time < bs.best_score_time))) AS placement FROM best_scores AS bs WHERE bs.host_id = $1 or bs.partner_id = $1 ORDER BY map_id, placement`
736 placementsRows, err = database.DB.Query(sql, user.SteamID) 744 placementsRows, err = database.DB.Query(sql, user.SteamID)
737 if err != nil { 745 if err != nil {
738 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 746 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
diff --git a/backend/main.go b/backend/main.go
index a50be7e..6499b51 100644
--- a/backend/main.go
+++ b/backend/main.go
@@ -4,11 +4,13 @@ import (
4 "fmt" 4 "fmt"
5 "log" 5 "log"
6 "os" 6 "os"
7 "time"
7 8
8 "lphub/api" 9 "lphub/api"
9 "lphub/database" 10 "lphub/database"
10 _ "lphub/docs" 11 _ "lphub/docs"
11 12
13 "github.com/gin-contrib/cors"
12 "github.com/gin-gonic/gin" 14 "github.com/gin-gonic/gin"
13 "github.com/joho/godotenv" 15 "github.com/joho/godotenv"
14) 16)
@@ -34,19 +36,19 @@ func main() {
34 database.ConnectDB() 36 database.ConnectDB()
35 api.InitRoutes(router) 37 api.InitRoutes(router)
36 // for debugging 38 // for debugging
37 // router.Use(cors.New(cors.Config{ 39 router.Use(cors.New(cors.Config{
38 // AllowOrigins: []string{"*"}, 40 AllowOrigins: []string{"*"},
39 // AllowMethods: []string{"GET", "POST", "DELETE", "PUT", "PATCH"}, 41 AllowMethods: []string{"GET", "POST", "DELETE", "PUT", "PATCH"},
40 // AllowHeaders: []string{"Origin"}, 42 AllowHeaders: []string{"Origin"},
41 // ExposeHeaders: []string{"Content-Length"}, 43 ExposeHeaders: []string{"Content-Length"},
42 // AllowCredentials: true, 44 AllowCredentials: true,
43 // MaxAge: 12 * time.Hour, 45 MaxAge: 12 * time.Hour,
44 // })) 46 }))
45 // router.Static("/static", "../frontend/build/static") 47 router.Static("/static", "../frontend/build/static")
46 // router.StaticFile("/", "../frontend/build/index.html") 48 router.StaticFile("/", "../frontend/build/index.html")
47 // router.NoRoute(func(c *gin.Context) { 49 router.NoRoute(func(c *gin.Context) {
48 // c.File("../frontend/build/index.html") 50 c.File("../frontend/build/index.html")
49 // }) 51 })
50 router.MaxMultipartMemory = 200 << 20 // 200 mb limit for demos 52 router.MaxMultipartMemory = 200 << 20 // 200 mb limit for demos
51 router.Run(fmt.Sprintf(":%s", os.Getenv("PORT"))) 53 router.Run(fmt.Sprintf(":%s", os.Getenv("PORT")))
52} 54}
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 8b901ad..a77d244 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -8,6 +8,7 @@
8 "name": "frontend", 8 "name": "frontend",
9 "version": "0.1.0", 9 "version": "0.1.0",
10 "dependencies": { 10 "dependencies": {
11 "@nekz/sdp": "^0.9.0",
11 "@testing-library/jest-dom": "^5.17.0", 12 "@testing-library/jest-dom": "^5.17.0",
12 "@testing-library/react": "^13.4.0", 13 "@testing-library/react": "^13.4.0",
13 "@testing-library/user-event": "^13.5.0", 14 "@testing-library/user-event": "^13.5.0",
@@ -3174,6 +3175,11 @@
3174 "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", 3175 "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
3175 "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==" 3176 "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw=="
3176 }, 3177 },
3178 "node_modules/@nekz/sdp": {
3179 "version": "0.9.0",
3180 "resolved": "https://registry.npmjs.org/@nekz/sdp/-/sdp-0.9.0.tgz",
3181 "integrity": "sha512-ndg3Ze/RZxsyX0QRhZ0HnkHUIth7/3jpTcABhzQzsXV0Y4MzpoZ0dj6KRqiBz+O3acZdk0/9gr40Smqc7S4ikg=="
3182 },
3177 "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { 3183 "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
3178 "version": "5.1.1-v1", 3184 "version": "5.1.1-v1",
3179 "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", 3185 "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 6c3022d..57c92a7 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -3,6 +3,7 @@
3 "version": "0.1.0", 3 "version": "0.1.0",
4 "private": true, 4 "private": true,
5 "dependencies": { 5 "dependencies": {
6 "@nekz/sdp": "^0.9.0",
6 "@testing-library/jest-dom": "^5.17.0", 7 "@testing-library/jest-dom": "^5.17.0",
7 "@testing-library/react": "^13.4.0", 8 "@testing-library/react": "^13.4.0",
8 "@testing-library/user-event": "^13.5.0", 9 "@testing-library/user-event": "^13.5.0",
diff --git a/frontend/src/components/UploadRunDialog.tsx b/frontend/src/components/UploadRunDialog.tsx
index d42ffe7..9f417b5 100644
--- a/frontend/src/components/UploadRunDialog.tsx
+++ b/frontend/src/components/UploadRunDialog.tsx
@@ -1,11 +1,16 @@
1import React from 'react'; 1import React from 'react';
2import { UploadRunContent } from '../types/Content'; 2import { UploadRunContent } from '../types/Content';
3import { DemoMessages, ScoreboardTempUpdate, SourceDemoParser, UserMessage } from '@nekz/sdp';
4import fs from 'fs';
5
6
3 7
4import '../css/UploadRunDialog.css'; 8import '../css/UploadRunDialog.css';
5import { Game } from '../types/Game'; 9import { Game } from '../types/Game';
6import { Map } from '../types/Map'; 10import { Map } from '../types/Map';
7import { API } from '../api/Api'; 11import { API } from '../api/Api';
8import { useNavigate } from 'react-router-dom'; 12import { useNavigate } from 'react-router-dom';
13import { SvcUserMessage } from '@nekz/sdp/script/src/types/NetMessages';
9 14
10interface UploadRunDialogProps { 15interface UploadRunDialogProps {
11 token?: string; 16 token?: string;
@@ -103,6 +108,16 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
103 return 108 return
104 } 109 }
105 } 110 }
111 const demo = SourceDemoParser.default()
112 .setOptions({ packets: true })
113 .parse(await uploadRunContent.host_demo.arrayBuffer());
114
115 const scoreboardPacket = demo.findPacket(ScoreboardTempUpdate)
116 if (scoreboardPacket) {
117 console.log(scoreboardPacket)
118 } else {
119 console.log("couldnt find scoreboard packet")
120 }
106 if (window.confirm("Are you sure you want to submit this run to LPHUB?")) { 121 if (window.confirm("Are you sure you want to submit this run to LPHUB?")) {
107 const message = await API.post_record(token, uploadRunContent); 122 const message = await API.post_record(token, uploadRunContent);
108 alert(message); 123 alert(message);
@@ -114,7 +129,7 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
114 129
115 React.useEffect(() => { 130 React.useEffect(() => {
116 if (open) { 131 if (open) {
117 _handle_game_select("1", "Portal 2 - Singleplayer"); // a different approach?. 132 _handle_game_select("1", "Portal 2 - Singleplayer"); // a different approach?.
118 } 133 }
119 }, [open]); 134 }, [open]);
120 135
@@ -125,15 +140,15 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
125 <div id='upload-run-menu'> 140 <div id='upload-run-menu'>
126 <div id='upload-run-menu-add'> 141 <div id='upload-run-menu-add'>
127 <div id='upload-run-route-category'> 142 <div id='upload-run-route-category'>
128 <div style={{padding: "15px 0px"}} className='upload-run-dropdown-container'> 143 <div style={{ padding: "15px 0px" }} className='upload-run-dropdown-container'>
129 <h1 style={{paddingBottom: "14px"}}>Select Game</h1> 144 <h1 style={{ paddingBottom: "14px" }}>Select Game</h1>
130 <div onClick={() => _handle_dropdowns(1)} style={{display: "flex", alignItems: "center", cursor: "pointer", justifyContent: "space-between"}}> 145 <div onClick={() => _handle_dropdowns(1)} style={{ display: "flex", alignItems: "center", cursor: "pointer", justifyContent: "space-between" }}>
131 <div className='dropdown-cur'>{selectedGameName}</div> 146 <div className='dropdown-cur'>{selectedGameName}</div>
132 <i style={{rotate: "-90deg", transform: "translate(-5px, 10px)"}} className="triangle"></i> 147 <i style={{ rotate: "-90deg", transform: "translate(-5px, 10px)" }} className="triangle"></i>
133 </div> 148 </div>
134 <div className={dropdown1Vis ? "upload-run-dropdown" : "upload-run-dropdown hidden"}> 149 <div className={dropdown1Vis ? "upload-run-dropdown" : "upload-run-dropdown hidden"}>
135 {games.map((game) => ( 150 {games.map((game) => (
136 <div onClick={() => {_handle_game_select(game.id.toString(), game.name); _handle_dropdowns(1)}} key={game.id}>{game.name}</div> 151 <div onClick={() => { _handle_game_select(game.id.toString(), game.name); _handle_dropdowns(1) }} key={game.id}>{game.name}</div>
137 ))} 152 ))}
138 </div> 153 </div>
139 </div> 154 </div>
@@ -141,45 +156,45 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose,
141 !loading && 156 !loading &&
142 ( 157 (
143 <> 158 <>
144 <div className='upload-run-map-container' style={{paddingBottom: "10px"}}> 159 <div className='upload-run-map-container' style={{ paddingBottom: "10px" }}>
145 <div style={{padding: "15px 0px"}}> 160 <div style={{ padding: "15px 0px" }}>
146 <h1 style={{paddingBottom: "14px"}}>Select Map</h1> 161 <h1 style={{ paddingBottom: "14px" }}>Select Map</h1>
147 <div onClick={() => _handle_dropdowns(2)} style={{display: "flex", alignItems: "center", cursor: "pointer", justifyContent: "space-between"}}> 162 <div onClick={() => _handle_dropdowns(2)} style={{ display: "flex", alignItems: "center", cursor: "pointer", justifyContent: "space-between" }}>
148 <span style={{userSelect: "none"}}>{currentMap}</span> 163 <span style={{ userSelect: "none" }}>{currentMap}</span>
149 <i style={{rotate: "-90deg", transform: "translate(-5px, 10px)"}} className="triangle"></i> 164 <i style={{ rotate: "-90deg", transform: "translate(-5px, 10px)" }} className="triangle"></i>
165 </div>
150 </div> 166 </div>
151 </div> 167 <div>
152 <div> 168 <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"}> 169 {selectedGameMaps && selectedGameMaps.map((gameMap) => (
154 {selectedGameMaps && selectedGameMaps.map((gameMap) => ( 170 <div onClick={() => { setUploadRunContent({ ...uploadRunContent, map_id: gameMap.id }); _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> 171 ))}
156 ))} 172 </div>
173 </div>
174 <span>Host Demo</span>
175 <input type="file" name="host_demo" id="host_demo" accept=".dem" onChange={(e) => _handle_file_change(e, true)} />
176 {
177 games[selectedGameID].is_coop &&
178 (
179 <>
180 <span>Partner Demo</span>
181 <input type="file" name="partner_demo" id="partner_demo" accept=".dem" onChange={(e) => _handle_file_change(e, false)} />
182 <span>Partner ID</span>
183 <input type="text" name="partner_id" id="partner_id" onChange={(e) => setUploadRunContent({
184 ...uploadRunContent,
185 partner_id: e.target.value,
186 })} />
187 </>
188 )
189 }
190 <div className='search-container'>
191
192 </div>
193 <div className='upload-run-buttons-container'>
194 <button onClick={_upload_run}>Submit</button>
195 <button onClick={() => onClose()}>Cancel</button>
157 </div> 196 </div>
158 </div> 197 </div>
159 <span>Host Demo</span>
160 <input type="file" name="host_demo" id="host_demo" accept=".dem" onChange={(e) => _handle_file_change(e, true)} />
161 {
162 games[selectedGameID].is_coop &&
163 (
164 <>
165 <span>Partner Demo</span>
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 })} />
172 </>
173 )
174 }
175 <div className='search-container'>
176
177 </div>
178 <div className='upload-run-buttons-container'>
179 <button onClick={_upload_run}>Submit</button>
180 <button onClick={() => onClose()}>Cancel</button>
181 </div>
182 </div>
183 </> 198 </>
184 ) 199 )
185 } 200 }