Skip to content

Create a Dynamic <TimeAgo /> Component #44

Description

@Vedant1703

📋 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

  • A reusable <TimeAgo /> component is created.
  • The component uses setInterval to update its own state independently every minute (or less).
  • It handles hydration mismatches cleanly (using a mounted state).
  • The old static formatTimeAgo logic is removed from notifications/page.tsx.
  • Existing timestamps in the profile Watchlist and Notification dropdown/page use the new component.

💡 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

  1. Fork the repository
  2. Create a branch: git checkout -b fix/issue-27-dynamic-timeago
  3. Create the new component
  4. Replace existing time formatting in notifications and profile page
  5. Test by looking at a timestamp and waiting 60 seconds to watch it change autonomously!
  6. Open a Pull Request!

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions