From 6f2ed9953f4270b3ddde746cc113b6eece20b501 Mon Sep 17 00:00:00 2001 From: Sri Harsha D V Date: Sun, 16 Jun 2024 10:55:49 +0530 Subject: [PATCH 1/3] Updating equation which defines when to fetch while scrolling --- src/hooks/useInfiniteScroll.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useInfiniteScroll.js b/src/hooks/useInfiniteScroll.js index 881b5b2..f5b58c1 100644 --- a/src/hooks/useInfiniteScroll.js +++ b/src/hooks/useInfiniteScroll.js @@ -4,7 +4,7 @@ const useInfiniteScroll = (callback) => { const [isFetching, setIsFetching] = useState(false); const handleScroll = () => { - if (!(window.innerHeight + window.pageYOffset >= document.body.scrollHeight) || isFetching) return; + if (!(Math.ceil(window.innerHeight + window.pageYOffset) >= document.body.scrollHeight) || isFetching) return; setIsFetching(true); } From a4bce34068f3e14ca230d21e20d9bfafae8eee5e Mon Sep 17 00:00:00 2001 From: Sri Harsha D V Date: Wed, 19 Jun 2024 19:49:03 +0530 Subject: [PATCH 2/3] Added files related to new MySubmissions component and updated few existing ones to support this new component. --- src/App.js | 2 + src/components/Card/MiniCard.component.jsx | 87 +++++++++ src/components/Card/ProfileCard.component.jsx | 87 +++++++++ .../LoginCard/LoginCard.component.js | 90 ++++++++++ src/pages/MySubmissions.js | 169 ++++++++++++++++++ src/utils/siteConfig.js | 7 +- src/utils/sorts.js | 9 + 7 files changed, 450 insertions(+), 1 deletion(-) create mode 100644 src/components/Card/MiniCard.component.jsx create mode 100644 src/components/Card/ProfileCard.component.jsx create mode 100644 src/components/LoginCard/LoginCard.component.js create mode 100644 src/pages/MySubmissions.js diff --git a/src/App.js b/src/App.js index aa95f98..1bd6713 100644 --- a/src/App.js +++ b/src/App.js @@ -16,6 +16,7 @@ import FourZeroFour from "./pages/FourZeroFour"; import Submit from "./pages/Submit"; import Leaderboard from "./pages/Leaderboard"; import Archive from "./pages/Archive"; +import MySubmissions from "./pages/MySubmissions"; import Nav from "./components/Navigation/Navigation.component"; @@ -67,6 +68,7 @@ function App({ location, location: { search }, history }) { + { diff --git a/src/components/Card/MiniCard.component.jsx b/src/components/Card/MiniCard.component.jsx new file mode 100644 index 0000000..7faada5 --- /dev/null +++ b/src/components/Card/MiniCard.component.jsx @@ -0,0 +1,87 @@ +import React from "react"; +import PropTypes from "prop-types"; +import moment from "moment"; +import makeStyles from "@material-ui/core/styles/makeStyles"; +import LangIcon from "../LangIcon/LangIcon.component"; +import { + Card, + CardContent, + Button, + CardHeader, + Typography +} from "@material-ui/core"; +import Divider from "@material-ui/core/Divider"; + +import UserRating from "../UserRating/UserRating.component"; + +const useStyles = makeStyles((theme) => ({ + root: { + width: theme.spacing(30), + height: theme.spacing(27), + margin: theme.spacing(1), + position: "relative" + }, + dayContainer: { + marginBottom: theme.spacing(1), + borderRadius: theme.shape.borderRadius, + padding: theme.spacing(0.5) + } +})); + +function MiniCard({ + username, + date, + day, + solutionUrl, + langName, + ratings, + ...props +}) { + const classes = useStyles(); + + return ( + + + + Day {day} + + + } + subheader={moment(date).format("MM/DD, hh:mm a")} + avatar={} + /> + + + + {}} + /> + + + ); +} + +MiniCard.propTypes = { + username: PropTypes.string.isRequired, + date: PropTypes.string.isRequired, + day: PropTypes.number.isRequired, + solutionUrl: PropTypes.string.isRequired, + ratings: PropTypes.number.isRequired +}; + +export default React.memo(MiniCard); diff --git a/src/components/Card/ProfileCard.component.jsx b/src/components/Card/ProfileCard.component.jsx new file mode 100644 index 0000000..df98201 --- /dev/null +++ b/src/components/Card/ProfileCard.component.jsx @@ -0,0 +1,87 @@ +import React from "react"; +import PropTypes from "prop-types"; +import makeStyles from "@material-ui/core/styles/makeStyles"; +import { + Card, + CardMedia, + CardContent, + CardHeader, + Typography +} from "@material-ui/core"; +import Divider from "@material-ui/core/Divider"; + +const useStyles = makeStyles((theme) => ({ + root: { + width: theme.spacing(32), + height: theme.spacing(56), + margin: theme.spacing(1), + position: "relative" + }, + cardMedia: { + minHeight: 240 + }, + cardContent: { + marginBottom: theme.spacing(1), + borderRadius: theme.shape.borderRadius, + padding: theme.spacing(0.5) + } +})); + +function ProfileCard({ + avatarUrl, + username, + submissionCount, + langCount, + avgRating, + ...props +}) { + const classes = useStyles(); + + const imageUrl = avatarUrl || `https://robohash.org/${username}`; + + return ( + + + +
+ { + e.target.src = `https://robohash.org/${username}`; + }} + image={imageUrl} + height={80} + component="img" + /> +
+ + +
+ + Total Submissions : {submissionCount} + +
+
+ + Languages used : {langCount} + +
+
+ + Average Rating : {avgRating} + +
+
+
+ ); +} + +ProfileCard.propTypes = { + username: PropTypes.string.isRequired, + avatarUrl: PropTypes.string, + submissionCount: PropTypes.number.isRequired, + langCount: PropTypes.number.isRequired, + avgRating: PropTypes.number.isRequired +}; + +export default React.memo(ProfileCard); diff --git a/src/components/LoginCard/LoginCard.component.js b/src/components/LoginCard/LoginCard.component.js new file mode 100644 index 0000000..396081c --- /dev/null +++ b/src/components/LoginCard/LoginCard.component.js @@ -0,0 +1,90 @@ +import { Link } from "react-router-dom"; + +import Button from "@material-ui/core/Button"; +import useMediaQuery from "@material-ui/core/useMediaQuery"; +import makeStyles from "@material-ui/core/styles/makeStyles"; +import useTheme from "@material-ui/core/styles/useTheme"; + +import Avatar from "../Avatar/Avatar.component"; + + +const useStyles = makeStyles(theme => ({ + root: { + width: "100%", + height: "100%", + display: "flex" + }, + form: ({ isMobile }) => ({ + backgroundColor: theme.palette.background.paper, + display: "flex", + flexDirection: "column", + ...theme.shape, + padding: theme.spacing(2, 4), + position: "relative", + margin: "auto", + minWidth: `${isMobile ? "auto" : "600px"}`, + width: `${isMobile ? "100%" : "auto"}` + }), + title: { + ...theme.typography.h6, + textAlign: "center", + marginBottom: theme.spacing(2) + }, + content: { + ...theme.typography.body1 + }, + closeIcon: { + color: theme.palette.error.main, + cursor: "pointer" + }, + infoContainer: { + minHeight: theme.spacing(12), + marginTop: theme.spacing(2), + display: "flex", + justifyContent: "center", + alignItems: "center", + flexDirection: "column", + "& :not(:first-child)": { + marginTop: theme.spacing(2) + } + } +})); + +const LoginCard = props => { + + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("xs")); + const classes = useStyles({ + isMobile, + classes: props.classes + }); + + return( +
+
+
+ Login +
+ +
+ +
+ +
+
+
+
+ ); +} + +export default LoginCard; \ No newline at end of file diff --git a/src/pages/MySubmissions.js b/src/pages/MySubmissions.js new file mode 100644 index 0000000..fdc8f28 --- /dev/null +++ b/src/pages/MySubmissions.js @@ -0,0 +1,169 @@ +import React,{ useState,useEffect,useRef } from 'react'; + +import { makeStyles } from "@material-ui/core/styles"; +import Divider from "@material-ui/core/Divider"; + +import { useUserContext } from "../contexts/user/user.context"; +import { sortByDay } from "../utils/sorts"; + +import MetaTags from "../components/MetaTags/MetaTags.component"; +import LoginCard from "../components/LoginCard/LoginCard.component"; +import LoadingCard from "../components/LoadingCard/LoadingCard.component"; +import MiniCard from "../components/Card/MiniCard.component"; +import ProfileCard from "../components/Card/ProfileCard.component"; +import NothingToSee from "../components/NothingToSee/NothingToSee.component"; + +const useStyles = makeStyles(theme => ({ + profileContainer: { + display: "flex", + justifyContent: "center" + }, + container: { + margin: theme.spacing(1, 2) + }, + yearHeading: { + margin: theme.spacing(0.25, 0.5), + fontSize: "20px" + }, + solutionsContainer: { + margin: theme.spacing(0.5, 1), + display: "flex", + [theme.breakpoints.down('xs')]: { + justifyContent: "center", + }, + flexWrap: "wrap", + justifyContent: "center", + alignItems: "center" + } +})); + +const MySubmissions = () => { + + const classes = useStyles(); + + + const userInfoRef = useRef(null); + const [{ user: userInfo }, userDispatch] = useUserContext(); + + const [dataFromApi,setDataFromApi] = useState({}); + const [isLoadingData,setIsLoadingData] = useState(true); + + const currentYearRef = useRef(new Date().getFullYear()); + const yearRef = useRef(2018); + userInfoRef.current = userInfo ? userInfo : null; + + const subCountRef = useRef(0); + const langRef = useRef({}); + const ratingRef = useRef([]); + + let title = "My Submissions | ZTM AoC"; + let description = + "My Submissions for Advent of Code."; + let pageUrl = "https://aoc.zerotomastery.io/my-submissions"; + + useEffect(() => { + if (userInfoRef.current) { + if (yearRef.current < currentYearRef.current-1) { + fetch(`https://aocbot.zerobot.app/archive/${yearRef.current}`) + .then(async (response) => { + let data = await response.json(); + data = yearRef.current === 2018 ? data.solutions : data; + data = data.filter(user => user.avatarUrl.includes(`avatars/${userInfoRef.current.id}/`)); + data = data.map(user => { + const updatedUser = {...user}; + updatedUser.Time = typeof updatedUser.Time === "object" ? updatedUser.Time.$date : updatedUser.Time; + return updatedUser; + }) + data = sortByDay(data); + const prevData = {...dataFromApi}; + prevData[yearRef.current] = data; + subCountRef.current += data.length; + for (const sub of data) { + langRef.current[sub.langName] = true; + if (sub.averageRating) { + ratingRef.current.push(sub.averageRating); + } + } + setDataFromApi({...prevData}); + yearRef.current += 1; + }); + }else if (yearRef.current === currentYearRef.current-1) { + fetch("https://aocbot.zerobot.app/solutions") + .then(async (response) => { + let data = await response.json(); + data = data.filter(user => user.avatarUrl.includes(`avatars/${userInfoRef.current.id}/`)); + data = sortByDay(data); + const prevData = {...dataFromApi}; + prevData[yearRef.current] = data; + subCountRef.current += data.length; + for (const sub of data) { + langRef.current[sub.langName] = true; + if (sub.averageRating) { + ratingRef.current.push(sub.averageRating); + } + } + setDataFromApi({...prevData}); + yearRef.current += 1; + setIsLoadingData(!isLoadingData); + }); + }else { + console.log(dataFromApi); + } + } + },[dataFromApi]); + + return( + <> + + + { + userInfoRef.current ? + isLoadingData ? + Loading Submissions... + : + <> +
+ acc+val,0)*100/ratingRef.current.length)/100 : 0} + /> +
+ { + Object.keys(dataFromApi).sort((a,b) => b-a).map(year => ( +
+
{year}
+ + { + dataFromApi[year].length ? +
+ { + dataFromApi[year].map(user => ( + + )) + } +
: + + } +
+ )) + } + + : + + } + + ); +} + +export default React.memo(MySubmissions); \ No newline at end of file diff --git a/src/utils/siteConfig.js b/src/utils/siteConfig.js index d5858fd..c455470 100644 --- a/src/utils/siteConfig.js +++ b/src/utils/siteConfig.js @@ -40,7 +40,12 @@ module.exports = { { name: "Submit Solution", route: "/submit", - icon: "Send", + icon: "Send" + }, + { + name: "My Submissions", + route: "/my-submissions", + icon: "", dividerAfter: true }, { diff --git a/src/utils/sorts.js b/src/utils/sorts.js index ffcaa43..992dea2 100644 --- a/src/utils/sorts.js +++ b/src/utils/sorts.js @@ -60,3 +60,12 @@ export function filterByDates(from, to, dataSet) { return dataSet; } } + +export function sortByDay(dataSet) { + const mutableCopy = [...dataSet]; + return mutableCopy.sort((a, b) => { + const dayA = Number(a.dayNumber); + const dayB = Number(b.dayNumber); + return dayA > dayB ? 1 : -1; + }); +} \ No newline at end of file From b86c4913b740527d1c943ad1f0c2cda73fc86f2f Mon Sep 17 00:00:00 2001 From: Sri Harsha D V Date: Fri, 4 Oct 2024 17:26:41 +0530 Subject: [PATCH 3/3] fix: adjust MySubmissions component code to remove warnings --- src/pages/MySubmissions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/MySubmissions.js b/src/pages/MySubmissions.js index fdc8f28..362abbc 100644 --- a/src/pages/MySubmissions.js +++ b/src/pages/MySubmissions.js @@ -43,7 +43,7 @@ const MySubmissions = () => { const userInfoRef = useRef(null); - const [{ user: userInfo }, userDispatch] = useUserContext(); + const userInfo = useUserContext()[0]["user"]; const [dataFromApi,setDataFromApi] = useState({}); const [isLoadingData,setIsLoadingData] = useState(true); @@ -104,7 +104,7 @@ const MySubmissions = () => { } setDataFromApi({...prevData}); yearRef.current += 1; - setIsLoadingData(!isLoadingData); + setIsLoadingData(prevValue => !prevValue); }); }else { console.log(dataFromApi);