Skip to content

Commit 6e71169

Browse files
committed
feat: implement real fallback activities for contributor feed when API fails
1 parent 4cb8477 commit 6e71169

1 file changed

Lines changed: 111 additions & 85 deletions

File tree

src/components/FloatingContributors/index.tsx

Lines changed: 111 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -126,73 +126,78 @@ const FloatingContributors: React.FC<FloatingContributorsProps> = ({
126126
const [lastFetched, setLastFetched] = useState<number | null>(null);
127127
const refreshTimerRef = useRef<NodeJS.Timeout | null>(null);
128128

129-
// Create fallback activities for when API fails
130-
const createFallbackActivities = useCallback((): ContributorActivity[] => {
131-
const fallbackContributors = [
132-
{
133-
login: "sanjay-kv",
134-
avatar_url: "https://avatars.githubusercontent.com/u/30715153?v=4",
135-
html_url: "https://github.com/sanjay-kv",
136-
},
137-
{
138-
login: "recodehive-team",
139-
avatar_url: "https://avatars.githubusercontent.com/u/150000000?v=4",
140-
html_url: "https://github.com/recodehive",
141-
},
142-
{
143-
login: "open-source-contributor",
144-
avatar_url: "https://avatars.githubusercontent.com/u/583231?v=4",
145-
html_url: "https://github.com/open-source-contributor",
146-
},
147-
{
148-
login: "developer",
149-
avatar_url: "https://avatars.githubusercontent.com/u/9919?v=4",
150-
html_url: "https://github.com/developer",
151-
},
152-
{
153-
login: "coder",
154-
avatar_url: "https://avatars.githubusercontent.com/u/6154722?v=4",
155-
html_url: "https://github.com/coder",
156-
},
157-
];
158-
159-
const actions: ContributorActivity["action"][] = [
160-
"pushed",
161-
"created",
162-
"merged",
163-
"opened",
164-
"commented",
165-
];
166-
const timeOffsets = [5, 10, 30, 60, 120, 240, 480]; // minutes
167-
const messages = [
168-
"Updated documentation",
169-
"Fixed styling issues",
170-
"Added new feature",
171-
"Resolved conflict in package.json",
172-
"Implemented responsive design",
173-
"Updated dependencies",
174-
"Fixed typo in README",
175-
];
176-
177-
return fallbackContributors.map((contributor, index) => {
178-
const now = new Date();
179-
const timestamp = new Date(
180-
now.getTime() - timeOffsets[index % timeOffsets.length] * 60 * 1000,
129+
// When there's no recent event-feed activity, fall back to the repo's
130+
// actual latest push (commits) and latest issue comments instead of mock data.
131+
const fetchRealFallbackActivities = useCallback(async (): Promise<
132+
ContributorActivity[]
133+
> => {
134+
const results: ContributorActivity[] = [];
135+
136+
try {
137+
const commitsResponse = await fetch(
138+
"https://api.github.com/repos/recodehive/recode-website/commits?per_page=5",
181139
);
140+
if (commitsResponse.ok) {
141+
const commits = await commitsResponse.json();
142+
if (Array.isArray(commits)) {
143+
commits.forEach((commit) => {
144+
const login = commit.author?.login;
145+
if (!login || isBotAccount(login)) return;
146+
const timestamp = new Date(
147+
commit.commit?.author?.date ?? Date.now(),
148+
);
149+
results.push({
150+
id: `commit-${commit.sha}`,
151+
contributor: {
152+
login,
153+
avatar_url: commit.author.avatar_url,
154+
html_url: `https://github.com/${login}`,
155+
},
156+
action: "pushed",
157+
message: commit.commit?.message?.slice(0, 50),
158+
timestamp,
159+
timeAgo: formatTimeAgo(timestamp),
160+
});
161+
});
162+
}
163+
}
164+
} catch (e) {
165+
console.warn("Error fetching fallback commits:", e);
166+
}
167+
168+
try {
169+
const commentsResponse = await fetch(
170+
"https://api.github.com/repos/recodehive/recode-website/issues/comments?per_page=5&sort=created&direction=desc",
171+
);
172+
if (commentsResponse.ok) {
173+
const comments = await commentsResponse.json();
174+
if (Array.isArray(comments)) {
175+
comments.forEach((comment) => {
176+
const login = comment.user?.login;
177+
if (!login || isBotAccount(login)) return;
178+
const timestamp = new Date(comment.created_at ?? Date.now());
179+
results.push({
180+
id: `comment-${comment.id}`,
181+
contributor: {
182+
login,
183+
avatar_url: comment.user.avatar_url,
184+
html_url: `https://github.com/${login}`,
185+
},
186+
action: "commented",
187+
message: comment.body?.slice(0, 50),
188+
timestamp,
189+
timeAgo: formatTimeAgo(timestamp),
190+
});
191+
});
192+
}
193+
}
194+
} catch (e) {
195+
console.warn("Error fetching fallback comments:", e);
196+
}
182197

183-
return {
184-
id: `fallback-${index}`,
185-
contributor: {
186-
login: contributor.login,
187-
avatar_url: contributor.avatar_url,
188-
html_url: contributor.html_url,
189-
},
190-
action: actions[index % actions.length],
191-
message: messages[index % messages.length]?.slice(0, 50), // Consistent message length
192-
timestamp,
193-
timeAgo: formatTimeAgo(timestamp),
194-
};
195-
});
198+
return results.sort(
199+
(a, b) => b.timestamp.getTime() - a.timestamp.getTime(),
200+
);
196201
}, []);
197202

198203
// Fetch live data from GitHub
@@ -368,6 +373,26 @@ const FloatingContributors: React.FC<FloatingContributorsProps> = ({
368373
setContributors(Array.from(contributorsMap.values()));
369374
}
370375
}
376+
377+
// If nothing left after filtering out bots/"other" events, pull the
378+
// repo's real latest push + comments instead of leaving it empty.
379+
const hasVisible = newActivities.some(
380+
(activity) =>
381+
!isBotAccount(activity.contributor.login) &&
382+
activity.action !== "other",
383+
);
384+
if (!hasVisible) {
385+
const realFallback = await fetchRealFallbackActivities();
386+
if (realFallback.length > 0) {
387+
setActivities(realFallback);
388+
}
389+
}
390+
} else {
391+
// No events at all from the API — use real fallback data.
392+
const realFallback = await fetchRealFallbackActivities();
393+
if (realFallback.length > 0) {
394+
setActivities(realFallback);
395+
}
371396
}
372397

373398
setLastFetched(now);
@@ -383,32 +408,33 @@ const FloatingContributors: React.FC<FloatingContributorsProps> = ({
383408
} catch (error) {
384409
console.warn("Error fetching GitHub events:", error);
385410

386-
// Use fallback data if we have no activities yet
411+
// Use the repo's real latest push + comments if we have no activities yet
387412
if (activities.length === 0) {
388-
const fallbackActivities = createFallbackActivities();
389-
setActivities(fallbackActivities);
390-
391-
// Create fallback contributors
392-
const contributorsMap = new Map<string, Contributor>();
393-
fallbackActivities.forEach((activity) => {
394-
const login = activity.contributor.login;
395-
if (!contributorsMap.has(login)) {
396-
contributorsMap.set(login, {
397-
id: `fallback-${login}`,
398-
login,
399-
avatar_url: activity.contributor.avatar_url,
400-
contributions: Math.floor(Math.random() * 50) + 10,
401-
html_url: activity.contributor.html_url,
402-
});
403-
}
404-
});
413+
const realFallback = await fetchRealFallbackActivities();
414+
if (realFallback.length > 0) {
415+
setActivities(realFallback);
416+
417+
const contributorsMap = new Map<string, Contributor>();
418+
realFallback.forEach((activity) => {
419+
const login = activity.contributor.login;
420+
if (!contributorsMap.has(login)) {
421+
contributorsMap.set(login, {
422+
id: `fallback-${login}`,
423+
login,
424+
avatar_url: activity.contributor.avatar_url,
425+
contributions: 0,
426+
html_url: activity.contributor.html_url,
427+
});
428+
}
429+
});
405430

406-
setContributors(Array.from(contributorsMap.values()));
431+
setContributors(Array.from(contributorsMap.values()));
432+
}
407433
}
408434

409435
setLoading(false);
410436
}
411-
}, [activities.length, createFallbackActivities, lastFetched]);
437+
}, [activities.length, fetchRealFallbackActivities, lastFetched]);
412438

413439
// Initialize component and start data fetching
414440
useEffect(() => {

0 commit comments

Comments
 (0)