52 lines
2.4 KiB
TypeScript
52 lines
2.4 KiB
TypeScript
import { useMutation, useQueryClient, type QueryKey } from '@tanstack/react-query';
|
|
import type { Paginated } from '@/lib/api/types';
|
|
import { notificationKeys } from '../keys';
|
|
import { notificationsApi } from '../apis';
|
|
import type { AppNotification } from '../types';
|
|
|
|
interface MarkReadContext {
|
|
prevCount?: number;
|
|
prevLists: Array<[QueryKey, Paginated<AppNotification> | undefined]>;
|
|
}
|
|
|
|
/**
|
|
* Mark one notification read — **optimistic** (phase §3.4): flip `isRead` across every cached list page and
|
|
* decrement the cached unread count at once (so the row de-emphasises and the bell badge drops instantly),
|
|
* roll both back on error, and invalidate on settle (no full refetch on the happy path). The count only
|
|
* decrements when the notification was actually unread, so re-opening a read one never underflows.
|
|
*/
|
|
export function useMarkNotificationRead() {
|
|
const queryClient = useQueryClient();
|
|
return useMutation<void, unknown, number, MarkReadContext>({
|
|
mutationFn: (notificationId) => notificationsApi.markRead(notificationId),
|
|
|
|
onMutate: async (notificationId) => {
|
|
await queryClient.cancelQueries({ queryKey: notificationKeys.all });
|
|
const prevCount = queryClient.getQueryData<number>(notificationKeys.unreadCount());
|
|
const prevLists = queryClient.getQueriesData<Paginated<AppNotification>>({ queryKey: notificationKeys.lists() });
|
|
|
|
const wasUnread = prevLists.some(([, data]) =>
|
|
data?.items.some((n) => n.id === notificationId && !n.isRead),
|
|
);
|
|
|
|
queryClient.setQueriesData<Paginated<AppNotification>>({ queryKey: notificationKeys.lists() }, (data) =>
|
|
data ? { ...data, items: data.items.map((n) => (n.id === notificationId ? { ...n, isRead: true } : n)) } : data,
|
|
);
|
|
if (wasUnread && typeof prevCount === 'number') {
|
|
queryClient.setQueryData<number>(notificationKeys.unreadCount(), Math.max(0, prevCount - 1));
|
|
}
|
|
return { prevCount, prevLists };
|
|
},
|
|
|
|
onError: (_err, _id, context) => {
|
|
if (context?.prevCount !== undefined) queryClient.setQueryData(notificationKeys.unreadCount(), context.prevCount);
|
|
context?.prevLists.forEach(([key, data]) => queryClient.setQueryData(key, data));
|
|
},
|
|
|
|
onSettled: () => {
|
|
queryClient.invalidateQueries({ queryKey: notificationKeys.lists() });
|
|
queryClient.invalidateQueries({ queryKey: notificationKeys.unreadCount() });
|
|
},
|
|
});
|
|
}
|