diff options
Diffstat (limited to '')
| -rw-r--r-- | frontend/src/components/pages/about.css | 17 | ||||
| -rw-r--r-- | frontend/src/components/pages/about.js | 32 | ||||
| -rw-r--r-- | frontend/src/components/pages/game.js | 46 | ||||
| -rw-r--r-- | frontend/src/components/pages/games.js | 62 | ||||
| -rw-r--r-- | frontend/src/components/pages/home.css | 92 | ||||
| -rw-r--r-- | frontend/src/components/pages/home.js | 242 | ||||
| -rw-r--r-- | frontend/src/components/pages/maplist.css | 403 | ||||
| -rw-r--r-- | frontend/src/components/pages/maplist.js | 890 | ||||
| -rw-r--r-- | frontend/src/components/pages/profile.js | 382 | ||||
| -rw-r--r-- | frontend/src/components/pages/summary.js | 650 | ||||
| -rw-r--r-- | frontend/src/components/pages/summary_modview.js | 254 | ||||
| -rw-r--r-- | frontend/src/css/Games.css (renamed from frontend/src/components/pages/games.css) | 0 | ||||
| -rw-r--r-- | frontend/src/css/Maps.css (renamed from frontend/src/components/pages/summary.css) | 10 | ||||
| -rw-r--r-- | frontend/src/css/ModMenu.css (renamed from frontend/src/components/pages/summary_modview.css) | 0 | ||||
| -rw-r--r-- | frontend/src/css/Profile.css (renamed from frontend/src/components/pages/profile.css) | 0 |
15 files changed, 8 insertions, 3072 deletions
diff --git a/frontend/src/components/pages/about.css b/frontend/src/components/pages/about.css deleted file mode 100644 index 0dec300..0000000 --- a/frontend/src/components/pages/about.css +++ /dev/null | |||
| @@ -1,17 +0,0 @@ | |||
| 1 | |||
| 2 | #about { | ||
| 3 | overflow: auto; | ||
| 4 | overflow-x: hidden; | ||
| 5 | position: relative; | ||
| 6 | |||
| 7 | width: calc(100% - 380px); | ||
| 8 | height: 100vh; | ||
| 9 | left: 350px; | ||
| 10 | |||
| 11 | padding-right: 30px; | ||
| 12 | |||
| 13 | font-size: 40px; | ||
| 14 | font-family: BarlowSemiCondensed-Regular; | ||
| 15 | color: #cdcfdf; | ||
| 16 | |||
| 17 | } | ||
diff --git a/frontend/src/components/pages/about.js b/frontend/src/components/pages/about.js deleted file mode 100644 index 11b065d..0000000 --- a/frontend/src/components/pages/about.js +++ /dev/null | |||
| @@ -1,32 +0,0 @@ | |||
| 1 | import React, { useState, useEffect } from 'react'; | ||
| 2 | import ReactMarkdown from 'react-markdown'; | ||
| 3 | |||
| 4 | import "./about.css"; | ||
| 5 | |||
| 6 | export default function About() { | ||
| 7 | const [aboutText, setAboutText] = useState(''); | ||
| 8 | |||
| 9 | useEffect(() => { | ||
| 10 | const fetchReadme = async () => { | ||
| 11 | try { | ||
| 12 | const response = await fetch( | ||
| 13 | 'https://raw.githubusercontent.com/pektezol/leastportalshub/main/README.md' | ||
| 14 | ); | ||
| 15 | if (!response.ok) { | ||
| 16 | throw new Error('Failed to fetch README'); | ||
| 17 | } | ||
| 18 | const readmeText = await response.text(); | ||
| 19 | setAboutText(readmeText); | ||
| 20 | } catch (error) { | ||
| 21 | console.error('Error fetching README:', error); | ||
| 22 | } | ||
| 23 | }; | ||
| 24 | fetchReadme(); | ||
| 25 | }, []); | ||
| 26 | |||
| 27 | return ( | ||
| 28 | <div id="about"> | ||
| 29 | <ReactMarkdown>{aboutText}</ReactMarkdown> | ||
| 30 | </div> | ||
| 31 | ); | ||
| 32 | }; | ||
diff --git a/frontend/src/components/pages/game.js b/frontend/src/components/pages/game.js deleted file mode 100644 index 301e035..0000000 --- a/frontend/src/components/pages/game.js +++ /dev/null | |||
| @@ -1,46 +0,0 @@ | |||
| 1 | import React, { useEffect, useRef, useState } from 'react'; | ||
| 2 | import { useLocation, Link } from "react-router-dom"; | ||
| 3 | |||
| 4 | import "./games.css" | ||
| 5 | |||
| 6 | export default function GameEntry({ gameInfo }) { | ||
| 7 | const [gameEntry, setGameEntry] = React.useState(null); | ||
| 8 | const location = useLocation(); | ||
| 9 | |||
| 10 | const gameInfoCats = gameInfo.category_portals; | ||
| 11 | |||
| 12 | useEffect(() => { | ||
| 13 | gameInfoCats.forEach(catInfo => { | ||
| 14 | const itemBody = document.createElement("div"); | ||
| 15 | const itemTitle = document.createElement("span"); | ||
| 16 | const spacing = document.createElement("br"); | ||
| 17 | const itemNum = document.createElement("span"); | ||
| 18 | |||
| 19 | itemTitle.innerText = catInfo.category.name; | ||
| 20 | itemNum.innerText = catInfo.portal_count; | ||
| 21 | itemTitle.classList.add("games-page-item-body-item-title"); | ||
| 22 | itemNum.classList.add("games-page-item-body-item-num"); | ||
| 23 | itemBody.appendChild(itemTitle); | ||
| 24 | itemBody.appendChild(spacing); | ||
| 25 | itemBody.appendChild(itemNum); | ||
| 26 | itemBody.className = "games-page-item-body-item"; | ||
| 27 | |||
| 28 | // itemBody.innerHTML = ` | ||
| 29 | // <span className='games-page-item-body-item-title'>${catInfo.category.name}</span><br /> | ||
| 30 | // <span className='games-page-item-body-item-num'>${catInfo.portal_count}</span>` | ||
| 31 | |||
| 32 | document.getElementById(`${gameInfo.id}`).appendChild(itemBody); | ||
| 33 | }); | ||
| 34 | }) | ||
| 35 | |||
| 36 | return ( | ||
| 37 | <Link to={"/games/" + gameInfo.id}><div className='games-page-item'> | ||
| 38 | <div className='games-page-item-header'> | ||
| 39 | <div style={{backgroundImage: `url(${gameInfo.image})`}} className='games-page-item-header-img'></div> | ||
| 40 | <span><b>{gameInfo.name}</b></span> | ||
| 41 | </div> | ||
| 42 | <div id={gameInfo.id} className='games-page-item-body'> | ||
| 43 | </div> | ||
| 44 | </div></Link> | ||
| 45 | ) | ||
| 46 | } | ||
diff --git a/frontend/src/components/pages/games.js b/frontend/src/components/pages/games.js deleted file mode 100644 index 75b5e44..0000000 --- a/frontend/src/components/pages/games.js +++ /dev/null | |||
| @@ -1,62 +0,0 @@ | |||
| 1 | import React, { useEffect, useState } from 'react'; | ||
| 2 | import { useLocation, Link } from "react-router-dom"; | ||
| 3 | |||
| 4 | import "./games.css" | ||
| 5 | import GameEntry from './game'; | ||
| 6 | |||
| 7 | export default function Games(prop) { | ||
| 8 | const { token } = prop; | ||
| 9 | const [games, setGames] = useState([]); | ||
| 10 | const location = useLocation(); | ||
| 11 | |||
| 12 | useEffect(() => { | ||
| 13 | document.querySelectorAll(".games-page-item-body").forEach((game, index) => { | ||
| 14 | game.innerHTML = ""; | ||
| 15 | }) | ||
| 16 | |||
| 17 | const fetchGames = async () => { | ||
| 18 | try { | ||
| 19 | const response = await fetch("https://lp.ardapektezol.com/api/v1/games", { | ||
| 20 | headers: { | ||
| 21 | 'Authorization': token | ||
| 22 | } | ||
| 23 | }); | ||
| 24 | |||
| 25 | const data = await response.json(); | ||
| 26 | setGames(data.data); | ||
| 27 | pageLoad(); | ||
| 28 | } catch (err) { | ||
| 29 | console.error("Error fetching games:", err); | ||
| 30 | } | ||
| 31 | }; | ||
| 32 | |||
| 33 | fetchGames(); | ||
| 34 | |||
| 35 | function pageLoad() { | ||
| 36 | const loaders = document.querySelectorAll(".loader"); | ||
| 37 | loaders.forEach((loader) => { | ||
| 38 | loader.style.display = "none"; | ||
| 39 | }); | ||
| 40 | } | ||
| 41 | }, [token]); | ||
| 42 | |||
| 43 | return ( | ||
| 44 | <div className='games-page'> | ||
| 45 | <section className='games-page-header'> | ||
| 46 | <span><b>Games list</b></span> | ||
| 47 | </section> | ||
| 48 | |||
| 49 | <section> | ||
| 50 | <div className='games-page-content'> | ||
| 51 | <div className='games-page-item-content'> | ||
| 52 | <div className='loader loader-game'></div> | ||
| 53 | <div className='loader loader-game'></div> | ||
| 54 | {games.map((game, index) => ( | ||
| 55 | <GameEntry gameInfo={game} key={index} /> | ||
| 56 | ))} | ||
| 57 | </div> | ||
| 58 | </div> | ||
| 59 | </section> | ||
| 60 | </div> | ||
| 61 | ); | ||
| 62 | } | ||
diff --git a/frontend/src/components/pages/home.css b/frontend/src/components/pages/home.css deleted file mode 100644 index e5a8eab..0000000 --- a/frontend/src/components/pages/home.css +++ /dev/null | |||
| @@ -1,92 +0,0 @@ | |||
| 1 | * { | ||
| 2 | scrollbar-width: thin; | ||
| 3 | } | ||
| 4 | |||
| 5 | .homepage-panel { | ||
| 6 | background-color: #202232; | ||
| 7 | margin: 10px 10px; | ||
| 8 | padding: 10px; | ||
| 9 | border-radius: 24px; | ||
| 10 | overflow: hidden; | ||
| 11 | flex: 1 1 100%; | ||
| 12 | align-items: stretch; | ||
| 13 | width: 100%; | ||
| 14 | } | ||
| 15 | |||
| 16 | .homepage-panel-title-div { | ||
| 17 | background-color: #2B2E46; | ||
| 18 | width: fit-content; | ||
| 19 | padding: 5px 18px; | ||
| 20 | border-radius: 200px; | ||
| 21 | font-family: BarlowSemiCondensed-SemiBold; | ||
| 22 | font-size: 32px; | ||
| 23 | margin-bottom: 10px; | ||
| 24 | } | ||
| 25 | |||
| 26 | .stats-div { | ||
| 27 | background-color: #2B2E46; | ||
| 28 | border-radius: 24px; | ||
| 29 | text-align: center; | ||
| 30 | display: grid; | ||
| 31 | padding: 10px 0px; | ||
| 32 | width: 100%; | ||
| 33 | } | ||
| 34 | |||
| 35 | .stats-div span { | ||
| 36 | font-family: BarlowSemiCondensed-Regular; | ||
| 37 | font-size: 18px; | ||
| 38 | } | ||
| 39 | |||
| 40 | .stats-div span>b { | ||
| 41 | font-family: BarlowSemiCondensed-SemiBold; | ||
| 42 | font-size: 42px; | ||
| 43 | } | ||
| 44 | |||
| 45 | .record-title div { | ||
| 46 | --padding: 20px; | ||
| 47 | width: calc(100% - calc(var(--padding * 2))); | ||
| 48 | height: 32px; | ||
| 49 | border-radius: 200px; | ||
| 50 | font-size: 18px; | ||
| 51 | display: grid; | ||
| 52 | grid-template-columns: calc(20% - 3.6px) calc(25% - 4.5px) calc(15% - 2.7px) calc(15% - 2.7px) calc(25% - 4.5px); | ||
| 53 | text-align: center; | ||
| 54 | padding: 0px var(--padding); | ||
| 55 | vertical-align: middle; | ||
| 56 | align-items: center; | ||
| 57 | font-family: BarlowSemiCondensed-SemiBold; | ||
| 58 | } | ||
| 59 | |||
| 60 | .record-title::after { | ||
| 61 | content: ""; | ||
| 62 | display: flex; | ||
| 63 | width: 100%; | ||
| 64 | height: 3px; | ||
| 65 | background-color: #2B2E46; | ||
| 66 | margin-bottom: 5px; | ||
| 67 | } | ||
| 68 | |||
| 69 | .recommended-map-img { | ||
| 70 | width: 250px; | ||
| 71 | border-radius: 19px; | ||
| 72 | margin-bottom: 0; | ||
| 73 | /* border: 7px solid #2B2E46; */ | ||
| 74 | background-size: cover; | ||
| 75 | background-position-x: 50%; | ||
| 76 | } | ||
| 77 | |||
| 78 | .difficulty-bar-home { | ||
| 79 | width: 100%; | ||
| 80 | display: grid; | ||
| 81 | grid-template-columns: 20% 20% 20% 20% 20%; | ||
| 82 | align-items: center; | ||
| 83 | margin: 0px; | ||
| 84 | margin-top: 7px; | ||
| 85 | } | ||
| 86 | |||
| 87 | .difficulty-point { | ||
| 88 | background: #2B2E46; | ||
| 89 | height: 4px; | ||
| 90 | margin: 5px; | ||
| 91 | border-radius: 10px; | ||
| 92 | } | ||
diff --git a/frontend/src/components/pages/home.js b/frontend/src/components/pages/home.js deleted file mode 100644 index 0a46bec..0000000 --- a/frontend/src/components/pages/home.js +++ /dev/null | |||
| @@ -1,242 +0,0 @@ | |||
| 1 | import React, { useEffect, useState } from 'react'; | ||
| 2 | |||
| 3 | import "./home.css" | ||
| 4 | import News from '../news'; | ||
| 5 | import Record from '../record'; | ||
| 6 | |||
| 7 | export default function Homepage({ token }) { | ||
| 8 | const [profile, setProfile] = useState(null); | ||
| 9 | |||
| 10 | // useEffect(() => { | ||
| 11 | |||
| 12 | // if (!token) { | ||
| 13 | // return; | ||
| 14 | // } | ||
| 15 | |||
| 16 | // async function home() { | ||
| 17 | |||
| 18 | // const profileResponse = await fetch(`https://lp.ardapektezol.com/api/v1/profile`, { | ||
| 19 | // headers: { | ||
| 20 | // Authorization: token | ||
| 21 | // } | ||
| 22 | // }) | ||
| 23 | |||
| 24 | // const profileData = await profileResponse.json(); | ||
| 25 | |||
| 26 | // setProfile(profileData); | ||
| 27 | |||
| 28 | // const gamesResponse = await fetch("https://lp.ardapektezol.com/api/v1/games", { | ||
| 29 | // headers: { | ||
| 30 | // Authorization: token | ||
| 31 | // } | ||
| 32 | // }); | ||
| 33 | |||
| 34 | // const gamesData = await gamesResponse.json(); | ||
| 35 | |||
| 36 | // const recommendedMapImg = document.querySelector("#recommendedMapImg"); | ||
| 37 | |||
| 38 | // recommendedMapImg.style.backgroundImage = `url(${gamesData.data[0].image})` | ||
| 39 | |||
| 40 | // const column1 = document.querySelector("#column1"); | ||
| 41 | // const column2 = document.querySelector("#column2"); | ||
| 42 | |||
| 43 | // column2.style.height = column1.clientHeight + "px"; | ||
| 44 | |||
| 45 | // const panels = document.querySelectorAll(".homepage-panel"); | ||
| 46 | // panels.forEach(e => { | ||
| 47 | // // this is cuz react is silly | ||
| 48 | // if (e.innerHTML.includes('<div class="homepage-panel-title-div">')) { | ||
| 49 | // return | ||
| 50 | // } | ||
| 51 | // const title = e.getAttribute("title"); | ||
| 52 | |||
| 53 | // const titleDiv = document.createElement("div"); | ||
| 54 | // const titleSpan = document.createElement("span"); | ||
| 55 | |||
| 56 | // titleDiv.classList.add("homepage-panel-title-div") | ||
| 57 | |||
| 58 | // titleSpan.innerText = title | ||
| 59 | |||
| 60 | // titleDiv.appendChild(titleSpan) | ||
| 61 | // e.insertBefore(titleDiv, e.firstChild) | ||
| 62 | // }); | ||
| 63 | // } | ||
| 64 | // try { | ||
| 65 | // home(); | ||
| 66 | // } catch (e) { | ||
| 67 | // console.log("error while setting up home page:", e); | ||
| 68 | // } | ||
| 69 | |||
| 70 | // }, [token]); | ||
| 71 | |||
| 72 | const newsList = [ | ||
| 73 | { | ||
| 74 | "title": "Portal Saved on Container Ride", | ||
| 75 | "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vehicula facilisis quam, non ultrices nisl aliquam at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas." | ||
| 76 | }, | ||
| 77 | { | ||
| 78 | "title": "Portal Saved on Container Ride", | ||
| 79 | "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vehicula facilisis quam, non ultrices nisl aliquam at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas." | ||
| 80 | }, | ||
| 81 | { | ||
| 82 | "title": "Portal Saved on Container Ride", | ||
| 83 | "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vehicula facilisis quam, non ultrices nisl aliquam at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas." | ||
| 84 | }, | ||
| 85 | { | ||
| 86 | "title": "Portal Saved on Container Ride", | ||
| 87 | "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vehicula facilisis quam, non ultrices nisl aliquam at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas." | ||
| 88 | }, | ||
| 89 | { | ||
| 90 | "title": "Portal Saved on Container Ride", | ||
| 91 | "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vehicula facilisis quam, non ultrices nisl aliquam at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas." | ||
| 92 | }, | ||
| 93 | { | ||
| 94 | "title": "Portal Saved on Container Ride", | ||
| 95 | "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vehicula facilisis quam, non ultrices nisl aliquam at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas." | ||
| 96 | }, | ||
| 97 | { | ||
| 98 | "title": "Portal Saved on Container Ride", | ||
| 99 | "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vehicula facilisis quam, non ultrices nisl aliquam at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas." | ||
| 100 | }, | ||
| 101 | { | ||
| 102 | "title": "Portal Saved on Container Ride", | ||
| 103 | "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vehicula facilisis quam, non ultrices nisl aliquam at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas." | ||
| 104 | }, | ||
| 105 | { | ||
| 106 | "title": "Portal Saved on Container Ride", | ||
| 107 | "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vehicula facilisis quam, non ultrices nisl aliquam at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas." | ||
| 108 | }, | ||
| 109 | { | ||
| 110 | "title": "Portal Saved on Container Ride", | ||
| 111 | "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vehicula facilisis quam, non ultrices nisl aliquam at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas." | ||
| 112 | }, | ||
| 113 | { | ||
| 114 | "title": "Portal Saved on Container Ride", | ||
| 115 | "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vehicula facilisis quam, non ultrices nisl aliquam at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas." | ||
| 116 | }, | ||
| 117 | { | ||
| 118 | "title": "Portal Saved on Container Ride", | ||
| 119 | "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vehicula facilisis quam, non ultrices nisl aliquam at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas." | ||
| 120 | }, | ||
| 121 | { | ||
| 122 | "title": "Portal Saved on Container Ride", | ||
| 123 | "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vehicula facilisis quam, non ultrices nisl aliquam at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas." | ||
| 124 | }, | ||
| 125 | { | ||
| 126 | "title": "Portal Saved on Container Ride", | ||
| 127 | "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vehicula facilisis quam, non ultrices nisl aliquam at. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas." | ||
| 128 | }, | ||
| 129 | ] | ||
| 130 | |||
| 131 | return ( | ||
| 132 | <main> | ||
| 133 | <section style={{ userSelect: "none", display: "flex" }}> | ||
| 134 | <h1 style={{ marginTop: "53.6px", fontSize: "80px", marginBottom: "15px" }}>Home</h1> | ||
| 135 | {profile ? | ||
| 136 | <div style={{ textAlign: "right", width: "100%", marginTop: "20px" }}> | ||
| 137 | <span style={{ fontSize: "25px" }}>Welcome back,</span><br /> | ||
| 138 | |||
| 139 | <span><b style={{ fontSize: "80px", transform: "translateY(-20px)", display: "block" }}>Wolfboy248</b></span> | ||
| 140 | </div> | ||
| 141 | : null} | ||
| 142 | </section> | ||
| 143 | |||
| 144 | <div style={{ display: "grid", gridTemplateColumns: "calc(50%) calc(50%)" }}> | ||
| 145 | <div id='column1' style={{ display: "flex", alignItems: "self-start", flexWrap: "wrap", alignContent: "start" }}> | ||
| 146 | {/* Column 1 */} | ||
| 147 | {profile ? | ||
| 148 | <section title="Your Profile" className='homepage-panel'> | ||
| 149 | <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: "12px" }}> | ||
| 150 | <div className='stats-div'> | ||
| 151 | <span>Overall rank</span><br /> | ||
| 152 | <span><b>{profile.rankings.overall.rank > 0 ? "#" + profile.rankings.overall.rank : "No rank"}</b></span> | ||
| 153 | </div> | ||
| 154 | <div className='stats-div'> | ||
| 155 | <span>Singleplayer</span><br /> | ||
| 156 | <span style={{ fontSize: "22px" }}><b>{profile.rankings.singleplayer.rank > 0 ? "#" + profile.rankings.singleplayer.rank : "No rank"}</b> {profile.rankings.singleplayer.rank > 0 ? "(" + profile.rankings.singleplayer.completion_count + "/" + profile.rankings.singleplayer.completion_total + ")" : ""}</span> | ||
| 157 | </div> | ||
| 158 | <div className='stats-div'> | ||
| 159 | <span>Cooperative rank</span><br /> | ||
| 160 | <span style={{ fontSize: "22px" }}><b>{profile.rankings.cooperative.rank > 0 ? "#" + profile.rankings.cooperative.rank : "No rank"}</b> {profile.rankings.cooperative.rank > 0 ? "(" + profile.rankings.cooperative.completion_count + "/" + profile.rankings.cooperative.completion_total + ")" : ""}</span> | ||
| 161 | </div> | ||
| 162 | </div> | ||
| 163 | </section> | ||
| 164 | : null} | ||
| 165 | {profile ? | ||
| 166 | <section title="What's Next?" className='homepage-panel'> | ||
| 167 | <div style={{ display: "flex" }}> | ||
| 168 | <div className='recommended-map-img' id="recommendedMapImg"></div> | ||
| 169 | <div style={{ marginLeft: "12px", display: "block", width: "100%" }}> | ||
| 170 | <span style={{ fontFamily: "BarlowSemiCondensed-SemiBold", fontSize: "32px", width: "100%", display: "block" }}>Container Ride</span> | ||
| 171 | <span style={{ fontSize: "20px", display: "block" }}>Your Record: 4 portals</span> | ||
| 172 | <span style={{ fontFamily: "BarlowSemiCondensed-SemiBold", fontSize: "36px", width: "100%", display: "block" }}>World Record: 2 portals</span> | ||
| 173 | <div className='difficulty-bar-home'> | ||
| 174 | <div className='difficulty-point' style={{ backgroundColor: "#51C355" }}></div> | ||
| 175 | <div className='difficulty-point'></div> | ||
| 176 | <div className='difficulty-point'></div> | ||
| 177 | <div className='difficulty-point'></div> | ||
| 178 | <div className='difficulty-point'></div> | ||
| 179 | </div> | ||
| 180 | </div> | ||
| 181 | </div> | ||
| 182 | </section> | ||
| 183 | : null} | ||
| 184 | <section title="Newest Records" className='homepage-panel' style={{ height: profile ? "250px" : "960px" }}> | ||
| 185 | <div className='record-title'> | ||
| 186 | <div> | ||
| 187 | <span>Place</span> | ||
| 188 | <span style={{ textAlign: "left" }}>Runner</span> | ||
| 189 | <span>Portals</span> | ||
| 190 | <span>Time</span> | ||
| 191 | <span>Date</span> | ||
| 192 | </div> | ||
| 193 | </div> | ||
| 194 | <div style={{ overflowY: "scroll", height: "calc(100% - 90px)", paddingRight: "10px" }}> | ||
| 195 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 196 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 197 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 198 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 199 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 200 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 201 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 202 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 203 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 204 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 205 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 206 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 207 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 208 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 209 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 210 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 211 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 212 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 213 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 214 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 215 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 216 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 217 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 218 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 219 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 220 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 221 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 222 | <Record name={"Krzyhau"} portals={"2"} date={new Date("2024-05-21T08:45:00")} place={"2"} time={"20.20"}></Record> | ||
| 223 | </div> | ||
| 224 | </section> | ||
| 225 | </div> | ||
| 226 | {/* Column 2 */} | ||
| 227 | <div id='column2' style={{ display: "flex", alignItems: "stretch", height: "1000px" }}> | ||
| 228 | <section title="News" className='homepage-panel'> | ||
| 229 | <div id='newsContent' style={{ display: "block", width: "100%", overflowY: "scroll", height: "calc(100% - 50px)" }}> | ||
| 230 | {newsList.map((newsList, index) => ( | ||
| 231 | <News newsInfo={newsList} key={index}></News> | ||
| 232 | ))} | ||
| 233 | </div> | ||
| 234 | </section> | ||
| 235 | </div> | ||
| 236 | </div> | ||
| 237 | |||
| 238 | |||
| 239 | |||
| 240 | </main> | ||
| 241 | ) | ||
| 242 | } \ No newline at end of file | ||
diff --git a/frontend/src/components/pages/maplist.css b/frontend/src/components/pages/maplist.css deleted file mode 100644 index b56aacc..0000000 --- a/frontend/src/components/pages/maplist.css +++ /dev/null | |||
| @@ -1,403 +0,0 @@ | |||
| 1 | .maplist-page { | ||
| 2 | position: relative; | ||
| 3 | left: 350px; | ||
| 4 | height: 100vh; | ||
| 5 | color: #cdcfdf; | ||
| 6 | width: calc(100% - 380px); | ||
| 7 | font-family: BarlowSemiCondensed-Regular; | ||
| 8 | overflow-y: scroll; | ||
| 9 | overflow-x: hidden; | ||
| 10 | padding-right: 30px; | ||
| 11 | } | ||
| 12 | |||
| 13 | a { | ||
| 14 | color: inherit; | ||
| 15 | width: fit-content; | ||
| 16 | } | ||
| 17 | |||
| 18 | .maplist-page-content { | ||
| 19 | position: absolute; | ||
| 20 | left: 0px; | ||
| 21 | width: calc(100% - 50px); | ||
| 22 | } | ||
| 23 | |||
| 24 | .maplist-page-header { | ||
| 25 | margin-top: 33px; | ||
| 26 | display: grid; | ||
| 27 | margin-bottom: 10px; | ||
| 28 | } | ||
| 29 | |||
| 30 | .nav-btn { | ||
| 31 | height: 40px; | ||
| 32 | background-color: #2b2e46; | ||
| 33 | color: inherit; | ||
| 34 | font-size: 18px; | ||
| 35 | font-family: inherit; | ||
| 36 | border: none; | ||
| 37 | border-radius: 20px; | ||
| 38 | transition: background-color .1s; | ||
| 39 | cursor: default; | ||
| 40 | width: fit-content; | ||
| 41 | } | ||
| 42 | |||
| 43 | .nav-btn>span { | ||
| 44 | padding: 0 8px 0 8px; | ||
| 45 | } | ||
| 46 | |||
| 47 | .nav-btn:hover { | ||
| 48 | background-color: #202232; | ||
| 49 | cursor: pointer; | ||
| 50 | } | ||
| 51 | |||
| 52 | .game { | ||
| 53 | width: 100%; | ||
| 54 | height: 192px; | ||
| 55 | /* background: #202232; */ | ||
| 56 | border-radius: 24px; | ||
| 57 | overflow: hidden; | ||
| 58 | } | ||
| 59 | |||
| 60 | .game-header { | ||
| 61 | width: 100%; | ||
| 62 | height: 144px; | ||
| 63 | display: flex; | ||
| 64 | justify-content: center; | ||
| 65 | align-items: center; | ||
| 66 | overflow: hidden; | ||
| 67 | } | ||
| 68 | |||
| 69 | .game-header-text { | ||
| 70 | display: flex; | ||
| 71 | justify-content: center; | ||
| 72 | align-items: center; | ||
| 73 | position: absolute; | ||
| 74 | } | ||
| 75 | |||
| 76 | .game-img { | ||
| 77 | width: 100%; | ||
| 78 | height: 100%; | ||
| 79 | background-size: cover; | ||
| 80 | filter: blur(4px); | ||
| 81 | } | ||
| 82 | |||
| 83 | .game-header-text>span { | ||
| 84 | font-size: 42px; | ||
| 85 | font-weight: 500; | ||
| 86 | margin: 5px; | ||
| 87 | } | ||
| 88 | |||
| 89 | .game-header-text span>b { | ||
| 90 | font-size: 96px; | ||
| 91 | font-weight: 600; | ||
| 92 | } | ||
| 93 | |||
| 94 | .game-nav { | ||
| 95 | display: flex; | ||
| 96 | height: 48px; | ||
| 97 | } | ||
| 98 | |||
| 99 | .game-nav-btn { | ||
| 100 | width: 100%; | ||
| 101 | height: 100%; | ||
| 102 | border: none; | ||
| 103 | border-radius: 0px; | ||
| 104 | color: inherit; | ||
| 105 | font-family: inherit; | ||
| 106 | font-size: 22px; | ||
| 107 | background: #2B2E46; | ||
| 108 | transition: background-color .1s; | ||
| 109 | margin: 0 1px; | ||
| 110 | display: flex; | ||
| 111 | justify-content: center; | ||
| 112 | align-items: center; | ||
| 113 | } | ||
| 114 | |||
| 115 | .game-nav-btn:hover { | ||
| 116 | cursor: pointer; | ||
| 117 | } | ||
| 118 | |||
| 119 | .selected { | ||
| 120 | background-color: #202232; | ||
| 121 | } | ||
| 122 | |||
| 123 | .gameview-nav { | ||
| 124 | margin-top: 20px; | ||
| 125 | display: flex; | ||
| 126 | height: 56px; | ||
| 127 | border-radius: 24px; | ||
| 128 | overflow: hidden; | ||
| 129 | gap: 0.06em; | ||
| 130 | /* background-color: #202232; */ | ||
| 131 | } | ||
| 132 | |||
| 133 | .maplist { | ||
| 134 | width: 100%; | ||
| 135 | margin-top: 20px; | ||
| 136 | margin-bottom: 40px; | ||
| 137 | } | ||
| 138 | |||
| 139 | .chapter-name { | ||
| 140 | font-size: 30px; | ||
| 141 | } | ||
| 142 | |||
| 143 | .chapter-page-div { | ||
| 144 | display: flex; | ||
| 145 | justify-content: right; | ||
| 146 | transform: translateY(-30px); | ||
| 147 | } | ||
| 148 | |||
| 149 | .chapter-page-div button { | ||
| 150 | background-color: #00000000; | ||
| 151 | border: 0; | ||
| 152 | cursor: pointer; | ||
| 153 | height: 30px; | ||
| 154 | padding: 0; | ||
| 155 | width: 30px; | ||
| 156 | } | ||
| 157 | |||
| 158 | .chapter-page-div span { | ||
| 159 | color: #cdcfdf; | ||
| 160 | font-family: BarlowSemiCondensed-Regular; | ||
| 161 | font-size: 20px; | ||
| 162 | } | ||
| 163 | |||
| 164 | .maplist-maps { | ||
| 165 | display: grid; | ||
| 166 | grid-template-columns: 25% 25% 25% 25%; | ||
| 167 | margin-top: 10px; | ||
| 168 | transform: translateY(-30px); | ||
| 169 | } | ||
| 170 | |||
| 171 | .maplist-item { | ||
| 172 | background: #202232; | ||
| 173 | border-radius: 24px; | ||
| 174 | overflow: hidden; | ||
| 175 | margin: 10px 10px; | ||
| 176 | /* padding: 10px 15px; */ | ||
| 177 | cursor: pointer; | ||
| 178 | user-select: none; | ||
| 179 | } | ||
| 180 | |||
| 181 | .loader-map { | ||
| 182 | border-radius: 24px; | ||
| 183 | overflow: hidden; | ||
| 184 | margin: 10px 10px; | ||
| 185 | /* padding: 10px 15px; */ | ||
| 186 | user-select: none; | ||
| 187 | width: calc(100% - 20px); | ||
| 188 | height: calc(223px); | ||
| 189 | } | ||
| 190 | |||
| 191 | .maplist-img-div { | ||
| 192 | height: 150px; | ||
| 193 | overflow: hidden; | ||
| 194 | } | ||
| 195 | |||
| 196 | .maplist-img { | ||
| 197 | width: 100%; | ||
| 198 | height: 100%; | ||
| 199 | background-size: cover; | ||
| 200 | filter: blur(4px); | ||
| 201 | opacity: 0.7; | ||
| 202 | } | ||
| 203 | |||
| 204 | .maplist-portalcount-div { | ||
| 205 | display: flex; | ||
| 206 | justify-content: center; | ||
| 207 | align-items: center; | ||
| 208 | text-align: center; | ||
| 209 | height: 100%; | ||
| 210 | transform: translateY(-100%); | ||
| 211 | overflow: hidden; | ||
| 212 | } | ||
| 213 | |||
| 214 | .maplist-title { | ||
| 215 | font-size: 22px; | ||
| 216 | text-align: center; | ||
| 217 | width: 100%; | ||
| 218 | display: inherit; | ||
| 219 | padding: 5px 0px; | ||
| 220 | color: #CDCFDF; | ||
| 221 | } | ||
| 222 | |||
| 223 | .maplist-portals { | ||
| 224 | margin-left: 5px; | ||
| 225 | font-size: 32px; | ||
| 226 | } | ||
| 227 | |||
| 228 | .difficulty-div { | ||
| 229 | display: flex; | ||
| 230 | padding: 7px 10px; | ||
| 231 | } | ||
| 232 | |||
| 233 | .difficulty-label { | ||
| 234 | font-size: 18px; | ||
| 235 | } | ||
| 236 | |||
| 237 | .difficulty-bar { | ||
| 238 | width: 100%; | ||
| 239 | display: grid; | ||
| 240 | grid-template-columns: 20% 20% 20% 20% 20%; | ||
| 241 | align-items: center; | ||
| 242 | margin: 5px; | ||
| 243 | } | ||
| 244 | |||
| 245 | .difficulty-point { | ||
| 246 | background: #2B2E46; | ||
| 247 | height: 3px; | ||
| 248 | margin: 5px; | ||
| 249 | border-radius: 10px; | ||
| 250 | } | ||
| 251 | |||
| 252 | .stats { | ||
| 253 | margin-top: 30px; | ||
| 254 | } | ||
| 255 | |||
| 256 | .portalcount-over-time-div { | ||
| 257 | width: 100%; | ||
| 258 | height: 450px; | ||
| 259 | position: relative; | ||
| 260 | background-color: #202232; | ||
| 261 | border-radius: 20px; | ||
| 262 | } | ||
| 263 | |||
| 264 | .graph-title { | ||
| 265 | width: 100%; | ||
| 266 | display: inherit; | ||
| 267 | font-size: 24px; | ||
| 268 | margin-top: 5px; | ||
| 269 | text-align: center; | ||
| 270 | font-family: BarlowSemiCondensed-SemiBold; | ||
| 271 | padding-top: 7px; | ||
| 272 | } | ||
| 273 | |||
| 274 | .portalcount-graph { | ||
| 275 | height: calc(100% - 30px); | ||
| 276 | width: calc(100% - 80px); | ||
| 277 | } | ||
| 278 | |||
| 279 | .chart { | ||
| 280 | height: calc(100% - 80px); | ||
| 281 | width: 100%; | ||
| 282 | position: relative; | ||
| 283 | padding: 0px 0px; | ||
| 284 | scrollbar-width: thin; | ||
| 285 | } | ||
| 286 | |||
| 287 | .line-chart { | ||
| 288 | list-style: none; | ||
| 289 | margin: 0; | ||
| 290 | padding: 0; | ||
| 291 | height: 100%; | ||
| 292 | border-bottom: 2px solid #2B2E46; | ||
| 293 | } | ||
| 294 | |||
| 295 | .data-point { | ||
| 296 | background-color: #202232; | ||
| 297 | border: 4px solid #006FDE; | ||
| 298 | border-radius: 50%; | ||
| 299 | height: 6px; | ||
| 300 | position: absolute; | ||
| 301 | width: 6px; | ||
| 302 | bottom: calc(var(--y) - 4.5px); | ||
| 303 | left: calc(var(--x) - 6.5px); | ||
| 304 | transition: all 0.2s cubic-bezier(0.075, 0.82, 0.165, 1); | ||
| 305 | z-index: 1; | ||
| 306 | animation: point_intro 0.2s cubic-bezier(0.075, 0.82, 0.165, 1.8); | ||
| 307 | animation-fill-mode: backwards; | ||
| 308 | } | ||
| 309 | |||
| 310 | .data-point:hover, .data-point-active { | ||
| 311 | background-color: #006FDE; | ||
| 312 | box-shadow: 0px 0px 10px #006FDE; | ||
| 313 | } | ||
| 314 | |||
| 315 | .line-segment { | ||
| 316 | background-color: #006FDE; | ||
| 317 | bottom: var(--y); | ||
| 318 | height: 4px; | ||
| 319 | left: var(--x); | ||
| 320 | position: absolute; | ||
| 321 | transform: rotate(calc(var(--angle) * -1deg)); | ||
| 322 | width: calc(var(--hypotenuse) * 1px); | ||
| 323 | transform-origin: left bottom; | ||
| 324 | border-radius: 20px; | ||
| 325 | z-index: 1; | ||
| 326 | animation: line_intro 0.05s cubic-bezier(0, 1, 0.31, 0.96); | ||
| 327 | animation-fill-mode: backwards; | ||
| 328 | } | ||
| 329 | |||
| 330 | #dataPointInfo { | ||
| 331 | position: absolute; | ||
| 332 | width: 400px; | ||
| 333 | height: 85px; | ||
| 334 | background: #202232; | ||
| 335 | box-shadow: 0px 4px 16px 0px #00000080; | ||
| 336 | transition: all 0.3s cubic-bezier(0.075, 0.82, 0.165, 1); | ||
| 337 | z-index: 1000; | ||
| 338 | opacity: 0; | ||
| 339 | left: auto; | ||
| 340 | border-radius: 20px; | ||
| 341 | padding: 15px 7px; | ||
| 342 | } | ||
| 343 | |||
| 344 | .section-header { | ||
| 345 | display: flex; | ||
| 346 | text-align: center; | ||
| 347 | font-family: BarlowSemiCondensed-SemiBold; | ||
| 348 | font-size: 18px; | ||
| 349 | height: 40%; | ||
| 350 | justify-content: space-evenly; | ||
| 351 | align-items: center; | ||
| 352 | } | ||
| 353 | |||
| 354 | .section-header span, .section-data span { | ||
| 355 | flex: 1; | ||
| 356 | } | ||
| 357 | |||
| 358 | .divider { | ||
| 359 | width: 100%; | ||
| 360 | height: 2px; | ||
| 361 | background-color: #2B2E46; | ||
| 362 | display: flex; | ||
| 363 | margin: 5px 0px 8px 0px; | ||
| 364 | } | ||
| 365 | |||
| 366 | .section-data { | ||
| 367 | display: flex; | ||
| 368 | grid-template-columns: 25% 25% 25% 25%; | ||
| 369 | text-align: center; | ||
| 370 | background-color: #2B2E46; | ||
| 371 | height: 52%; | ||
| 372 | border-radius: 200px; | ||
| 373 | align-items: center; | ||
| 374 | justify-content: space-evenly; | ||
| 375 | flex-grow: 1; | ||
| 376 | font-family: BarlowSemiCondensed-Regular; | ||
| 377 | font-size: 18px; | ||
| 378 | padding: 0px 5px; | ||
| 379 | } | ||
| 380 | |||
| 381 | @keyframes line_intro { | ||
| 382 | 0% { | ||
| 383 | width: 0; | ||
| 384 | } | ||
| 385 | 100% { | ||
| 386 | width: calc(var(--hypotenuse) * 1px); | ||
| 387 | } | ||
| 388 | } | ||
| 389 | |||
| 390 | @keyframes point_intro { | ||
| 391 | 0% { | ||
| 392 | opacity: 0; | ||
| 393 | width: 0; | ||
| 394 | height: 0; | ||
| 395 | transform: translate(3px, -3px); | ||
| 396 | } | ||
| 397 | 100% { | ||
| 398 | width: 6px; | ||
| 399 | height: 6px; | ||
| 400 | transform: translate(0px, 0px); | ||
| 401 | opacity: 1; | ||
| 402 | } | ||
| 403 | } | ||
diff --git a/frontend/src/components/pages/maplist.js b/frontend/src/components/pages/maplist.js deleted file mode 100644 index a5c6c19..0000000 --- a/frontend/src/components/pages/maplist.js +++ /dev/null | |||
| @@ -1,890 +0,0 @@ | |||
| 1 | import React, { useEffect, useRef, useState } from 'react'; | ||
| 2 | import { useLocation, Link } from "react-router-dom"; | ||
| 3 | import { BrowserRouter as Router, Route, Routes, useNavigate } from 'react-router-dom'; | ||
| 4 | |||
| 5 | import "./maplist.css" | ||
| 6 | import img5 from "../../imgs/5.png" | ||
| 7 | import img6 from "../../imgs/6.png" | ||
| 8 | |||
| 9 | export default function Maplist(prop) { | ||
| 10 | const { token, setToken } = prop | ||
| 11 | const scrollRef = useRef(null) | ||
| 12 | const [games, setGames] = React.useState(null); | ||
| 13 | const [hasOpenedStatistics, setHasOpenedStatistics] = React.useState(false); | ||
| 14 | const [totalPortals, setTotalPortals] = React.useState(0); | ||
| 15 | const [loading, setLoading] = React.useState(true) | ||
| 16 | const location = useLocation(); | ||
| 17 | |||
| 18 | const [gameTitle, setGameTitle] = React.useState(""); | ||
| 19 | const [catPortalCount, setCatPortalCount] = React.useState(0); | ||
| 20 | let minPage; | ||
| 21 | let maxPage; | ||
| 22 | let currentPage; | ||
| 23 | let add = 0; | ||
| 24 | let gameState; | ||
| 25 | let catState = 0; | ||
| 26 | async function detectGame() { | ||
| 27 | const response = await fetch("https://lp.ardapektezol.com/api/v1/games", { | ||
| 28 | headers: { | ||
| 29 | 'Authorization': token | ||
| 30 | } | ||
| 31 | }); | ||
| 32 | |||
| 33 | const data = await response.json(); | ||
| 34 | |||
| 35 | const url = new URL(window.location.href) | ||
| 36 | |||
| 37 | const params = new URLSearchParams(url.search) | ||
| 38 | gameState = parseFloat(location.pathname.split("/")[2]) | ||
| 39 | |||
| 40 | if (gameState == 1) { | ||
| 41 | setGameTitle(data.data[0].name); | ||
| 42 | |||
| 43 | maxPage = 9; | ||
| 44 | minPage = 1; | ||
| 45 | createCategories(1); | ||
| 46 | } else if (gameState == 2) { | ||
| 47 | setGameTitle(data.data[1].name); | ||
| 48 | |||
| 49 | maxPage = 16; | ||
| 50 | minPage = 10; | ||
| 51 | add = 10 | ||
| 52 | createCategories(2); | ||
| 53 | } | ||
| 54 | |||
| 55 | let chapterParam = params.get("chapter") | ||
| 56 | |||
| 57 | currentPage = minPage; | ||
| 58 | |||
| 59 | if (chapterParam) { | ||
| 60 | currentPage = +chapterParam + add | ||
| 61 | } | ||
| 62 | |||
| 63 | changePage(currentPage); | ||
| 64 | |||
| 65 | // if (!loading) { | ||
| 66 | |||
| 67 | // document.querySelector("#catPortalCount").innerText = data.data[gameState - 1].category_portals[0].portal_count; | ||
| 68 | |||
| 69 | // } | ||
| 70 | |||
| 71 | setCatPortalCount(data.data[gameState - 1].category_portals[0].portal_count); | ||
| 72 | |||
| 73 | // if (chapterParam) { | ||
| 74 | // document.querySelector("#pageNumbers").innerText = `${chapterParam - minPage + 1}/${maxPage - minPage + 1}` | ||
| 75 | // } | ||
| 76 | } | ||
| 77 | |||
| 78 | function changeMaplistOrStatistics(index, name) { | ||
| 79 | const maplistBtns = document.querySelectorAll("#maplistBtn"); | ||
| 80 | maplistBtns.forEach((btn, i) => { | ||
| 81 | if (i == index) { | ||
| 82 | btn.className = "game-nav-btn selected" | ||
| 83 | |||
| 84 | if (name == "maplist") { | ||
| 85 | document.querySelector(".stats").style.display = "none"; | ||
| 86 | document.querySelector(".maplist").style.display = "block"; | ||
| 87 | document.querySelector(".maplist").setAttribute("currentTab", "maplist"); | ||
| 88 | } else { | ||
| 89 | document.querySelector(".stats").style.display = "block"; | ||
| 90 | document.querySelector(".maplist").style.display = "none"; | ||
| 91 | |||
| 92 | document.querySelector(".maplist-page").scrollTo({ top: 372, behavior: "smooth" }) | ||
| 93 | document.querySelector(".maplist").setAttribute("currentTab", "stats"); | ||
| 94 | setHasOpenedStatistics(true); | ||
| 95 | } | ||
| 96 | } else { | ||
| 97 | btn.className = "game-nav-btn"; | ||
| 98 | } | ||
| 99 | }); | ||
| 100 | } | ||
| 101 | |||
| 102 | async function createCategories(gameID) { | ||
| 103 | const response = await fetch("https://lp.ardapektezol.com/api/v1/games", { | ||
| 104 | headers: { | ||
| 105 | 'Authorization': token | ||
| 106 | } | ||
| 107 | }); | ||
| 108 | |||
| 109 | const data = await response.json(); | ||
| 110 | let categoriesArr = data.data[gameID - 1].category_portals; | ||
| 111 | |||
| 112 | if (document.querySelector(".maplist-maps") == null) { | ||
| 113 | return; | ||
| 114 | } | ||
| 115 | const gameNav = document.querySelector(".game-nav"); | ||
| 116 | gameNav.innerHTML = ""; | ||
| 117 | categoriesArr.forEach((category) => { | ||
| 118 | createCategory(category); | ||
| 119 | }); | ||
| 120 | |||
| 121 | setLoading(false); | ||
| 122 | } | ||
| 123 | |||
| 124 | let categoryNum = 0; | ||
| 125 | function createCategory(category) { | ||
| 126 | const gameNav = document.querySelector(".game-nav"); | ||
| 127 | |||
| 128 | categoryNum++; | ||
| 129 | const gameNavBtn = document.createElement("button"); | ||
| 130 | if (categoryNum == 1) { | ||
| 131 | gameNavBtn.className = "game-nav-btn selected"; | ||
| 132 | } else { | ||
| 133 | gameNavBtn.className = "game-nav-btn"; | ||
| 134 | } | ||
| 135 | gameNavBtn.id = "catBtn" | ||
| 136 | gameNavBtn.innerText = category.category.name; | ||
| 137 | |||
| 138 | gameNavBtn.addEventListener("click", (e) => { | ||
| 139 | changeCategory(category, e); | ||
| 140 | changePage(currentPage); | ||
| 141 | }) | ||
| 142 | |||
| 143 | gameNav.appendChild(gameNavBtn); | ||
| 144 | } | ||
| 145 | |||
| 146 | async function changeCategory(category, btn) { | ||
| 147 | const navBtns = document.querySelectorAll("#catBtn"); | ||
| 148 | navBtns.forEach((btns) => { | ||
| 149 | btns.classList.remove("selected"); | ||
| 150 | }); | ||
| 151 | |||
| 152 | btn.srcElement.classList.add("selected"); | ||
| 153 | const response = await fetch("https://lp.ardapektezol.com/api/v1/games", { | ||
| 154 | headers: { | ||
| 155 | 'Authorization': token | ||
| 156 | } | ||
| 157 | }); | ||
| 158 | |||
| 159 | const data = await response.json(); | ||
| 160 | catState = category.category.id - 1; | ||
| 161 | // console.log(catState) | ||
| 162 | document.querySelector("#catPortalCount").innerText = category.portal_count; | ||
| 163 | } | ||
| 164 | |||
| 165 | async function changePage(page) { | ||
| 166 | const pageNumbers = document.querySelector("#pageNumbers"); | ||
| 167 | |||
| 168 | pageNumbers.innerText = `${currentPage - minPage + 1}/${maxPage - minPage + 1}`; | ||
| 169 | |||
| 170 | const maplistMaps = document.querySelector(".maplist-maps"); | ||
| 171 | maplistMaps.innerHTML = ""; | ||
| 172 | for (let index = 0; index < 8; index++) { | ||
| 173 | const loadingAnimation = document.createElement("div"); | ||
| 174 | loadingAnimation.classList.add("loader"); | ||
| 175 | loadingAnimation.classList.add("loader-map") | ||
| 176 | maplistMaps.appendChild(loadingAnimation); | ||
| 177 | } | ||
| 178 | const data = await fetchMaps(page); | ||
| 179 | const maps = data.data.maps; | ||
| 180 | const name = data.data.chapter.name; | ||
| 181 | |||
| 182 | let chapterName = "Chapter"; | ||
| 183 | const chapterNumberOld = name.split(" - ")[0]; | ||
| 184 | let chapterNumber1 = chapterNumberOld.split("Chapter ")[1]; | ||
| 185 | if (chapterNumber1 == undefined) { | ||
| 186 | chapterName = "Course" | ||
| 187 | chapterNumber1 = chapterNumberOld.split("Course ")[1]; | ||
| 188 | } | ||
| 189 | const chapterNumber = chapterNumber1.toString().padStart(2, "0"); | ||
| 190 | const chapterTitle = name.split(" - ")[1]; | ||
| 191 | |||
| 192 | if (document.querySelector(".maplist-maps") == null) { | ||
| 193 | return; | ||
| 194 | } | ||
| 195 | const chapterNumberElement = document.querySelector(".chapter-num") | ||
| 196 | const chapterTitleElement = document.querySelector(".chapter-name") | ||
| 197 | chapterNumberElement.innerText = chapterName + " " + chapterNumber; | ||
| 198 | chapterTitleElement.innerText = chapterTitle; | ||
| 199 | |||
| 200 | maplistMaps.innerHTML = ""; | ||
| 201 | maps.forEach(map => { | ||
| 202 | let portalCount; | ||
| 203 | if (map.category_portals[catState] != undefined) { | ||
| 204 | portalCount = map.category_portals[catState].portal_count; | ||
| 205 | } else { | ||
| 206 | portalCount = map.category_portals[0].portal_count; | ||
| 207 | } | ||
| 208 | addMap(map.name, portalCount, map.image, map.difficulty + 1, map.id); | ||
| 209 | }); | ||
| 210 | |||
| 211 | const url = new URL(window.location.href) | ||
| 212 | |||
| 213 | const params = new URLSearchParams(url.search) | ||
| 214 | |||
| 215 | let chapterParam = params.get("chapter") | ||
| 216 | |||
| 217 | try { | ||
| 218 | const response = await fetch("https://lp.ardapektezol.com/api/v1/games", { | ||
| 219 | headers: { | ||
| 220 | 'Authorization': token | ||
| 221 | } | ||
| 222 | }); | ||
| 223 | |||
| 224 | const data = await response.json(); | ||
| 225 | |||
| 226 | const gameImg = document.querySelector(".game-img"); | ||
| 227 | |||
| 228 | gameImg.style.backgroundImage = `url(${data.data[0].image})`; | ||
| 229 | |||
| 230 | // const mapImg = document.querySelectorAll(".maplist-img"); | ||
| 231 | // mapImg.forEach((map) => { | ||
| 232 | // map.style.backgroundImage = `url(${data.data[0].image})`; | ||
| 233 | // }); | ||
| 234 | |||
| 235 | } catch (error) { | ||
| 236 | console.log("error fetching games:", error); | ||
| 237 | } | ||
| 238 | |||
| 239 | asignDifficulties(); | ||
| 240 | } | ||
| 241 | |||
| 242 | async function addMap(mapName, mapPortalCount, mapImage, difficulty, mapID) { | ||
| 243 | // jesus christ | ||
| 244 | const maplistItem = document.createElement("div"); | ||
| 245 | const maplistTitle = document.createElement("span"); | ||
| 246 | const maplistImgDiv = document.createElement("div"); | ||
| 247 | const maplistImg = document.createElement("div"); | ||
| 248 | const maplistPortalcountDiv = document.createElement("div"); | ||
| 249 | const maplistPortalcount = document.createElement("span"); | ||
| 250 | const b = document.createElement("b"); | ||
| 251 | const maplistPortalcountPortals = document.createElement("span"); | ||
| 252 | const difficultyDiv = document.createElement("div"); | ||
| 253 | const difficultyLabel = document.createElement("span"); | ||
| 254 | const difficultyBar = document.createElement("div"); | ||
| 255 | const difficultyPoint1 = document.createElement("div"); | ||
| 256 | const difficultyPoint2 = document.createElement("div"); | ||
| 257 | const difficultyPoint3 = document.createElement("div"); | ||
| 258 | const difficultyPoint4 = document.createElement("div"); | ||
| 259 | const difficultyPoint5 = document.createElement("div"); | ||
| 260 | |||
| 261 | maplistItem.className = "maplist-item"; | ||
| 262 | maplistTitle.className = "maplist-title"; | ||
| 263 | maplistImgDiv.className = "maplist-img-div"; | ||
| 264 | maplistImg.className = "maplist-img"; | ||
| 265 | maplistPortalcountDiv.className = "maplist-portalcount-div"; | ||
| 266 | maplistPortalcount.className = "maplist-portalcount"; | ||
| 267 | maplistPortalcountPortals.className = "maplist-portals"; | ||
| 268 | difficultyDiv.className = "difficulty-div"; | ||
| 269 | difficultyLabel.className = "difficulty-label"; | ||
| 270 | difficultyBar.className = "difficulty-bar"; | ||
| 271 | difficultyPoint1.className = "difficulty-point"; | ||
| 272 | difficultyPoint2.className = "difficulty-point"; | ||
| 273 | difficultyPoint3.className = "difficulty-point"; | ||
| 274 | difficultyPoint4.className = "difficulty-point"; | ||
| 275 | difficultyPoint5.className = "difficulty-point"; | ||
| 276 | |||
| 277 | |||
| 278 | maplistTitle.innerText = mapName; | ||
| 279 | difficultyLabel.innerText = "Difficulty: " | ||
| 280 | maplistPortalcountPortals.innerText = "portals" | ||
| 281 | b.innerText = mapPortalCount; | ||
| 282 | maplistImg.style.backgroundImage = `url(${mapImage})`; | ||
| 283 | difficultyBar.setAttribute("difficulty", difficulty) | ||
| 284 | maplistItem.setAttribute("id", mapID) | ||
| 285 | maplistItem.addEventListener("click", () => { | ||
| 286 | console.log(mapID) | ||
| 287 | window.location.href = "/maps/" + mapID | ||
| 288 | }) | ||
| 289 | |||
| 290 | // appends | ||
| 291 | // maplist item | ||
| 292 | maplistItem.appendChild(maplistTitle); | ||
| 293 | maplistImgDiv.appendChild(maplistImg); | ||
| 294 | maplistImgDiv.appendChild(maplistPortalcountDiv); | ||
| 295 | maplistPortalcountDiv.appendChild(maplistPortalcount); | ||
| 296 | maplistPortalcount.appendChild(b); | ||
| 297 | maplistPortalcountDiv.appendChild(maplistPortalcountPortals); | ||
| 298 | maplistItem.appendChild(maplistImgDiv); | ||
| 299 | maplistItem.appendChild(difficultyDiv); | ||
| 300 | difficultyDiv.appendChild(difficultyLabel); | ||
| 301 | difficultyDiv.appendChild(difficultyBar); | ||
| 302 | difficultyBar.appendChild(difficultyPoint1); | ||
| 303 | difficultyBar.appendChild(difficultyPoint2); | ||
| 304 | difficultyBar.appendChild(difficultyPoint3); | ||
| 305 | difficultyBar.appendChild(difficultyPoint4); | ||
| 306 | difficultyBar.appendChild(difficultyPoint5); | ||
| 307 | |||
| 308 | // display in place | ||
| 309 | const maplistMaps = document.querySelector(".maplist-maps"); | ||
| 310 | maplistMaps.appendChild(maplistItem); | ||
| 311 | } | ||
| 312 | |||
| 313 | async function fetchMaps(chapterID) { | ||
| 314 | try { | ||
| 315 | const response = await fetch(`https://lp.ardapektezol.com/api/v1/chapters/${chapterID}`, { | ||
| 316 | headers: { | ||
| 317 | 'Authorization': token | ||
| 318 | } | ||
| 319 | }); | ||
| 320 | |||
| 321 | const data = await response.json(); | ||
| 322 | return data; | ||
| 323 | } catch (err) { | ||
| 324 | console.log(err) | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 328 | // difficulty stuff | ||
| 329 | function asignDifficulties() { | ||
| 330 | const difficulties = document.querySelectorAll(".difficulty-bar"); | ||
| 331 | difficulties.forEach((difficultyElement) => { | ||
| 332 | let difficulty = difficultyElement.getAttribute("difficulty"); | ||
| 333 | if (difficulty == "1") { | ||
| 334 | difficultyElement.childNodes[0].style.backgroundColor = "#51C355"; | ||
| 335 | } else if (difficulty == "2") { | ||
| 336 | difficultyElement.childNodes[0].style.backgroundColor = "#8AC93A"; | ||
| 337 | difficultyElement.childNodes[1].style.backgroundColor = "#8AC93A"; | ||
| 338 | } else if (difficulty == "3") { | ||
| 339 | difficultyElement.childNodes[0].style.backgroundColor = "#8AC93A"; | ||
| 340 | difficultyElement.childNodes[1].style.backgroundColor = "#8AC93A"; | ||
| 341 | difficultyElement.childNodes[2].style.backgroundColor = "#8AC93A"; | ||
| 342 | } else if (difficulty == "4") { | ||
| 343 | difficultyElement.childNodes[0].style.backgroundColor = "#C35F51"; | ||
| 344 | difficultyElement.childNodes[1].style.backgroundColor = "#C35F51"; | ||
| 345 | difficultyElement.childNodes[2].style.backgroundColor = "#C35F51"; | ||
| 346 | difficultyElement.childNodes[3].style.backgroundColor = "#C35F51"; | ||
| 347 | } else if (difficulty == "5") { | ||
| 348 | difficultyElement.childNodes[0].style.backgroundColor = "#C35F51"; | ||
| 349 | difficultyElement.childNodes[1].style.backgroundColor = "#C35F51"; | ||
| 350 | difficultyElement.childNodes[2].style.backgroundColor = "#C35F51"; | ||
| 351 | difficultyElement.childNodes[3].style.backgroundColor = "#C35F51"; | ||
| 352 | difficultyElement.childNodes[4].style.backgroundColor = "#C35F51"; | ||
| 353 | } | ||
| 354 | }); | ||
| 355 | } | ||
| 356 | |||
| 357 | const divRef = useRef(null); | ||
| 358 | |||
| 359 | React.useEffect(() => { | ||
| 360 | |||
| 361 | const lineChart = document.querySelector(".line-chart") | ||
| 362 | let tempTotalPortals = 0 | ||
| 363 | fetch("https://lp.ardapektezol.com/api/v1/games/1/maps", { | ||
| 364 | headers: { | ||
| 365 | 'Authorization': token | ||
| 366 | } | ||
| 367 | }) | ||
| 368 | .then(r => r.json()) | ||
| 369 | .then(d => { | ||
| 370 | d.data.maps.forEach((map, i) => { | ||
| 371 | tempTotalPortals += map.portal_count | ||
| 372 | }) | ||
| 373 | }) | ||
| 374 | .then(() => { | ||
| 375 | setTotalPortals(tempTotalPortals) | ||
| 376 | }) | ||
| 377 | async function createGraph() { | ||
| 378 | console.log(totalPortals) | ||
| 379 | // max | ||
| 380 | let items = [ | ||
| 381 | { | ||
| 382 | record: "100", | ||
| 383 | date: new Date(2011, 4, 4), | ||
| 384 | map: "Container Ride", | ||
| 385 | first: "tiny zach" | ||
| 386 | }, | ||
| 387 | { | ||
| 388 | record: "98", | ||
| 389 | date: new Date(2012, 6, 4), | ||
| 390 | map: "Container Ride", | ||
| 391 | first: "tiny zach" | ||
| 392 | }, | ||
| 393 | { | ||
| 394 | record: "94", | ||
| 395 | date: new Date(2013, 0, 1), | ||
| 396 | map: "Container Ride", | ||
| 397 | first: "tiny zach" | ||
| 398 | }, | ||
| 399 | { | ||
| 400 | record: "90", | ||
| 401 | date: new Date(2014, 0, 1), | ||
| 402 | map: "Container Ride", | ||
| 403 | first: "tiny zach" | ||
| 404 | }, | ||
| 405 | { | ||
| 406 | record: "88", | ||
| 407 | date: new Date(2015, 6, 14), | ||
| 408 | map: "Container Ride", | ||
| 409 | first: "tiny zach" | ||
| 410 | }, | ||
| 411 | { | ||
| 412 | record: "84", | ||
| 413 | date: new Date(2016, 8, 19), | ||
| 414 | map: "Container Ride", | ||
| 415 | first: "tiny zach" | ||
| 416 | }, | ||
| 417 | { | ||
| 418 | record: "82", | ||
| 419 | date: new Date(2017, 3, 20), | ||
| 420 | map: "Container Ride", | ||
| 421 | first: "tiny zach" | ||
| 422 | }, | ||
| 423 | { | ||
| 424 | record: "81", | ||
| 425 | date: new Date(2018, 2, 25), | ||
| 426 | map: "Container Ride", | ||
| 427 | first: "tiny zach" | ||
| 428 | }, | ||
| 429 | { | ||
| 430 | record: "80", | ||
| 431 | date: new Date(2019, 3, 4), | ||
| 432 | map: "Container Ride", | ||
| 433 | first: "tiny zach" | ||
| 434 | }, | ||
| 435 | { | ||
| 436 | record: "78", | ||
| 437 | date: new Date(2020, 11, 21), | ||
| 438 | map: "Container Ride", | ||
| 439 | first: "tiny zach" | ||
| 440 | }, | ||
| 441 | { | ||
| 442 | record: "77", | ||
| 443 | date: new Date(2021, 10, 25), | ||
| 444 | map: "Container Ride", | ||
| 445 | first: "tiny zach" | ||
| 446 | }, | ||
| 447 | { | ||
| 448 | record: "76", | ||
| 449 | date: new Date(2022, 4, 17), | ||
| 450 | map: "Container Ride", | ||
| 451 | first: "tiny zach" | ||
| 452 | }, | ||
| 453 | { | ||
| 454 | record: "75", | ||
| 455 | date: new Date(2023, 9, 31), | ||
| 456 | map: "Container Ride", | ||
| 457 | first: "tiny zach" | ||
| 458 | }, | ||
| 459 | { | ||
| 460 | record: "74", | ||
| 461 | date: new Date(2024, 4, 4), | ||
| 462 | map: "Container Ride", | ||
| 463 | first: "tiny zach" | ||
| 464 | }, | ||
| 465 | ] | ||
| 466 | |||
| 467 | function calculatePosition(date, startDate, endDate, maxWidth) { | ||
| 468 | const totalMilliseconds = endDate - startDate + 10000000000; | ||
| 469 | const millisecondsFromStart = date - startDate + 5000000000; | ||
| 470 | return (millisecondsFromStart / totalMilliseconds) * maxWidth | ||
| 471 | } | ||
| 472 | |||
| 473 | const minDate = items.reduce((min, dp) => dp.date < min ? dp.date : min, items[0].date) | ||
| 474 | const maxDate = items.reduce((max, dp) => dp.date > max ? dp.date : max, items[0].date) | ||
| 475 | |||
| 476 | const graph_width = document.querySelector(".portalcount-over-time-div").clientWidth | ||
| 477 | // console.log(graph_width) | ||
| 478 | |||
| 479 | const uniqueYears = new Set() | ||
| 480 | items.forEach(dp => uniqueYears.add(dp.date.getFullYear())) | ||
| 481 | let minYear = Infinity; | ||
| 482 | let maxYear = -Infinity; | ||
| 483 | |||
| 484 | items.forEach(dp => { | ||
| 485 | const year = dp.date.getFullYear(); | ||
| 486 | minYear = Math.min(minYear, year); | ||
| 487 | maxYear = Math.max(maxYear, year); | ||
| 488 | }); | ||
| 489 | |||
| 490 | // Add missing years to the set | ||
| 491 | for (let year = minYear; year <= maxYear; year++) { | ||
| 492 | uniqueYears.add(year); | ||
| 493 | } | ||
| 494 | const uniqueYearsArr = Array.from(uniqueYears) | ||
| 495 | |||
| 496 | items = items.map(dp => ({ | ||
| 497 | record: dp.record, | ||
| 498 | date: dp.date, | ||
| 499 | x: calculatePosition(dp.date, minDate, maxDate, lineChart.clientWidth), | ||
| 500 | map: dp.map, | ||
| 501 | first: dp.first | ||
| 502 | })) | ||
| 503 | |||
| 504 | const yearInterval = lineChart.clientWidth / uniqueYears.size | ||
| 505 | for (let index = 1; index < (uniqueYears.size); index++) { | ||
| 506 | const placeholderlmao = document.createElement("div") | ||
| 507 | const yearSpan = document.createElement("span") | ||
| 508 | yearSpan.style.position = "absolute" | ||
| 509 | placeholderlmao.style.height = "100%" | ||
| 510 | placeholderlmao.style.width = "2px" | ||
| 511 | placeholderlmao.style.backgroundColor = "#00000080" | ||
| 512 | placeholderlmao.style.position = `absolute` | ||
| 513 | const thing = calculatePosition(new Date(uniqueYearsArr[index], 0, 0), minDate, maxDate, lineChart.clientWidth) | ||
| 514 | placeholderlmao.style.left = `${thing}px` | ||
| 515 | yearSpan.style.left = `${thing}px` | ||
| 516 | yearSpan.style.bottom = "-34px" | ||
| 517 | yearSpan.innerText = uniqueYearsArr[index] | ||
| 518 | yearSpan.style.fontFamily = "BarlowSemiCondensed-Regular" | ||
| 519 | yearSpan.style.fontSize = "22px" | ||
| 520 | yearSpan.style.opacity = "0.8" | ||
| 521 | lineChart.appendChild(yearSpan) | ||
| 522 | |||
| 523 | } | ||
| 524 | |||
| 525 | let maxPortals; | ||
| 526 | let minPortals; | ||
| 527 | let precision; | ||
| 528 | let multiplier = 1; | ||
| 529 | for (let index = 0; index < items.length; index++) { | ||
| 530 | precision = Math.floor((items[0].record - items[items.length - 1].record)) | ||
| 531 | if (precision > 20) { | ||
| 532 | precision = 20 | ||
| 533 | } | ||
| 534 | minPortals = Math.floor((items[items.length - 1].record) / 10) * 10 | ||
| 535 | if (index == 0) { | ||
| 536 | maxPortals = items[index].record - minPortals | ||
| 537 | } | ||
| 538 | } | ||
| 539 | function calculateMultiplier(value) { | ||
| 540 | while (value > precision) { | ||
| 541 | multiplier += 1; | ||
| 542 | value -= precision; | ||
| 543 | } | ||
| 544 | } | ||
| 545 | calculateMultiplier(items[0].record); | ||
| 546 | // if (items[0].record > 10) { | ||
| 547 | // multiplier = 2; | ||
| 548 | // } | ||
| 549 | |||
| 550 | // Original cubic bezier control points | ||
| 551 | const P0 = { x: 0, y: 0 }; | ||
| 552 | const P1 = { x: 0.26, y: 1 }; | ||
| 553 | const P2 = { x: 0.74, y: 1 }; | ||
| 554 | const P3 = { x: 1, y: 0 }; | ||
| 555 | |||
| 556 | function calculateIntermediateControlPoints(t, P0, P1, P2, P3) { | ||
| 557 | const x = (1 - t) ** 3 * P0.x + | ||
| 558 | 3 * (1 - t) ** 2 * t * P1.x + | ||
| 559 | 3 * (1 - t) * t ** 2 * P2.x + | ||
| 560 | t ** 3 * P3.x; | ||
| 561 | |||
| 562 | const y = (1 - t) ** 3 * P0.y + | ||
| 563 | 3 * (1 - t) ** 2 * t * P1.y + | ||
| 564 | 3 * (1 - t) * t ** 2 * P2.y + | ||
| 565 | t ** 3 * P3.y; | ||
| 566 | |||
| 567 | return { x, y }; | ||
| 568 | } | ||
| 569 | |||
| 570 | |||
| 571 | let delay = 0; | ||
| 572 | for (let index = 0; index < items.length; index++) { | ||
| 573 | let chart_height = 340; | ||
| 574 | const item = items[index]; | ||
| 575 | delay += 0.05; | ||
| 576 | // console.log(lineChart.clientWidth) | ||
| 577 | |||
| 578 | // maxPortals++; | ||
| 579 | // maxPortals++; | ||
| 580 | |||
| 581 | let point_height = (chart_height / maxPortals) | ||
| 582 | |||
| 583 | for (let index = 0; index < (maxPortals / multiplier); index++) { | ||
| 584 | // console.log((index + 1) * multiplier) | ||
| 585 | let current_portal_count = (index + 1); | ||
| 586 | |||
| 587 | const placeholderDiv = document.createElement("div") | ||
| 588 | const numPortalsText = document.createElement("span") | ||
| 589 | const numPortalsTextBottom = document.createElement("span") | ||
| 590 | numPortalsText.innerText = (current_portal_count * multiplier) + minPortals | ||
| 591 | numPortalsTextBottom.innerText = minPortals | ||
| 592 | placeholderDiv.style.position = "absolute" | ||
| 593 | numPortalsText.style.position = "absolute" | ||
| 594 | numPortalsTextBottom.style.position = "absolute" | ||
| 595 | numPortalsText.style.left = "-37px" | ||
| 596 | numPortalsText.style.opacity = "0.2" | ||
| 597 | numPortalsTextBottom.style.opacity = "0.2" | ||
| 598 | numPortalsText.style.fontFamily = "BarlowSemiCondensed-Regular" | ||
| 599 | numPortalsTextBottom.style.fontFamily = "BarlowSemiCondensed-Regular" | ||
| 600 | numPortalsText.style.fontSize = "22px" | ||
| 601 | numPortalsTextBottom.style.left = "-37px" | ||
| 602 | numPortalsTextBottom.style.fontSize = "22px" | ||
| 603 | numPortalsTextBottom.style.fontWeight = "400" | ||
| 604 | numPortalsText.style.color = "#CDCFDF" | ||
| 605 | numPortalsTextBottom.style.color = "#CDCFDF" | ||
| 606 | numPortalsText.style.fontFamily = "inherit" | ||
| 607 | numPortalsTextBottom.style.fontFamily = "inherit" | ||
| 608 | numPortalsText.style.textAlign = "right" | ||
| 609 | numPortalsTextBottom.style.textAlign = "right" | ||
| 610 | numPortalsText.style.width = "30px" | ||
| 611 | numPortalsTextBottom.style.width = "30px" | ||
| 612 | placeholderDiv.style.bottom = `${(point_height * current_portal_count * multiplier) - 2}px` | ||
| 613 | numPortalsText.style.bottom = `${(point_height * current_portal_count * multiplier) - 2 - 9}px` | ||
| 614 | numPortalsTextBottom.style.bottom = `${0 - 2 - 8}px` | ||
| 615 | placeholderDiv.id = placeholderDiv.style.bottom | ||
| 616 | placeholderDiv.style.width = "100%" | ||
| 617 | placeholderDiv.style.height = "2px" | ||
| 618 | placeholderDiv.style.backgroundColor = "#2B2E46" | ||
| 619 | placeholderDiv.style.zIndex = "0" | ||
| 620 | |||
| 621 | if (index == 0) { | ||
| 622 | lineChart.appendChild(numPortalsTextBottom) | ||
| 623 | } | ||
| 624 | lineChart.appendChild(numPortalsText) | ||
| 625 | lineChart.appendChild(placeholderDiv) | ||
| 626 | } | ||
| 627 | |||
| 628 | const li = document.createElement("li"); | ||
| 629 | const lineSeg = document.createElement("div"); | ||
| 630 | const dataPoint = document.createElement("div"); | ||
| 631 | |||
| 632 | li.style = `--y: ${point_height * (item.record - minPortals) - 3}px; --x: ${item.x}px`; | ||
| 633 | lineSeg.className = "line-segment"; | ||
| 634 | dataPoint.className = "data-point"; | ||
| 635 | |||
| 636 | if (items[index + 1] !== undefined) { | ||
| 637 | const hypotenuse = Math.sqrt( | ||
| 638 | Math.pow(items[index + 1].x - items[index].x, 2) + | ||
| 639 | Math.pow((point_height * items[index + 1].record) - point_height * item.record, 2) | ||
| 640 | ); | ||
| 641 | const angle = Math.asin( | ||
| 642 | ((point_height * item.record) - (point_height * items[index + 1].record)) / hypotenuse | ||
| 643 | ); | ||
| 644 | |||
| 645 | lineSeg.style = `--hypotenuse: ${hypotenuse}; --angle: ${angle * (-180 / Math.PI)}`; | ||
| 646 | const t0 = index / items.length; | ||
| 647 | const t1 = (index + 1) / items.length | ||
| 648 | |||
| 649 | const P0t0 = calculateIntermediateControlPoints(t0, P0, P1, P2, P3); | ||
| 650 | const P1t1 = calculateIntermediateControlPoints(t1, P0, P1, P2, P3); | ||
| 651 | const bezierStyle = `cubic-bezier(${P0t0.x.toFixed(3)}, ${P0t0.y.toFixed(3)}, ${P1t1.x.toFixed(3)}, ${P1t1.y.toFixed(3)})` | ||
| 652 | lineSeg.style.animationTimingFunction = bezierStyle | ||
| 653 | lineSeg.style.animationDelay = delay + "s" | ||
| 654 | } | ||
| 655 | dataPoint.style.animationDelay = delay + "s" | ||
| 656 | |||
| 657 | let isHoveringOverData = true; | ||
| 658 | let isDataActive = false; | ||
| 659 | document.querySelector("#dataPointInfo").style.left = item.x + "px"; | ||
| 660 | document.querySelector("#dataPointInfo").style.bottom = (point_height * item.record - 3) + "px"; | ||
| 661 | dataPoint.addEventListener("mouseenter", (e) => { | ||
| 662 | isDataActive = true; | ||
| 663 | isHoveringOverData = true; | ||
| 664 | const dataPoints = document.querySelectorAll(".data-point") | ||
| 665 | dataPoints.forEach(point => { | ||
| 666 | point.classList.remove("data-point-active") | ||
| 667 | }); | ||
| 668 | dataPoint.classList.add("data-point-active") | ||
| 669 | document.querySelector("#dataPointRecord").innerText = item.record; | ||
| 670 | document.querySelector("#dataPointMap").innerText = item.map; | ||
| 671 | document.querySelector("#dataPointDate").innerText = item.date.toLocaleDateString("en-GB"); | ||
| 672 | document.querySelector("#dataPointFirst").innerText = item.first; | ||
| 673 | if ((lineChart.clientWidth - 400) < item.x) { | ||
| 674 | document.querySelector("#dataPointInfo").style.left = item.x - 400 + "px"; | ||
| 675 | } else { | ||
| 676 | document.querySelector("#dataPointInfo").style.left = item.x + "px"; | ||
| 677 | } | ||
| 678 | if ((lineChart.clientHeight - 115) < (point_height * (item.record - minPortals) - 3)) { | ||
| 679 | document.querySelector("#dataPointInfo").style.bottom = (point_height * (item.record - minPortals) - 3) - 115 + "px"; | ||
| 680 | } else { | ||
| 681 | document.querySelector("#dataPointInfo").style.bottom = (point_height * (item.record - minPortals) - 3) + "px"; | ||
| 682 | } | ||
| 683 | document.querySelector("#dataPointInfo").style.opacity = "1"; | ||
| 684 | document.querySelector("#dataPointInfo").style.zIndex = "10"; | ||
| 685 | }); | ||
| 686 | document.querySelector("#dataPointInfo").addEventListener("mouseenter", (e) => { | ||
| 687 | isHoveringOverData = true; | ||
| 688 | }) | ||
| 689 | document.querySelector("#dataPointInfo").addEventListener("mouseleave", (e) => { | ||
| 690 | isHoveringOverData = false; | ||
| 691 | }) | ||
| 692 | document.addEventListener("mousedown", () => { | ||
| 693 | if (!isHoveringOverData) { | ||
| 694 | isDataActive = false | ||
| 695 | dataPoint.classList.remove("data-point-active") | ||
| 696 | document.querySelector("#dataPointInfo").style.opacity = "0"; | ||
| 697 | document.querySelector("#dataPointInfo").style.zIndex = "0"; | ||
| 698 | } | ||
| 699 | }) | ||
| 700 | dataPoint.addEventListener("mouseenter", (e) => { | ||
| 701 | isHoveringOverData = false; | ||
| 702 | }) | ||
| 703 | document.querySelector(".chart").addEventListener("mouseleave", () => { | ||
| 704 | isDataActive = false | ||
| 705 | // fuck you | ||
| 706 | isHoveringOverData = true; | ||
| 707 | dataPoint.classList.remove("data-point-active") | ||
| 708 | document.querySelector("#dataPointInfo").style.opacity = "0"; | ||
| 709 | document.querySelector("#dataPointInfo").style.zIndex = "0"; | ||
| 710 | }) | ||
| 711 | |||
| 712 | li.appendChild(lineSeg); | ||
| 713 | li.appendChild(dataPoint); | ||
| 714 | lineChart.appendChild(li); | ||
| 715 | } | ||
| 716 | } | ||
| 717 | |||
| 718 | async function fetchGames() { | ||
| 719 | try { | ||
| 720 | const response = await fetch("https://lp.ardapektezol.com/api/v1/games", { | ||
| 721 | headers: { | ||
| 722 | 'Authorization': token | ||
| 723 | } | ||
| 724 | }); | ||
| 725 | |||
| 726 | const data = await response.json(); | ||
| 727 | |||
| 728 | const gameImg = document.querySelector(".game-img"); | ||
| 729 | |||
| 730 | gameImg.style.backgroundImage = `url(${data.data[0].image})`; | ||
| 731 | |||
| 732 | // const mapImg = document.querySelectorAll(".maplist-img"); | ||
| 733 | // mapImg.forEach((map) => { | ||
| 734 | // map.style.backgroundImage = `url(${data.data[0].image})`; | ||
| 735 | // }); | ||
| 736 | |||
| 737 | } catch (error) { | ||
| 738 | console.log("error fetching games:", error); | ||
| 739 | } | ||
| 740 | } | ||
| 741 | |||
| 742 | detectGame(); | ||
| 743 | |||
| 744 | const maplistImg = document.querySelector("#maplistImg"); | ||
| 745 | maplistImg.src = img5; | ||
| 746 | const statisticsImg = document.querySelector("#statisticsImg"); | ||
| 747 | statisticsImg.src = img6; | ||
| 748 | |||
| 749 | fetchGames(); | ||
| 750 | |||
| 751 | const handleResize = (entries) => { | ||
| 752 | for (let entry of entries) { | ||
| 753 | if (hasOpenedStatistics) { | ||
| 754 | lineChart.innerHTML = "" | ||
| 755 | createGraph() | ||
| 756 | } | ||
| 757 | if (document.querySelector(".maplist").getAttribute("currentTab") == "stats") { | ||
| 758 | document.querySelector(".stats").style.display = "block" | ||
| 759 | } else { | ||
| 760 | document.querySelector(".stats").style.display = "none" | ||
| 761 | } | ||
| 762 | } | ||
| 763 | }; | ||
| 764 | |||
| 765 | const resizeObserver = new ResizeObserver(handleResize); | ||
| 766 | |||
| 767 | // if (scrollRef.current) { | ||
| 768 | // //hi | ||
| 769 | // if (new URLSearchParams(new URL(window.location.href).search).get("chapter")) { | ||
| 770 | // setTimeout(() => { | ||
| 771 | // scrollRef.current.scrollIntoView({ behavior: "smooth", block: "start" }) | ||
| 772 | // }, 200); | ||
| 773 | // } | ||
| 774 | |||
| 775 | // } | ||
| 776 | |||
| 777 | if (divRef.current) { | ||
| 778 | resizeObserver.observe(divRef.current); | ||
| 779 | } | ||
| 780 | |||
| 781 | return () => { | ||
| 782 | if (divRef.current) { | ||
| 783 | resizeObserver.unobserve(divRef.current); | ||
| 784 | } | ||
| 785 | resizeObserver.disconnect(); | ||
| 786 | }; | ||
| 787 | |||
| 788 | |||
| 789 | }) | ||
| 790 | return ( | ||
| 791 | <div ref={divRef} className='maplist-page'> | ||
| 792 | <div className='maplist-page-content'> | ||
| 793 | <section className='maplist-page-header'> | ||
| 794 | <Link to='/games'><button className='nav-btn'> | ||
| 795 | <i className='triangle'></i> | ||
| 796 | <span>Games list</span> | ||
| 797 | </button></Link> | ||
| 798 | {!loading ? | ||
| 799 | <span><b id='gameTitle'>{gameTitle}</b></span> | ||
| 800 | : | ||
| 801 | <span><b id='gameTitle' className='loader-text'>LOADINGLOADING</b></span>} | ||
| 802 | </section> | ||
| 803 | |||
| 804 | <div className='game'> | ||
| 805 | {!loading ? | ||
| 806 | <div className='game-header'> | ||
| 807 | <div className='game-img'></div> | ||
| 808 | <div className='game-header-text'> | ||
| 809 | <span><b id='catPortalCount'>{catPortalCount}</b></span> | ||
| 810 | <span>portals</span> | ||
| 811 | </div> | ||
| 812 | </div> | ||
| 813 | : <div className='game-header loader'> | ||
| 814 | <div className='game-img'></div> | ||
| 815 | <div className='game-header-text'> | ||
| 816 | <span className='loader-text'><b id='catPortalCount'>00</b></span> | ||
| 817 | <span className='loader-text'>portals</span> | ||
| 818 | </div> | ||
| 819 | </div>} | ||
| 820 | {!loading ? | ||
| 821 | <div className='game-nav'> | ||
| 822 | </div> | ||
| 823 | : <div className='game-nav loader'> | ||
| 824 | </div>} | ||
| 825 | </div> | ||
| 826 | |||
| 827 | <div className='gameview-nav'> | ||
| 828 | <button id='maplistBtn' onClick={() => { changeMaplistOrStatistics(0, "maplist") }} className='game-nav-btn selected'> | ||
| 829 | <img id='maplistImg' /> | ||
| 830 | <span>Map List</span> | ||
| 831 | </button> | ||
| 832 | <button id='maplistBtn' onClick={() => changeMaplistOrStatistics(1, "stats")} className='game-nav-btn'> | ||
| 833 | <img id='statisticsImg' /> | ||
| 834 | <span>Statistics</span> | ||
| 835 | </button> | ||
| 836 | </div> | ||
| 837 | |||
| 838 | <div ref={scrollRef} className='maplist'> | ||
| 839 | <div className='chapter'> | ||
| 840 | <span className='chapter-num'>undefined</span><br /> | ||
| 841 | <span className='chapter-name'>undefined</span> | ||
| 842 | |||
| 843 | <div className='chapter-page-div'> | ||
| 844 | <button id='pageChanger' onClick={() => { currentPage--; currentPage < minPage ? currentPage = minPage : changePage(currentPage); }}> | ||
| 845 | <i className='triangle'></i> | ||
| 846 | </button> | ||
| 847 | <span id='pageNumbers'>0/0</span> | ||
| 848 | <button id='pageChanger' onClick={() => { currentPage++; currentPage > maxPage ? currentPage = maxPage : changePage(currentPage); }}> | ||
| 849 | <i style={{ transform: "rotate(180deg)" }} className='triangle'></i> | ||
| 850 | </button> | ||
| 851 | </div> | ||
| 852 | |||
| 853 | <div className='maplist-maps'> | ||
| 854 | </div> | ||
| 855 | </div> | ||
| 856 | </div> | ||
| 857 | |||
| 858 | <div style={{ display: "block" }} className='stats'> | ||
| 859 | <div className='portalcount-over-time-div'> | ||
| 860 | <span className='graph-title'>Portal count over time</span><br /> | ||
| 861 | |||
| 862 | <div className='portalcount-graph'> | ||
| 863 | <figure className='chart'> | ||
| 864 | <div style={{ display: "block" }}></div> | ||
| 865 | <div id="dataPointInfo"> | ||
| 866 | <div className='section-header'> | ||
| 867 | <span className='header-title'>Date</span> | ||
| 868 | <span className='header-title'>Map</span> | ||
| 869 | <span className='header-title'>Record</span> | ||
| 870 | <span className='header-title'>First completion</span> | ||
| 871 | </div> | ||
| 872 | <div className='divider'></div> | ||
| 873 | <div className='section-data'> | ||
| 874 | <span id='dataPointDate'></span> | ||
| 875 | <span id='dataPointMap'></span> | ||
| 876 | <span id='dataPointRecord'></span> | ||
| 877 | <span id='dataPointFirst'>Hello</span> | ||
| 878 | </div> | ||
| 879 | </div> | ||
| 880 | <ul className='line-chart'> | ||
| 881 | |||
| 882 | </ul> | ||
| 883 | </figure> | ||
| 884 | </div> | ||
| 885 | </div> | ||
| 886 | </div> | ||
| 887 | </div> | ||
| 888 | </div> | ||
| 889 | ) | ||
| 890 | } \ No newline at end of file | ||
diff --git a/frontend/src/components/pages/profile.js b/frontend/src/components/pages/profile.js deleted file mode 100644 index 7c45320..0000000 --- a/frontend/src/components/pages/profile.js +++ /dev/null | |||
| @@ -1,382 +0,0 @@ | |||
| 1 | import React from 'react'; | ||
| 2 | import { useLocation } from "react-router-dom"; | ||
| 3 | |||
| 4 | import img4 from "../../imgs/4.png" | ||
| 5 | import img5 from "../../imgs/5.png" | ||
| 6 | import img12 from "../../imgs/12.png" | ||
| 7 | import img13 from "../../imgs/13.png" | ||
| 8 | import img14 from "../../imgs/14.png" | ||
| 9 | import img15 from "../../imgs/15.png" | ||
| 10 | import img16 from "../../imgs/16.png" | ||
| 11 | import img17 from "../../imgs/17.png" | ||
| 12 | import img18 from "../../imgs/18.png" | ||
| 13 | import img19 from "../../imgs/19.png" | ||
| 14 | import "./profile.css"; | ||
| 15 | |||
| 16 | export default function Profile(props) { | ||
| 17 | const {token} = props | ||
| 18 | |||
| 19 | |||
| 20 | const location = useLocation() | ||
| 21 | |||
| 22 | |||
| 23 | const [profileData, setProfileData] = React.useState(null) | ||
| 24 | React.useEffect(()=>{ | ||
| 25 | setProfileData(null) | ||
| 26 | setChapterData(null) | ||
| 27 | setMaps(null) | ||
| 28 | setPageNumber(1) | ||
| 29 | |||
| 30 | if(location.pathname==="/profile"){ | ||
| 31 | fetch(`https://lp.ardapektezol.com/api/v1/${location.pathname}`,{ | ||
| 32 | headers: { | ||
| 33 | 'Authorization': token | ||
| 34 | }}) | ||
| 35 | .then(r=>r.json()) | ||
| 36 | .then(d=>{ | ||
| 37 | setProfileData(d.data) | ||
| 38 | setPageMax(Math.ceil(d.data.records.length/20)) | ||
| 39 | }) | ||
| 40 | }else{ | ||
| 41 | fetch(`https://lp.ardapektezol.com/api/v1/${location.pathname}`) | ||
| 42 | .then(r=>r.json()) | ||
| 43 | .then(d=>{ | ||
| 44 | setProfileData(d.data) | ||
| 45 | setPageMax(Math.ceil(d.data.records.length/20)) | ||
| 46 | }) | ||
| 47 | } | ||
| 48 | },[location.pathname]) | ||
| 49 | |||
| 50 | |||
| 51 | |||
| 52 | const [game,setGame] = React.useState(0) | ||
| 53 | const [gameData,setGameData] = React.useState(null) | ||
| 54 | const [chapter,setChapter] = React.useState("0") | ||
| 55 | const [chapterData,setChapterData] = React.useState(null) | ||
| 56 | const [maps,setMaps] = React.useState(null) | ||
| 57 | |||
| 58 | React.useEffect(()=>{ | ||
| 59 | fetch("https://lp.ardapektezol.com/api/v1/games") | ||
| 60 | .then(r=>r.json()) | ||
| 61 | .then(d=>{ | ||
| 62 | setGameData(d.data) | ||
| 63 | setGame(0) | ||
| 64 | }) | ||
| 65 | |||
| 66 | },[location]) | ||
| 67 | |||
| 68 | React.useEffect(()=>{ | ||
| 69 | if(game!==null && game!= 0){ | ||
| 70 | fetch(`https://lp.ardapektezol.com/api/v1/games/${game}`) | ||
| 71 | .then(r=>r.json()) | ||
| 72 | .then(d=>{ | ||
| 73 | setChapterData(d.data) | ||
| 74 | setChapter("0") | ||
| 75 | document.querySelector('#select-chapter').value=0 | ||
| 76 | }) | ||
| 77 | |||
| 78 | } else if (game!==null && game==0 && profileData!== null){ | ||
| 79 | setPageMax(Math.ceil(profileData.records.length/20)) | ||
| 80 | setPageNumber(1) | ||
| 81 | } | ||
| 82 | |||
| 83 | },[game,location]) | ||
| 84 | |||
| 85 | React.useEffect(()=>{ | ||
| 86 | if(chapter!==null){ | ||
| 87 | if(chapter==0){ | ||
| 88 | setMaps(null) | ||
| 89 | fetch(`https://lp.ardapektezol.com/api/v1/games/${game}/maps`) | ||
| 90 | .then(r=>r.json()) | ||
| 91 | .then(d=>{ | ||
| 92 | setMaps(d.data.maps); | ||
| 93 | setPageMax(Math.ceil(d.data.maps.length/20)) | ||
| 94 | setPageNumber(1) | ||
| 95 | }) | ||
| 96 | }else{ | ||
| 97 | setMaps(null) | ||
| 98 | fetch(`https://lp.ardapektezol.com/api/v1/chapters/${chapter}`) | ||
| 99 | .then(r=>r.json()) | ||
| 100 | .then(d=>{ | ||
| 101 | setMaps(d.data.maps); | ||
| 102 | setPageMax(Math.ceil(d.data.maps.length/20)) | ||
| 103 | setPageNumber(1) | ||
| 104 | }) | ||
| 105 | |||
| 106 | } | ||
| 107 | } | ||
| 108 | },[chapter,chapterData]) | ||
| 109 | |||
| 110 | |||
| 111 | |||
| 112 | const [pageNumber, setPageNumber] = React.useState(1); | ||
| 113 | const [pageMax, setPageMax] = React.useState(0); | ||
| 114 | const [navState, setNavState] = React.useState(0); // eslint-disable-next-line | ||
| 115 | React.useEffect(() => {NavClick();}, [[],navState]); | ||
| 116 | function NavClick() { | ||
| 117 | if(profileData!==null){ | ||
| 118 | const btn = document.querySelectorAll("#section2 button"); | ||
| 119 | btn.forEach((e) => {e.style.backgroundColor = "#2b2e46"}); | ||
| 120 | btn[navState].style.backgroundColor = "#202232"; | ||
| 121 | |||
| 122 | document.querySelectorAll("section").forEach((e,i)=>i>=2?e.style.display="none":"") | ||
| 123 | if(navState === 0){document.querySelectorAll(".profile1").forEach((e) => {e.style.display = "block"});} | ||
| 124 | if(navState === 1){document.querySelectorAll(".profile2").forEach((e) => {e.style.display = "block"});} | ||
| 125 | } | ||
| 126 | } | ||
| 127 | function UpdateProfile(){ | ||
| 128 | fetch(`https://lp.ardapektezol.com/api/v1/profile`,{ | ||
| 129 | method: 'POST', | ||
| 130 | headers: {Authorization: token} | ||
| 131 | }).then(r=>r.json()) | ||
| 132 | .then(d=>d.success?window.alert("profile updated"):window.alert(`Error: ${d.message}`)) | ||
| 133 | } | ||
| 134 | |||
| 135 | function TimeAgo(date) { | ||
| 136 | const seconds = Math.floor((new Date() - date) / 1000); | ||
| 137 | |||
| 138 | let interval = Math.floor(seconds / 31536000); | ||
| 139 | if (interval > 1) {return interval + ' years ago';} | ||
| 140 | |||
| 141 | interval = Math.floor(seconds / 2592000); | ||
| 142 | if (interval > 1) {return interval + ' months ago';} | ||
| 143 | |||
| 144 | interval = Math.floor(seconds / 86400); | ||
| 145 | if (interval > 1) {return interval + ' days ago';} | ||
| 146 | |||
| 147 | interval = Math.floor(seconds / 3600); | ||
| 148 | if (interval > 1) {return interval + ' hours ago';} | ||
| 149 | |||
| 150 | interval = Math.floor(seconds / 60); | ||
| 151 | if (interval > 1) {return interval + ' minutes ago';} | ||
| 152 | |||
| 153 | if(seconds < 10) return 'just now'; | ||
| 154 | |||
| 155 | return Math.floor(seconds) + ' seconds ago'; | ||
| 156 | }; | ||
| 157 | |||
| 158 | function TicksToTime(ticks) { | ||
| 159 | |||
| 160 | let seconds = Math.floor(ticks/60) | ||
| 161 | let minutes = Math.floor(seconds/60) | ||
| 162 | let hours = Math.floor(minutes/60) | ||
| 163 | |||
| 164 | let milliseconds = Math.floor((ticks%60)*1000/60) | ||
| 165 | seconds = seconds % 60; | ||
| 166 | minutes = minutes % 60; | ||
| 167 | |||
| 168 | return `${hours===0?"":hours+":"}${minutes===0?"":hours>0?minutes.toString().padStart(2, '0')+":":(minutes+":")}${minutes>0?seconds.toString().padStart(2, '0'):seconds}.${milliseconds.toString().padStart(3, '0')} (${ticks})`; | ||
| 169 | } | ||
| 170 | |||
| 171 | |||
| 172 | if(profileData!==null){ | ||
| 173 | return ( | ||
| 174 | <main> | ||
| 175 | <section id='section1' className='profile'> | ||
| 176 | |||
| 177 | {profileData.profile?( | ||
| 178 | <div id='profile-image' onClick={()=>UpdateProfile()}> | ||
| 179 | <img src={profileData.avatar_link} alt=""></img> | ||
| 180 | <span>Refresh</span> | ||
| 181 | </div> | ||
| 182 | ):( | ||
| 183 | <div> | ||
| 184 | <img src={profileData.avatar_link} alt=""></img> | ||
| 185 | </div> | ||
| 186 | )} | ||
| 187 | |||
| 188 | <div id='profile-top'> | ||
| 189 | <div> | ||
| 190 | <div>{profileData.user_name}</div> | ||
| 191 | <div> | ||
| 192 | {profileData.country_code==="XX"?"":<img src={`https://flagcdn.com/w80/${profileData.country_code.toLowerCase()}.jpg`} alt={profileData.country_code} />} | ||
| 193 | </div> | ||
| 194 | <div> | ||
| 195 | {profileData.titles.map(e=>( | ||
| 196 | <span className="titles" style={{backgroundColor:`#${e.color}`}}> | ||
| 197 | {e.name} | ||
| 198 | </span> | ||
| 199 | ))} | ||
| 200 | </div> | ||
| 201 | </div> | ||
| 202 | <div> | ||
| 203 | {profileData.links.p2sr==="-"?"":<a href={profileData.links.p2sr}><img src={img17} alt="Steam" /></a>} | ||
| 204 | {profileData.links.p2sr==="-"?"":<a href={profileData.links.p2sr}><img src={img15} alt="Twitch" /></a>} | ||
| 205 | {profileData.links.p2sr==="-"?"":<a href={profileData.links.p2sr}><img src={img16} alt="Youtube" /></a>} | ||
| 206 | {profileData.links.p2sr==="-"?"":<a href={profileData.links.p2sr}><img src={img4} alt="P2SR" style={{padding:"0"}} /></a>} | ||
| 207 | </div> | ||
| 208 | |||
| 209 | </div> | ||
| 210 | <div id='profile-bottom'> | ||
| 211 | <div> | ||
| 212 | <span>Overall</span> | ||
| 213 | <span>{profileData.rankings.overall.rank===0?"N/A ":"#"+profileData.rankings.overall.rank+" "} | ||
| 214 | <span>({profileData.rankings.overall.completion_count}/{profileData.rankings.overall.completion_total})</span> | ||
| 215 | </span> | ||
| 216 | </div> | ||
| 217 | <div> | ||
| 218 | <span>Singleplayer</span> | ||
| 219 | <span>{profileData.rankings.singleplayer.rank===0?"N/A ":"#"+profileData.rankings.singleplayer.rank+" "} | ||
| 220 | <span>({profileData.rankings.singleplayer.completion_count}/{profileData.rankings.singleplayer.completion_total})</span> | ||
| 221 | </span> | ||
| 222 | </div> | ||
| 223 | <div> | ||
| 224 | <span>Cooperative</span> | ||
| 225 | <span>{profileData.rankings.cooperative.rank===0?"N/A ":"#"+profileData.rankings.cooperative.rank+" "} | ||
| 226 | <span>({profileData.rankings.cooperative.completion_count}/{profileData.rankings.cooperative.completion_total})</span> | ||
| 227 | </span> | ||
| 228 | </div> | ||
| 229 | </div> | ||
| 230 | </section> | ||
| 231 | |||
| 232 | |||
| 233 | <section id='section2' className='profile'> | ||
| 234 | <button onClick={()=>setNavState(0)}><img src={img5} alt="" /> Player Records</button> | ||
| 235 | <button onClick={()=>setNavState(1)}><img src={img14} alt="" /> Statistics</button> | ||
| 236 | </section> | ||
| 237 | |||
| 238 | |||
| 239 | |||
| 240 | |||
| 241 | |||
| 242 | <section id='section3' className='profile1'> | ||
| 243 | <div id='profileboard-nav'> | ||
| 244 | {gameData===null?<select>error</select>: | ||
| 245 | |||
| 246 | <select id='select-game' | ||
| 247 | onChange={()=>setGame(document.querySelector('#select-game').value)}> | ||
| 248 | <option value={0} key={0}>All Scores</option> | ||
| 249 | {gameData.map((e,i)=>( | ||
| 250 | <option value={e.id} key={i+1}>{e.name}</option> | ||
| 251 | ))}</select> | ||
| 252 | } | ||
| 253 | |||
| 254 | {game==0? | ||
| 255 | <select disabled> | ||
| 256 | <option>All Scores</option> | ||
| 257 | </select> | ||
| 258 | :chapterData===null?<select></select>: | ||
| 259 | |||
| 260 | <select id='select-chapter' | ||
| 261 | onChange={()=>setChapter(document.querySelector('#select-chapter').value)}> | ||
| 262 | <option value="0" key="0">All</option> | ||
| 263 | {chapterData.chapters.filter(e=>e.is_disabled===false).map((e,i)=>( | ||
| 264 | <option value={e.id} key={i+1}>{e.name}</option> | ||
| 265 | ))}</select> | ||
| 266 | } | ||
| 267 | </div> | ||
| 268 | <div id='profileboard-top'> | ||
| 269 | <span><span>Map Name</span><img src={img19} alt="" /></span> | ||
| 270 | <span style={{justifyContent:'center'}}><span>Portals</span><img src={img19} alt="" /></span> | ||
| 271 | <span style={{justifyContent:'center'}}><span>WRΔ </span><img src={img19} alt="" /></span> | ||
| 272 | <span style={{justifyContent:'center'}}><span>Time</span><img src={img19} alt="" /></span> | ||
| 273 | <span> </span> | ||
| 274 | <span><span>Rank</span><img src={img19} alt="" /></span> | ||
| 275 | <span><span>Date</span><img src={img19} alt="" /></span> | ||
| 276 | <div id='page-number'> | ||
| 277 | <div> | ||
| 278 | <button onClick={() => pageNumber === 1 ? null : setPageNumber(prevPageNumber => prevPageNumber - 1)} | ||
| 279 | ><i className='triangle' style={{position:'relative',left:'-5px',}}></i> </button> | ||
| 280 | <span>{pageNumber}/{pageMax}</span> | ||
| 281 | <button onClick={() => pageNumber === pageMax? null : setPageNumber(prevPageNumber => prevPageNumber + 1)} | ||
| 282 | ><i className='triangle' style={{position:'relative',left:'5px',transform:'rotate(180deg)'}}></i> </button> | ||
| 283 | </div> | ||
| 284 | </div> | ||
| 285 | </div> | ||
| 286 | <hr/> | ||
| 287 | <div id='profileboard-records'> | ||
| 288 | |||
| 289 | {game == 0 && profileData !== null | ||
| 290 | ? ( | ||
| 291 | |||
| 292 | profileData.records.sort((a,b)=>a.map_id - b.map_id) | ||
| 293 | .map((r, index) => ( | ||
| 294 | |||
| 295 | Math.ceil((index+1)/20)===pageNumber ? ( | ||
| 296 | <button className="profileboard-record" key={index}> | ||
| 297 | {r.scores.map((e,i)=>(<> | ||
| 298 | {i!==0?<hr style={{gridColumn:"1 / span 8"}}/>:""} | ||
| 299 | |||
| 300 | <span>{r.map_name}</span> | ||
| 301 | |||
| 302 | <span style={{ display: "grid" }}>{e.score_count}</span> | ||
| 303 | |||
| 304 | <span style={{ display: "grid" }}>{e.score_count-r.map_wr_count}</span> | ||
| 305 | <span style={{ display: "grid" }}>{TicksToTime(e.score_time)}</span> | ||
| 306 | <span> </span> | ||
| 307 | {i===0?<span>#{r.placement}</span>:<span> </span>} | ||
| 308 | <span>{e.date.split("T")[0]}</span> | ||
| 309 | <span style={{ flexDirection: "row-reverse" }}> | ||
| 310 | |||
| 311 | <button onClick={()=>{window.alert(`Demo ID: ${e.demo_id}`)}}><img src={img13} alt="demo_id" /></button> | ||
| 312 | <button onClick={()=>window.location.href=`https://lp.ardapektezol.com/api/v1/demos?uuid=${e.demo_id}`}><img src={img12} alt="download" /></button> | ||
| 313 | {i===0&&r.scores.length>1?<button onClick={()=> | ||
| 314 | { | ||
| 315 | document.querySelectorAll(".profileboard-record")[index%20].style.height==="44px"|| | ||
| 316 | document.querySelectorAll(".profileboard-record")[index%20].style.height===""? | ||
| 317 | document.querySelectorAll(".profileboard-record")[index%20].style.height=`${r.scores.length*46}px`: | ||
| 318 | document.querySelectorAll(".profileboard-record")[index%20].style.height="44px" | ||
| 319 | } | ||
| 320 | }><img src={img18} alt="history" /></button>:""} | ||
| 321 | |||
| 322 | </span> | ||
| 323 | </>))} | ||
| 324 | |||
| 325 | </button> | ||
| 326 | ) : "" | ||
| 327 | ))) : maps !== null ? | ||
| 328 | |||
| 329 | maps.filter(e=>e.is_disabled===false).sort((a,b)=>a.id - b.id) | ||
| 330 | .map((r, index) => { | ||
| 331 | if(Math.ceil((index+1)/20)===pageNumber){ | ||
| 332 | let record = profileData.records.find((e) => e.map_id === r.id); | ||
| 333 | return record === undefined ? ( | ||
| 334 | <button className="profileboard-record" key={index} style={{backgroundColor:"#1b1b20"}}> | ||
| 335 | <span>{r.name}</span> | ||
| 336 | <span style={{ display: "grid" }}>N/A</span> | ||
| 337 | <span style={{ display: "grid" }}>N/A</span> | ||
| 338 | <span>N/A</span> | ||
| 339 | <span> </span> | ||
| 340 | <span>N/A</span> | ||
| 341 | <span>N/A</span> | ||
| 342 | <span style={{ flexDirection: "row-reverse" }}></span> | ||
| 343 | </button> | ||
| 344 | ) : ( | ||
| 345 | <button className="profileboard-record" key={index}> | ||
| 346 | {record.scores.map((e,i)=>(<> | ||
| 347 | {i!==0?<hr style={{gridColumn:"1 / span 8"}}/>:""} | ||
| 348 | <span>{r.name}</span> | ||
| 349 | <span style={{ display: "grid" }}>{record.scores[i].score_count}</span> | ||
| 350 | <span style={{ display: "grid" }}>{record.scores[i].score_count-record.map_wr_count}</span> | ||
| 351 | <span style={{ display: "grid" }}>{TicksToTime(record.scores[i].score_time)}</span> | ||
| 352 | <span> </span> | ||
| 353 | {i===0?<span>#{record.placement}</span>:<span> </span>} | ||
| 354 | <span>{record.scores[i].date.split("T")[0]}</span> | ||
| 355 | <span style={{ flexDirection: "row-reverse" }}> | ||
| 356 | |||
| 357 | <button onClick={()=>{window.alert(`Demo ID: ${e.demo_id}`)}}><img src={img13} alt="demo_id" /></button> | ||
| 358 | <button onClick={()=>window.location.href=`https://lp.ardapektezol.com/api/v1/demos?uuid=${e.demo_id}`}><img src={img12} alt="download" /></button> | ||
| 359 | {i===0&&record.scores.length>1?<button onClick={()=> | ||
| 360 | { | ||
| 361 | document.querySelectorAll(".profileboard-record")[index%20].style.height==="44px"|| | ||
| 362 | document.querySelectorAll(".profileboard-record")[index%20].style.height===""? | ||
| 363 | document.querySelectorAll(".profileboard-record")[index%20].style.height=`${record.scores.length*46}px`: | ||
| 364 | document.querySelectorAll(".profileboard-record")[index%20].style.height="44px" | ||
| 365 | } | ||
| 366 | }><img src={img18} alt="history" /></button>:""} | ||
| 367 | |||
| 368 | </span> | ||
| 369 | </>))} | ||
| 370 | </button> | ||
| 371 | |||
| 372 | ) | ||
| 373 | }else{return null} | ||
| 374 | }):(<>{console.warn(maps)}</>)} | ||
| 375 | </div> | ||
| 376 | </section> | ||
| 377 | |||
| 378 | </main> | ||
| 379 | )} | ||
| 380 | } | ||
| 381 | |||
| 382 | |||
diff --git a/frontend/src/components/pages/summary.js b/frontend/src/components/pages/summary.js deleted file mode 100644 index d276408..0000000 --- a/frontend/src/components/pages/summary.js +++ /dev/null | |||
| @@ -1,650 +0,0 @@ | |||
| 1 | import React, { useEffect } from 'react'; | ||
| 2 | import { useLocation, Link } from "react-router-dom"; | ||
| 3 | import ReactMarkdown from 'react-markdown' | ||
| 4 | |||
| 5 | import "./summary.css"; | ||
| 6 | |||
| 7 | import img4 from "../../imgs/4.png" | ||
| 8 | import img5 from "../../imgs/5.png" | ||
| 9 | import img6 from "../../imgs/6.png" | ||
| 10 | import img12 from "../../imgs/12.png" | ||
| 11 | import img13 from "../../imgs/13.png" | ||
| 12 | import Modview from "./summary_modview.js" | ||
| 13 | |||
| 14 | export default function Summary(prop) { | ||
| 15 | const {token,mod} = prop | ||
| 16 | const fakedata={} //for debug | ||
| 17 | |||
| 18 | const location = useLocation() | ||
| 19 | |||
| 20 | //fetching data | ||
| 21 | const [data, setData] = React.useState(null); | ||
| 22 | React.useEffect(() => { | ||
| 23 | setData(null) | ||
| 24 | setDiscussionThread(null) | ||
| 25 | setCreatePostState(0) | ||
| 26 | setSelectedRun(0) | ||
| 27 | setCatState(1) | ||
| 28 | fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/summary`) | ||
| 29 | .then(r => r.json()) | ||
| 30 | .then(d => { | ||
| 31 | if(Object.keys(fakedata).length!==0){setData(fakedata)} | ||
| 32 | else{setData(d.data)} | ||
| 33 | if(d.data.summary.routes.length===0){d.data.summary.routes[0]={"category": "","history": {"score_count": 0,},"rating": 0,"description": "","showcase": ""}} | ||
| 34 | }) | ||
| 35 | // eslint-disable-next-line | ||
| 36 | }, [location.pathname]); | ||
| 37 | |||
| 38 | const [pageNumber, setPageNumber] = React.useState(1); | ||
| 39 | const [lbData, setLbData] = React.useState(null); | ||
| 40 | React.useEffect(() => { | ||
| 41 | fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/leaderboards?page=${pageNumber}`) | ||
| 42 | .then(r => r.json()) | ||
| 43 | .then(d => setLbData(d)) | ||
| 44 | // eslint-disable-next-line | ||
| 45 | }, [pageNumber,location.pathname]); | ||
| 46 | |||
| 47 | const [discussions,setDiscussions] = React.useState(null) | ||
| 48 | function fetchDiscussions() { | ||
| 49 | fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/discussions`) | ||
| 50 | .then(r=>r.json()) | ||
| 51 | .then(d=>setDiscussions(d.data.discussions)) | ||
| 52 | } | ||
| 53 | |||
| 54 | React.useEffect(()=>{ | ||
| 55 | fetchDiscussions() | ||
| 56 | },[location.pathname]) | ||
| 57 | |||
| 58 | |||
| 59 | |||
| 60 | const [discussionThread,setDiscussionThread] = React.useState(null) | ||
| 61 | function openDiscussion(x){ | ||
| 62 | fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/discussions/${x}`) | ||
| 63 | .then(r=>r.json()) | ||
| 64 | .then(d=>setDiscussionThread(d.data.discussion)) | ||
| 65 | } | ||
| 66 | const [discussionSearch, setDiscussionSearch] = React.useState("") | ||
| 67 | |||
| 68 | |||
| 69 | |||
| 70 | |||
| 71 | const [navState, setNavState] = React.useState(0); // eslint-disable-next-line | ||
| 72 | React.useEffect(() => {NavClick();}, [[],navState]); | ||
| 73 | |||
| 74 | function NavClick() { | ||
| 75 | if(data!==null){ | ||
| 76 | const btn = document.querySelectorAll("#section2 button.nav-button"); | ||
| 77 | btn.forEach((e) => {e.style.backgroundColor = "#2b2e46"}); | ||
| 78 | btn[navState].style.backgroundColor = "#202232"; | ||
| 79 | |||
| 80 | document.querySelectorAll("section").forEach((e,i)=>i>=2?e.style.display="none":"") | ||
| 81 | if(navState === 0){document.querySelectorAll(".summary1").forEach((e) => {e.style.display = "grid"});} | ||
| 82 | if(navState === 1){document.querySelectorAll(".summary2").forEach((e) => {e.style.display = "block"});} | ||
| 83 | if(navState === 2){document.querySelectorAll(".summary3").forEach((e) => {e.style.display = "block"});} | ||
| 84 | }} | ||
| 85 | |||
| 86 | |||
| 87 | const [catState, setCatState] = React.useState(1); // eslint-disable-next-line | ||
| 88 | React.useEffect(() => {CatClick();}, [[],catState]); | ||
| 89 | |||
| 90 | function CatClick() { | ||
| 91 | if(data!==null){ | ||
| 92 | const btn = document.querySelectorAll("#section3 #category span button"); | ||
| 93 | btn.forEach((e) => {e.style.backgroundColor = "#2b2e46"}); | ||
| 94 | btn[catState-1].style.backgroundColor = "#202232"; | ||
| 95 | }} | ||
| 96 | React.useEffect(()=>{ | ||
| 97 | if(data!==null && data.summary.routes.filter(e=>e.category.id===catState).length!==0){ | ||
| 98 | selectRun(0,catState)} // eslint-disable-next-line | ||
| 99 | },[catState,data]) | ||
| 100 | |||
| 101 | |||
| 102 | const [hisState, setHisState] = React.useState(0); // eslint-disable-next-line | ||
| 103 | React.useEffect(() => {HisClick();}, [[],hisState]); | ||
| 104 | |||
| 105 | function HisClick() { | ||
| 106 | if(data!==null){ | ||
| 107 | const btn = document.querySelectorAll("#section3 #history span button"); | ||
| 108 | btn.forEach((e) => {e.style.backgroundColor = "#2b2e46"}); | ||
| 109 | btn[hisState].style.backgroundColor = "#202232"; | ||
| 110 | |||
| 111 | }} | ||
| 112 | |||
| 113 | const [selectedRun,setSelectedRun] = React.useState(0) | ||
| 114 | |||
| 115 | function selectRun(x,y){ | ||
| 116 | let r = document.querySelectorAll("button.record") | ||
| 117 | r.forEach(e=>e.style.backgroundColor="#2b2e46") | ||
| 118 | r[x].style.backgroundColor="#161723" | ||
| 119 | |||
| 120 | |||
| 121 | if(data!==null && data.summary.routes.length!==0 && data.summary.routes.length!==0){ | ||
| 122 | if(y===2){x+=data.summary.routes.filter(e=>e.category.id<2).length} | ||
| 123 | if(y===3){x+=data.summary.routes.filter(e=>e.category.id<3).length} | ||
| 124 | if(y===4){x+=data.summary.routes.filter(e=>e.category.id<4).length} | ||
| 125 | setSelectedRun(x) | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | function graph(state) { | ||
| 130 | // this is such a mess | ||
| 131 | let graph = data.summary.routes.filter(e=>e.category.id===catState) | ||
| 132 | let graph_score = [] | ||
| 133 | data.summary.routes.filter(e=>e.category.id===catState).forEach(e=>graph_score.push(e.history.score_count)) | ||
| 134 | let graph_dates = [] | ||
| 135 | data.summary.routes.filter(e=>e.category.id===catState).forEach(e=>graph_dates.push(e.history.date.split("T")[0])) | ||
| 136 | let graph_max = graph[graph.length-1].history.score_count | ||
| 137 | let graph_numbers = [] | ||
| 138 | for (let i=graph_max;i>=0;i--){ | ||
| 139 | graph_numbers[i]=i | ||
| 140 | } | ||
| 141 | |||
| 142 | switch (state) { | ||
| 143 | case 1: //numbers | ||
| 144 | return graph_numbers | ||
| 145 | .reverse().map(e=>( | ||
| 146 | graph_score.includes(e) || e===0 ? | ||
| 147 | <span>{e}<br/></span> | ||
| 148 | : | ||
| 149 | <span><br/></span> | ||
| 150 | )) | ||
| 151 | case 2: // graph | ||
| 152 | let g = 0 | ||
| 153 | let h = 0 | ||
| 154 | return graph_numbers.map((e,j)=>( | ||
| 155 | <tr id={'graph_row-'+(graph_max-j)} | ||
| 156 | data-graph={ graph_score.includes(graph_max-j) ? g++ : 0} | ||
| 157 | data-graph2={h=0} | ||
| 158 | |||
| 159 | > | ||
| 160 | { | ||
| 161 | graph_score.map((e,i)=>( | ||
| 162 | <> | ||
| 163 | <td className='graph_ver' | ||
| 164 | data-graph={ h++ } | ||
| 165 | style={{outline: | ||
| 166 | g===h-1 ? | ||
| 167 | "1px solid #2b2e46" : g>=h ? "1px dashed white" : "0" }} | ||
| 168 | ></td> | ||
| 169 | |||
| 170 | {g===h && graph_score.includes(graph_max-j) ? | ||
| 171 | <button className='graph-button' | ||
| 172 | onClick={()=>{ | ||
| 173 | selectRun(graph_dates.length-(i-1),catState); | ||
| 174 | }} | ||
| 175 | style={{left: `calc(100% / ${graph_dates.length} * ${h-1})`}} | ||
| 176 | ></button> | ||
| 177 | : ""} | ||
| 178 | |||
| 179 | <td className='graph_hor' id={'graph_table-'+i++} | ||
| 180 | style={{ | ||
| 181 | outline: | ||
| 182 | graph_score.includes(graph_max-j) ? | ||
| 183 | g>=h ? | ||
| 184 | g-1>=h ? "1px dashed #2b2e46" : "1px solid white" : "0" | ||
| 185 | : "0"}} | ||
| 186 | ></td> | ||
| 187 | |||
| 188 | |||
| 189 | |||
| 190 | <td className='graph_hor' id={'graph_table-'+i++} | ||
| 191 | style={{outline: | ||
| 192 | graph_score.includes(graph_max-j) ? | ||
| 193 | g>=h ? | ||
| 194 | g-1>=h ? "1px dashed #2b2e46" : "1px solid white" : "0" | ||
| 195 | : "0"}} | ||
| 196 | ></td> | ||
| 197 | |||
| 198 | </> | ||
| 199 | )) | ||
| 200 | |||
| 201 | } | ||
| 202 | |||
| 203 | </tr> | ||
| 204 | )) | ||
| 205 | |||
| 206 | case 3: // dates | ||
| 207 | return graph_dates | ||
| 208 | .reverse().map(e=>( | ||
| 209 | <span>{e}</span> | ||
| 210 | )) | ||
| 211 | default: | ||
| 212 | break; | ||
| 213 | |||
| 214 | } | ||
| 215 | |||
| 216 | } | ||
| 217 | |||
| 218 | const [vid,setVid] = React.useState("") | ||
| 219 | React.useEffect(()=>{ | ||
| 220 | if(data!==null){ | ||
| 221 | let showcase = data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].showcase | ||
| 222 | showcase.length>6 ? setVid("https://www.youtube.com/embed/"+YouTubeGetID(showcase)) | ||
| 223 | : setVid("") | ||
| 224 | } // eslint-disable-next-line | ||
| 225 | },[[],selectedRun]) | ||
| 226 | |||
| 227 | function YouTubeGetID(url){ | ||
| 228 | url = url.split(/(vi\/|v=|\/v\/|youtu\.be\/|\/embed\/)/); | ||
| 229 | return (url[2] !== undefined) ? url[2].split(/[^0-9a-z_]/i)[0] : url[0]; | ||
| 230 | } | ||
| 231 | |||
| 232 | function TimeAgo(date) { | ||
| 233 | // const seconds = Math.floor((new Date() - date) / 1000); | ||
| 234 | |||
| 235 | const seconds = Math.floor(((new Date(new Date() - (date.getTimezoneOffset()*-60000))) - date) / 1000); | ||
| 236 | |||
| 237 | let interval = Math.floor(seconds / 31536000); | ||
| 238 | if (interval === 1) {return interval + ' year ago';} | ||
| 239 | if (interval > 1) {return interval + ' years ago';} | ||
| 240 | |||
| 241 | interval = Math.floor(seconds / 2592000); | ||
| 242 | if (interval === 1) {return interval + ' month ago';} | ||
| 243 | if (interval > 1) {return interval + ' months ago';} | ||
| 244 | |||
| 245 | interval = Math.floor(seconds / 86400); | ||
| 246 | if (interval === 1) {return interval + ' day ago';} | ||
| 247 | if (interval > 1) {return interval + ' days ago';} | ||
| 248 | |||
| 249 | interval = Math.floor(seconds / 3600); | ||
| 250 | if (interval === 1) {return interval + ' hour ago';} | ||
| 251 | if (interval > 1) {return interval + ' hours ago';} | ||
| 252 | |||
| 253 | interval = Math.floor(seconds / 60); | ||
| 254 | if (interval === 1) {return interval + ' minute ago';} | ||
| 255 | if (interval > 1) {return interval + ' minutes ago';} | ||
| 256 | |||
| 257 | if(seconds < 10) return 'just now'; | ||
| 258 | |||
| 259 | return Math.floor(seconds) + ' seconds ago'; | ||
| 260 | }; | ||
| 261 | |||
| 262 | function TicksToTime(ticks) { | ||
| 263 | |||
| 264 | let seconds = Math.floor(ticks/60) | ||
| 265 | let minutes = Math.floor(seconds/60) | ||
| 266 | let hours = Math.floor(minutes/60) | ||
| 267 | |||
| 268 | let milliseconds = Math.floor((ticks%60)*1000/60) | ||
| 269 | seconds = seconds % 60; | ||
| 270 | minutes = minutes % 60; | ||
| 271 | |||
| 272 | return `${hours===0?"":hours+":"}${minutes===0?"":hours>0?minutes.toString().padStart(2, '0')+":":(minutes+":")}${minutes>0?seconds.toString().padStart(2, '0'):seconds}.${milliseconds.toString().padStart(3, '0')} (${ticks})`; | ||
| 273 | } | ||
| 274 | |||
| 275 | function PostComment() { | ||
| 276 | |||
| 277 | fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/discussions/${discussionThread.id}`,{ | ||
| 278 | method:"POST", | ||
| 279 | headers:{authorization:token}, | ||
| 280 | body:JSON.stringify({"comment":document.querySelector("#discussion-send>input").value}) | ||
| 281 | }) | ||
| 282 | .then(r=>r.json()) | ||
| 283 | .then(d=>{ | ||
| 284 | document.querySelector("#discussion-send>input").value="" | ||
| 285 | openDiscussion(discussionThread.id) | ||
| 286 | }) | ||
| 287 | } | ||
| 288 | |||
| 289 | |||
| 290 | const [createPostState,setCreatePostState] = React.useState(0) | ||
| 291 | function CreatePost() { | ||
| 292 | |||
| 293 | fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/discussions`,{ | ||
| 294 | method:"POST", | ||
| 295 | headers:{authorization:token}, | ||
| 296 | body:JSON.stringify({"title":document.querySelector("#discussion-create-title").value,"content":document.querySelector("#discussion-create-content").value}) | ||
| 297 | }) | ||
| 298 | .then(r=>r.json()) | ||
| 299 | .then(d=>{ | ||
| 300 | setCreatePostState(0) | ||
| 301 | fetchDiscussions() | ||
| 302 | }) | ||
| 303 | } | ||
| 304 | |||
| 305 | function DeletePost(post) { | ||
| 306 | if(window.confirm(`Are you sure you want to remove post: ${post.title}?`)){ | ||
| 307 | console.log("deleted",post.id) | ||
| 308 | fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/discussions/${post.id}`,{ | ||
| 309 | method:"DELETE", | ||
| 310 | headers:{authorization:token}, | ||
| 311 | }) | ||
| 312 | .then(r=>r.json()) | ||
| 313 | .then(d=>{ | ||
| 314 | fetchDiscussions() | ||
| 315 | }) | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 319 | |||
| 320 | if(data!==null){ | ||
| 321 | console.log(data) | ||
| 322 | |||
| 323 | let current_chapter = data.map.chapter_name | ||
| 324 | let isCoop = false; | ||
| 325 | if (data.map.game_name == "Portal 2 - Cooperative") { | ||
| 326 | isCoop = true | ||
| 327 | } | ||
| 328 | |||
| 329 | current_chapter = data.map.chapter_name.split(" ") | ||
| 330 | // current_chapter = current_chapter.split("-") | ||
| 331 | current_chapter = current_chapter[1] | ||
| 332 | |||
| 333 | return ( | ||
| 334 | <> | ||
| 335 | {token!==null?mod===true?<Modview selectedRun={selectedRun} data={data} token={token}/>:"":""} | ||
| 336 | |||
| 337 | <div id='background-image'> | ||
| 338 | <img src={data.map.image} alt="" /> | ||
| 339 | </div> | ||
| 340 | <main> | ||
| 341 | <section id='section1' className='summary1'> | ||
| 342 | <div> | ||
| 343 | <Link to="/games"><button className='nav-button' style={{borderRadius: "20px 0px 0px 20px"}}><i className='triangle'></i><span>Games list</span></button></Link> | ||
| 344 | <Link to={`/games/${!data.map.is_coop ? "1" : "2"}?chapter=${current_chapter}`}><button className='nav-button' style={{borderRadius: "0px 20px 20px 0px", marginLeft: "2px"}}><i className='triangle'></i><span>{data.map.chapter_name}</span></button></Link> | ||
| 345 | <br/><span><b>{data.map.map_name}</b></span> | ||
| 346 | </div> | ||
| 347 | |||
| 348 | |||
| 349 | </section> | ||
| 350 | |||
| 351 | <section id='section2' className='summary1'> | ||
| 352 | <button className='nav-button' onClick={()=>setNavState(0)}><img src={img4} alt="" /><span>Summary</span></button> | ||
| 353 | <button className='nav-button' onClick={()=>setNavState(1)}><img src={img5} alt="" /><span>Leaderboards</span></button> | ||
| 354 | <button className='nav-button' onClick={()=>setNavState(2)}><img src={img6} alt="" /><span>Discussions</span></button> | ||
| 355 | </section> | ||
| 356 | <section id='section3' className='summary1'> | ||
| 357 | <div id='category' | ||
| 358 | style={data.map.image===""?{backgroundColor:"#202232"}:{}}> | ||
| 359 | <img src={data.map.image} alt="" id='category-image'></img> | ||
| 360 | <p><span className='portal-count'>{data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].history.score_count}</span> | ||
| 361 | {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].history.score_count === 1 ? ` portal` : ` portals` }</p> | ||
| 362 | <span> | ||
| 363 | <button onClick={()=>setCatState(1)}>CM</button> | ||
| 364 | <button onClick={()=>setCatState(2)}>NoSLA</button> | ||
| 365 | {data.map.is_coop?<button onClick={()=>setCatState(3)}>SLA</button> | ||
| 366 | :<button onClick={()=>setCatState(3)}>Inbounds SLA</button>} | ||
| 367 | <button onClick={()=>setCatState(4)}>Any%</button> | ||
| 368 | </span> | ||
| 369 | |||
| 370 | </div> | ||
| 371 | |||
| 372 | <div id='history'> | ||
| 373 | |||
| 374 | <div style={{display: hisState ? "none" : "block"}}> | ||
| 375 | {data.summary.routes.filter(e=>e.category.id===catState).length===0 ? <h5>There are no records for this map.</h5> : | ||
| 376 | <> | ||
| 377 | <div className='record-top'> | ||
| 378 | <span>Date</span> | ||
| 379 | <span>Record</span> | ||
| 380 | <span>First completion</span> | ||
| 381 | </div> | ||
| 382 | <hr/> | ||
| 383 | <div id='records'> | ||
| 384 | |||
| 385 | {data.summary.routes | ||
| 386 | .sort((a, b) => a.history.score_count - b.history.score_count) | ||
| 387 | .filter(e=>e.category.id===catState) | ||
| 388 | .map((r, index) => ( | ||
| 389 | <button className='record' key={index} onClick={()=>{ | ||
| 390 | selectRun(index,r.category.id); | ||
| 391 | }}> | ||
| 392 | <span>{ new Date(r.history.date).toLocaleDateString( | ||
| 393 | "en-US", { month: 'long', day: 'numeric', year: 'numeric' } | ||
| 394 | )}</span> | ||
| 395 | <span>{r.history.score_count}</span> | ||
| 396 | <span>{r.history.runner_name}</span> | ||
| 397 | </button> | ||
| 398 | ))} | ||
| 399 | </div> | ||
| 400 | </> | ||
| 401 | } | ||
| 402 | </div> | ||
| 403 | |||
| 404 | <div style={{display: hisState ? "block" : "none"}}> | ||
| 405 | {data.summary.routes.filter(e=>e.category.id===catState).length===0 ? <h5>There are no records for this map.</h5> : | ||
| 406 | <div id='graph'> | ||
| 407 | <div>{graph(1)}</div> | ||
| 408 | <div>{graph(2)}</div> | ||
| 409 | <div>{graph(3)}</div> | ||
| 410 | </div> | ||
| 411 | } | ||
| 412 | </div> | ||
| 413 | <span> | ||
| 414 | <button onClick={()=>setHisState(0)}>List</button> | ||
| 415 | <button onClick={()=>setHisState(1)}>Graph</button> | ||
| 416 | </span> | ||
| 417 | </div> | ||
| 418 | |||
| 419 | |||
| 420 | </section> | ||
| 421 | <section id='section4' className='summary1'> | ||
| 422 | <div id='difficulty'> | ||
| 423 | <span>Difficulty</span> | ||
| 424 | {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].rating === 0 ? (<span>N/A</span>):null} | ||
| 425 | {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].rating === 1 ? (<span style={{color:"lime"}}>Very easy</span>):null} | ||
| 426 | {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].rating === 2 ? (<span style={{color:"green"}}>Easy</span>):null} | ||
| 427 | {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].rating === 3 ? (<span style={{color:"yellow"}}>Medium</span>):null} | ||
| 428 | {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].rating === 4 ? (<span style={{color:"orange"}}>Hard</span>):null} | ||
| 429 | {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].rating === 5 ? (<span style={{color:"red"}}>Very hard</span>):null} | ||
| 430 | <div> | ||
| 431 | {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].rating === 1 ? (<div className='difficulty-rating' style={{backgroundColor:"lime"}}></div>) : (<div className='difficulty-rating'></div>)} | ||
| 432 | {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].rating === 2 ? (<div className='difficulty-rating' style={{backgroundColor:"green"}}></div>) : (<div className='difficulty-rating'></div>)} | ||
| 433 | {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].rating === 3 ? (<div className='difficulty-rating' style={{backgroundColor:"yellow"}}></div>) : (<div className='difficulty-rating'></div>)} | ||
| 434 | {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].rating === 4 ? (<div className='difficulty-rating' style={{backgroundColor:"orange"}}></div>) : (<div className='difficulty-rating'></div>)} | ||
| 435 | {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].rating === 5 ? (<div className='difficulty-rating' style={{backgroundColor:"red"}}></div>) : (<div className='difficulty-rating'></div>)} | ||
| 436 | </div> | ||
| 437 | </div> | ||
| 438 | <div id='count'> | ||
| 439 | <span>Completion count</span> | ||
| 440 | <div>{catState===1?data.summary.routes[selectedRun].completion_count:"N/A"}</div> | ||
| 441 | </div> | ||
| 442 | </section> | ||
| 443 | |||
| 444 | <section id='section5' className='summary1'> | ||
| 445 | <div id='description'> | ||
| 446 | {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].showcase!=="" ? | ||
| 447 | <iframe title='Showcase video' src={vid}> </iframe> | ||
| 448 | : ""} | ||
| 449 | <h3>Route description</h3> | ||
| 450 | <span id='description-text'> | ||
| 451 | <ReactMarkdown> | ||
| 452 | {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].description} | ||
| 453 | </ReactMarkdown> | ||
| 454 | </span> | ||
| 455 | </div> | ||
| 456 | </section> | ||
| 457 | |||
| 458 | {/* Leaderboards */} | ||
| 459 | |||
| 460 | {lbData===null?"":lbData.success===false?( | ||
| 461 | <section id='section6' className='summary2'> | ||
| 462 | <h1 style={{textAlign:"center"}}>Map is not available for competitive boards.</h1> | ||
| 463 | </section> | ||
| 464 | ):lbData.data.records.length===0?( | ||
| 465 | <section id='section6' className='summary2'> | ||
| 466 | <h1 style={{textAlign:"center"}}>No records found.</h1> | ||
| 467 | </section> | ||
| 468 | ):( | ||
| 469 | <section id='section6' className='summary2'> | ||
| 470 | |||
| 471 | <div id='leaderboard-top' | ||
| 472 | style={lbData.data.map.is_coop?{gridTemplateColumns:"7.5% 40% 7.5% 15% 15% 15%"}:{gridTemplateColumns:"7.5% 30% 10% 20% 17.5% 15%"}} | ||
| 473 | > | ||
| 474 | <span>Place</span> | ||
| 475 | |||
| 476 | {lbData.data.map.is_coop?( | ||
| 477 | <div id='runner'> | ||
| 478 | <span>Host</span> | ||
| 479 | <span>Partner</span> | ||
| 480 | </div> | ||
| 481 | ):( | ||
| 482 | <span>Runner</span> | ||
| 483 | )} | ||
| 484 | |||
| 485 | <span>Portals</span> | ||
| 486 | <span>Time</span> | ||
| 487 | <span>Date</span> | ||
| 488 | <div id='page-number'> | ||
| 489 | <div> | ||
| 490 | |||
| 491 | <button onClick={() => pageNumber === 1 ? null : setPageNumber(prevPageNumber => prevPageNumber - 1)} | ||
| 492 | ><i className='triangle' style={{position:'relative',left:'-5px',}}></i> </button> | ||
| 493 | <span>{lbData.data.pagination.current_page}/{lbData.data.pagination.total_pages}</span> | ||
| 494 | <button onClick={() => pageNumber === lbData.data.pagination.total_pages ? null : setPageNumber(prevPageNumber => prevPageNumber + 1)} | ||
| 495 | ><i className='triangle' style={{position:'relative',left:'5px',transform:'rotate(180deg)'}}></i> </button> | ||
| 496 | </div> | ||
| 497 | </div> | ||
| 498 | </div> | ||
| 499 | <hr/> | ||
| 500 | <div id='leaderboard-records'> | ||
| 501 | {lbData.data.records.map((r, index) => ( | ||
| 502 | <span className='leaderboard-record' key={index} | ||
| 503 | style={lbData.data.map.is_coop?{gridTemplateColumns:"3% 4.5% 40% 4% 3.5% 15% 15% 14.5%"}:{gridTemplateColumns:"3% 4.5% 30% 4% 6% 20% 17% 15%"}} | ||
| 504 | > | ||
| 505 | <span>{r.placement}</span> | ||
| 506 | <span> </span> | ||
| 507 | {lbData.data.map.is_coop?( | ||
| 508 | <div> | ||
| 509 | <span><img src={r.host.avatar_link} alt='' /> {r.host.user_name}</span> | ||
| 510 | <span><img src={r.partner.avatar_link} alt='' /> {r.partner.user_name}</span> | ||
| 511 | </div> | ||
| 512 | ):( | ||
| 513 | <div><span><img src={r.user.avatar_link} alt='' /> {r.user.user_name}</span></div> | ||
| 514 | )} | ||
| 515 | |||
| 516 | <span>{r.score_count}</span> | ||
| 517 | <span> </span> | ||
| 518 | <span>{TicksToTime(r.score_time)}</span> | ||
| 519 | <span className='hover-popup' popup-text={r.record_date.replace("T",' ').split(".")[0]}>{ TimeAgo(new Date(r.record_date.replace("T"," ").replace("Z",""))) }</span> | ||
| 520 | |||
| 521 | {lbData.data.map.is_coop?( | ||
| 522 | <span> | ||
| 523 | <button onClick={()=>{window.alert(`Host demo ID: ${r.host_demo_id} \nParnter demo ID: ${r.partner_demo_id}`)}}><img src={img13} alt="demo_id" /></button> | ||
| 524 | <button onClick={()=>window.location.href=`https://lp.ardapektezol.com/api/v1/demos?uuid=${r.partner_demo_id}`}><img src={img12} alt="download" style={{filter:"hue-rotate(160deg) contrast(60%) saturate(1000%)"}}/></button> | ||
| 525 | <button onClick={()=>window.location.href=`https://lp.ardapektezol.com/api/v1/demos?uuid=${r.host_demo_id}`}><img src={img12} alt="download" style={{filter:"hue-rotate(300deg) contrast(60%) saturate(1000%)"}}/></button> | ||
| 526 | </span> | ||
| 527 | ):( | ||
| 528 | |||
| 529 | <span> | ||
| 530 | <button onClick={()=>{window.alert(`Demo ID: ${r.demo_id}`)}}><img src={img13} alt="demo_id" /></button> | ||
| 531 | <button onClick={()=>window.location.href=`https://lp.ardapektezol.com/api/v1/demos?uuid=${r.demo_id}`}><img src={img12} alt="download" /></button> | ||
| 532 | </span> | ||
| 533 | )} | ||
| 534 | </span> | ||
| 535 | ))} | ||
| 536 | </div> | ||
| 537 | </section> | ||
| 538 | )} | ||
| 539 | |||
| 540 | |||
| 541 | {/* Discussions */} | ||
| 542 | <section id='section7' className='summary3'> | ||
| 543 | |||
| 544 | {discussionThread === null ? ( | ||
| 545 | createPostState === 0 ? ( | ||
| 546 | discussions !== null ? ( | ||
| 547 | // Main screen | ||
| 548 | <> | ||
| 549 | <div id='discussion-search'> | ||
| 550 | <input type="text" value={discussionSearch} placeholder={"Search for posts..."} onChange={()=>setDiscussionSearch(document.querySelector("#discussion-search>input").value)} /> | ||
| 551 | <div><button onClick={()=>setCreatePostState(1)}>New Post</button></div> | ||
| 552 | </div> | ||
| 553 | {discussions.filter(f=>f.title.includes(discussionSearch)).sort((a, b) => new Date(b.updated_at) - new Date(a.updated_at)) | ||
| 554 | .map((e, i) => ( | ||
| 555 | <div id='discussion-post'> | ||
| 556 | |||
| 557 | <button key={e.id} onClick={() => openDiscussion(e.id)}> | ||
| 558 | <span>{e.title}</span> | ||
| 559 | |||
| 560 | {token!==null?e.creator.steam_id===JSON.parse(atob(token.split(".")[1])).sub? | ||
| 561 | <button onClick={()=>DeletePost(e)}>Delete Post</button> | ||
| 562 | :<span></span>:<span></span>} | ||
| 563 | <span><b>{e.creator.user_name}:</b> {e.content}</span> | ||
| 564 | <span>last updated: {TimeAgo(new Date(e.updated_at.replace("T"," ").replace("Z","")))}</span> | ||
| 565 | </button> | ||
| 566 | </div> | ||
| 567 | ))} | ||
| 568 | </> | ||
| 569 | ):( | ||
| 570 | |||
| 571 | // Main screen (no posts) | ||
| 572 | <> | ||
| 573 | <div id='discussion-search'> | ||
| 574 | <input type="text" value={discussionSearch} placeholder={"Search for posts..."} onChange={()=>setDiscussionSearch(document.querySelector("#discussion-search>input").value)} /> | ||
| 575 | <div><button onClick={()=>setCreatePostState(1)}>New Post</button></div> | ||
| 576 | </div> | ||
| 577 | <span style={{textAlign:"center",display:"block"}}>no discussions</span> | ||
| 578 | </> | ||
| 579 | ) | ||
| 580 | ):( | ||
| 581 | // Creating post | ||
| 582 | <div id='discussion-create'> | ||
| 583 | <span>Create post</span> | ||
| 584 | <button onClick={()=>setCreatePostState(0)}>X</button> | ||
| 585 | <div style={{gridColumn:"1 / span 2"}}> | ||
| 586 | <input id='discussion-create-title' placeholder='Title...'></input> | ||
| 587 | <input id='discussion-create-content' placeholder='Enter the comment...' ></input> | ||
| 588 | </div> | ||
| 589 | <div style={{placeItems:"end",gridColumn:"1 / span 2"}}> | ||
| 590 | <button id='discussion-create-button' onClick={()=>CreatePost()}>Post</button> | ||
| 591 | </div> | ||
| 592 | |||
| 593 | </div> | ||
| 594 | |||
| 595 | )):( | ||
| 596 | // Post screen | ||
| 597 | <div id='discussion-thread'> | ||
| 598 | <div> | ||
| 599 | <span>{discussionThread.title}</span> | ||
| 600 | <button onClick={()=>setDiscussionThread(null)}>X</button> | ||
| 601 | </div> | ||
| 602 | |||
| 603 | <div> | ||
| 604 | <img src={discussionThread.creator.avatar_link} alt="" /> | ||
| 605 | <div> | ||
| 606 | <span>{discussionThread.creator.user_name}</span> | ||
| 607 | <span>{TimeAgo(new Date(discussionThread.created_at.replace("T"," ").replace("Z","")))}</span> | ||
| 608 | <span>{discussionThread.content}</span> | ||
| 609 | </div> | ||
| 610 | {discussionThread.comments!==null? | ||
| 611 | discussionThread.comments.sort((a, b) => new Date(a.date) - new Date(b.date)) | ||
| 612 | .map(e=>( | ||
| 613 | <> | ||
| 614 | <img src={e.user.avatar_link} alt="" /> | ||
| 615 | <div> | ||
| 616 | <span>{e.user.user_name}</span> | ||
| 617 | <span>{TimeAgo(new Date(e.date.replace("T"," ").replace("Z","")))}</span> | ||
| 618 | <span>{e.comment}</span> | ||
| 619 | </div> | ||
| 620 | </> | ||
| 621 | |||
| 622 | )):""} | ||
| 623 | |||
| 624 | |||
| 625 | </div> | ||
| 626 | <div id='discussion-send'> | ||
| 627 | <input type="text" placeholder={"Message"} onKeyDown={(e)=>e.key==="Enter"?PostComment():""}/> | ||
| 628 | <div><button onClick={()=>PostComment()}>Send</button></div> | ||
| 629 | </div> | ||
| 630 | |||
| 631 | </div> | ||
| 632 | |||
| 633 | |||
| 634 | )} | ||
| 635 | |||
| 636 | </section> | ||
| 637 | |||
| 638 | </main> | ||
| 639 | </> | ||
| 640 | ) | ||
| 641 | }else{ | ||
| 642 | return ( | ||
| 643 | <main></main> | ||
| 644 | ) | ||
| 645 | } | ||
| 646 | |||
| 647 | |||
| 648 | } | ||
| 649 | |||
| 650 | |||
diff --git a/frontend/src/components/pages/summary_modview.js b/frontend/src/components/pages/summary_modview.js deleted file mode 100644 index 3541c48..0000000 --- a/frontend/src/components/pages/summary_modview.js +++ /dev/null | |||
| @@ -1,254 +0,0 @@ | |||
| 1 | import React from 'react'; | ||
| 2 | import { useLocation } from "react-router-dom"; | ||
| 3 | import ReactMarkdown from 'react-markdown' | ||
| 4 | |||
| 5 | import "./summary_modview.css"; | ||
| 6 | |||
| 7 | |||
| 8 | export default function Modview(prop) { | ||
| 9 | const {selectedRun,data,token} = prop | ||
| 10 | |||
| 11 | const [menu,setMenu] = React.useState(0) | ||
| 12 | React.useEffect(()=>{ | ||
| 13 | if(menu===3){ // add | ||
| 14 | document.querySelector("#modview-route-name>input").value="" | ||
| 15 | document.querySelector("#modview-route-score>input").value="" | ||
| 16 | document.querySelector("#modview-route-date>input").value="" | ||
| 17 | document.querySelector("#modview-route-showcase>input").value="" | ||
| 18 | document.querySelector("#modview-route-description>textarea").value="" | ||
| 19 | } | ||
| 20 | if(menu===2){ // edit | ||
| 21 | document.querySelector("#modview-route-id>input").value=data.summary.routes[selectedRun].route_id | ||
| 22 | document.querySelector("#modview-route-name>input").value=data.summary.routes[selectedRun].history.runner_name | ||
| 23 | document.querySelector("#modview-route-score>input").value=data.summary.routes[selectedRun].history.score_count | ||
| 24 | document.querySelector("#modview-route-date>input").value=data.summary.routes[selectedRun].history.date.split("T")[0] | ||
| 25 | document.querySelector("#modview-route-showcase>input").value=data.summary.routes[selectedRun].showcase | ||
| 26 | document.querySelector("#modview-route-description>textarea").value=data.summary.routes[selectedRun].description | ||
| 27 | } // eslint-disable-next-line | ||
| 28 | },[menu]) | ||
| 29 | |||
| 30 | function compressImage(file) { | ||
| 31 | const reader = new FileReader(); | ||
| 32 | reader.readAsDataURL(file); | ||
| 33 | return new Promise(resolve => { | ||
| 34 | reader.onload = () => { | ||
| 35 | const img = new Image(); | ||
| 36 | img.src = reader.result; | ||
| 37 | img.onload = () => { | ||
| 38 | let {width, height} = img; | ||
| 39 | if (width > 550) { | ||
| 40 | height *= 550 / width; | ||
| 41 | width = 550; | ||
| 42 | } | ||
| 43 | if (height > 320) { | ||
| 44 | width *= 320 / height; | ||
| 45 | height = 320; | ||
| 46 | } | ||
| 47 | const canvas = document.createElement('canvas'); | ||
| 48 | canvas.width = width; | ||
| 49 | canvas.height = height; | ||
| 50 | canvas.getContext('2d').drawImage(img, 0, 0, width, height); | ||
| 51 | resolve(canvas.toDataURL(file.type, 0.6)); | ||
| 52 | }; | ||
| 53 | }; | ||
| 54 | }); | ||
| 55 | } | ||
| 56 | const [image,setImage] = React.useState(null) | ||
| 57 | function uploadImage(){ | ||
| 58 | if(window.confirm("Are you sure you want to submit this to the database?")){ | ||
| 59 | fetch(`/api/v1/maps/${location.pathname.split('/')[2]}/image`,{ | ||
| 60 | method: 'PUT', | ||
| 61 | headers: {Authorization: token}, | ||
| 62 | body: JSON.stringify({"image": image}) | ||
| 63 | }).then(r=>window.location.reload()) | ||
| 64 | } | ||
| 65 | } | ||
| 66 | const location = useLocation() | ||
| 67 | function editRoute(){ | ||
| 68 | if(window.confirm("Are you sure you want to submit this to the database?")){ | ||
| 69 | let payload = { | ||
| 70 | "description": document.querySelector("#modview-route-description>textarea").value===""?"No description available.":document.querySelector("#modview-route-description>textarea").value, | ||
| 71 | "record_date": document.querySelector("#modview-route-date>input").value+"T00:00:00Z", | ||
| 72 | "route_id": parseInt(document.querySelector("#modview-route-id>input").value), | ||
| 73 | "score_count": parseInt(document.querySelector("#modview-route-score>input").value), | ||
| 74 | "showcase": document.querySelector("#modview-route-showcase>input").value, | ||
| 75 | "user_name": document.querySelector("#modview-route-name>input").value | ||
| 76 | } | ||
| 77 | fetch(`/api/v1/maps/${location.pathname.split('/')[2]}/summary`,{ | ||
| 78 | method: 'PUT', | ||
| 79 | headers: {Authorization: token}, | ||
| 80 | body: JSON.stringify(payload) | ||
| 81 | }).then(r=>window.location.reload()) | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | |||
| 86 | function addRoute(){ | ||
| 87 | if(window.confirm("Are you sure you want to submit this to the database?")){ | ||
| 88 | let payload = { | ||
| 89 | "category_id": parseInt(document.querySelector("#modview-route-category>select").value), | ||
| 90 | "description": document.querySelector("#modview-route-description>textarea").value===""?"No description available.":document.querySelector("#modview-route-description>textarea").value, | ||
| 91 | "record_date": document.querySelector("#modview-route-date>input").value+"T00:00:00Z", | ||
| 92 | "score_count": parseInt(document.querySelector("#modview-route-score>input").value), | ||
| 93 | "showcase": document.querySelector("#modview-route-showcase>input").value, | ||
| 94 | "user_name": document.querySelector("#modview-route-name>input").value | ||
| 95 | } | ||
| 96 | fetch(`/api/v1/maps/${location.pathname.split('/')[2]}/summary`,{ | ||
| 97 | method: 'POST', | ||
| 98 | headers: {Authorization: token}, | ||
| 99 | body: JSON.stringify(payload) | ||
| 100 | }).then(r=>window.location.reload()) | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | function deleteRoute(){ | ||
| 105 | if(data.summary.routes[0].category==='') | ||
| 106 | {window.alert("no run selected")}else{ | ||
| 107 | if(window.confirm(`Are you sure you want to delete this run from the database? | ||
| 108 | ${data.summary.routes[selectedRun].category.name} ${data.summary.routes[selectedRun].history.score_count} portals ${data.summary.routes[selectedRun].history.runner_name}`)===true){ | ||
| 109 | console.log("deleted:",selectedRun) | ||
| 110 | fetch(`/api/v1/maps/${location.pathname.split('/')[2]}/summary`,{ | ||
| 111 | method: 'DELETE', | ||
| 112 | headers: {Authorization: token}, | ||
| 113 | body: JSON.stringify({"route_id":data.summary.routes[selectedRun].route_id}) | ||
| 114 | }).then(r=>window.location.reload()) | ||
| 115 | }} | ||
| 116 | |||
| 117 | } | ||
| 118 | |||
| 119 | const [showButton, setShowButton] = React.useState(1) | ||
| 120 | const modview = document.querySelector("div#modview") | ||
| 121 | React.useEffect(()=>{ | ||
| 122 | if(modview!==null){ | ||
| 123 | showButton ? modview.style.transform="translateY(-68%)" | ||
| 124 | : modview.style.transform="translateY(0%)" | ||
| 125 | } | ||
| 126 | let modview_block = document.querySelector("#modview_block") | ||
| 127 | showButton===1?modview_block.style.display="none":modview_block.style.display="block"// eslint-disable-next-line | ||
| 128 | },[showButton]) | ||
| 129 | |||
| 130 | const [md,setMd] = React.useState("") | ||
| 131 | |||
| 132 | return ( | ||
| 133 | <> | ||
| 134 | <div id="modview_block"></div> | ||
| 135 | <div id='modview'> | ||
| 136 | <div> | ||
| 137 | <button onClick={()=>setMenu(1)}>edit image</button> | ||
| 138 | <button onClick={ | ||
| 139 | data.summary.routes[0].category===''?()=>window.alert("no run selected"):()=>setMenu(2)}>edit selected route</button> | ||
| 140 | <button onClick={()=>setMenu(3)}>add new route</button> | ||
| 141 | <button onClick={()=>deleteRoute()}>delete selected route</button> | ||
| 142 | </div> | ||
| 143 | <div> | ||
| 144 | {showButton ?( | ||
| 145 | <button onClick={()=>setShowButton(0)}>Show</button> | ||
| 146 | ) : ( | ||
| 147 | <button onClick={()=>{setShowButton(1);setMenu(0)}}>Hide</button> | ||
| 148 | )} | ||
| 149 | </div> | ||
| 150 | </div> | ||
| 151 | {menu!==0? ( | ||
| 152 | <div id='modview-menu'> | ||
| 153 | {menu===1? ( | ||
| 154 | // image | ||
| 155 | <div id='modview-menu-image'> | ||
| 156 | <div> | ||
| 157 | <span>current image:</span> | ||
| 158 | <img src={data.map.image} alt="missing" /> | ||
| 159 | </div> | ||
| 160 | |||
| 161 | <div> | ||
| 162 | <span>new image: | ||
| 163 | <input type="file" accept='image/*' onChange={e=> | ||
| 164 | compressImage(e.target.files[0]) | ||
| 165 | .then(d=>setImage(d)) | ||
| 166 | }/></span> | ||
| 167 | {image!==null?(<button onClick={()=>uploadImage()}>upload</button>):<span></span>} | ||
| 168 | <img src={image} alt="" id='modview-menu-image-file'/> | ||
| 169 | |||
| 170 | </div> | ||
| 171 | </div> | ||
| 172 | ):menu===2?( | ||
| 173 | // edit route | ||
| 174 | <div id='modview-menu-edit'> | ||
| 175 | <div id='modview-route-id'> | ||
| 176 | <span>route id:</span> | ||
| 177 | <input type="number" disabled/> | ||
| 178 | </div> | ||
| 179 | <div id='modview-route-name'> | ||
| 180 | <span>runner name:</span> | ||
| 181 | <input type="text"/> | ||
| 182 | </div> | ||
| 183 | <div id='modview-route-score'> | ||
| 184 | <span>score:</span> | ||
| 185 | <input type="number"/> | ||
| 186 | </div> | ||
| 187 | <div id='modview-route-date'> | ||
| 188 | <span>date:</span> | ||
| 189 | <input type="date"/> | ||
| 190 | </div> | ||
| 191 | <div id='modview-route-showcase'> | ||
| 192 | <span>showcase video:</span> | ||
| 193 | <input type="text"/> | ||
| 194 | </div> | ||
| 195 | <div id='modview-route-description' style={{height:"180px",gridColumn:"1 / span 5"}}> | ||
| 196 | <span>description:</span> | ||
| 197 | <textarea onChange={()=>setMd(document.querySelector("#modview-route-description>textarea").value)}></textarea> | ||
| 198 | </div> | ||
| 199 | <button style={{gridColumn:"2 / span 3",height:"40px"}} onClick={editRoute}>Apply</button> | ||
| 200 | </div> | ||
| 201 | ):menu===3?( | ||
| 202 | // add route | ||
| 203 | <div id='modview-menu-add'> | ||
| 204 | <div id='modview-route-category'> | ||
| 205 | <span>category:</span> | ||
| 206 | <select> | ||
| 207 | <option value="1" key="1">CM</option> | ||
| 208 | <option value="2" key="2">No SLA</option> | ||
| 209 | {data.map.game_name==="Portal 2 - Cooperative"?"":( | ||
| 210 | <option value="3" key="3">Inbounds SLA</option>)} | ||
| 211 | <option value="4" key="4">Any%</option> | ||
| 212 | </select> | ||
| 213 | </div> | ||
| 214 | <div id='modview-route-name'> | ||
| 215 | <span>runner name:</span> | ||
| 216 | <input type="text" /> | ||
| 217 | </div> | ||
| 218 | <div id='modview-route-score'> | ||
| 219 | <span>score:</span> | ||
| 220 | <input type="number" /> | ||
| 221 | </div> | ||
| 222 | <div id='modview-route-date'> | ||
| 223 | <span>date:</span> | ||
| 224 | <input type="date" /> | ||
| 225 | </div> | ||
| 226 | <div id='modview-route-showcase'> | ||
| 227 | <span>showcase video:</span> | ||
| 228 | <input type="text" /> | ||
| 229 | </div> | ||
| 230 | <div id='modview-route-description' style={{height:"180px",gridColumn:"1 / span 5"}}> | ||
| 231 | <span>description:</span> | ||
| 232 | <textarea defaultValue={"No description available."} onChange={()=>setMd(document.querySelector("#modview-route-description>textarea").value)}></textarea> | ||
| 233 | </div> | ||
| 234 | <button style={{gridColumn:"2 / span 3",height:"40px"}} onClick={addRoute}>Apply</button> | ||
| 235 | </div> | ||
| 236 | ):("error")} | ||
| 237 | |||
| 238 | {menu!==1?( | ||
| 239 | <div id='modview-md'> | ||
| 240 | <span>Markdown preview</span> | ||
| 241 | <span><a href="https://commonmark.org/help/" rel="noreferrer" target='_blank'>documentation</a></span> | ||
| 242 | <span><a href="https://remarkjs.github.io/react-markdown/" rel="noreferrer" target='_blank'>demo</a></span> | ||
| 243 | <p> | ||
| 244 | <ReactMarkdown>{md} | ||
| 245 | </ReactMarkdown> | ||
| 246 | </p> | ||
| 247 | </div> | ||
| 248 | ):""} | ||
| 249 | </div>):""} | ||
| 250 | |||
| 251 | </> | ||
| 252 | ) | ||
| 253 | } | ||
| 254 | |||
diff --git a/frontend/src/components/pages/games.css b/frontend/src/css/Games.css index ec57a71..ec57a71 100644 --- a/frontend/src/components/pages/games.css +++ b/frontend/src/css/Games.css | |||
diff --git a/frontend/src/components/pages/summary.css b/frontend/src/css/Maps.css index 8c6ec35..d164d3b 100644 --- a/frontend/src/components/pages/summary.css +++ b/frontend/src/css/Maps.css | |||
| @@ -65,7 +65,7 @@ | |||
| 65 | #section3{ | 65 | #section3{ |
| 66 | margin: 40px 0 0 0; | 66 | margin: 40px 0 0 0; |
| 67 | 67 | ||
| 68 | display: none; | 68 | display: grid; |
| 69 | grid-template-columns: 1fr 1fr; | 69 | grid-template-columns: 1fr 1fr; |
| 70 | gap: 20px; | 70 | gap: 20px; |
| 71 | } | 71 | } |
| @@ -239,7 +239,7 @@ p>span.portal-count{font-weight: bold;font-size: 100px;vertical-align: -15%;} | |||
| 239 | /* Section 4: Difficulty + count */ | 239 | /* Section 4: Difficulty + count */ |
| 240 | 240 | ||
| 241 | #section4{ | 241 | #section4{ |
| 242 | display: none; | 242 | display: grid; |
| 243 | grid-template-columns: 1fr 1fr; | 243 | grid-template-columns: 1fr 1fr; |
| 244 | gap: 20px; | 244 | gap: 20px; |
| 245 | margin: 40px 0 0 0; | 245 | margin: 40px 0 0 0; |
| @@ -424,6 +424,7 @@ text-align: center; | |||
| 424 | 424 | ||
| 425 | .hover-popup { | 425 | .hover-popup { |
| 426 | position: relative; | 426 | position: relative; |
| 427 | display: inline-block; | ||
| 427 | } | 428 | } |
| 428 | 429 | ||
| 429 | .hover-popup::after { | 430 | .hover-popup::after { |
| @@ -438,8 +439,13 @@ text-align: center; | |||
| 438 | border-radius: 8px; | 439 | border-radius: 8px; |
| 439 | visibility: hidden; | 440 | visibility: hidden; |
| 440 | opacity: 0; | 441 | opacity: 0; |
| 442 | color: #cdcfdf; | ||
| 441 | /* transition: visibility 0s, opacity 0.3s ease; */ | 443 | /* transition: visibility 0s, opacity 0.3s ease; */ |
| 442 | } | 444 | } |
| 445 | |||
| 446 | .hover-popup:hover { | ||
| 447 | color: transparent; | ||
| 448 | } | ||
| 443 | 449 | ||
| 444 | .hover-popup:hover::after { | 450 | .hover-popup:hover::after { |
| 445 | visibility: visible; | 451 | visibility: visible; |
diff --git a/frontend/src/components/pages/summary_modview.css b/frontend/src/css/ModMenu.css index c6d3d8d..c6d3d8d 100644 --- a/frontend/src/components/pages/summary_modview.css +++ b/frontend/src/css/ModMenu.css | |||
diff --git a/frontend/src/components/pages/profile.css b/frontend/src/css/Profile.css index 4944ade..4944ade 100644 --- a/frontend/src/components/pages/profile.css +++ b/frontend/src/css/Profile.css | |||