Amba-Agent-App/lib/hooks/useNotifications.ts
2026-01-16 00:22:35 +03:00

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,
};
}