All files / src/hooks/queries useNotificationQueries.ts

100% Statements 26/26
100% Branches 4/4
100% Functions 16/16
100% Lines 26/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114                        6x     3x 3x                                   4x     2x 2x                       1x   1x 1x   1x     1x               2x   2x   2x   2x     2x               1x   1x 1x   1x     1x               1x   1x 1x   1x     1x            
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { notificationsApi } from "../../lib/api";
import { queryKeys } from "../../lib/query-keys";
 
// ─── Queries ──────────────────────────────────────────────────────────────────
 
export function useNotifications(filters?: {
	category?: string;
	search?: string;
	page?: number;
	limit?: number;
}) {
	return useQuery({
		queryKey: queryKeys.notifications.list(filters),
		queryFn: async () => {
			const response = await notificationsApi.getAll(filters);
			return (
				response.data || {
					notifications: [],
					total: 0,
					unread_count: 0,
					page: 1,
					has_more: false,
				}
			);
		},
		staleTime: 30_000, // 30s — avoid excessive refetches on page navigation
		refetchInterval: 60000, // Poll every 60s — same as unread count
		refetchIntervalInBackground: true, // Keep polling even when tab is not focused
		refetchOnWindowFocus: true,
	});
}
 
export function useUnreadCount() {
	return useQuery({
		queryKey: queryKeys.notifications.unreadCount(),
		queryFn: async () => {
			const response = await notificationsApi.getUnreadCount();
			return response.data?.count ?? 0;
		},
		refetchInterval: 60000,
		refetchIntervalInBackground: true, // Keep polling even when tab is not focused
		staleTime: 10000, // 10s — avoid duplicate fetches from sidebar + mobile header
		refetchOnWindowFocus: true,
	});
}
 
// ─── Mutations ────────────────────────────────────────────────────────────────
 
export function useMarkAsRead() {
	const queryClient = useQueryClient();
 
	return useMutation({
		mutationFn: (id: string) => notificationsApi.markAsRead(id),
		onSuccess: () => {
			queryClient.invalidateQueries({
				queryKey: queryKeys.notifications.lists(),
			});
			queryClient.invalidateQueries({
				queryKey: queryKeys.notifications.unreadCount(),
			});
		},
	});
}
 
export function useMarkAllAsRead() {
	const queryClient = useQueryClient();
 
	return useMutation({
		mutationFn: (category?: string) =>
			notificationsApi.markAllAsRead(category),
		onSuccess: () => {
			queryClient.invalidateQueries({
				queryKey: queryKeys.notifications.lists(),
			});
			queryClient.invalidateQueries({
				queryKey: queryKeys.notifications.unreadCount(),
			});
		},
	});
}
 
export function useDeleteNotification() {
	const queryClient = useQueryClient();
 
	return useMutation({
		mutationFn: (id: string) => notificationsApi.delete(id),
		onSuccess: () => {
			queryClient.invalidateQueries({
				queryKey: queryKeys.notifications.lists(),
			});
			queryClient.invalidateQueries({
				queryKey: queryKeys.notifications.unreadCount(),
			});
		},
	});
}
 
export function useDeleteNotificationsBulk() {
	const queryClient = useQueryClient();
 
	return useMutation({
		mutationFn: (ids: string[]) => notificationsApi.deleteBulk(ids),
		onSuccess: () => {
			queryClient.invalidateQueries({
				queryKey: queryKeys.notifications.lists(),
			});
			queryClient.invalidateQueries({
				queryKey: queryKeys.notifications.unreadCount(),
			});
		},
	});
}