-
Notifications
You must be signed in to change notification settings - Fork 99
Added Live GitHub Activity Feed using GitHub Events API. #276
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,89 @@ | ||||||||||||||
| import { useEffect, useState } from "react"; | ||||||||||||||
|
|
||||||||||||||
| interface EventType { | ||||||||||||||
| id: string; | ||||||||||||||
| type: string; | ||||||||||||||
| created_at: string; | ||||||||||||||
| repo?: { | ||||||||||||||
| name: string; | ||||||||||||||
| }; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| export default function ActivityFeed({ username }: { username: string }) { | ||||||||||||||
| const [events, setEvents] = useState<EventType[]>([]); | ||||||||||||||
| const [loading, setLoading] = useState(true); | ||||||||||||||
|
|
||||||||||||||
| // 🕒 time ago function | ||||||||||||||
| const getTimeAgo = (dateString: string) => { | ||||||||||||||
| const diff = Math.floor( | ||||||||||||||
| (Date.now() - new Date(dateString).getTime()) / 1000 | ||||||||||||||
| ); | ||||||||||||||
|
|
||||||||||||||
| if (diff < 60) return `${diff}s ago`; | ||||||||||||||
| if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; | ||||||||||||||
| if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; | ||||||||||||||
| return `${Math.floor(diff / 86400)}d ago`; | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| useEffect(() => { | ||||||||||||||
| const fetchEvents = async () => { | ||||||||||||||
| try { | ||||||||||||||
| setLoading(true); | ||||||||||||||
|
|
||||||||||||||
| const res = await fetch( | ||||||||||||||
| `https://api.github.com/users/${username}/events` | ||||||||||||||
| ); | ||||||||||||||
| const data = await res.json(); | ||||||||||||||
|
|
||||||||||||||
| setEvents(data); | ||||||||||||||
| setLoading(false); | ||||||||||||||
| } catch (err) { | ||||||||||||||
| console.error(err); | ||||||||||||||
| setLoading(false); | ||||||||||||||
| } | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| fetchEvents(); | ||||||||||||||
|
|
||||||||||||||
| const interval = setInterval(fetchEvents, 30000); | ||||||||||||||
| return () => clearInterval(interval); | ||||||||||||||
| }, [username]); | ||||||||||||||
|
|
||||||||||||||
| return ( | ||||||||||||||
| <div className="p-4"> | ||||||||||||||
| <h2 className="text-xl font-bold mb-4 text-center"> | ||||||||||||||
| Activity Feed | ||||||||||||||
| </h2> | ||||||||||||||
|
|
||||||||||||||
| {loading ? ( | ||||||||||||||
| <p className="text-center">Loading...</p> | ||||||||||||||
| ) : events.length === 0 ? ( | ||||||||||||||
| <p className="text-center">No activity found</p> | ||||||||||||||
| ) : ( | ||||||||||||||
| events.slice(0, 10).map((event) => ( | ||||||||||||||
| <div | ||||||||||||||
| key={event.id} | ||||||||||||||
| className="border rounded-lg p-3 mb-3 shadow-sm bg-white dark:bg-gray-700" | ||||||||||||||
| > | ||||||||||||||
| <p className="text-sm font-semibold"> | ||||||||||||||
| {event.type === "PushEvent" && "🚀 Commit pushed"} | ||||||||||||||
| {event.type === "PullRequestEvent" && "🔀 Pull Request"} | ||||||||||||||
| {event.type === "IssuesEvent" && "🐛 Issue"} | ||||||||||||||
| {event.type === "WatchEvent" && "⭐ Starred repo"} | ||||||||||||||
| {![ | ||||||||||||||
| "PushEvent", | ||||||||||||||
| "PullRequestEvent", | ||||||||||||||
| "IssuesEvent", | ||||||||||||||
| "WatchEvent", | ||||||||||||||
| ].includes(event.type) && event.type} | ||||||||||||||
| </p> | ||||||||||||||
|
|
||||||||||||||
| <p className="text-xs text-gray-500 mt-1"> | ||||||||||||||
| {event.repo?.name} • {getTimeAgo(event.created_at)} | ||||||||||||||
| </p> | ||||||||||||||
|
Comment on lines
+81
to
+83
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid rendering Some events may not carry Proposed fix- {event.repo?.name} • {getTimeAgo(event.created_at)}
+ {event.repo?.name ?? "Unknown repository"} • {getTimeAgo(event.created_at)}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
| </div> | ||||||||||||||
| )) | ||||||||||||||
| )} | ||||||||||||||
| </div> | ||||||||||||||
| ); | ||||||||||||||
| } | ||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import ActivityFeed from "../components/ActivityFeed"; | ||
| export default function Activity() { | ||
|
Comment on lines
+1
to
+2
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Candidate page/router files =="
fd -i 'ctivity\.tsx|Activity\.tsx|App\.tsx|router\.tsx|routes\.tsx'
echo
echo "== References to activity routes/imports =="
rg -n -C2 --type=tsx 'ctivity|Activity|/activity|Live GitHub Activity'Repository: GitMetricsLab/github_tracker Length of output: 253 🏁 Script executed: cat -n src/Routes/Router.tsxRepository: GitMetricsLab/github_tracker Length of output: 1271 🏁 Script executed: cat -n src/pages/ctivity.tsxRepository: GitMetricsLab/github_tracker Length of output: 573 Remove the orphaned The file 🤖 Prompt for AI Agents |
||
| return ( | ||
| <div className="w-full h-full p-6 bg-gray-50 dark:bg-gray-800"> | ||
| <div className="max-w-2xl mx-auto"> | ||
| <h1 className="text-2xl font-bold mb-4 text-center"> | ||
| Live GitHub Activity | ||
| </h1> | ||
|
|
||
| <ActivityFeed username="aparna24bce11388" /> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove hardcoded username from the page contract. This locks the feature to one account instead of tracking the selected user. Proposed direction (wire username from parent/router state)-export default function Activity() {
+export default function Activity({ username }: { username: string }) {
return (
@@
- <ActivityFeed username="aparna24bce11388" />
+ <ActivityFeed username={username} />
</div>
</div>
);
}🤖 Prompt for AI Agents |
||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: GitMetricsLab/github_tracker
Length of output: 2690
🏁 Script executed:
git ls-files | head -20Repository: GitMetricsLab/github_tracker
Length of output: 567
🏁 Script executed:
fd -t f "ActivityFeed" --case-sensitiveRepository: GitMetricsLab/github_tracker
Length of output: 104
🏁 Script executed:
Repository: GitMetricsLab/github_tracker
Length of output: 106
🏁 Script executed:
Repository: GitMetricsLab/github_tracker
Length of output: 107
🏁 Script executed:
Repository: GitMetricsLab/github_tracker
Length of output: 3146
Guard non-OK and invalid GitHub payloads before setting
events.The code assigns the JSON response directly to
eventswithout checkingres.okor validating the payload shape. When GitHub returns an error (e.g., 404, 403), it sends a JSON object, not an event array. This causes a runtime crash when the component later callsevents.slice(0, 10).Proposed fix
🤖 Prompt for AI Agents