Skip to content

Commit

Permalink
Fixed the rate limit exceed of GitHub API on contributors page (#1426)
Browse files Browse the repository at this point in the history
  • Loading branch information
ThePhoenix08 authored Dec 29, 2024
1 parent 8086579 commit d1866c1
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 56 deletions.
73 changes: 72 additions & 1 deletion assets/css_files/contributor.css
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,12 @@ body {
display: flex;
flex-direction: column;
align-items: center;
width: 120px;
width: 120px;
margin: 10px;
transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
text-align: center;
opacity: 0;
animation: fadeIn 0.3s ease forwards;
}

.contributor-card:hover {
Expand All @@ -123,3 +125,72 @@ body {
margin-top: 5px;
color: #c9d1d9; /* Ensure visibility */
}

.contributions-count-bubble {
font-size: 12px;
color: #c9d1d9;
background-color: #3A0088;
padding: 5px;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
top: -130px;
right: -40px;
}

.lazy {
filter: blur(5px);
transition: filter 0.3s;
}

.lazy.loaded {
filter: blur(0);
}

#loading-spinner {
text-align: center;
padding: 20px;
}

.spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto;
}

@keyframes spin {
0% {
transform: rotate(0deg);
}

100% {
transform: rotate(360deg);
}
}

@keyframes fadeIn {
from {
opacity: 0;
}

to {
opacity: 1;
}
}

#error-message {
color: white;
padding: 20px;
display: none;
font-size: 19px;
font-weight: 600;
text-align: center;
}
10 changes: 9 additions & 1 deletion assets/html_files/contributor.html
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,15 @@
<h1 class="heading">Our Valuable Contributors</h1>
<p class="subheading">Meet the minds</p>
</div>
<div id="contributor"></div>
<div id="contributor">
<div id="loading-spinner">
<div class="spinner"></div>
<p>Loading contributors...</p>
</div>
</div>
<div id="error-message">
Failed to load the contributors. Please try again later.
</div>
</div>

<footer>
Expand Down
1 change: 1 addition & 0 deletions assets/images/avatar.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
160 changes: 106 additions & 54 deletions assets/js_files/contributor.js
Original file line number Diff line number Diff line change
@@ -1,76 +1,128 @@
const cont = document.getElementById('contributor');
let currentPage = 1; // Start from page 1
let isLoading = false; // Flag to track loading state
let hasMore = true; // Flag to track if there's more data to load

// Loading spinner element
const loadingSpinner = document.getElementById('loading-spinner');

// Error message element
const errorMessage = document.getElementById('error-message');

// Intersection Observer for infinite scroll
const observerOptions = {
root: null,
rootMargin: '0px',
threshold: 0.1
};

// Use when one page is loaded, and then scroll down to load more
const intersectionObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !isLoading && hasMore) {
// If intersecting, not in loading state, and has more data to load
fetchContributors(currentPage);
}
});
}, observerOptions);

// Lazy loading observer for avatars
const lazyLoadObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
lazyLoadObserver.unobserve(img);
}
});
}, observerOptions);

// Loads a single page of contributors
async function fetchContributors(pageNumber) {
const perPage = 100;
const apiUrl = '/.netlify/functions/contributors'; // Netlify serverless function path
if (isLoading) return;

const response = await fetch(`${apiUrl}?page=${pageNumber}&per_page=${perPage}`);

if (!response.ok) {
throw new Error(`Failed to fetch the contributors data. Status code: ${response.status}`);
}
isLoading = true;
const perPage = 20; // Number of items per page
const apiUrl = '/.netlify/functions/contributors';

const contributorsData = await response.json();
return contributorsData;
}
try {
// Show loading spinner at the bottom
loadingSpinner.style.display = 'block';

async function fetchAllContributors() {
let allContributors = [];
let pageNumber = 1;
const maxPages = 10; // Limiting the number of pages to avoid overload (can be adjusted)
const response = await fetch(`${apiUrl}?page=${pageNumber}&per_page=${perPage}`);

try {
// Fetch all contributors in parallel using Promise.all()
const fetchPromises = [];
if (!response.ok) {
throw new Error(`Failed to fetch contributors. Status: ${response.status}`);
}

const contributorsData = await response.json();

// Fetch data for multiple pages concurrently
for (let i = 1; i <= maxPages; i++) {
fetchPromises.push(fetchContributors(i));
// Check if we have more data to load
hasMore = contributorsData.length === perPage;

// Create and append contributor cards
await displayContributors(contributorsData);

currentPage++;
} catch (error) {
errorMessage.style.display = 'block';
console.error('Error fetching contributors:', error);
} finally {
isLoading = false;
loadingSpinner.style.display = 'none';

// Add observer to the last card for infinite scroll
const allCards = cont.querySelectorAll('.contributor-card');
if (allCards.length > 0) {
intersectionObserver.observe(allCards[allCards.length - 1]);
}
}
}

const contributorsArray = await Promise.all(fetchPromises);
// Displays the contributors on the page
async function displayContributors(contributors) {
const fragment = document.createDocumentFragment();

// Combine all the results
contributorsArray.forEach(contributorsData => {
allContributors = allContributors.concat(contributorsData);
});
for (const contributor of contributors) {
if (contributor.login === 'Rakesh9100') continue; // Skip owner

// Display contributor cards
allContributors.forEach((contributor) => {
if (contributor.login === 'Rakesh9100') return; // Skip owner
const contributorCard = document.createElement('div');
contributorCard.classList.add('contributor-card');

const contributorCard = document.createElement('div');
contributorCard.classList.add('contributor-card');
// Create avatar with lazy loading
const avatarImg = document.createElement('img');
avatarImg.classList.add('lazy');
avatarImg.src = '../images/avatar.svg'; // Add a placeholder image
avatarImg.dataset.src = contributor.avatar_url;
avatarImg.alt = `${contributor.login}'s Picture`;

const avatarImg = document.createElement('img');
avatarImg.src = contributor.avatar_url;
avatarImg.alt = `${contributor.login}'s Picture`;
const loginLink = document.createElement('a');
loginLink.href = contributor.html_url;
loginLink.target = '_blank';
loginLink.appendChild(avatarImg);

const loginLink = document.createElement('a');
loginLink.href = contributor.html_url;
loginLink.target = '_blank';
loginLink.appendChild(avatarImg);
const displayName = contributor.login;

// Fetch detailed info for the name
fetch(contributor.url)
.then(contributorDetails => contributorDetails.json())
.then(contributorData => {
const displayName = contributorData.name || contributor.login;
const nameDiv = document.createElement('div');
nameDiv.classList.add('contributor-name');
nameDiv.textContent = displayName;

const nameDiv = document.createElement('div');
nameDiv.classList.add('contributor-name');
nameDiv.textContent = displayName;
const contributionsCountBubbleDiv = document.createElement('div');
contributionsCountBubbleDiv.classList.add('contributions-count-bubble');
contributionsCountBubbleDiv.textContent = contributor.contributions;

contributorCard.appendChild(loginLink);
contributorCard.appendChild(nameDiv);
contributorCard.appendChild(loginLink);
contributorCard.appendChild(nameDiv);
contributorCard.appendChild(contributionsCountBubbleDiv);
fragment.appendChild(contributorCard);

cont.appendChild(contributorCard);
})
.catch(error => console.error('Error fetching the contributor details:', error));
});
} catch (error) {
console.error('Error fetching the contributors:', error);
// Observe the image for lazy loading
lazyLoadObserver.observe(avatarImg);
}

cont.appendChild(fragment);
}

fetchAllContributors();
// Initial load
fetchContributors(currentPage);

0 comments on commit d1866c1

Please sign in to comment.