diff --git a/src/components/ActivityFeed.tsx b/src/components/ActivityFeed.tsx index 7a03d86..4773f81 100644 --- a/src/components/ActivityFeed.tsx +++ b/src/components/ActivityFeed.tsx @@ -12,8 +12,8 @@ interface EventType { export default function ActivityFeed({ username }: { username: string }) { const [events, setEvents] = useState([]); const [loading, setLoading] = useState(true); + const [error, setError] = useState(""); - // 🕒 time ago function const getTimeAgo = (dateString: string) => { const diff = Math.floor( (Date.now() - new Date(dateString).getTime()) / 1000 @@ -27,18 +27,50 @@ export default function ActivityFeed({ username }: { username: string }) { useEffect(() => { const fetchEvents = async () => { + if (!username.trim()) { + setEvents([]); + setError("Please enter a GitHub username to get started."); + setLoading(false); + return; + } + try { setLoading(true); + setError(""); const res = await fetch( `https://api.github.com/users/${username}/events` ); + + if (!res.ok) { + let message = "Unable to load activity. Please try again."; + if (res.status === 404) { + message = "GitHub user not found. Please check the username."; + } else if (res.status === 403) { + message = + "GitHub rate limit exceeded. Wait a moment and try again."; + } + setEvents([]); + setError(message); + setLoading(false); + return; + } + const data = await res.json(); + if (!Array.isArray(data)) { + setError("Unexpected response from GitHub. Please try again."); + setEvents([]); + setLoading(false); + return; + } + setEvents(data); - setLoading(false); } catch (err) { console.error(err); + setError("Unable to fetch activity. Check your connection and try again."); + setEvents([]); + } finally { setLoading(false); } }; @@ -49,45 +81,59 @@ export default function ActivityFeed({ username }: { username: string }) { return () => clearInterval(interval); }, [username]); + const currentEvents = events.slice(0, 10); + return ( -
-

- Activity Feed -

+
+
+
+

Activity Feed

+

+ Tracking {username} +

+
+

+ Refreshes every 30s +

+
{loading ? ( -
- Fetching recent activity... +
+ Loading GitHub activity... +
+ ) : error ? ( +
+ {error} +
+ ) : currentEvents.length === 0 ? ( +
+ No recent public activity found for this user.
- ) : events.length === 0 ? ( - ) : ( - events.slice(0, 10).map((event) => ( -
-

- {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} -

- -

- {event.repo?.name} • {getTimeAgo(event.created_at)} -

-
- )) +
+ {currentEvents.map((event) => ( +
+

+ {event.type === "PushEvent" && "🚀 Commit pushed"} + {event.type === "PullRequestEvent" && "🔀 Pull request event"} + {event.type === "IssuesEvent" && "🐛 Issue event"} + {event.type === "WatchEvent" && "⭐ Starred repository"} + {![ + "PushEvent", + "PullRequestEvent", + "IssuesEvent", + "WatchEvent", + ].includes(event.type) && event.type} +

+

+ {event.repo?.name || "Unknown repository"} • {getTimeAgo(event.created_at)} +

+
+ ))} +
)}
); diff --git a/src/pages/Activity.tsx b/src/pages/Activity.tsx index ccc3d20..a2a3313 100644 --- a/src/pages/Activity.tsx +++ b/src/pages/Activity.tsx @@ -1,13 +1,51 @@ +import { FormEvent, useState } from "react"; import ActivityFeed from "../components/ActivityFeed"; + export default function Activity() { + const [query, setQuery] = useState("octocat"); + const [trackedUsername, setTrackedUsername] = useState("octocat"); + + const handleSubmit = (event: FormEvent) => { + event.preventDefault(); + const sanitized = query.trim(); + if (!sanitized) return; + setTrackedUsername(sanitized); + }; + return ( -
-
-

+
+
+

Live GitHub Activity

- +
+

+ Enter any GitHub username below to track recent public activity in real time. +

+ +
+ + setQuery(event.target.value)} + placeholder="e.g. octocat" + className="w-full min-w-0 rounded-3xl border border-gray-300 bg-gray-50 px-4 py-3 text-sm text-gray-900 transition duration-200 focus:border-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-200 dark:border-gray-700 dark:bg-gray-800 dark:text-white dark:placeholder:text-gray-500 dark:focus:ring-indigo-500" + /> + +
+
+ +
);