📋 Description
Currently across the app (in notifications/page.tsx, the Watchlist on profile/page.tsx, etc.), relative timestamps like "10s ago", "2m ago", or "5h ago" are evaluated statically.
For example, in NotificationsPage:
const formatTimeAgo = (date: Date) => {
// Calculates seconds difference from Date.now() at render time
}
// Usage:
<span className="text-xs text-gray-500">
{formatTimeAgo(notification.timestamp)}
</span>
The Bug: Because this is evaluated once at render, if a user receives a compilation notification "1s ago" and leaves the browser tab open, 10 minutes later that notification will still say "1s ago". The UI gets completely stale until a hard page refresh or a new notification forces a state update.
To make the app feel alive and accurate, these static function calls should be replaced with a dynamic React component that re-evaluates the "time ago" string on a timer.
📍 Files to Change
- Create:
frontend/components/ui/time-ago.tsx
- Modify:
frontend/app/(auth)/notifications/page.tsx
- Modify:
frontend/components/notification-bell.tsx
- Modify:
frontend/app/(auth)/profile/page.tsx (Watchlist timestamps "Last checked X mins ago")
✅ What To Do
Step 1 — Create a robust dynamic component in frontend/components/ui/time-ago.tsx:
"use client";
import { useState, useEffect } from "react";
interface TimeAgoProps {
date: Date | string | number;
className?: string;
}
export function TimeAgo({ date, className = "" }: TimeAgoProps) {
const [timeStr, setTimeStr] = useState("");
useEffect(() => {
const parseDate = (d: Date | string | number) => {
if (d instanceof Date) return d;
return new Date(d);
};
const targetDate = parseDate(date);
const updateTime = () => {
const seconds = Math.floor((new Date().getTime() - targetDate.getTime()) / 1000);
let str = "";
if (seconds < 60) str = "just now";
else if (seconds < 3600) str = `${Math.floor(seconds / 60)}m ago`;
else if (seconds < 86400) str = `${Math.floor(seconds / 3600)}h ago`;
else str = `${Math.floor(seconds / 86400)}d ago`;
setTimeStr(str);
};
// Run immediately
updateTime();
// Re-run every minute (60000ms)
// For extreme accuracy on "just now", you might run every 10-30s instead.
const intervalId = setInterval(updateTime, 60000);
return () => clearInterval(intervalId);
}, [date]);
// Initial SSR pass will render nothing or a fallback to avoid hydration mismatch,
// or return a standard text.
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
if (!mounted) return <span className={className}>...</span>;
return <span className={className}>{timeStr}</span>;
}
Step 2 — Refactor existing static implementations.
In frontend/app/(auth)/notifications/page.tsx:
- Delete the old
formatTimeAgo function.
- Update the UI to use the component:
import { TimeAgo } from "@/components/ui/time-ago";
// Inside the component:
<TimeAgo
date={notification.timestamp}
className="text-xs text-gray-500"
/>
Find other areas dealing with relative timestamps and apply the same refactoring!
🏁 Acceptance Criteria
💡 Technical Hints
- Hydration Mismatches: If the server renders "10s ago" but by the time the React frontend hydrates the browser says "12s ago", Next.js will throw a Hydration Error. The
mounted pattern included in Step 1 prevents this.
- Make sure to clear the interval in the
useEffect cleanup hook (return () => clearInterval(...)), otherwise you'll create a massive memory leak!
🚀 Getting Started
- Fork the repository
- Create a branch:
git checkout -b fix/issue-27-dynamic-timeago
- Create the new component
- Replace existing time formatting in notifications and profile page
- Test by looking at a timestamp and waiting 60 seconds to watch it change autonomously!
- Open a Pull Request!
📋 Description
Currently across the app (in
notifications/page.tsx, theWatchlistonprofile/page.tsx, etc.), relative timestamps like "10s ago", "2m ago", or "5h ago" are evaluated statically.For example, in
NotificationsPage:The Bug: Because this is evaluated once at render, if a user receives a compilation notification "1s ago" and leaves the browser tab open, 10 minutes later that notification will still say "1s ago". The UI gets completely stale until a hard page refresh or a new notification forces a state update.
To make the app feel alive and accurate, these static function calls should be replaced with a dynamic React component that re-evaluates the "time ago" string on a timer.
📍 Files to Change
frontend/components/ui/time-ago.tsxfrontend/app/(auth)/notifications/page.tsxfrontend/components/notification-bell.tsxfrontend/app/(auth)/profile/page.tsx(Watchlist timestamps "Last checked X mins ago")✅ What To Do
Step 1 — Create a robust dynamic component in
frontend/components/ui/time-ago.tsx:Step 2 — Refactor existing static implementations.
In
frontend/app/(auth)/notifications/page.tsx:formatTimeAgofunction.Find other areas dealing with relative timestamps and apply the same refactoring!
🏁 Acceptance Criteria
<TimeAgo />component is created.setIntervalto update its own state independently every minute (or less).mountedstate).formatTimeAgologic is removed fromnotifications/page.tsx.💡 Technical Hints
mountedpattern included in Step 1 prevents this.useEffectcleanup hook (return () => clearInterval(...)), otherwise you'll create a massive memory leak!🚀 Getting Started
git checkout -b fix/issue-27-dynamic-timeago