From a7c282ca348c1e8e60559e5c064caee28ba11eec Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:34:12 +0300 Subject: refactor: so much shit --- frontend/src/App.tsx | 46 +- frontend/src/api/Api.tsx | 205 +-- frontend/src/api/Auth.tsx | 14 + frontend/src/api/Games.tsx | 31 + frontend/src/api/Maps.tsx | 76 ++ frontend/src/api/Mod.tsx | 58 + frontend/src/api/Rankings.tsx | 13 + frontend/src/api/User.tsx | 25 + frontend/src/components/Discussions.tsx | 45 +- frontend/src/components/Leaderboards.tsx | 6 +- frontend/src/components/Login.tsx | 1923 +-------------------------- frontend/src/components/ModMenu.tsx | 44 +- frontend/src/components/RankingEntry.tsx | 2 +- frontend/src/components/Sidebar.tsx | 2 +- frontend/src/components/UploadRunDialog.tsx | 6 +- frontend/src/css/Profile.css | 6 + frontend/src/pages/Homepage.tsx | 2 +- frontend/src/pages/Maplist.tsx | 5 +- frontend/src/pages/Maps.tsx | 14 +- frontend/src/pages/Profile.tsx | 131 +- frontend/src/pages/Rankings.tsx | 65 +- frontend/src/pages/Rules.tsx | 4 +- frontend/src/pages/User.tsx | 146 +- frontend/src/utils/Jwt.tsx | 44 + 24 files changed, 597 insertions(+), 2316 deletions(-) create mode 100644 frontend/src/api/Auth.tsx create mode 100644 frontend/src/api/Games.tsx create mode 100644 frontend/src/api/Maps.tsx create mode 100644 frontend/src/api/Mod.tsx create mode 100644 frontend/src/api/Rankings.tsx create mode 100644 frontend/src/api/User.tsx create mode 100644 frontend/src/utils/Jwt.tsx (limited to 'frontend') diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 3980e1b..3a7fa18 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Routes, Route } from "react-router-dom"; +import { Routes, Route, useLocation } from "react-router-dom"; import { UserProfile } from './types/Profile'; import Sidebar from './components/Sidebar'; @@ -17,29 +17,53 @@ import { Game } from './types/Game'; import { API } from './api/Api'; import Maplist from './pages/Maplist'; import Rankings from './pages/Rankings'; +import { get_user_id_from_token, get_user_mod_from_token } from './utils/Jwt'; const App: React.FC = () => { const [token, setToken] = React.useState(undefined); const [profile, setProfile] = React.useState(undefined); - const [isModerator, setIsModerator] = React.useState(true); + const [isModerator, setIsModerator] = React.useState(false); const [games, setGames] = React.useState([]); const [uploadRunDialog, setUploadRunDialog] = React.useState(false); const [uploadRunDialogMapID, setUploadRunDialogMapID] = React.useState(undefined); - // React.useEffect(() => { - // if (token) { - // setIsModerator(JSON.parse(atob(token.split(".")[1])).mod) - // } - // }, [token]); + const _fetch_token = async () => { + const token = await API.get_token(); + setToken(token); + }; const _fetch_games = async () => { const games = await API.get_games(); setGames(games); }; + const _set_profile = async (user_id: string | undefined) => { + if (user_id) { + setProfile({} as UserProfile); // placeholder before we call actual user profile + const user = await API.get_profile(token!); + setProfile(user); + } + }; + + React.useEffect(() => { + if (token === undefined) { + setProfile(undefined); + setIsModerator(false); + } else { + _set_profile(get_user_id_from_token(token)) + const modStatus = get_user_mod_from_token(token) + if (modStatus) { + setIsModerator(true); + } else { + setIsModerator(false); + } + } + }, [token]); + React.useEffect(() => { + _fetch_token(); _fetch_games(); }, []); @@ -55,14 +79,14 @@ const App: React.FC = () => { setUploadRunDialog(true)} /> } /> - } /> - } /> + } /> + } /> } /> }> - {setUploadRunDialog(true);setUploadRunDialogMapID(mapID)}} />}/> + }/> } /> } /> - }> + }> diff --git a/frontend/src/api/Api.tsx b/frontend/src/api/Api.tsx index 30c0ad6..6731cb3 100644 --- a/frontend/src/api/Api.tsx +++ b/frontend/src/api/Api.tsx @@ -1,197 +1,52 @@ -import axios from 'axios'; - -import { GameChapter, GamesChapters } from '../types/Chapters'; -import { Game } from '../types/Game'; -import { Map, MapDiscussion, MapDiscussions, MapLeaderboard, MapSummary } from '../types/Map'; import { MapDiscussionCommentContent, MapDiscussionContent, ModMenuContent } from '../types/Content'; -import { Search } from '../types/Search'; -import { UserProfile } from '../types/Profile'; -import { Ranking } from '../types/Ranking'; +import { delete_token, get_token } from './Auth'; +import { get_user, get_profile, post_profile } from './User'; +import { get_games, get_chapters, get_games_chapters, get_game_maps, get_search } from './Games'; +import { get_official_rankings, get_unofficial_rankings } from './Rankings'; +import { get_map_summary, get_map_leaderboard, get_map_discussions, get_map_discussion, post_map_discussion, post_map_discussion_comment, delete_map_discussion } from './Maps'; +import { delete_map_summary, post_map_summary, put_map_image, put_map_summary } from './Mod'; // add new api call function entries here // example usage: API.get_games(); export const API = { - user_logout: () => user_logout(), - + // Auth + get_token: () => get_token(), + + delete_token: () => delete_token(), + // User get_user: (user_id: string) => get_user(user_id), + get_profile: (token: string) => get_profile(token), + post_profile: (token: string) => post_profile(token), + // Games get_games: () => get_games(), - get_chapters: (chapter_id: string) => get_chapters(chapter_id), get_games_chapters: (game_id: string) => get_games_chapters(game_id), get_game_maps: (game_id: string) => get_game_maps(game_id), - get_rankings: () => get_rankings(), get_search: (q: string) => get_search(q), - + // Rankings + get_official_rankings: () => get_official_rankings(), + get_unofficial_rankings: () => get_unofficial_rankings(), + // Maps get_map_summary: (map_id: string) => get_map_summary(map_id), get_map_leaderboard: (map_id: string) => get_map_leaderboard(map_id), get_map_discussions: (map_id: string) => get_map_discussions(map_id), get_map_discussion: (map_id: string, discussion_id: number) => get_map_discussion(map_id, discussion_id), - post_map_summary: (map_id: string, content: ModMenuContent) => post_map_summary(map_id, content), - post_map_discussion: (map_id: string, content: MapDiscussionContent) => post_map_discussion(map_id, content), - post_map_discussion_comment: (map_id: string, discussion_id: number, content: MapDiscussionCommentContent) => post_map_discussion_comment(map_id, discussion_id, content), - - put_map_image: (map_id: string, image: string) => put_map_image(map_id, image), - put_map_summary: (map_id: string, content: ModMenuContent) => put_map_summary(map_id, content), + post_map_discussion: (token: string, map_id: string, content: MapDiscussionContent) => post_map_discussion(token, map_id, content), + post_map_discussion_comment: (token: string, map_id: string, discussion_id: number, comment: string) => post_map_discussion_comment(token, map_id, discussion_id, comment), - delete_map_summary: (map_id: string, route_id: number) => delete_map_summary(map_id, route_id), - delete_map_discussion: (map_id: string, discussion_id: number) => delete_map_discussion(map_id, discussion_id), + delete_map_discussion: (token: string, map_id: string, discussion_id: number) => delete_map_discussion(token, map_id, discussion_id), + // Mod + post_map_summary: (token: string, map_id: string, content: ModMenuContent) => post_map_summary(token, map_id, content), + + put_map_image: (token: string, map_id: string, image: string) => put_map_image(token, map_id, image), + put_map_summary: (token: string, map_id: string, content: ModMenuContent) => put_map_summary(token, map_id, content), + + delete_map_summary: (token: string, map_id: string, route_id: number) => delete_map_summary(token, map_id, route_id), }; -const BASE_API_URL: string = "https://lp.ardapektezol.com/api/v1/" +const BASE_API_URL: string = "/api/v1/" -function url(path: string): string { +export function url(path: string): string { return BASE_API_URL + path; -} - -// USER - -const user_logout = async () => { - await axios.delete(url("token")); -}; - -const get_user = async (user_id: string): Promise => { - const response = await axios.get(url(`users/${user_id}`)) - return response.data.data; -}; - - -// GAMES - -const get_games = async (): Promise => { - const response = await axios.get(url("games")) - return response.data.data; -}; - -const get_chapters = async (chapter_id: string): Promise => { - const response = await axios.get(url(`chapters/${chapter_id}`)); - return response.data.data; -} - -const get_games_chapters = async (game_id: string): Promise => { - const response = await axios.get(url(`games/${game_id}`)); - return response.data.data; -}; - -const get_game_maps = async (game_id: string): Promise => { - const response = await axios.get(url(`games/${game_id}/maps`)) - return response.data.data.maps; -}; - - -// RANKINGS -const get_rankings = async (): Promise => { - const response = await axios.get(url(`rankings`)); - return response.data.data; -} - -// SEARCH - -const get_search = async (q: string): Promise => { - const response = await axios.get(url(`search?q=${q}`)) - return response.data.data; -}; - -// MAP SUMMARY - -const put_map_image = async (map_id: string, image: string): Promise => { - const response = await axios.put(url(`maps/${map_id}/image`), { - "image": image, - }); - return response.data.success; -}; - -const get_map_summary = async (map_id: string): Promise => { - const response = await axios.get(url(`maps/${map_id}/summary`)) - return response.data.data; -}; - -const post_map_summary = async (map_id: string, content: ModMenuContent): Promise => { - const response = await axios.post(url(`maps/${map_id}/summary`), { - "user_name": content.name, - "score_count": content.score, - "record_date": content.date, - "showcase": content.showcase, - "description": content.description, - }); - return response.data.success; -}; - -const put_map_summary = async (map_id: string, content: ModMenuContent): Promise => { - const response = await axios.put(url(`maps/${map_id}/summary`), { - "route_id": content.id, - "user_name": content.name, - "score_count": content.score, - "record_date": content.date, - "showcase": content.showcase, - "description": content.description, - }); - return response.data.success; -}; - -const delete_map_summary = async (map_id: string, route_id: number): Promise => { - const response = await axios.delete(url(`maps/${map_id}/summary`), { - data: { - "route_id": route_id, - } - }); - return response.data.success; -}; - -// MAP LEADERBOARDS - -const get_map_leaderboard = async (map_id: string): Promise => { - const response = await axios.get(url(`maps/${map_id}/leaderboards`)) - if (!response.data.success) { - return undefined; - } - const data = response.data.data; - // map the kind of leaderboard - data.records = data.records.map((record: any) => { - if (record.host && record.partner) { - return { ...record, kind: 'multiplayer' }; - } else { - return { ...record, kind: 'singleplayer' }; - } - }); - // should be unreachable - return undefined; -}; - -// MAP DISCUSSIONS - -const get_map_discussions = async (map_id: string): Promise => { - const response = await axios.get(url(`maps/${map_id}/discussions`)); - if (!response.data.data.discussions) { - return undefined; - } - return response.data.data; -}; - -const get_map_discussion = async (map_id: string, discussion_id: number): Promise => { - const response = await axios.get(url(`maps/${map_id}/discussions/${discussion_id}`)); - if (!response.data.data.discussion) { - return undefined; - } - return response.data.data; -}; - -const post_map_discussion = async (map_id: string, content: MapDiscussionContent): Promise => { - const response = await axios.post(url(`maps/${map_id}/discussions`), { - "title": content.title, - "content": content.content, - }); - return response.data.success; -}; - -const post_map_discussion_comment = async (map_id: string, discussion_id: number, content: MapDiscussionCommentContent): Promise => { - const response = await axios.post(url(`maps/${map_id}/discussions/${discussion_id}`), { - "comment": content.comment, - }); - return response.data.success; -}; - -const delete_map_discussion = async (map_id: string, discussion_id: number): Promise => { - const response = await axios.delete(url(`maps/${map_id}/discussions/${discussion_id}`)); - return response.data.success; }; diff --git a/frontend/src/api/Auth.tsx b/frontend/src/api/Auth.tsx new file mode 100644 index 0000000..09269e6 --- /dev/null +++ b/frontend/src/api/Auth.tsx @@ -0,0 +1,14 @@ +import axios from "axios"; +import { url } from "./Api"; + +export const get_token = async (): Promise => { + const response = await axios.get(url(`token`)) + if (!response.data.success) { + return undefined; + } + return response.data.data.token; +}; + +export const delete_token = async () => { + await axios.delete(url("token")); +}; diff --git a/frontend/src/api/Games.tsx b/frontend/src/api/Games.tsx new file mode 100644 index 0000000..84b5f74 --- /dev/null +++ b/frontend/src/api/Games.tsx @@ -0,0 +1,31 @@ +import axios from "axios"; +import { url } from "./Api"; +import { GameChapter, GamesChapters } from "../types/Chapters"; +import { Game } from "../types/Game"; +import { Map } from "../types/Map"; +import { Search } from "../types/Search"; + +export const get_games = async (): Promise => { + const response = await axios.get(url(`games`)) + return response.data.data; +}; + +export const get_chapters = async (chapter_id: string): Promise => { + const response = await axios.get(url(`chapters/${chapter_id}`)); + return response.data.data; +} + +export const get_games_chapters = async (game_id: string): Promise => { + const response = await axios.get(url(`games/${game_id}`)); + return response.data.data; +}; + +export const get_game_maps = async (game_id: string): Promise => { + const response = await axios.get(url(`games/${game_id}/maps`)) + return response.data.data.maps; +}; + +export const get_search = async (q: string): Promise => { + const response = await axios.get(url(`search?q=${q}`)) + return response.data.data; +}; diff --git a/frontend/src/api/Maps.tsx b/frontend/src/api/Maps.tsx new file mode 100644 index 0000000..fbad78c --- /dev/null +++ b/frontend/src/api/Maps.tsx @@ -0,0 +1,76 @@ +import axios from "axios"; +import { url } from "./Api"; +import { MapDiscussionContent } from "../types/Content"; +import { MapSummary, MapLeaderboard, MapDiscussions, MapDiscussion } from "../types/Map"; + +export const get_map_summary = async (map_id: string): Promise => { + const response = await axios.get(url(`maps/${map_id}/summary`)) + return response.data.data; +}; + +export const get_map_leaderboard = async (map_id: string): Promise => { + const response = await axios.get(url(`maps/${map_id}/leaderboards`)); + console.log(response) + if (!response.data.success) { + return undefined; + } + const data = response.data.data; + console.log(data.records) + // map the kind of leaderboard + data.records = data.records.map((record: any) => { + if (record.host && record.partner) { + return { ...record, kind: 'multiplayer' }; + } else { + return { ...record, kind: 'singleplayer' }; + } + }); + return data; +}; + +export const get_map_discussions = async (map_id: string): Promise => { + const response = await axios.get(url(`maps/${map_id}/discussions`)); + if (!response.data.data.discussions) { + return undefined; + } + return response.data.data; +}; + +export const get_map_discussion = async (map_id: string, discussion_id: number): Promise => { + const response = await axios.get(url(`maps/${map_id}/discussions/${discussion_id}`)); + if (!response.data.data.discussion) { + return undefined; + } + return response.data.data; +}; + +export const post_map_discussion = async (token: string, map_id: string, content: MapDiscussionContent): Promise => { + const response = await axios.post(url(`maps/${map_id}/discussions`), { + "title": content.title, + "content": content.content, + }, { + headers: { + "Authorization": token, + } + }); + return response.data.success; +}; + +export const post_map_discussion_comment = async (token: string, map_id: string, discussion_id: number, comment: string): Promise => { + const response = await axios.post(url(`maps/${map_id}/discussions/${discussion_id}`), { + "comment": comment, + }, { + headers: { + "Authorization": token, + } + }); + return response.data.success; +}; + +export const delete_map_discussion = async (token: string, map_id: string, discussion_id: number): Promise => { + const response = await axios.delete(url(`maps/${map_id}/discussions/${discussion_id}`), { + headers: { + "Authorization": token, + } + }); + return response.data.success; +}; diff --git a/frontend/src/api/Mod.tsx b/frontend/src/api/Mod.tsx new file mode 100644 index 0000000..9091379 --- /dev/null +++ b/frontend/src/api/Mod.tsx @@ -0,0 +1,58 @@ +import axios from "axios"; +import { url } from "./Api"; +import { ModMenuContent } from "../types/Content"; + +export const put_map_image = async (token: string, map_id: string, image: string): Promise => { + const response = await axios.put(url(`maps/${map_id}/image`), { + "image": image, + }, { + headers: { + "Authorization": token, + } + }); + return response.data.success; +}; + +export const post_map_summary = async (token: string, map_id: string, content: ModMenuContent): Promise => { + const response = await axios.post(url(`maps/${map_id}/summary`), { + "category_id": content.category_id, + "user_name": content.name, + "score_count": content.score, + "record_date": content.date, + "showcase": content.showcase, + "description": content.description, + }, { + headers: { + "Authorization": token, + } + }); + return response.data.success; +}; + +export const put_map_summary = async (token: string, map_id: string, content: ModMenuContent): Promise => { + const response = await axios.put(url(`maps/${map_id}/summary`), { + "route_id": content.id, + "user_name": content.name, + "score_count": content.score, + "record_date": content.date, + "showcase": content.showcase, + "description": content.description, + }, { + headers: { + "Authorization": token, + } + }); + return response.data.success; +}; + +export const delete_map_summary = async (token: string, map_id: string, route_id: number): Promise => { + const response = await axios.delete(url(`maps/${map_id}/summary`), { + data: { + "route_id": route_id, + }, + headers: { + "Authorization": token, + } + }); + return response.data.success; +}; diff --git a/frontend/src/api/Rankings.tsx b/frontend/src/api/Rankings.tsx new file mode 100644 index 0000000..384f826 --- /dev/null +++ b/frontend/src/api/Rankings.tsx @@ -0,0 +1,13 @@ +import axios from "axios"; +import { url } from "./Api"; +import { Ranking, SteamRanking } from "../types/Ranking"; + +export const get_official_rankings = async (): Promise => { + const response = await axios.get(url(`rankings/lphub`)); + return response.data.data; +}; + +export const get_unofficial_rankings = async (): Promise => { + const response = await axios.get(url(`rankings/steam`)); + return response.data.data; +}; diff --git a/frontend/src/api/User.tsx b/frontend/src/api/User.tsx new file mode 100644 index 0000000..c4d1944 --- /dev/null +++ b/frontend/src/api/User.tsx @@ -0,0 +1,25 @@ +import axios from "axios"; +import { url } from "./Api"; +import { UserProfile } from "../types/Profile"; + +export const get_user = async (user_id: string): Promise => { + const response = await axios.get(url(`users/${user_id}`)); + return response.data.data; +}; + +export const get_profile = async (token: string): Promise => { + const response = await axios.get(url(`profile`), { + headers: { + "Authorization": token, + } + }); + return response.data.data; +}; + +export const post_profile = async (token: string) => { + const _ = await axios.post(url(`profile`), {}, { + headers: { + "Authorization": token, + } + }); +}; diff --git a/frontend/src/components/Discussions.tsx b/frontend/src/components/Discussions.tsx index 787104b..c22de79 100644 --- a/frontend/src/components/Discussions.tsx +++ b/frontend/src/components/Discussions.tsx @@ -8,13 +8,14 @@ import "../css/Maps.css" import { Link } from 'react-router-dom'; interface DiscussionsProps { + token?: string data?: MapDiscussions; isModerator: boolean; mapID: string; onRefresh: () => void; } -const Discussions: React.FC = ({ data, isModerator, mapID, onRefresh }) => { +const Discussions: React.FC = ({ token, data, isModerator, mapID, onRefresh }) => { const [discussionThread, setDiscussionThread] = React.useState(undefined); const [discussionSearch, setDiscussionSearch] = React.useState(""); @@ -24,9 +25,7 @@ const Discussions: React.FC = ({ data, isModerator, mapID, onR title: "", content: "", }); - const [createDiscussionCommentContent, setCreateDiscussionCommentContent] = React.useState({ - comment: "", - }); + const [createDiscussionCommentContent, setCreateDiscussionCommentContent] = React.useState(""); const _open_map_discussion = async (discussion_id: number) => { const mapDiscussion = await API.get_map_discussion(mapID, discussion_id); @@ -34,20 +33,26 @@ const Discussions: React.FC = ({ data, isModerator, mapID, onR }; const _create_map_discussion = async () => { - await API.post_map_discussion(mapID, createDiscussionContent); - setCreateDiscussion(false); - onRefresh(); + if (token) { + await API.post_map_discussion(token, mapID, createDiscussionContent); + setCreateDiscussion(false); + onRefresh(); + } }; const _create_map_discussion_comment = async (discussion_id: number) => { - await API.post_map_discussion_comment(mapID, discussion_id, createDiscussionCommentContent); - await _open_map_discussion(discussion_id); + if (token) { + await API.post_map_discussion_comment(token, mapID, discussion_id, createDiscussionCommentContent); + await _open_map_discussion(discussion_id); + } }; const _delete_map_discussion = async (discussion: MapDiscussionsDetail) => { if (window.confirm(`Are you sure you want to remove post: ${discussion.title}?`)) { - await API.delete_map_discussion(mapID, discussion.id); - onRefresh(); + if (token) { + await API.delete_map_discussion(token, mapID, discussion.id); + onRefresh(); + } } }; @@ -69,9 +74,9 @@ const Discussions: React.FC = ({ data, isModerator, mapID, onR ...createDiscussionContent, title: e.target.value, })} /> - setCreateDiscussionContent({ + setCreateDiscussionContent({ ...createDiscussionContent, - title: e.target.value, + content: e.target.value, })} />
@@ -114,11 +119,15 @@ const Discussions: React.FC = ({ data, isModerator, mapID, onR }
- e.key === "Enter" && _create_map_discussion_comment(discussionThread.discussion.id)} onChange={(e) => setCreateDiscussionCommentContent({ - ...createDiscussionContent, - comment: e.target.value, - })} /> -
+ e.key === "Enter" && _create_map_discussion_comment(discussionThread.discussion.id)} + onChange={(e) => setCreateDiscussionCommentContent(e.target.value)} /> +
diff --git a/frontend/src/components/Leaderboards.tsx b/frontend/src/components/Leaderboards.tsx index 159f3ed..f4d666d 100644 --- a/frontend/src/components/Leaderboards.tsx +++ b/frontend/src/components/Leaderboards.tsx @@ -88,14 +88,14 @@ const Leaderboards: React.FC = ({ data }) => { {r.kind === "multiplayer" ? ( - - + + ) : r.kind === "singleplayer" && ( - + )} diff --git a/frontend/src/components/Login.tsx b/frontend/src/components/Login.tsx index 367dc72..a8c5503 100644 --- a/frontend/src/components/Login.tsx +++ b/frontend/src/components/Login.tsx @@ -7,22 +7,25 @@ import { API } from '../api/Api'; import "../css/Login.css"; interface LoginProps { - token?: string; setToken: React.Dispatch>; profile?: UserProfile; setProfile: React.Dispatch>; }; -const Login: React.FC = ({ token, setToken, profile, setProfile }) => { +const Login: React.FC = ({ setToken, profile, setProfile }) => { const navigate = useNavigate(); + const _login = () => { + window.location.href = "/api/v1/login"; + }; + const _logout = () => { setProfile(undefined); setToken(undefined); - API.user_logout(); + API.delete_token(); navigate("/"); - } + }; return ( <> @@ -30,1893 +33,41 @@ const Login: React.FC = ({ token, setToken, profile, setProfile }) = ? ( <> - - - - + {profile.profile ? + ( + <> + + + + + + ) + : + ( + <> + + + + + + ) + } ) : ( - : diff --git a/frontend/src/components/UploadRunDialog.tsx b/frontend/src/components/UploadRunDialog.tsx index e099a40..aa7ab5a 100644 --- a/frontend/src/components/UploadRunDialog.tsx +++ b/frontend/src/components/UploadRunDialog.tsx @@ -42,8 +42,10 @@ const UploadRunDialog: React.FC = ({ open, onClose, mapID, }; React.useEffect(() => { - _handle_game_select("1"); // a different approach? - }, []); + if (open) { + _handle_game_select("1"); // a different approach?. + } + }, [open]); if (open) { return ( diff --git a/frontend/src/css/Profile.css b/frontend/src/css/Profile.css index 4944ade..0829477 100644 --- a/frontend/src/css/Profile.css +++ b/frontend/src/css/Profile.css @@ -232,6 +232,12 @@ span.titles{ place-items: center; height: 44px; } +.profileboard-record>a>span{ + + display: flex; + place-items: center; + height: 44px; +} .profileboard-record>span>button{ background-color: #0000; border: 0; diff --git a/frontend/src/pages/Homepage.tsx b/frontend/src/pages/Homepage.tsx index 8e5bb4b..8c1cd48 100644 --- a/frontend/src/pages/Homepage.tsx +++ b/frontend/src/pages/Homepage.tsx @@ -7,7 +7,7 @@ const Homepage: React.FC = () => {

-

lphubWelcome to Least Portals Hub!

+

Welcome to Least Portals Hub!

At the moment, LPHUB is in beta state. This means that the site has only the core functionalities enabled for providing both collaborative information and competitive leaderboards.

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

By using LPHUB, you agree that you have read the 'Leaderboard Rules' and the 'About LPHUB' pages.

diff --git a/frontend/src/pages/Maplist.tsx b/frontend/src/pages/Maplist.tsx index 5d0c852..9526d18 100644 --- a/frontend/src/pages/Maplist.tsx +++ b/frontend/src/pages/Maplist.tsx @@ -64,6 +64,7 @@ const Maplist: React.FC = () => { // console.log(foundGame) if (foundGame) { setGame(foundGame); + setLoad(false); } }; @@ -73,9 +74,9 @@ const Maplist: React.FC = () => { setNumChapters(games_chapters.chapters.length); } + setLoad(true); _fetch_game(); _fetch_game_chapters(); - setLoad(true); }, []); useEffect(() => { @@ -96,7 +97,7 @@ const Maplist: React.FC = () => {
- {!load ? ( + {load ? (
) : (
diff --git a/frontend/src/pages/Maps.tsx b/frontend/src/pages/Maps.tsx index 62fc3cc..13ee4d9 100644 --- a/frontend/src/pages/Maps.tsx +++ b/frontend/src/pages/Maps.tsx @@ -7,18 +7,16 @@ import Leaderboards from '../components/Leaderboards'; import Discussions from '../components/Discussions'; import ModMenu from '../components/ModMenu'; import { MapDiscussions, MapLeaderboard, MapSummary } from '../types/Map'; -import { UserProfile } from '../types/Profile'; import { API } from '../api/Api'; import "../css/Maps.css"; import Loading from '../components/Loading'; interface MapProps { - profile?: UserProfile; + token?: string; isModerator: boolean; - onUploadRun: (mapID: number) => void; }; -const Maps: React.FC = ({ profile, isModerator, onUploadRun }) => { +const Maps: React.FC = ({ token, isModerator }) => { const [selectedRun, setSelectedRun] = React.useState(0); @@ -39,7 +37,8 @@ const Maps: React.FC = ({ profile, isModerator, onUploadRun }) => { const _fetch_map_leaderboards = async () => { const mapLeaderboards = await API.get_map_leaderboard(mapID); - console.log(mapLeaderboards?.records[0]); + console.log("lbs:") + console.log(mapLeaderboards); setMapLeaderboardData(mapLeaderboards); }; @@ -77,7 +76,7 @@ const Maps: React.FC = ({ profile, isModerator, onUploadRun }) => { return ( <> - {isModerator && } + {isModerator && }
@@ -88,7 +87,6 @@ const Maps: React.FC = ({ profile, isModerator, onUploadRun }) => {
{mapSummaryData.map.map_name} - {profile && }
@@ -100,7 +98,7 @@ const Maps: React.FC = ({ profile, isModerator, onUploadRun }) => { {navState === 0 && } {navState === 1 && } - {navState === 2 && _fetch_map_discussions()} />} + {navState === 2 && _fetch_map_discussions()} />}
); diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx index 8eec23c..e20d930 100644 --- a/frontend/src/pages/Profile.tsx +++ b/frontend/src/pages/Profile.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useLocation, useNavigate } from 'react-router-dom'; +import { Link, useLocation, useNavigate } from 'react-router-dom'; import { SteamIcon, TwitchIcon, YouTubeIcon, PortalIcon, FlagIcon, StatisticsIcon, SortIcon, ThreedotIcon, DownloadIcon, HistoryIcon } from '../images/Images'; import { UserProfile } from '../types/Profile'; @@ -7,104 +7,74 @@ import { Game, GameChapters } from '../types/Game'; import { Map } from '../types/Map'; import { ticks_to_time } from '../utils/Time'; import "../css/Profile.css"; +import { API } from '../api/Api'; interface ProfileProps { profile?: UserProfile; + token?: string; + gameData: Game[]; } -const Profile: React.FC = ({ profile }) => { - - - const location = useLocation(); - const navigate = useNavigate(); - - React.useEffect(() => { - if (!profile) { - navigate("/"); - }; - }, [profile]); +const Profile: React.FC = ({ profile, token, gameData }) => { const [navState, setNavState] = React.useState(0); const [pageNumber, setPageNumber] = React.useState(1); const [pageMax, setPageMax] = React.useState(0); const [game, setGame] = React.useState("0") - const [gameData, setGameData] = React.useState([]); const [chapter, setChapter] = React.useState("0") const [chapterData, setChapterData] = React.useState(null); const [maps, setMaps] = React.useState([]); - function NavClick() { - if (profile) { - const btn = document.querySelectorAll("#section2 button"); - btn.forEach((e) => { (e as HTMLElement).style.backgroundColor = "#2b2e46" }); - (btn[navState] as HTMLElement).style.backgroundColor = "#202232"; + const navigate = useNavigate(); - document.querySelectorAll("section").forEach((e, i) => i >= 2 ? e.style.display = "none" : "") - if (navState === 0) { document.querySelectorAll(".profile1").forEach((e) => { (e as HTMLElement).style.display = "block" }); } - if (navState === 1) { document.querySelectorAll(".profile2").forEach((e) => { (e as HTMLElement).style.display = "block" }); } + const _update_profile = () => { + if (token) { + API.post_profile(token).then(() => navigate(0)); } - } + }; - function UpdateProfile() { - fetch(`https://lp.ardapektezol.com/api/v1/profile`, { - method: 'POST', - headers: { Authorization: "" } - }).then(r => r.json()) - .then(d => d.success ? window.alert("profile updated") : window.alert(`Error: ${d.message}`)) - } + const _get_game_chapters = async () => { + if (game && game !== "0") { + const gameChapters = await API.get_games_chapters(game); + setChapterData(gameChapters); + } else if (game && game === "0") { + setPageMax(Math.ceil(profile!.records.length / 20)); + setPageNumber(1); + } + }; - React.useEffect(() => { - if (profile) { - fetch("https://lp.ardapektezol.com/api/v1/games") - .then(r => r.json()) - .then(d => { - setGameData(d.data) - setGame("0") - }) + const _get_game_maps = async () => { + if (chapter === "0") { + const gameMaps = await API.get_game_maps(game); + setMaps(gameMaps); + setPageMax(Math.ceil(gameMaps.length / 20)); + setPageNumber(1); + } else { + const gameChapters = await API.get_chapters(chapter); + setMaps(gameChapters.maps); + setPageMax(Math.ceil(gameChapters.maps.length / 20)); + setPageNumber(1); } - }, [profile, location]); + }; + + React.useEffect(() => { + if (!profile) { + navigate("/"); + }; + }, [profile]); React.useEffect(() => { if (profile) { - if (game && 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') as HTMLInputElement).value = "0" - }) - - } else if (game && game === "0") { - setPageMax(Math.ceil(profile.records.length / 20)) - setPageNumber(1) - } + _get_game_chapters(); } - }, [profile, game, location]); + }, [profile, game]); React.useEffect(() => { - if (game !== "0") { - if (chapter === "0") { - 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 { - 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) - }) - - } + if (profile && game !== "0") { + _get_game_maps(); } - }, [game, chapter, chapterData]) + }, [profile, game, chapter, chapterData]) if (!profile) { return ( @@ -118,7 +88,7 @@ const Profile: React.FC = ({ profile }) => { {profile.profile ? ( -
UpdateProfile()}> +
profile-image Refresh
@@ -187,7 +157,14 @@ const Profile: React.FC = ({ profile }) => { {gameData === null ? : error :