From eeab67611f16cfce112ac2779bb324d089a7c2fa Mon Sep 17 00:00:00 2001 From: Nidboj132 <28981031+Nidboj132@users.noreply.github.com> Date: Sun, 5 Nov 2023 20:27:51 +0100 Subject: feat: profile (#54) Former-commit-id: be6cd428190c05b2fa99e36c32765cc0fd4044e8 --- frontend/src/App.js | 4 +- frontend/src/components/login.js | 16 +- frontend/src/components/pages/profile.css | 238 +++++++++++++++++++ frontend/src/components/pages/profile.js | 382 ++++++++++++++++++++++++++++++ frontend/src/components/pages/summary.css | 14 +- frontend/src/components/pages/summary.js | 7 +- frontend/src/components/sidebar.js | 19 +- frontend/src/imgs/14.png | Bin 0 -> 1363 bytes frontend/src/imgs/15.png | Bin 0 -> 2988 bytes frontend/src/imgs/16.png | Bin 0 -> 3078 bytes frontend/src/imgs/17.png | Bin 0 -> 4943 bytes frontend/src/imgs/18.png | Bin 0 -> 2434 bytes frontend/src/imgs/19.png | Bin 0 -> 1266 bytes 13 files changed, 653 insertions(+), 27 deletions(-) create mode 100644 frontend/src/components/pages/profile.css create mode 100644 frontend/src/components/pages/profile.js create mode 100644 frontend/src/imgs/14.png create mode 100644 frontend/src/imgs/15.png create mode 100644 frontend/src/imgs/16.png create mode 100644 frontend/src/imgs/17.png create mode 100644 frontend/src/imgs/18.png create mode 100644 frontend/src/imgs/19.png diff --git a/frontend/src/App.js b/frontend/src/App.js index d75636f..93652e6 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -6,6 +6,7 @@ import Main from "./components/main.js" import "./App.css"; import Summary from "./components/pages/summary.js" +import Profile from "./components/pages/profile.js" import About from './components/pages/about.js'; @@ -30,7 +31,8 @@ export default function App() { }> }> }> - }> + }> + }> }> }> }> diff --git a/frontend/src/components/login.js b/frontend/src/components/login.js index 204007f..2b735cb 100644 --- a/frontend/src/components/login.js +++ b/frontend/src/components/login.js @@ -8,7 +8,7 @@ import img3 from "../imgs/11.png" export default function Login(prop) { -const {token,setToken} = prop +const {setToken,profile,setProfile} = prop function login() { window.location.href="https://lp.ardapektezol.com/api/v1/login" } @@ -16,26 +16,16 @@ function logout() { setIsLoggedIn(false) setProfile(null) setToken(null) - fetch(`/api/v1/token`,{'method':'DELETE'}) + fetch(`https://lp.ardapektezol.com/api/v1/token`,{'method':'DELETE'}) .then(r=>window.location.href="/") } const [isLoggedIn, setIsLoggedIn] = React.useState(false); React.useEffect(() => { - fetch(`/api/v1/token`) + fetch(`https://lp.ardapektezol.com/api/v1/token`) .then(r => r.json()) .then(d => setToken(d.data.token)) }, []); -const [profile, setProfile] = React.useState(null); -React.useEffect(() => { - fetch(`/api/v1/profile`,{ - headers: { - 'Content-Type': 'application/json', - Authorization: token - }}) - .then(r => r.json()) - .then(d => setProfile(d.data)) - }, [token]); React.useEffect(() => { if(profile!==null){setIsLoggedIn(true)} diff --git a/frontend/src/components/pages/profile.css b/frontend/src/components/pages/profile.css new file mode 100644 index 0000000..677fa9b --- /dev/null +++ b/frontend/src/components/pages/profile.css @@ -0,0 +1,238 @@ +#section1.profile{ + margin: 20px; + background: linear-gradient(0deg, #202232 50%, #2b2e46 50%); + border-radius: 24px; + height: 200px; + + display: grid; + grid-template-columns: 250px 1fr; + +} +#section1.profile>div:first-child{ + overflow: hidden; + border-radius: 100%; + display: grid; + + place-items: center; + + margin: 8px 33px 8px 33px; + scale: 0.9; + grid-row: 1 / 3; + + +} +#profile-image>img{ + border-radius: 100%; + transition: filter 0.3s; + cursor: pointer; +} + +#profile-image>span{ + z-index: 1; + position: absolute; + opacity: 0; + color:white; + transition: opacity 0.3s; + cursor: pointer; +} + +#profile-image:hover > img{filter: blur(5px) brightness(60%);z-index: 1;} +#profile-image:hover > span{opacity: 1;} + +#profile-top{ + height: 100px; + display: grid; + grid-template-columns: 80% 20%; +} +#profile-top>div:nth-child(1)>div>img{ + margin: 12px; + border-radius: 10px; +} + +#profile-top>div:nth-child(1){ + display: flex; + place-items: center; + font-size: 50px; + font-weight: bold; + color: white; +} +#profile-top>div:nth-child(1)>div{ + display: flex; + height: 60px; +} +span.titles{ + margin: 12px 12px 12px 0; + + font-size: 18px; + font-weight: 100; + + padding: 6px 20px 0px 20px; + border-radius: 10px; +} + +#profile-top>div:nth-child(2){ + display: flex; + flex-direction: row-reverse; + align-items: center; + padding-right: 10px; +} +#profile-top>div:nth-child(2)>a>img{ + height: 50px; + padding: 0 5px 0 5px; + scale: 0.9; + filter: brightness(200%); + +} + + +#profile-bottom{ + height: 100px; + display: grid; + grid-template-columns: 1fr 1fr 1fr; +} +#profile-bottom>div{ + margin: 12px; + background-color: #2b2e46; + border-radius: 20px; + display: grid; + place-items: center; + grid-template-rows: 40% 50%; +} +#profile-bottom>div>span:nth-child(1){ + color: inherit; + font-size: 18px; +} +#profile-bottom>div>span:nth-child(2){ + color: white; + font-size: 40px; +} +#profile-bottom>div>span:nth-child(2)>span{ + color: white; + font-size: 20px; +} +/* #section1.profile>div>div{outline: red 1px dashed;} */ + + +#section2.profile{ + margin: 20px; + height: 60px; + display: grid; + grid-template-columns: 1fr 1fr; +} +#section2.profile>button{ + display: flex; + justify-content: center; + align-items: center; + + background-color: #2b2e46; + border: 0; + color: inherit; + font-family: inherit; + font-size: 24px; + cursor: pointer; + + transition: background-color .1s; +} +#section2.profile>button:nth-child(1){border-radius: 24px 0 0 24px;} +#section2.profile>button:nth-child(2){border-radius: 0 24px 24px 0;} + +#section3.profile1>hr{border: 1px solid #2b2e46;margin: 8px 20px 0px 20px;} +#section3.profile1{ + margin: 20px; + display: block; + + background-color: #202232; + border-radius: 24px; +} + +#profileboard-nav{ + display: grid; + grid-template-columns: 1fr 1fr; +} + +#profileboard-nav>select{ + + /* appearance: none; */ + margin: 10px 20px 20px 20px; + height: 50px; + + border-radius: 24px; + text-align: center; + + color: inherit; + font-family: inherit; + font-size: 24px; + border: 0; + + background-color: #2b2e46; +} + + +#profileboard-top>span>img{height: 20px;scale: .8;} +#profileboard-top>span>img,#profileboard-top>span>span{cursor: pointer;} +#profileboard-top{ + height: 34px; + display: grid; + font-size: 20px; + padding-left: 60px; + margin: 0 20px; + grid-template-columns: 15% 15% 5% 15% 5% 15% 15% 15%; +} + +#profileboard-top>span{ + display: flex; + place-items: flex-end; +} + +#profileboard-records{ + padding-bottom: 10px; +} + +.profileboard-record{ + width: calc(100% - 40px); + margin: 10px 20px 0px 20px; + height: 44px; + + border-radius: 20px; + padding: 0 0 0 60px; + font-size: 20px; + + color: inherit; + font-family: inherit; + border: 0; + transition: background-color .1s; + background-color: #2b2e46; + display: grid; + grid-template-columns: 15% 15% 5% 15% 5% 15% 15% 15%; + overflow: hidden; + + transition: height .2s +} + +/* this right here should be illegal */ +.profileboard-record>span:nth-child(-n+8){filter: brightness(100%);} +.profileboard-record>span{ + display: flex; + place-items: flex-end; + filter: brightness(65%); +} + +.profileboard-record>hr{ + margin: 0 0 0 -60px; + border: 0; + height: 2px; + background-color: #202232; +} + +.profileboard-record>span:nth-child(4){display: grid;} +.profileboard-record>span{ + + display: flex; + place-items: center; + height: 44px; +} +.profileboard-record>span>button{ + background-color: #0000; + border: 0; + cursor: pointer; +} diff --git a/frontend/src/components/pages/profile.js b/frontend/src/components/pages/profile.js new file mode 100644 index 0000000..aa28546 --- /dev/null +++ b/frontend/src/components/pages/profile.js @@ -0,0 +1,382 @@ +import React from 'react'; +import { useLocation } from "react-router-dom"; + +import img4 from "../../imgs/4.png" +import img5 from "../../imgs/5.png" +import img12 from "../../imgs/12.png" +import img13 from "../../imgs/13.png" +import img14 from "../../imgs/14.png" +import img15 from "../../imgs/15.png" +import img16 from "../../imgs/16.png" +import img17 from "../../imgs/17.png" +import img18 from "../../imgs/18.png" +import img19 from "../../imgs/19.png" +import "./profile.css"; + +export default function Profile(props) { +const {token} = props + + +const location = useLocation() + + +const [profileData, setProfileData] = React.useState(null) +React.useEffect(()=>{ + setProfileData(null) + setChapterData(null) + setMaps(null) + setPageNumber(1) + + if(location.pathname==="/profile"){ + fetch(`https://lp.ardapektezol.com/api/v1/${location.pathname}`,{ + headers: { + 'Authorization': token + }}) + .then(r=>r.json()) + .then(d=>{ + setProfileData(d.data) + setPageMax(Math.ceil(d.data.records.length/20)) + }) + }else{ + fetch(`https://lp.ardapektezol.com/api/v1/${location.pathname}`) + .then(r=>r.json()) + .then(d=>{ + setProfileData(d.data) + setPageMax(Math.ceil(d.data.records.length/20)) + }) + } +},[location.pathname]) + + + +const [game,setGame] = React.useState(0) +const [gameData,setGameData] = React.useState(null) +const [chapter,setChapter] = React.useState("0") +const [chapterData,setChapterData] = React.useState(null) +const [maps,setMaps] = React.useState(null) + +React.useEffect(()=>{ + fetch("https://lp.ardapektezol.com/api/v1/games") + .then(r=>r.json()) + .then(d=>{ + setGameData(d.data) + setGame(0) + }) + +},[location]) + +React.useEffect(()=>{ + if(game!==null && game!= 0){ + fetch(`https://lp.ardapektezol.com/api/v1/games/${game}`) + .then(r=>r.json()) + .then(d=>{ + setChapterData(d.data) + setChapter("0") + document.querySelector('#select-chapter').value=0 + }) + + } else if (game!==null && game==0 && profileData!== null){ + setPageMax(Math.ceil(profileData.records.length/20)) + setPageNumber(1) + } + +},[game,location]) + +React.useEffect(()=>{ + if(chapter!==null){ + if(chapter==0){ + setMaps(null) + fetch(`https://lp.ardapektezol.com/api/v1/games/${game}/maps`) + .then(r=>r.json()) + .then(d=>{ + setMaps(d.data.maps); + setPageMax(Math.ceil(d.data.maps.length/20)) + setPageNumber(1) + }) + }else{ + setMaps(null) + fetch(`https://lp.ardapektezol.com/api/v1/chapters/${chapter}`) + .then(r=>r.json()) + .then(d=>{ + setMaps(d.data.maps); + setPageMax(Math.ceil(d.data.maps.length/20)) + setPageNumber(1) + }) + + } + } +},[chapter,chapterData]) + + + +const [pageNumber, setPageNumber] = React.useState(1); +const [pageMax, setPageMax] = React.useState(0); +const [navState, setNavState] = React.useState(0); // eslint-disable-next-line +React.useEffect(() => {NavClick();}, [[],navState]); +function NavClick() { + if(profileData!==null){ + const btn = document.querySelectorAll("#section2 button"); + btn.forEach((e) => {e.style.backgroundColor = "#2b2e46"}); + btn[navState].style.backgroundColor = "#202232"; + + document.querySelectorAll("section").forEach((e,i)=>i>=2?e.style.display="none":"") + if(navState === 0){document.querySelectorAll(".profile1").forEach((e) => {e.style.display = "block"});} + if(navState === 1){document.querySelectorAll(".profile2").forEach((e) => {e.style.display = "block"});} +} +} +function UpdateProfile(){ + fetch(`/api/v1/profile`,{ + method: 'POST', + headers: {Authorization: token} + }).then(r=>window.location.reload()) +} + +function TimeAgo(date) { + const seconds = Math.floor((new Date() - date) / 1000); + + let interval = Math.floor(seconds / 31536000); + if (interval > 1) {return interval + ' years ago';} + + interval = Math.floor(seconds / 2592000); + if (interval > 1) {return interval + ' months ago';} + + interval = Math.floor(seconds / 86400); + if (interval > 1) {return interval + ' days ago';} + + interval = Math.floor(seconds / 3600); + if (interval > 1) {return interval + ' hours ago';} + + interval = Math.floor(seconds / 60); + if (interval > 1) {return interval + ' minutes ago';} + + if(seconds < 10) return 'just now'; + + return Math.floor(seconds) + ' seconds ago'; + }; + +function TicksToTime(ticks) { + + let seconds = Math.floor(ticks/60) + let minutes = Math.floor(seconds/60) + let hours = Math.floor(minutes/60) + + let milliseconds = Math.floor((ticks%60)*1000/60) + seconds = seconds % 60; + minutes = minutes % 60; + + 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})`; +} + + +if(profileData!==null){ +return ( +
+
+ + {profileData.profile?( +
UpdateProfile()}> + + Refresh +
+ ):( +
+ +
+ )} + +
+
+
{profileData.user_name}
+
+ {profileData.country_code==="XX"?"":{profileData.country_code}} +
+
+ {profileData.titles.map(e=>( + + {e.name} + + ))} +
+
+
+ {profileData.links.p2sr==="-"?"":Steam} + {profileData.links.p2sr==="-"?"":Twitch} + {profileData.links.p2sr==="-"?"":Youtube} + {profileData.links.p2sr==="-"?"":P2SR} +
+ +
+
+
+ Overall + {profileData.rankings.overall.rank===0?"N/A ":"#"+profileData.rankings.overall.rank+" "} + ({profileData.rankings.overall.completion_count}/{profileData.rankings.overall.completion_total}) + +
+
+ Singleplayer + {profileData.rankings.singleplayer.rank===0?"N/A ":"#"+profileData.rankings.singleplayer.rank+" "} + ({profileData.rankings.singleplayer.completion_count}/{profileData.rankings.singleplayer.completion_total}) + +
+
+ Cooperative + {profileData.rankings.cooperative.rank===0?"N/A":"#"+profileData.rankings.cooperative.rank+" "} + ({profileData.rankings.cooperative.completion_count}/{profileData.rankings.cooperative.completion_total}) + +
+
+
+ + +
+ + +
+ + + + + +
+
+ {gameData===null?: + + + } + + {game==0? + + :chapterData===null?: + + + } +
+
+ Map Name + Portals + WRΔ + Time + + Rank + Date +
+
+ + {pageNumber}/{pageMax} + +
+
+
+
+
+ + {console.log(profileData)} + {game == 0 && profileData !== null + ? ( + + profileData.records.sort((a,b)=>a.map_id - b.map_id) + .map((r, index) => ( + + Math.ceil((index+1)/20)===pageNumber ? ( + + + {i===0&&r.scores.length>1?:""} + + + ))} + + + ) : "" + ))) : maps !== null ? + + maps.filter(e=>e.is_disabled===false).sort((a,b)=>a.id - b.id) + .map((r, index) => { + if(Math.ceil((index+1)/20)===pageNumber){ + let record = profileData.records.find((e) => e.map_id === r.id); + return record === undefined ? ( + + ) : ( + + + {i===0&&record.scores.length>1?:""} + + + ))} + + + ) + }else{return null} + }):(<>{console.warn(maps)})} +
+
+ +
+)} +} + + diff --git a/frontend/src/components/pages/summary.css b/frontend/src/components/pages/summary.css index 51f8515..47c3f4b 100644 --- a/frontend/src/components/pages/summary.css +++ b/frontend/src/components/pages/summary.css @@ -463,11 +463,11 @@ text-align: center; /* such responsive, very mobile */ @media screen and (max-width: 1480px) { - #section3{grid-template-columns: auto;} + #section3.summary1{grid-template-columns: auto;} #category{min-width: 608px;} #history{min-width: 608px;} #description{min-width: 608px;} - #section4{min-width: 588px;} + #section4.summary1{min-width: 588px;} #description>iframe{ padding: 0 0 0 calc(50% - 304px); @@ -476,19 +476,19 @@ text-align: center; align-items: center; } - #section1{ + #section1.summary1{ grid-template-columns: auto; place-items: center; text-align: center; } - #section2{ + #section2.summary1{ grid-template-columns: auto; width: 450px; margin: 40px auto 0 auto; } - #section2>.nav-button:nth-child(1){border-radius: 30px 30px 0 0;} - #section2>.nav-button:nth-child(2){border-radius: 0;} - #section2>.nav-button:nth-child(3){border-radius: 0 0 30px 30px;} + #section2.summary1>.nav-button:nth-child(1){border-radius: 30px 30px 0 0;} + #section2.summary1>.nav-button:nth-child(2){border-radius: 0;} + #section2.summary1>.nav-button:nth-child(3){border-radius: 0 0 30px 30px;} } \ No newline at end of file diff --git a/frontend/src/components/pages/summary.js b/frontend/src/components/pages/summary.js index c03d7b9..7c72c48 100644 --- a/frontend/src/components/pages/summary.js +++ b/frontend/src/components/pages/summary.js @@ -20,6 +20,7 @@ const fakedata={} //for debug //fetching data const [data, setData] = React.useState(null); React.useEffect(() => { + setData(null) fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/summary`) .then(r => r.json()) .then(d => { @@ -37,7 +38,7 @@ const fakedata={} //for debug .then(r => r.json()) .then(d => setLbData(d)) // eslint-disable-next-line - }, [pageNumber]); + }, [pageNumber,location.pathname]); @@ -247,7 +248,7 @@ return (
-
+
@@ -257,7 +258,7 @@ return (
-
+
diff --git a/frontend/src/components/sidebar.js b/frontend/src/components/sidebar.js index d9b8012..e587c3c 100644 --- a/frontend/src/components/sidebar.js +++ b/frontend/src/components/sidebar.js @@ -17,6 +17,17 @@ import Login from "./login.js" export default function Sidebar(prop) { const {token,setToken} = prop +const [profile, setProfile] = React.useState(null); +React.useEffect(() => { + fetch(`https://lp.ardapektezol.com/api/v1/profile`,{ + headers: { + 'Content-Type': 'application/json', + Authorization: token + }}) + .then(r => r.json()) + .then(d => setProfile(d.data)) + }, [token]); + // Locks search button for 300ms before it can be clicked again, prevents spam const [isLocked, setIsLocked] = React.useState(false); @@ -147,7 +158,7 @@ return ( diff --git a/frontend/src/imgs/14.png b/frontend/src/imgs/14.png new file mode 100644 index 0000000..7be6359 Binary files /dev/null and b/frontend/src/imgs/14.png differ diff --git a/frontend/src/imgs/15.png b/frontend/src/imgs/15.png new file mode 100644 index 0000000..e5ae8aa Binary files /dev/null and b/frontend/src/imgs/15.png differ diff --git a/frontend/src/imgs/16.png b/frontend/src/imgs/16.png new file mode 100644 index 0000000..bf3ae0c Binary files /dev/null and b/frontend/src/imgs/16.png differ diff --git a/frontend/src/imgs/17.png b/frontend/src/imgs/17.png new file mode 100644 index 0000000..85e39f0 Binary files /dev/null and b/frontend/src/imgs/17.png differ diff --git a/frontend/src/imgs/18.png b/frontend/src/imgs/18.png new file mode 100644 index 0000000..048cda9 Binary files /dev/null and b/frontend/src/imgs/18.png differ diff --git a/frontend/src/imgs/19.png b/frontend/src/imgs/19.png new file mode 100644 index 0000000..0d97d16 Binary files /dev/null and b/frontend/src/imgs/19.png differ -- cgit v1.2.3