150 lines
4.3 KiB
TypeScript
150 lines
4.3 KiB
TypeScript
/**
|
|
* useNotifications Hook - Platform-aware notifications management
|
|
* Uses Firebase abstraction layer for cross-platform support
|
|
*/
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import { collection } from '../firebase';
|
|
import { NotificationService } from '../services/notificationService';
|
|
import { useGlobalLoading } from './useGlobalLoading';
|
|
|
|
export interface UseNotificationsReturn {
|
|
notifications: any[];
|
|
loading: boolean;
|
|
error: string | null;
|
|
unreadCount: number;
|
|
markAsRead: (notificationId: string) => Promise<void>;
|
|
markAllAsRead: () => Promise<void>;
|
|
refreshNotifications: () => void;
|
|
}
|
|
|
|
export function useNotifications(uid: string | null): UseNotificationsReturn {
|
|
const [notifications, setNotifications] = useState<any[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const { withLoading } = useGlobalLoading();
|
|
|
|
// Calculate unread count
|
|
const unreadCount = notifications.filter(notification => !notification.read).length;
|
|
|
|
// Mark single notification as read
|
|
const markAsRead = async (notificationId: string): Promise<void> => {
|
|
try {
|
|
const result = await withLoading(() => NotificationService.markAsRead(notificationId));
|
|
if (result.success) {
|
|
// Update local state
|
|
setNotifications(prev =>
|
|
prev.map(notification =>
|
|
notification.id === notificationId
|
|
? { ...notification, read: true, updatedAt: new Date() }
|
|
: notification
|
|
)
|
|
);
|
|
} else {
|
|
setError(result.error || 'Failed to mark notification as read');
|
|
}
|
|
} catch (err) {
|
|
console.error('Error marking notification as read:', err);
|
|
setError('Failed to mark notification as read');
|
|
}
|
|
};
|
|
|
|
// Mark all notifications as read
|
|
const markAllAsRead = async (): Promise<void> => {
|
|
try {
|
|
// Update all notifications in local state
|
|
setNotifications(prev =>
|
|
prev.map(notification => ({ ...notification, read: true, updatedAt: new Date() }))
|
|
);
|
|
|
|
// Update in Firestore
|
|
const unreadNotifications = notifications.filter(n => !n.read);
|
|
const updatePromises = unreadNotifications.map(notification =>
|
|
notification.id ? withLoading(() => NotificationService.markAsRead(notification.id)) : Promise.resolve({ success: true })
|
|
);
|
|
|
|
await Promise.all(updatePromises);
|
|
} catch (err) {
|
|
console.error('Error marking all notifications as read:', err);
|
|
setError('Failed to mark all notifications as read');
|
|
}
|
|
};
|
|
|
|
// Refresh notifications
|
|
const [refreshKey, setRefreshKey] = useState(0);
|
|
|
|
const refreshNotifications = useCallback((): void => {
|
|
setLoading(true);
|
|
setError(null);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
let isMounted = true;
|
|
|
|
const fetchNotifications = async () => {
|
|
if (!uid) {
|
|
if (isMounted) {
|
|
setNotifications([]);
|
|
setLoading(false);
|
|
setError(null);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (isMounted) {
|
|
setLoading(true);
|
|
setError(null);
|
|
}
|
|
|
|
try {
|
|
const notificationsCollection = collection('notifications');
|
|
const snapshot = await withLoading(async () => {
|
|
return await notificationsCollection
|
|
.where('userId', '==', uid)
|
|
.orderBy('createdAt', 'desc')
|
|
.get();
|
|
}) as any;
|
|
|
|
const notificationsData: any[] = [];
|
|
|
|
if (snapshot && snapshot.forEach) {
|
|
snapshot.forEach((docData: any) => {
|
|
const data = docData.data();
|
|
notificationsData.push({
|
|
id: docData.id,
|
|
...data,
|
|
});
|
|
});
|
|
}
|
|
|
|
if (isMounted) {
|
|
setNotifications(notificationsData);
|
|
setLoading(false);
|
|
setError(null);
|
|
}
|
|
} catch (err) {
|
|
console.error('Error fetching notifications:', err);
|
|
if (isMounted) {
|
|
setError('Failed to fetch notifications');
|
|
setLoading(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
void fetchNotifications();
|
|
|
|
return () => {
|
|
isMounted = false;
|
|
};
|
|
}, [uid, refreshKey, withLoading]);
|
|
|
|
return {
|
|
notifications,
|
|
loading,
|
|
error,
|
|
unreadCount,
|
|
markAsRead,
|
|
markAllAsRead,
|
|
refreshNotifications,
|
|
};
|
|
}
|