81 lines
2.4 KiB
TypeScript
81 lines
2.4 KiB
TypeScript
import React, { createContext, useContext, useEffect, useState } from 'react';
|
|
import { io } from 'socket.io-client';
|
|
|
|
export interface Notification {
|
|
id: string;
|
|
type: 'assignment' | 'mention' | 'update';
|
|
title: string;
|
|
message: string;
|
|
link?: string;
|
|
is_read: boolean;
|
|
created_at: string;
|
|
}
|
|
|
|
interface NotificationContextType {
|
|
notifications: Notification[];
|
|
unreadCount: number;
|
|
markAsRead: (id: string) => Promise<void>;
|
|
}
|
|
|
|
const NotificationContext = createContext<NotificationContextType | undefined>(undefined);
|
|
|
|
export const NotificationProvider: React.FC<{ children: React.ReactNode, userId: string }> = ({ children, userId }) => {
|
|
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
|
|
useEffect(() => {
|
|
if (!userId) return;
|
|
|
|
const newSocket = io(window.location.protocol + '//' + window.location.hostname + ':3001');
|
|
|
|
newSocket.emit('join', userId);
|
|
|
|
newSocket.on('notification', (notif: Notification) => {
|
|
setNotifications(prev => [notif, ...prev]);
|
|
// Optional: Show browser toast here
|
|
});
|
|
|
|
fetchNotifications();
|
|
|
|
return () => {
|
|
newSocket.close();
|
|
};
|
|
}, [userId]);
|
|
|
|
const fetchNotifications = async () => {
|
|
try {
|
|
const res = await fetch(`/api/notifications/${userId}`);
|
|
if (res.ok) {
|
|
const data = await res.json();
|
|
setNotifications(data);
|
|
}
|
|
} catch (err) {
|
|
console.error('Fetch notifications failed', err);
|
|
}
|
|
};
|
|
|
|
const markAsRead = async (id: string) => {
|
|
try {
|
|
const res = await fetch(`/api/notifications/${id}/read`, { method: 'PUT' });
|
|
if (res.ok) {
|
|
setNotifications(prev => prev.map(n => n.id === id ? { ...n, is_read: true } : n));
|
|
}
|
|
} catch (err) {
|
|
console.error('Mark read failed', err);
|
|
}
|
|
};
|
|
|
|
const unreadCount = notifications.filter(n => !n.is_read).length;
|
|
|
|
return (
|
|
<NotificationContext.Provider value={{ notifications, unreadCount, markAsRead }}>
|
|
{children}
|
|
</NotificationContext.Provider>
|
|
);
|
|
};
|
|
|
|
export const useNotifications = () => {
|
|
const context = useContext(NotificationContext);
|
|
if (!context) throw new Error('useNotifications must be used within NotificationProvider');
|
|
return context;
|
|
};
|