Skip to content

Commit 9d5eaec

Browse files
committed
Refactor participant loading and rendering logic
1 parent a448b02 commit 9d5eaec

File tree

2 files changed

+116
-38
lines changed

2 files changed

+116
-38
lines changed

script.js

Lines changed: 87 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,82 @@
11
let programs = {};
22
const apiUrl = "https://api2.hackclub.com/v0.1/Unified%20YSWS%20Projects%20DB/YSWS%20Programs?cache=true";
3-
var participants = []
3+
let participants = [];
4+
let initialParticipants = new Map();
45

56
async function startRender() {
6-
await Promise.all([loadParticipants(), loadPrograms()]);
7+
await loadPrograms();
8+
Object.values(programs).flat().forEach(program => {
9+
if (program.participants !== undefined) {
10+
initialParticipants.set(program.name, program.participants);
11+
}
12+
});
13+
714
renderPrograms();
15+
await loadParticipants();
16+
updateParticipantCounts();
817
}
9-
startRender()
1018

1119
function loadParticipants() {
12-
fetch(apiUrl).then(response => {
13-
if (!response.ok) {
14-
throw new Error(`Failed to Fetch Participants Data! ${response.status}`);
15-
}
16-
return response.json();
17-
}).then(data => {
18-
participants = data.map(item => ({
19-
name: item.fields.Name,
20-
total: item.fields["Unweighted–Total"]
21-
}));
20+
return fetch(apiUrl)
21+
.then(response => {
22+
if (!response.ok) {
23+
throw new Error(`Failed to Fetch Participants Data! ${response.status}`);
24+
}
25+
return response.json();
26+
})
27+
.then(data => {
28+
participants = data.map(item => ({
29+
name: item.fields.Name,
30+
total: item.fields["Unweighted–Total"]
31+
}));
32+
})
33+
.catch(error => {
34+
console.error("Error fetching data:", error);
35+
});
36+
}
37+
38+
function animateNumber(element, start, end, duration = 1000) {
39+
const startTime = performance.now();
40+
const startNum = parseInt(start) || 0;
41+
const endNum = parseInt(end) || 0;
42+
const numberSpan = element.querySelector('span');
43+
44+
function update(currentTime) {
45+
const elapsed = currentTime - startTime;
46+
const progress = Math.min(elapsed / duration, 1);
47+
48+
const easeOutQuad = 1 - Math.pow(1 - progress, 2);
49+
const current = Math.round(startNum + (endNum - startNum) * easeOutQuad);
50+
51+
numberSpan.textContent = current;
52+
element.textContent = `${current} participant${current !== 1 ? 's' : ''}`;
2253

23-
console.log(participants);
54+
if (progress < 1) {
55+
requestAnimationFrame(update);
56+
} else {
57+
element.classList.remove('updating');
58+
}
59+
}
60+
61+
element.classList.add('updating');
62+
requestAnimationFrame(update);
63+
}
2464

25-
})
26-
.catch(error => {
27-
console.error("Error fetching data:", error);
65+
function updateParticipantCounts() {
66+
const participantElements = document.querySelectorAll('.program-participants');
67+
68+
participantElements.forEach(element => {
69+
const programCard = element.closest('.program-card');
70+
const programData = JSON.parse(decodeURIComponent(programCard.dataset.program));
71+
const programName = programData.name;
72+
73+
const apiData = participants.find(p => p.name === programName);
74+
if (apiData) {
75+
const initialCount = initialParticipants.get(programName) || 0;
76+
animateNumber(element, initialCount, apiData.total);
77+
}
2878
});
2979
}
30-
loadParticipants()
3180

3281
function getParticipantsByName(programName) {
3382
if (!participants.length) {
@@ -136,8 +185,9 @@ function getDeadlineClass(deadlineStr) {
136185
}
137186

138187
function formatParticipants(name) {
139-
if (name === undefined) return '';
140-
return getParticipantsByName(name)
188+
const initial = initialParticipants.get(name);
189+
if (initial === undefined) return '';
190+
return `<span>${initial}</span> participant${initial !== 1 ? 's' : ''}`;
141191
}
142192

143193
function createProgramCard(program) {
@@ -466,26 +516,25 @@ function updateDeadlines() {
466516
}
467517

468518
document.addEventListener('DOMContentLoaded', () => {
469-
loadPrograms().then(() => {
470-
const searchInput = document.getElementById('program-search');
471-
searchInput.addEventListener('input', (e) => searchPrograms(e.target.value));
472-
473-
document.querySelectorAll('.filter-btn').forEach(button => {
474-
button.addEventListener('click', () => {
475-
filterPrograms(button.dataset.category);
476-
searchPrograms(searchInput.value);
477-
});
519+
startRender();
520+
const searchInput = document.getElementById('program-search');
521+
searchInput.addEventListener('input', (e) => searchPrograms(e.target.value));
522+
523+
document.querySelectorAll('.filter-btn').forEach(button => {
524+
button.addEventListener('click', () => {
525+
filterPrograms(button.dataset.category);
526+
searchPrograms(searchInput.value);
478527
});
479-
480-
initializeTheme();
481-
document.getElementById('theme-toggle').addEventListener('click', toggleTheme);
482-
483-
setInterval(updateDeadlines, 60000);
528+
});
529+
530+
initializeTheme();
531+
document.getElementById('theme-toggle').addEventListener('click', toggleTheme);
532+
533+
setInterval(updateDeadlines, 60000);
484534

485-
document.querySelectorAll('.sort-btn').forEach(button => {
486-
button.addEventListener('click', () => {
487-
updateSort(button.dataset.sort);
488-
});
535+
document.querySelectorAll('.sort-btn').forEach(button => {
536+
button.addEventListener('click', () => {
537+
updateSort(button.dataset.sort);
489538
});
490539
});
491540

styles.css

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,35 @@ body.modal-open {
10831083
}
10841084
}
10851085

1086+
.program-participants {
1087+
font-size: var(--font-1);
1088+
color: var(--muted);
1089+
margin-top: var(--spacing-2);
1090+
font-weight: var(--font-weight-bold);
1091+
transition: opacity 0.3s ease;
1092+
}
1093+
1094+
.program-participants span {
1095+
display: inline-block;
1096+
transition: transform 0.3s ease;
1097+
}
1098+
1099+
.program-participants.updating {
1100+
opacity: 0.5;
1101+
}
1102+
1103+
.program-participants {
1104+
font-size: var(--font-1);
1105+
color: var(--muted);
1106+
margin-top: var(--spacing-2);
1107+
font-weight: var(--font-weight-bold);
1108+
transition: opacity 0.3s ease;
1109+
}
1110+
1111+
.program-participants.updating {
1112+
opacity: 0.5;
1113+
}
1114+
10861115
.program-participants {
10871116
font-size: var(--font-1);
10881117
color: var(--muted);

0 commit comments

Comments
 (0)