From 89560a61bc6e41d86acaea596762eda2da38fe50 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 9 Sep 2024 19:29:42 +0300 Subject: refactor: upload run form, lots of random shit --- frontend/src/App.css | 5 ++ frontend/src/App.tsx | 34 +++++++-- frontend/src/api/Api.tsx | 15 ++-- frontend/src/components/Loading.tsx | 11 +++ frontend/src/components/Login.tsx | 2 +- frontend/src/components/Sidebar.tsx | 33 +++++++-- frontend/src/components/UploadRunDialog.tsx | 103 ++++++++++++++++++++++++++++ frontend/src/css/About.css | 16 +++++ frontend/src/css/Maplist.css | 2 +- frontend/src/css/Maps.css | 3 +- frontend/src/css/Sidebar.css | 65 ++++++++++++++++-- frontend/src/css/UploadRunDialog.css | 47 +++++++++++++ frontend/src/images/Images.tsx | 4 +- frontend/src/images/png/20.png | Bin 0 -> 2072 bytes frontend/src/pages/About.tsx | 36 ++++++++++ frontend/src/pages/Games.tsx | 15 ++-- frontend/src/pages/Homepage.tsx | 1 + frontend/src/pages/Maplist.tsx | 11 +-- frontend/src/pages/Maps.tsx | 33 ++++++--- frontend/src/types/Content.tsx | 8 +++ 20 files changed, 394 insertions(+), 50 deletions(-) create mode 100644 frontend/src/components/Loading.tsx create mode 100644 frontend/src/components/UploadRunDialog.tsx create mode 100644 frontend/src/css/About.css create mode 100644 frontend/src/css/UploadRunDialog.css create mode 100644 frontend/src/images/png/20.png create mode 100644 frontend/src/pages/About.tsx (limited to 'frontend') diff --git a/frontend/src/App.css b/frontend/src/App.css index 3b732f0..b0445d8 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -15,6 +15,11 @@ main { } +a { + color: inherit; + width: fit-content; +} + body { overflow: hidden; background-color: #141520; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index d45cd97..095cbbe 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -10,6 +10,10 @@ import Games from './pages/Games'; import Maps from './pages/Maps'; import User from './pages/User'; import Homepage from './pages/Homepage'; +import UploadRunDialog from './components/UploadRunDialog'; +import About from './pages/About'; +import { Game } from './types/Game'; +import { API } from './api/Api'; import Maplist from './pages/Maplist'; import Rankings from './pages/Rankings'; @@ -18,22 +22,44 @@ const App: React.FC = () => { const [profile, setProfile] = React.useState(undefined); const [isModerator, setIsModerator] = React.useState(true); + const [games, setGames] = React.useState([]); + + const [uploadRunDialog, setUploadRunDialog] = React.useState(false); + const [uploadRunDialogMapID, setUploadRunDialogMapID] = React.useState(undefined); + // React.useEffect(() => { // if (token) { // setIsModerator(JSON.parse(atob(token.split(".")[1])).mod) // } // }, [token]); + const _fetch_games = async () => { + const games = await API.get_games(); + setGames(games); + }; + + React.useEffect(() => { + _fetch_games(); + }, []); + + if (!games) { + return ( + <> + ) + }; + return ( <> - + setUploadRunDialog(false)} mapID={uploadRunDialogMapID} games={games} /> + setUploadRunDialog(true)} /> } /> } /> } /> - } /> - } /> - }> + } /> + }> + {setUploadRunDialog(true);setUploadRunDialogMapID(mapID)}} />}/> + } /> }> diff --git a/frontend/src/api/Api.tsx b/frontend/src/api/Api.tsx index e62bb22..1f77088 100644 --- a/frontend/src/api/Api.tsx +++ b/frontend/src/api/Api.tsx @@ -1,8 +1,8 @@ import axios from 'axios'; -import { Game, GameChapters } from '../types/Game'; import { GameChapter, GamesChapters } from '../types/Chapters'; -import { MapDiscussion, MapDiscussions, MapLeaderboard, MapSummary, Map } from '../types/Map'; +import { Game } from '../types/Game'; +import { Map, MapDiscussion, MapDiscussions, MapLeaderboard, MapSummary } from '../types/Map'; import { MapDiscussionCommentContent, MapDiscussionContent, ModMenuContent } from '../types/Content'; import { Search } from '../types/Search'; import { UserProfile } from '../types/Profile'; @@ -15,11 +15,13 @@ export const API = { get_user: (user_id: string) => get_user(user_id), get_games: () => get_games(), + get_chapters: (chapter_id: string) => get_chapters(chapter_id), get_games_chapters: (game_id: string) => get_games_chapters(game_id), - get_games_maps: (game_id: string) => get_games_maps(game_id), + get_game_maps: (game_id: string) => get_game_maps(game_id), get_rankings: () => get_rankings(), get_search: (q: string) => get_search(q), + get_map_summary: (map_id: string) => get_map_summary(map_id), get_map_leaderboard: (map_id: string) => get_map_leaderboard(map_id), get_map_discussions: (map_id: string) => get_map_discussions(map_id), @@ -71,10 +73,11 @@ const get_games_chapters = async (game_id: string): Promise => { return response.data.data; }; -const get_games_maps = async (game_id: string): Promise => { +const get_game_maps = async (game_id: string): Promise => { const response = await axios.get(url(`games/${game_id}/maps`)) - return response.data.data; -} + return response.data.data.maps; +}; + // RANKINGS const get_rankings = async (): Promise => { diff --git a/frontend/src/components/Loading.tsx b/frontend/src/components/Loading.tsx new file mode 100644 index 0000000..b7973f1 --- /dev/null +++ b/frontend/src/components/Loading.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +const Loading: React.FC = () => { + + return ( +
+ ); + +}; + +export default Loading; diff --git a/frontend/src/components/Login.tsx b/frontend/src/components/Login.tsx index adfa718..367dc72 100644 --- a/frontend/src/components/Login.tsx +++ b/frontend/src/components/Login.tsx @@ -35,7 +35,7 @@ const Login: React.FC = ({ token, setToken, profile, setProfile }) = {profile.user_name} - diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index d303927..22d5c8b 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Link, useLocation } from 'react-router-dom'; -import { BookIcon, FlagIcon, HelpIcon, HomeIcon, LogoIcon, NewsIcon, PortalIcon, SearchIcon, TableIcon } from '../images/Images'; +import { BookIcon, FlagIcon, HelpIcon, HomeIcon, LogoIcon, PortalIcon, SearchIcon, UploadIcon } from '../images/Images'; import Login from './Login'; import { UserProfile } from '../types/Profile'; import { Search } from '../types/Search'; @@ -12,9 +12,10 @@ interface SidebarProps { setToken: React.Dispatch>; profile?: UserProfile; setProfile: React.Dispatch>; + onUploadRun: () => void; }; -const Sidebar: React.FC = ({ setToken, profile, setProfile }) => { +const Sidebar: React.FC = ({ setToken, profile, setProfile, onUploadRun }) => { const [searchData, setSearchData] = React.useState(undefined); const [isSidebarLocked, setIsSidebarLocked] = React.useState(false); @@ -40,11 +41,19 @@ const Sidebar: React.FC = ({ setToken, profile, setProfile }) => { const span = document.querySelectorAll("button.sidebar-button>span") as NodeListOf const side = document.querySelector("#sidebar-list") as HTMLElement; const searchbar = document.querySelector("#searchbar") as HTMLInputElement; + const uploadRunBtn = document.querySelector("#upload-run") as HTMLInputElement; + const uploadRunSpan = document.querySelector("#upload-run>span") as HTMLInputElement; if (isSidebarOpen) { if (profile) { const login = document.querySelectorAll(".login>button")[1] as HTMLElement; login.style.opacity = "1" + uploadRunBtn.style.width = "310px" + uploadRunBtn.style.padding = "0.4em 0 0 11px" + uploadRunSpan.style.opacity = "0" + setTimeout(() => { + uploadRunSpan.style.opacity = "1" + }, 100) } setSidebarOpen(false); side.style.width = "320px" @@ -54,14 +63,17 @@ const Sidebar: React.FC = ({ setToken, profile, setProfile }) => { setTimeout(() => { span[i].style.opacity = "1" }, 100) - }) + }); side.style.zIndex = "2" } else { if (profile) { const login = document.querySelectorAll(".login>button")[1] as HTMLElement; login.style.opacity = "0" + uploadRunBtn.style.width = "40px" + uploadRunBtn.style.padding = "0.4em 0 0 5px" + uploadRunSpan.style.opacity = "0" } - setSidebarOpen(true) + setSidebarOpen(true); side.style.width = "40px"; searchbar.focus(); btn.forEach((e, i) => { @@ -106,7 +118,7 @@ const Sidebar: React.FC = ({ setToken, profile, setProfile }) => {
PORTAL 2
- Least Portals + Least Portals Hub
@@ -140,14 +152,21 @@ const Sidebar: React.FC = ({ setToken, profile, setProfile }) => { diff --git a/frontend/src/components/UploadRunDialog.tsx b/frontend/src/components/UploadRunDialog.tsx new file mode 100644 index 0000000..e099a40 --- /dev/null +++ b/frontend/src/components/UploadRunDialog.tsx @@ -0,0 +1,103 @@ +import React from 'react'; +import { UploadRunContent } from '../types/Content'; + +import '../css/UploadRunDialog.css'; +import { Game } from '../types/Game'; +import Games from '../pages/Games'; +import { Map } from '../types/Map'; +import { API } from '../api/Api'; + +interface UploadRunDialogProps { + open: boolean; + onClose: () => void; + mapID?: number; + games: Game[]; +} + +const UploadRunDialog: React.FC = ({ open, onClose, mapID, games }) => { + + const [uploadRunContent, setUploadRunContent] = React.useState({ + map_id: 0, + host_demo: null, + partner_demo: null, + partner_id: undefined, + is_partner_orange: undefined, + }); + + const [selectedGameID, setSelectedGameID] = React.useState(0); + const [selectedGameMaps, setSelectedGameMaps] = React.useState([]); + + const [loading, setLoading] = React.useState(false); + + const _handle_game_select = async (game_id: string) => { + setLoading(true); + const gameMaps = await API.get_game_maps(game_id); + setSelectedGameMaps(gameMaps); + setUploadRunContent({ + ...uploadRunContent, + map_id: gameMaps[0].id, + }); + setSelectedGameID(parseInt(game_id) - 1); + setLoading(false); + }; + + React.useEffect(() => { + _handle_game_select("1"); // a different approach? + }, []); + + if (open) { + return ( + <> +
+
+
+
+ Select Game + + { + !loading && + ( + <> + Select Map + + Host Demo + + { + games[selectedGameID].is_coop && + ( + <> + Partner Demo + + + ) + } + + + + ) + } +
+
+
+ + ); + } + + return ( + <> + ); + +}; + +export default UploadRunDialog; \ No newline at end of file diff --git a/frontend/src/css/About.css b/frontend/src/css/About.css new file mode 100644 index 0000000..f998699 --- /dev/null +++ b/frontend/src/css/About.css @@ -0,0 +1,16 @@ +#about { + overflow: auto; + overflow-x: hidden; + position: relative; + + width: calc(100% - 380px); + height: 100vh; + left: 350px; + + padding-right: 30px; + + font-size: 24px; + font-family: BarlowSemiCondensed-Regular; + color: #cdcfdf; + +} \ No newline at end of file diff --git a/frontend/src/css/Maplist.css b/frontend/src/css/Maplist.css index bd1f646..230ac98 100644 --- a/frontend/src/css/Maplist.css +++ b/frontend/src/css/Maplist.css @@ -107,7 +107,7 @@ h3 { .difficulty-bar { display: flex; - margin: 0px 10px; + margin: 15px 10px; } .difficulty-bar span { diff --git a/frontend/src/css/Maps.css b/frontend/src/css/Maps.css index 335e0b2..d38eea5 100644 --- a/frontend/src/css/Maps.css +++ b/frontend/src/css/Maps.css @@ -299,7 +299,7 @@ p>span.portal-count{font-weight: bold;font-size: 100px;vertical-align: -15%;} #description>iframe{ - margin: 4px; + margin: 20px; float:right; border: 0; border-radius: 24px; @@ -311,6 +311,7 @@ p>span.portal-count{font-weight: bold;font-size: 100px;vertical-align: -15%;} display: block; font-size: 21px; word-wrap: break-word; + text-align: justify; } #description-text>b{font-size: inherit;} #description-text>a{font-size: inherit;color: #3c91e6;} diff --git a/frontend/src/css/Sidebar.css b/frontend/src/css/Sidebar.css index 34ede80..396f6ac 100644 --- a/frontend/src/css/Sidebar.css +++ b/frontend/src/css/Sidebar.css @@ -10,22 +10,22 @@ /* logo */ #logo{ display: grid; - grid-template-columns: 60px 200px; + grid-template-columns: 60px 220px; height: 80px; - padding: 20px 0 20px 30px; + padding: 20px 0 20px 20px; cursor: pointer; user-select: none; } #logo-text{ font-family: BarlowCondensed-Regular; - font-size: 42px; + font-size: 36px; color: #FFF; line-height: 38px; } -span>b{ +#logo-text>span>b{ font-family: BarlowCondensed-Bold; font-size: 56px; } @@ -55,7 +55,7 @@ span>b{ margin: 0 5px 0 5px; justify-items: left; - grid-template-rows: calc(100vh - 670px) 50px 50px 50px; + grid-template-rows: calc(100vh - 720px) 50px 50px 50px; } .sidebar-button>span{ font-family: BarlowSemiCondensed-Regular; @@ -85,6 +85,61 @@ span>b{ padding .3s; } +.logout-button{ + display: grid; + grid-template-columns: 50px auto; + place-items: left; + text-align: left; + + background-color: inherit; + cursor: pointer; + border: none; + width: 310px; + height: 40px; + border-radius: 20px; + padding: 0.4em 0 0 11px; + + transition: + width .3s, + background-color .15s, + padding .3s; +} + +.submit-run-button { + display: grid; + grid-template-columns: 50px auto; + place-items: left; + text-align: left; + + background-color: inherit; + cursor: pointer; + border: none; + width: 310px; + height: 40px; + border-radius: 20px; + padding: 0.4em 0 0 11px; + + transition: + width .3s, + background-color .15s, + padding .3s; + background-color: #202232aa; +} + +.submit-run-button:hover { + background-color: #202232; +} + + +.submit-run-button>span{ + font-family: BarlowSemiCondensed-Regular; + font-size: 18px; + color: #CDCFDF; + height: 32px; + line-height: 28px; + transition: opacity .1s; +} + .sidebar-button-selected { background-color: #202232; } diff --git a/frontend/src/css/UploadRunDialog.css b/frontend/src/css/UploadRunDialog.css new file mode 100644 index 0000000..60f0a28 --- /dev/null +++ b/frontend/src/css/UploadRunDialog.css @@ -0,0 +1,47 @@ +div#upload-run{ + position: absolute; + left: 50%; + z-index: 20; + width: 320px; height: auto; + /* background-color: red; */ + + transform: translateY(-68%); +} + + +#upload-run-menu{ + position: absolute; + left: calc(50% + 160px); top: 130px; + transform: translateX(-50%); + background-color: #2b2e46; + z-index: 2; color: white; +} + +#upload-run-menu-add, +#upload-run-menu-edit{ + box-shadow: 0 0 40px 16px black; + outline: 8px solid #2b2e46; + border-radius: 20px; + font-size: 40px; + display: grid; + grid-template-columns: 20% 20% 20% 20% 20%; +} + +#upload-run-menu-add>div, +#upload-run-menu-edit>div{ + display: grid; + margin: 20px; + width: 200px; + font-size: 20px; +} + +#upload-run-block{ + position: absolute; + background-color: black; + opacity: .3; + left: 320px; + width: calc(100% - 320px); + height: 100%; + z-index: 2; + cursor: no-drop; +} diff --git a/frontend/src/images/Images.tsx b/frontend/src/images/Images.tsx index d2f6dfb..89b99c0 100644 --- a/frontend/src/images/Images.tsx +++ b/frontend/src/images/Images.tsx @@ -19,6 +19,7 @@ import img16 from './png/16.png'; import img17 from './png/17.png'; import img18 from './png/18.png'; import img19 from './png/19.png'; +import img20 from './png/20.png'; export const LogoIcon = logo; export const LoginIcon = login; @@ -41,4 +42,5 @@ export const TwitchIcon = img15; export const YouTubeIcon = img16; export const SteamIcon = img17; export const HistoryIcon = img18; -export const SortIcon = img19; \ No newline at end of file +export const SortIcon = img19; +export const UploadIcon = img20; \ No newline at end of file diff --git a/frontend/src/images/png/20.png b/frontend/src/images/png/20.png new file mode 100644 index 0000000..1af0a97 Binary files /dev/null and b/frontend/src/images/png/20.png differ diff --git a/frontend/src/pages/About.tsx b/frontend/src/pages/About.tsx new file mode 100644 index 0000000..886808b --- /dev/null +++ b/frontend/src/pages/About.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import ReactMarkdown from 'react-markdown'; + +import '../css/About.css'; + +const About: React.FC = () => { + + const [aboutText, setAboutText] = React.useState(""); + + React.useEffect(() => { + const fetchReadme = async () => { + try { + const response = await fetch( + 'https://raw.githubusercontent.com/pektezol/leastportalshub/main/README.md' + ); + if (!response.ok) { + throw new Error('Failed to fetch README'); + } + const readmeText = await response.text(); + setAboutText(readmeText); + } catch (error) { + console.error('Error fetching README:', error); + } + }; + fetchReadme(); + }, []); + + + return ( +
+ {aboutText} +
+ ); +}; + +export default About; diff --git a/frontend/src/pages/Games.tsx b/frontend/src/pages/Games.tsx index eb7177f..ea136c2 100644 --- a/frontend/src/pages/Games.tsx +++ b/frontend/src/pages/Games.tsx @@ -2,16 +2,13 @@ import React from 'react'; import GameEntry from '../components/GameEntry'; import { Game } from '../types/Game'; -import { API } from '../api/Api'; import "../css/Maps.css" -const Games: React.FC = () => { - const [games, setGames] = React.useState([]); +interface GamesProps { + games: Game[]; +} - const _fetch_games = async () => { - const games = await API.get_games(); - setGames(games); - }; +const Games: React.FC = ({ games }) => { const _page_load = () => { const loaders = document.querySelectorAll(".loader"); @@ -21,7 +18,9 @@ const Games: React.FC = () => { } React.useEffect(() => { - _fetch_games(); + document.querySelectorAll(".games-page-item-body").forEach((game, index) => { + game.innerHTML = ""; + }); _page_load(); }, []); diff --git a/frontend/src/pages/Homepage.tsx b/frontend/src/pages/Homepage.tsx index 62a8f1c..d4f3095 100644 --- a/frontend/src/pages/Homepage.tsx +++ b/frontend/src/pages/Homepage.tsx @@ -6,6 +6,7 @@ const Homepage: React.FC = () => { return (
+

lphubWelcome to Least Portals Hub!

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.

The website should feel intuitive to navigate around. For any type of feedback, reach us at LPHUB Discord server.

diff --git a/frontend/src/pages/Maplist.tsx b/frontend/src/pages/Maplist.tsx index 3bc5f74..5d0c852 100644 --- a/frontend/src/pages/Maplist.tsx +++ b/frontend/src/pages/Maplist.tsx @@ -73,14 +73,9 @@ const Maplist: React.FC = () => { setNumChapters(games_chapters.chapters.length); } - const _fetch_maps = async () => { - const maps = await API.get_games_maps(gameId.toString()); - setLoad(true); - } - _fetch_game(); _fetch_game_chapters(); - _fetch_maps(); + setLoad(true); }, []); useEffect(() => { @@ -97,7 +92,7 @@ const Maplist: React.FC = () => {
@@ -162,7 +157,7 @@ const Maplist: React.FC = () => {
- Difficulty: + {/* Difficulty: */}
diff --git a/frontend/src/pages/Maps.tsx b/frontend/src/pages/Maps.tsx index 707d865..5548650 100644 --- a/frontend/src/pages/Maps.tsx +++ b/frontend/src/pages/Maps.tsx @@ -7,21 +7,24 @@ import Leaderboards from '../components/Leaderboards'; import Discussions from '../components/Discussions'; import ModMenu from '../components/ModMenu'; import { MapDiscussions, MapLeaderboard, MapSummary } from '../types/Map'; +import { UserProfile } from '../types/Profile'; import { API } from '../api/Api'; import "../css/Maps.css"; +import Loading from '../components/Loading'; interface MapProps { + profile?: UserProfile; isModerator: boolean; + onUploadRun: (mapID: number) => void; }; -const Maps: React.FC = ({ isModerator }) => { +const Maps: React.FC = ({ profile, isModerator, onUploadRun }) => { const [selectedRun, setSelectedRun] = React.useState(0); const [mapSummaryData, setMapSummaryData] = React.useState(undefined); const [mapLeaderboardData, setMapLeaderboardData] = React.useState(undefined); - const [mapDiscussionsData, setMapDiscussionsData] = React.useState(undefined) - + const [mapDiscussionsData, setMapDiscussionsData] = React.useState(undefined); const [navState, setNavState] = React.useState(0); @@ -51,8 +54,23 @@ const Maps: React.FC = ({ isModerator }) => { }, []); if (!mapSummaryData) { + // loading placeholder return ( - <> +
+
+
+ +
+
+ +
+ + + +
+ + +
); } @@ -66,12 +84,11 @@ const Maps: React.FC = ({ isModerator }) => {
- - + +
{mapSummaryData.map.map_name} + {profile && }
- -
diff --git a/frontend/src/types/Content.tsx b/frontend/src/types/Content.tsx index e593505..5733f1f 100644 --- a/frontend/src/types/Content.tsx +++ b/frontend/src/types/Content.tsx @@ -16,3 +16,11 @@ export interface MapDiscussionContent { export interface MapDiscussionCommentContent { comment: string; }; + +export interface UploadRunContent { + map_id: number; + host_demo: File | null; + partner_demo: File | null; + partner_id?: string; + is_partner_orange?: boolean; +}; -- cgit v1.2.3