194 lines
5.4 KiB
TypeScript
194 lines
5.4 KiB
TypeScript
import { useState, useEffect, useCallback } from 'react';
|
|
import { useSession } from 'next-auth/react';
|
|
import { Notification, NotificationCount } from '@/lib/types/notification';
|
|
|
|
// Default empty notification count
|
|
const defaultNotificationCount: NotificationCount = {
|
|
total: 0,
|
|
unread: 0,
|
|
sources: {}
|
|
};
|
|
|
|
export function useNotifications() {
|
|
const { data: session, status } = useSession();
|
|
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
const [notificationCount, setNotificationCount] = useState<NotificationCount>(defaultNotificationCount);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [pollingInterval, setPollingInterval] = useState<NodeJS.Timeout | null>(null);
|
|
|
|
// Fetch notification count
|
|
const fetchNotificationCount = useCallback(async () => {
|
|
if (!session?.user) return;
|
|
|
|
try {
|
|
setError(null);
|
|
|
|
const response = await fetch('/api/notifications/count');
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
console.error('Failed to fetch notification count:', errorData);
|
|
setError(errorData.error || 'Failed to fetch notification count');
|
|
return;
|
|
}
|
|
|
|
const data = await response.json();
|
|
setNotificationCount(data);
|
|
} catch (err) {
|
|
console.error('Error fetching notification count:', err);
|
|
setError('Failed to fetch notification count');
|
|
}
|
|
}, [session?.user]);
|
|
|
|
// Fetch notifications
|
|
const fetchNotifications = useCallback(async (page = 1, limit = 20) => {
|
|
if (!session?.user) return;
|
|
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const response = await fetch(`/api/notifications?page=${page}&limit=${limit}`);
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
console.error('Failed to fetch notifications:', errorData);
|
|
setError(errorData.error || 'Failed to fetch notifications');
|
|
return;
|
|
}
|
|
|
|
const data = await response.json();
|
|
setNotifications(data.notifications);
|
|
} catch (err) {
|
|
console.error('Error fetching notifications:', err);
|
|
setError('Failed to fetch notifications');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [session?.user]);
|
|
|
|
// Mark notification as read
|
|
const markAsRead = useCallback(async (notificationId: string) => {
|
|
if (!session?.user) return;
|
|
|
|
try {
|
|
const response = await fetch(`/api/notifications/${notificationId}/read`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
console.error('Failed to mark notification as read:', errorData);
|
|
return false;
|
|
}
|
|
|
|
// Update local state
|
|
setNotifications(prev =>
|
|
prev.map(notification =>
|
|
notification.id === notificationId
|
|
? { ...notification, isRead: true }
|
|
: notification
|
|
)
|
|
);
|
|
|
|
// Refresh notification count
|
|
fetchNotificationCount();
|
|
|
|
return true;
|
|
} catch (err) {
|
|
console.error('Error marking notification as read:', err);
|
|
return false;
|
|
}
|
|
}, [session?.user, fetchNotificationCount]);
|
|
|
|
// Mark all notifications as read
|
|
const markAllAsRead = useCallback(async () => {
|
|
if (!session?.user) return;
|
|
|
|
try {
|
|
const response = await fetch('/api/notifications/read-all', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
console.error('Failed to mark all notifications as read:', errorData);
|
|
return false;
|
|
}
|
|
|
|
// Update local state
|
|
setNotifications(prev =>
|
|
prev.map(notification => ({ ...notification, isRead: true }))
|
|
);
|
|
|
|
// Refresh notification count
|
|
fetchNotificationCount();
|
|
|
|
return true;
|
|
} catch (err) {
|
|
console.error('Error marking all notifications as read:', err);
|
|
return false;
|
|
}
|
|
}, [session?.user, fetchNotificationCount]);
|
|
|
|
// Start polling for notification count
|
|
const startPolling = useCallback((interval = 30000) => {
|
|
if (pollingInterval) {
|
|
clearInterval(pollingInterval);
|
|
}
|
|
|
|
const id = setInterval(() => {
|
|
fetchNotificationCount();
|
|
}, interval);
|
|
|
|
setPollingInterval(id);
|
|
|
|
return () => {
|
|
clearInterval(id);
|
|
setPollingInterval(null);
|
|
};
|
|
}, [fetchNotificationCount, pollingInterval]);
|
|
|
|
// Stop polling
|
|
const stopPolling = useCallback(() => {
|
|
if (pollingInterval) {
|
|
clearInterval(pollingInterval);
|
|
setPollingInterval(null);
|
|
}
|
|
}, [pollingInterval]);
|
|
|
|
// Initialize fetching on component mount
|
|
useEffect(() => {
|
|
if (status === 'authenticated' && session?.user) {
|
|
fetchNotificationCount();
|
|
fetchNotifications();
|
|
|
|
// Start polling
|
|
const cleanup = startPolling();
|
|
|
|
return () => {
|
|
cleanup();
|
|
};
|
|
}
|
|
}, [status, session?.user, fetchNotificationCount, fetchNotifications, startPolling]);
|
|
|
|
return {
|
|
notifications,
|
|
notificationCount,
|
|
loading,
|
|
error,
|
|
fetchNotifications,
|
|
fetchNotificationCount,
|
|
markAsRead,
|
|
markAllAsRead,
|
|
startPolling,
|
|
stopPolling
|
|
};
|
|
}
|