Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Fetch contributions from GitHub API #44

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Generate your token at https://github.com/settings/tokens
# The token must have the repo scope defined

VITE_GH_TOKEN=''
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ node_modules
dist
dist-ssr
*.local
.env

# Editor directories and files
.vscode/*
Expand Down
229 changes: 43 additions & 186 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^4.4.0",
"react-lottie": "^1.2.3",
"redbox-react": "^1.6.0"
"react-lottie-player": "1.5.4"
},
"devDependencies": {
"@types/react": "^18.0.17",
Expand Down
2 changes: 1 addition & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const App = () => {
<SkillsAndExperience />
<Education />
<Projects />
<BlogPosts enabled={true} />
<BlogPosts enabled={false} />
<OpenSource />
<ExtraCurricular />
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/components/Education.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import styles, { layout } from "../style";
import { educationList } from "../constants";
import Lottie from "react-lottie";
import Lottie from "react-lottie-player";
import animationData from "../lotties/quiz-mode-teal-dark.json";
import { motion } from "framer-motion";

// lottie config
const defaultOptions = {
loop: true,
autoplay: true,
play: true,
animationData: animationData,
rendererSettings: {
preserveAspectRatio: "xMidYMid slice",
Expand Down Expand Up @@ -65,7 +65,7 @@ const Education = () => {
>
<div className={layout.sectionImgReverse}>
<div className="w-[80%] h-[80%] relative z-[5]">
<Lottie options={defaultOptions} />
<Lottie {...defaultOptions} />
</div>

{/* gradient start */}
Expand Down
6 changes: 3 additions & 3 deletions src/components/Hero.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import styles from "../style";
import LetsConnect from "./LetsConnect";
import Lottie from "react-lottie";
import Lottie from "react-lottie-player";
import animationData from "../lotties/person-coding.json";
import { aboutMe } from "../constants";


// lottie config
const defaultOptions = {
loop: true,
autoplay: true,
play: true,
animationData: animationData,
rendererSettings: {
preserveAspectRatio: "xMidYMid slice",
Expand Down Expand Up @@ -48,7 +48,7 @@ const Hero = () => {
className={`flex-1 flex ${styles.flexCenter} md:my-0 my-10 relative`}
>
<div className="relative z-index-[5] h-[90%] w-[85%]">
<Lottie options={defaultOptions} />
<Lottie {...defaultOptions} />
</div>
<div className="absolute z-[1] w-[50%] h-[50%] rounded-full bottom-40 white__gradient"></div>
</div>
Expand Down
4 changes: 3 additions & 1 deletion src/components/LetsConnect.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { arrowUp } from "../assets";
import { callToAction } from "../constants";

const LetsConnect = () => {
return (pr} w-[140px] h-[140px] rounded-full bg-blue-gradient p-[2px] cursor-pointer`}
return (
<div
className={`${styles.flexCenter} w-[140px] h-[140px] rounded-full bg-blue-gradient p-[2px] cursor-pointer`}
onClick={() => window.open(callToAction)}
>
<div
Expand Down
56 changes: 51 additions & 5 deletions src/components/OpenSource.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { useState, useEffect } from "react";
import { openSourceContributions } from "../constants";
import { DiGitMerge, DiGitPullRequest } from "react-icons/di";
import { VscIssues } from "react-icons/vsc";
import { motion } from "framer-motion";
import { fetchContributions } from '../constants/contributions'
import { AiFillApi } from "react-icons/ai";

const Contribution = (props) => {
return (
Expand Down Expand Up @@ -70,14 +71,57 @@ const Contribution = (props) => {
);
};

const LoadFailure = () => {
return (
<section id="openSource">
<h1 className="flex-1 font-poppins font-semibold ss:text-[55px] text-[45px] text-white ss:leading-[80px] leading-[80px]">
Open Source Contributions
</h1>

<motion.div
className="px-12 py-8 my-8 transition-colors duration-300 transform rounded-xl group dark:border-gray-700 dark:hover:border-transparent feature-card"
whileInView={{ y: [-40, 0], opacity: [0, 1] }}
transition={{ duration: 1 }}
>
<div className="flex flex-col sm:-mx-4 sm:flex-row">
<AiFillApi
size="2rem"
className="text-white mr-1 hover:text-teal-200"
/>

<div className="mt-4 sm:mx-4 sm:mt-0">
<h1 className="text-xl font-semibold font-poppins text-gray-700 md:text-2xl group-hover:text-white text-gradient">
Something went wrong loading this section.
</h1>
<p className="font-poppins font-normal text-dimWhite mt-3">
Please wait a few minutes and try reloading the page.
</p>
</div>
</div>
</motion.div>
</section>
)
}

const OpenSource = () => {
const [contributions, setContributions] = useState([]);
const [filterContribution, setFilterContribution] = useState([]);
const [activeFilter, setActiveFilter] = useState("All");
const [failPlaceholder, setFailPlaceholder] = useState(false)

useEffect(() => {
setContributions(openSourceContributions);
setFilterContribution(openSourceContributions);
const fetchData = async () => {
const response = await fetchContributions('mittal-parth')
mittal-parth marked this conversation as resolved.
Show resolved Hide resolved
const data = response?.slice(0, 12)

if (!data) setFailPlaceholder(true)

setContributions(data);
setFilterContribution(data);
}

fetchData()
.catch(console.error)
}, []);

const handleContributionFilter = (item) => {
Expand All @@ -89,14 +133,16 @@ const OpenSource = () => {
} else {
setFilterContribution(
contributions.filter(
(contribution) => contribution.organisation == item
(contribution) => contribution.organization == item
)
);
}
}, 500);
};

return (
return failPlaceholder ? (
<LoadFailure />
) : (
<section id="openSource">
<h1 className="flex-1 font-poppins font-semibold ss:text-[55px] text-[45px] text-white ss:leading-[80px] leading-[80px]">
Open Source Contributions
Expand Down
112 changes: 112 additions & 0 deletions src/constants/contributions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
const openSourceContributions = []
let options = {}

if (import.meta.env.VITE_GH_TOKEN) {
options = {
method: 'GET',
headers: {
Authorization: `Bearer ${import.meta.env.VITE_GH_TOKEN}`,
}
}
} else {
options = {
method: 'GET',
}
}

export async function fetchContributions(username) {
const pullsResponse = await fetch(`https://api.github.com/search/issues?q=is:pr+author:${username}`, options)
const issuesResponse = await fetch(`https://api.github.com/search/issues?q=is:issue+author:${username}`, options)

if (pullsResponse.status !== 200 || issuesResponse.status !== 200) {
return null
}

const pulls = await pullsResponse.json()
const issues = await issuesResponse.json()

pulls.items.forEach((pr) => {
const {organization, repo, logoUrl} = parseOriginFromUrl(pr.url)

createContribution({
id: pr.id,
organization: organization,
logo: logoUrl,
repo: repo,
type: 'pull-request',
status: pr.pull_request.merged_at ?
(
'merged'
) : (
pr.closed_at ?
(
'closed'
) : (
'open'
)
),
title: pr.title,
link: pr.html_url,
number: `#${pr.number}`,
date: pr.created_at,
})
})

issues.items.forEach((issue) => {
const {organization, repo, logoUrl} = parseOriginFromUrl(issue.url)

createContribution({
id: issue.id,
organization: organization,
logo: logoUrl,
repo: repo,
type: 'issue',
status: issue.closed_at ? 'closed' : 'open',
title: issue.title,
link: issue.html_url,
number: `#${issue.number}`,
date: issue.created_at,
})
})

return Object.values(openSourceContributions)
}

function createContribution({ id, organization, logo, repo, type, status, title, link, number, date }) {
openSourceContributions.push({
id,
organization,
logo,
repo,
type,
status,
title,
link,
number,
date,
linesAdded: '',
linesRemoved: ''
})
}

function parseOriginFromUrl(url) {
/**
* splits https://api.github.com/repos/org-name/repo-name/issues/25
* into ["https://", "api.github.com", "repos", "org-name", "repo-name", "issues", "25"]
*/
const parts = url.split(/https:\/\/|\//gm)
const organization = parts[3]
const repo = parts[4]

/**
* accessing a github profile or organization and adding a .png
* at the end of the URL will return their logo/profile picture
*/
const logoUrl = `https://github.com/${organization}.png`

return {
organization,
repo,
logoUrl
}
}