'use client'; import { useState, useEffect } from 'react'; import { BellIcon } from '@/components/Icons'; import Link from 'next/link'; import { useAuth } from '@/lib/contexts/AuthContext'; interface NotificationActor { id: string; handle: string; displayName: string | null; avatarUrl: string | null; } interface NotificationPost { id: string; content: string; } interface Notification { id: string; type: 'follow' | 'like' | 'repost' | 'mention' | 'reply'; createdAt: string; readAt: string | null; actor: NotificationActor | null; post: NotificationPost | null; } export default function NotificationsPage() { const { loading: authLoading } = useAuth(); const [notifications, setNotifications] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { if (authLoading) { return; } fetchNotifications(); }, [authLoading]); const fetchNotifications = async () => { try { const res = await fetch('/api/notifications'); if (!res.ok) { if (res.status === 401) { setError('Please log in to view notifications'); return; } throw new Error('Failed to fetch notifications'); } const data = await res.json(); setNotifications(data.notifications || []); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load notifications'); } finally { setLoading(false); } }; const markAllRead = async () => { try { const res = await fetch('/api/notifications', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ all: true }), }); if (!res.ok) { throw new Error('Failed to mark notifications as read'); } setNotifications(prev => prev.map(n => ({ ...n, readAt: new Date().toISOString() }))); window.dispatchEvent(new Event('synapsis:notifications-updated')); } catch (err) { console.error('Failed to mark notifications as read:', err); } }; const getNotificationText = (notification: Notification) => { switch (notification.type) { case 'follow': return 'followed you'; case 'like': return 'liked your post'; case 'repost': return 'reposted your post'; case 'mention': return 'mentioned you'; case 'reply': return 'replied to your post'; default: return 'interacted with you'; } }; const formatTime = (dateStr: string) => { const date = new Date(dateStr); const now = new Date(); const diffMs = now.getTime() - date.getTime(); const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMs / 3600000); const diffDays = Math.floor(diffMs / 86400000); if (diffMins < 1) return 'just now'; if (diffMins < 60) return `${diffMins}m`; if (diffHours < 24) return `${diffHours}h`; if (diffDays < 7) return `${diffDays}d`; return date.toLocaleDateString(); }; return (

Notifications

{notifications.length > 0 && ( )}
{loading ? (
Loading...
) : error ? (
{error}
) : notifications.length === 0 ? (

No notifications yet

When you get interactions, they'll show up here.

) : (
{notifications.map((notification) => ( ))}
)}
); } function NotificationItem({ notification, getNotificationText, formatTime, }: { notification: Notification; getNotificationText: (n: Notification) => string; formatTime: (d: string) => string; }) { const isUnread = !notification.readAt; const actor = notification.actor; return (
{actor?.avatarUrl ? ( {actor.displayName ) : (
{(actor?.displayName || actor?.handle || '?')[0].toUpperCase()}
)}
{actor?.displayName || actor?.handle || 'Someone'} @{actor?.handle} {getNotificationText(notification)} ยท {formatTime(notification.createdAt)}
{notification.post && ( {notification.post.content} )}
{isUnread && (
)}
); }