frontend phase 14
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
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() });
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user