From 1c697f63fe495583eaad4352a09c8f33266ee81b Mon Sep 17 00:00:00 2001 From: Agastya Gaur Date: Wed, 13 Aug 2025 20:47:20 +0530 Subject: [PATCH] feat: add meme based comparision --- .DS_Store | Bin 0 -> 6148 bytes www/app/meme/layout.tsx | 81 ++++ www/app/meme/meme.css | 14 + www/app/meme/page.tsx | 385 ++++++++++++++++++ www/app/page.tsx | 4 + www/components/CompareButton.tsx | 17 + www/components/meme-comp/Container.tsx | 12 + www/components/meme-comp/InputText.tsx | 35 ++ www/components/meme-comp/LinneChart.tsx | 101 +++++ www/components/meme-comp/Loader.tsx | 25 ++ www/components/meme-comp/MemeGenerator.tsx | 159 ++++++++ www/lib/api.ts | 26 ++ www/package.json | 4 + www/pnpm-lock.yaml | 144 +++++++ www/public/assets/buttonArrow.jsx | 37 ++ www/public/assets/buttonArrow.tsx | 19 + www/public/assets/compare meme collection.png | Bin 0 -> 800701 bytes www/public/assets/gorilla-bg-min.jpg | Bin 0 -> 166351 bytes www/public/assets/index.js | 5 + www/public/assets/index.ts | 5 + www/public/assets/memes/eighth.png | Bin 0 -> 28382 bytes www/public/assets/memes/fifth.png | Bin 0 -> 59580 bytes www/public/assets/memes/first.png | Bin 0 -> 40005 bytes www/public/assets/memes/fourth.png | Bin 0 -> 52439 bytes www/public/assets/memes/index.js | 23 ++ www/public/assets/memes/index.ts | 23 ++ www/public/assets/memes/ninth.png | Bin 0 -> 54547 bytes www/public/assets/memes/second.png | Bin 0 -> 55603 bytes www/public/assets/memes/seventh.png | Bin 0 -> 29523 bytes www/public/assets/memes/sixth.png | Bin 0 -> 22631 bytes www/public/assets/memes/tenth.png | Bin 0 -> 43380 bytes www/public/assets/memes/third.png | Bin 0 -> 46672 bytes www/public/assets/react.svg | 1 + www/public/assets/smile.png | Bin 0 -> 48461 bytes www/types/types.ts | 65 +++ 35 files changed, 1185 insertions(+) create mode 100644 .DS_Store create mode 100644 www/app/meme/layout.tsx create mode 100644 www/app/meme/meme.css create mode 100644 www/app/meme/page.tsx create mode 100644 www/components/CompareButton.tsx create mode 100644 www/components/meme-comp/Container.tsx create mode 100644 www/components/meme-comp/InputText.tsx create mode 100644 www/components/meme-comp/LinneChart.tsx create mode 100644 www/components/meme-comp/Loader.tsx create mode 100644 www/components/meme-comp/MemeGenerator.tsx create mode 100644 www/public/assets/buttonArrow.jsx create mode 100644 www/public/assets/buttonArrow.tsx create mode 100644 www/public/assets/compare meme collection.png create mode 100644 www/public/assets/gorilla-bg-min.jpg create mode 100644 www/public/assets/index.js create mode 100644 www/public/assets/index.ts create mode 100644 www/public/assets/memes/eighth.png create mode 100644 www/public/assets/memes/fifth.png create mode 100644 www/public/assets/memes/first.png create mode 100644 www/public/assets/memes/fourth.png create mode 100644 www/public/assets/memes/index.js create mode 100644 www/public/assets/memes/index.ts create mode 100644 www/public/assets/memes/ninth.png create mode 100644 www/public/assets/memes/second.png create mode 100644 www/public/assets/memes/seventh.png create mode 100644 www/public/assets/memes/sixth.png create mode 100644 www/public/assets/memes/tenth.png create mode 100644 www/public/assets/memes/third.png create mode 100644 www/public/assets/react.svg create mode 100644 www/public/assets/smile.png diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..74cdf3eecef7b9824f2beae41065b362e82edb68 GIT binary patch literal 6148 zcmeHKU2hUW6g^XDfi@~Jn)JnF6JIN0HBE>wNFj#AkEB$6P*b6cb<<_HvWwU>Bs}YX z@Gto4FY&+lr032K$?g^(Yt+moGiN^Txij}H%P;^`YvSGkssPF?g2i=KOHAgaoU;`( zB4i~o!oz_d4%m|sYgPfPz~8Qbyr~8BaRRUFXWrijoT>fPrjuC1AwmRr3lB#*->*5} zE7-+TJi$Kp&_xT+*y^(X5H*b8X4&dSj$MSy9mVM9{66RWuF@~$u8uLp7`OEPM$9Bc z#E}|Kxt5=c@go;w9qm*M$<4)yL?+Kc<~Bx?1?FQvC1hW=pD?hl@O!|6fw+HM%ceJ>b}HNkU- zbb0l{3*2zp3MYXZX>Ko>s8q_8gX-ql*>+=R%elK#pKUp3jqUoDbFZ;In^nr!Z{EJY zcW~^Fg76cSq$!?@+AbMffIJ(M^M~Fz@WVj9Wee|x*Tte)t-Nuu%h>2v0jt12sDS)F zI4puqgYyLSPX{Y~1t1nVtc`8`=bz}v0I_Lso*+kPDxne+sxns$W$q3tp>w`z{O1WK zbSS+thiWEhYpZ}&Age$j=~m_Z@BIG$pC#FzRlq9nUn!u9?OwaZ zl+4+BE;%`C9hNsNV&XSXP*Yf$<5)N3DBfhz#yLkC#HPV{f*hgQKLT0?TUiDEr~) { + return ( + + + + + {/* Google Analytics */} + + + + + + {children} + + + + ); +} diff --git a/www/app/meme/meme.css b/www/app/meme/meme.css new file mode 100644 index 00000000..8a4d2a5e --- /dev/null +++ b/www/app/meme/meme.css @@ -0,0 +1,14 @@ +body { + background-color: black; + height: 100%; + +} + +.body-container { + background-image: radial-gradient(circle at var(--x) var(--y), var(--color1) 0%, var(--color2) var(--position2)), url('/assets/gorilla-bg-min.jpg'); + background-position: center top; + background-size: cover; + height: 100vh; + + +} \ No newline at end of file diff --git a/www/app/meme/page.tsx b/www/app/meme/page.tsx new file mode 100644 index 00000000..5d9e7379 --- /dev/null +++ b/www/app/meme/page.tsx @@ -0,0 +1,385 @@ +"use client" +import { useEffect, useRef, useState } from "react"; +import "./meme.css"; +import { LinneChart } from "@/components/meme-comp/LinneChart"; +import MemeGenerator from "@/components/meme-comp/MemeGenerator"; +import { getGithubInfo, getMemeProfileData } from "@/lib/api"; +import { ContributionsData } from "@/types/types"; +import { Container } from "@/components/meme-comp/Container"; + +import { ButtonArrow } from "@/public/assets/buttonArrow"; +import { AnimatePresence, motion } from "framer-motion"; +import html2canvas from "html2canvas"; +import { Loader } from "@/components/meme-comp/Loader"; +import { AxiosError } from "axios"; + +function page(){ + const [myUserName, setMyUserName] = useState(""); + const [comparerUserInput, setComparerUserInput] = useState(""); + const [myData, setMyData] = useState(null); + const [comparerData, setComparerData] = useState(null); + const [myName, setMyName] = useState(""); + const [myTotalContribution, setMyTotalContribution] = useState(0); + const [comparertotalContri, setComparertotalContri] = useState(0); + const [comparerName, setComparerName] = useState(""); + const [memeIndex, setMemeIndex] = useState(0); + const [isLoading, setIsLoading] = useState(false); + const [winner, setWinner] = useState(null); + const [loser, setLoser] = useState(null); + const [haveData, setHaveData] = useState(false); + const [myError, setMyError] = useState(""); + const [comparerError, setComparerError] = useState(""); + const headingContainer = useRef(null); + const containerRef = useRef(null); + + const handleSubmit = async (e: React.FormEvent): Promise => { + e.preventDefault(); + + // Clear previous data + setMyData(null); + setComparerData(null); + + // Start loading + setIsLoading(true); + + if (myUserName === comparerUserInput) { + setMyError("You can't compare to yourself :')") + setIsLoading(false); + return; + } + try { + const [ + myProfileData, + myGitHubInfo, + comparerProfileData, + comparerGitHubInfo, + ] = await Promise.all([ + getMemeProfileData(myUserName), + getGithubInfo(myUserName), + getMemeProfileData(comparerUserInput), + getGithubInfo(comparerUserInput), + ]); + + setMyData(myProfileData); + setMyName(myGitHubInfo?.name || myUserName); + setComparerData(comparerProfileData); + setComparerName(comparerGitHubInfo?.name || comparerUserInput); + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError.response && axiosError.response.status === 404) { + if (axiosError.config?.url?.includes(myUserName)) { + setMyError("Username not found"); + } else if (axiosError.config?.url?.includes(comparerUserInput)) { + setComparerError("Comparer username not found"); + } + } else { + console.error("Error fetching data:", error); + } + } finally { + setIsLoading(false); + } + }; + + const getMemeIndex = (myContribution: number, comparerContribution: number): number => { + if (myContribution === 0 && comparerContribution === 0) { + return 0; + } + const maxContribution = Math.max(myContribution, comparerContribution); + const difference = Math.abs(myContribution - comparerContribution); + const percentageDifference = (difference / maxContribution) * 100; + const index = Math.floor(percentageDifference / 10); + return Math.min(index, 9); + }; + + const downloadImage = (e: React.MouseEvent): void => { + if (!containerRef.current || !headingContainer.current) return; + + if (isMobile) { + headingContainer.current.style.display = 'none'; + } + e.currentTarget.style.display = "none"; + html2canvas(containerRef.current, { backgroundColor: "#2b3137" }) + .then((canvas) => { + const link = document.createElement("a"); + link.href = canvas.toDataURL("image/png"); + link.download = "github-compare.png"; + link.click(); + }) + .catch((error) => { + console.error("Error capturing the image:", error); + }) + .finally(() => { + e.currentTarget.style.display = "flex"; + if (headingContainer.current) { + headingContainer.current.style.display = 'flex'; + } + }); + }; + + useEffect(() => { + if (myData && comparerData) { + const arrOfMyContri = Object.values(myData.total); + const arrOfComparerContri = Object.values(comparerData.total); + + const myTotalContribution = arrOfMyContri.reduce( + (total, curVal) => total + curVal, + 0 + ); + setMyTotalContribution(myTotalContribution); + + const comparerTotalContribution = arrOfComparerContri.reduce( + (total, curVal) => total + curVal, + 0 + ); + setComparertotalContri(comparerTotalContribution); + + const memeIndex = getMemeIndex( + myTotalContribution, + comparerTotalContribution + ); + setMemeIndex(memeIndex); + const myfirstName = myName.split(" ")[0]; + const comparerfirstName = comparerName.split(" ")[0]; + + if (myTotalContribution > comparerTotalContribution) { + setWinner(myfirstName); + setLoser(comparerfirstName); + } else { + setWinner(comparerfirstName); + setLoser(myfirstName); + } + setHaveData(true); + } + }, [myData, comparerData, myName, comparerName]); + + const transition = { duration: 1, ease: "easeInOut" }; + + const [isMobile, setIsMobile] = useState(false); + useEffect(() => { + // Initialize mobile state after component mounts + const checkIsMobile = () => { + if (typeof window !== 'undefined') { + setIsMobile(window.innerWidth <= 768); + } + }; + + const handleResize = (): void => { + if (typeof window !== 'undefined') { + setIsMobile(window.innerWidth <= 768); + } + }; + + // Set initial value + checkIsMobile(); + + // Add event listener + if (typeof window !== 'undefined') { + window.addEventListener("resize", handleResize); + } + + return () => { + if (typeof window !== 'undefined') { + window.removeEventListener("resize", handleResize); + } + }; + }, []); + + const fontSize = haveData + ? isMobile + ? "34px" + : "52px" + : isMobile + ? "44px" + : "68px"; + + const clearDataCallBack = (): void => { + setMyData(null); + setMyName(''); + setComparerData(null); + setComparerName(''); + setHaveData(false); + setLoser(null); + setWinner(null); + setMyError(''); + setComparerError(''); + }; + + return ( + + + +
+ + + GitHub Profile
{" "} + + Comparison + +
+
+ + {!haveData && ( + +
+ {myError && ( +

+ {myError} +

+ )} + setMyUserName(e.currentTarget.value)} + placeholder="Your Username" + required + className={`border bg-white shadow-[0px_4px_50px_rgba(0,_255,_87,_0.5)] w-[294px] ${ + myError ? " outline outline-red-500" : "" + } py-3 px-4 text-[18px] rounded-full`} + /> +
+
+ {isLoading ? : VS} +
+
+ {comparerError && ( +

{comparerError}

+ )} + + setComparerUserInput(e.currentTarget.value) + } + placeholder="Enter other persons username" + required + className={`border bg-white shadow-[0px_4px_50px_rgba(0,_255,_87,_0.5)] w-[294px] ${ + comparerError ? " outline outline-red-500" : "" + } py-3 px-4 text-[18px] rounded-full`} + /> +
+
+ +
+
+ )} +
+ + + {myData && comparerData && ( + +
+ +
+
+
+
+

+ {myName} +

+

+ Total Contribution : {myTotalContribution} +

+
+
+
+
+
+

+ {comparerName} +

+

+ Total Contribution : {comparertotalContri} +

+
+
+
+ +
+
+
+ {winner && loser && ( + + )} +
+ +
+
+ )} +
+
+
+
+ ); +} + +export default page; diff --git a/www/app/page.tsx b/www/app/page.tsx index d8650ad9..3720414e 100644 --- a/www/app/page.tsx +++ b/www/app/page.tsx @@ -8,6 +8,7 @@ import ProfileCard from "@/components/profile-card/server"; import HowItWorksCard from "@/components/how-it-works-card/server"; import NextContributorCard from "@/components/next-contributor-card"; import { Compare } from "@/components/ui/compare"; +import CompareButton from "@/components/CompareButton"; // Types interface Profile { @@ -343,6 +344,9 @@ export default async function Home() { )} +
+ +