From dea41ea59c558068078c98dd1758cdba2beceda8 Mon Sep 17 00:00:00 2001 From: Wolfboy248 <121288977+Wolfboy248@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:48:01 +0200 Subject: refactor: maplist page --- frontend/src/App.tsx | 3 +- frontend/src/api/Api.tsx | 23 +++- frontend/src/components/MapEntry.tsx | 12 +++ frontend/src/css/Maplist.css | 198 +++++++++++++++++++++++++++++++++++ frontend/src/css/Maps.css | 2 +- frontend/src/pages/Games.tsx | 6 +- frontend/src/pages/Maplist.tsx | 183 ++++++++++++++++++++++++++++++++ frontend/src/types/Chapters.tsx | 19 ++++ 8 files changed, 437 insertions(+), 9 deletions(-) create mode 100644 frontend/src/components/MapEntry.tsx create mode 100644 frontend/src/css/Maplist.css create mode 100644 frontend/src/pages/Maplist.tsx create mode 100644 frontend/src/types/Chapters.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b9e84f4..fdf1077 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -10,7 +10,7 @@ import Games from './pages/Games'; import Maps from './pages/Maps'; import User from './pages/User'; import Homepage from './pages/Homepage'; - +import Maplist from './pages/Maplist'; const App: React.FC = () => { const [token, setToken] = React.useState(undefined); @@ -32,6 +32,7 @@ const App: React.FC = () => { } /> } /> } /> + }> diff --git a/frontend/src/api/Api.tsx b/frontend/src/api/Api.tsx index 9e45bc4..326052f 100644 --- a/frontend/src/api/Api.tsx +++ b/frontend/src/api/Api.tsx @@ -1,7 +1,8 @@ import axios from 'axios'; -import { Game } from '../types/Game'; -import { MapDiscussion, MapDiscussions, MapLeaderboard, MapSummary } from '../types/Map'; +import { Game, GameChapters } from '../types/Game'; +import { GameChapter, GamesChapters } from '../types/Chapters'; +import { MapDiscussion, MapDiscussions, MapLeaderboard, MapSummary, Map } from '../types/Map'; import { MapDiscussionCommentContent, MapDiscussionContent, ModMenuContent } from '../types/Content'; import { Search } from '../types/Search'; import { UserProfile } from '../types/Profile'; @@ -13,6 +14,9 @@ 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_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), @@ -55,6 +59,21 @@ const get_games = async (): Promise => { return response.data.data; }; +const get_chapters = async (chapter_id: string): Promise => { + const response = await axios.get(url(`chapters/${chapter_id}`)); + return response.data.data; +} + +const get_games_chapters = async (game_id: string): Promise => { + const response = await axios.get(url(`games/${game_id}`)); + return response.data.data; +}; + +const get_games_maps = async (game_id: string): Promise => { + const response = await axios.get(url(`games/${game_id}/maps`)) + return response.data.data; +} + // SEARCH const get_search = async (q: string): Promise => { diff --git a/frontend/src/components/MapEntry.tsx b/frontend/src/components/MapEntry.tsx new file mode 100644 index 0000000..0f494ad --- /dev/null +++ b/frontend/src/components/MapEntry.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { Link } from "react-router-dom"; + +const MapEntry: React.FC = () => { + return ( +
+ +
+ ) +} + +export default MapEntry; diff --git a/frontend/src/css/Maplist.css b/frontend/src/css/Maplist.css new file mode 100644 index 0000000..bd1f646 --- /dev/null +++ b/frontend/src/css/Maplist.css @@ -0,0 +1,198 @@ +h1 { + font-family: "BarlowCondensed-Bold"; + margin: 0px 0px; +} + +h2 { + margin: 20px 0px; + font-family: "BarlowSemiCondensed-SemiBold"; + font-size: 96px; +} + +h3 { + font-family: "BarlowSemiCondensed-Regular"; + margin: 0px 10px; + font-size: 42px; +} + +.game-header { + text-align: center; + border-radius: 24px; + overflow: hidden; + background-size: cover; + background-position: 25%; + margin-top: 12px; +} + +.blur { + backdrop-filter: blur(4px); + display: flex; + flex-direction: column; + width: 100%; +} + +.blur.map { + flex-direction: row; + align-items: center; + justify-content: center; +} + +.blur.map span { + width: fit-content; +} + +.blur.map span:nth-child(1) { + font-family: "BarlowSemiCondensed-SemiBold"; + font-size: 60px; + margin-right: 6px; +} + +.game-header-portal-count { + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.game-header-categories { + display: flex; + height: 50px; + background-color: #202232; + gap: 2px; +} + +.game-cat-button { + background-color: #2b2e46; + border: 0; + color: #cdcfdf; + font-family: "BarlowSemiCondensed-Regular"; + font-size: 22px; + cursor: pointer; + transition: all 0.1s; + width: 100%; +} + +.game-cat-button:hover, .game-cat-button.selected { + background-color: #202232; +} + +/* maplist */ +.maplist { + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-gap: 20px; + margin: 20px 0px; +} + +.maplist-entry { + background-color: #202232; + border-radius: 24px; + overflow: hidden; +} + +.maplist-entry span { + text-align: center; + font-size: 20px; + width: 100%; + display: block; + margin: 5px 0px; + color: #cdcfdf; +} + +.map-entry-image { + display: flex; + height: 150px; + background-size: cover; +} + +.difficulty-bar { + display: flex; + margin: 0px 10px; +} + +.difficulty-bar span { + text-align: left; + margin-left: 0px; + width: fit-content; +} + +.difficulty-bar div { + display: flex; + width: 100%; + align-items: center; + justify-content: center; + gap: 5px; + border-radius: 2000px; + margin-left: 2px; + transform: translateY(1px); +} + +.difficulty-bar div div { + display: flex; + height: 3px; + width: 100%; + background-color: #2B2E46; +} + +.difficulty-bar div.one .difficulty-point:nth-child(1) { + background-color: #51C355; +} + +.difficulty-bar div.two .difficulty-point:nth-child(-n+2) { + background-color: #8AC93A; +} + +.difficulty-bar div.three .difficulty-point:nth-child(-n+3) { + background-color: #8AC93A; +} + +.difficulty-bar div.four .difficulty-point:nth-child(-n+4) { + background-color: #C35F51; +} + +.difficulty-bar div.five .difficulty-point:nth-child(-n+5) { + background-color: #dd422e; +} + +.dropdown { + cursor: pointer; + user-select: none; + display: flex; + width: fit-content; + align-items: center; +} + +.dropdown i { + transform: translate(5px, 8px) rotate(-90deg); +} + +.dropdown-elements { + position: absolute; + z-index: 1000; + background-color: #2B2E46; + border-radius: 15px; + overflow: hidden; + padding: 4px 4px; + animation: dropdown-in 0.1s ease; +} + +.dropdown-element { + cursor: pointer; + font-size: 20px; + border-radius: 2000px; + padding: 1px 4px; +} + +.dropdown-element:hover { + background-color: #202232; +} + +@keyframes dropdown-in { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} diff --git a/frontend/src/css/Maps.css b/frontend/src/css/Maps.css index d164d3b..335e0b2 100644 --- a/frontend/src/css/Maps.css +++ b/frontend/src/css/Maps.css @@ -29,7 +29,7 @@ height: 40px; background-color: #2b2e46; - color: inherit; + color: #cdcfdf; font-size: 18px; font-family: inherit; border: none; diff --git a/frontend/src/pages/Games.tsx b/frontend/src/pages/Games.tsx index ce3db76..eb7177f 100644 --- a/frontend/src/pages/Games.tsx +++ b/frontend/src/pages/Games.tsx @@ -18,13 +18,9 @@ const Games: React.FC = () => { loaders.forEach((loader) => { (loader as HTMLElement).style.display = "none"; }); - } + } React.useEffect(() => { - document.querySelectorAll(".games-page-item-body").forEach((game, index) => { - game.innerHTML = ""; - }); - _fetch_games(); _page_load(); }, []); diff --git a/frontend/src/pages/Maplist.tsx b/frontend/src/pages/Maplist.tsx new file mode 100644 index 0000000..b1b1664 --- /dev/null +++ b/frontend/src/pages/Maplist.tsx @@ -0,0 +1,183 @@ +import React, { useEffect } from "react"; +import { Link, useLocation, useParams } from "react-router-dom"; + +import "../css/Maplist.css"; +import { API } from "../api/Api"; +import { Game, GameChapters } from "../types/Game"; +import { GameChapter, GamesChapters } from "../types/Chapters"; +import { Map } from "../types/Map"; + +const Maplist: React.FC = () => { + const [game, setGame] = React.useState(null); + const [catNum, setCatNum] = React.useState(0); + const [id, setId] = React.useState(0); + const [category, setCategory] = React.useState(0); + const [load, setLoad] = React.useState(false); + const [currentlySelected, setCurrentlySelected] = React.useState(0); + const [hasClicked, setHasClicked] = React.useState(false); + const [gameChapters, setGameChapters] = React.useState(); + const [curChapter, setCurChapter] = React.useState(); + const [numChapters, setNumChapters] = React.useState(0); + + const [dropdownActive, setDropdownActive] = React.useState("none"); + + const params = useParams<{ id: string }>(); + const location = useLocation(); + + function _update_currently_selected(catNum2: number) { + setCurrentlySelected(catNum2); + setHasClicked(true); + } + + const _fetch_chapters = async (chapter_id: string) => { + const chapters = await API.get_chapters(chapter_id); + setCurChapter(chapters); + } + + const _handle_dropdown_click = () => { + if (dropdownActive == "none") { + setDropdownActive("block"); + } else { + setDropdownActive("none"); + } + } + + // im sorry but im too lazy to fix this right now + useEffect(() => { + // gameID + const gameId = parseFloat(params.id || ""); + setId(gameId); + + // location query params + const queryParams = new URLSearchParams(location.search); + if (queryParams.get("cat")) { + const cat = parseFloat(queryParams.get("cat") || ""); + setCategory(cat); + setCatNum(cat - 1); + } + + const _fetch_game = async () => { + const games = await API.get_games(); + const foundGame = games.find((game) => game.id === gameId); + // console.log(foundGame) + if (foundGame) { + setGame(foundGame); + } + }; + + const _fetch_game_chapters = async () => { + const games_chapters = await API.get_games_chapters(gameId.toString()); + setGameChapters(games_chapters); + 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(); + }, []); + + useEffect(() => { + if (gameChapters != undefined) { + _fetch_chapters(gameChapters!.chapters[0].id.toString()); + } + }, [gameChapters]) + + + + return ( +
+
+ + + +
+ {!load ? ( +
+ ) : ( +
+

{game?.name}

+
+
+
+

+ { + game?.category_portals.find( + (obj) => obj.category.id === catNum + 1 + )?.portal_count + } +

+

portals

+
+
+ {game?.category_portals.map((cat, index) => ( + + ))} +
+
+
+ +
+
+
+ {curChapter?.chapter.name.split(" - ")[0]} +
+
+ {curChapter?.chapter.name.split(" - ")[1]} + +
+
+ {gameChapters?.chapters.map((chapter, i) => { + return
{_fetch_chapters(chapter.id.toString()); _handle_dropdown_click()}}>{chapter.name}
+ }) + + } +
+
+
+ {curChapter?.maps.map((map, i) => { + return
+ + {map.name} +
+
+ {map.is_disabled ? map.category_portals[0].portal_count : map.category_portals.find( + (obj) => obj.category.id === catNum + 1 + )?.portal_count} + portals +
+
+
+ Difficulty: +
+
+
+
+
+
+
+
+ +
+ })} +
+
+
+ )} +
+ ); +}; + +export default Maplist; diff --git a/frontend/src/types/Chapters.tsx b/frontend/src/types/Chapters.tsx new file mode 100644 index 0000000..2c0afdd --- /dev/null +++ b/frontend/src/types/Chapters.tsx @@ -0,0 +1,19 @@ +import { Game } from "./Game"; +import { Map } from "./Map"; + +interface Chapter { + id: number; + name: string; + image: string; + is_disabled: boolean; +} + +export interface GameChapter { + chapter: Chapter; + maps: Map[]; +} + +export interface GamesChapters { + game: Game; + chapters: Chapter[]; +} \ No newline at end of file -- cgit v1.2.3