frontend phase 14

This commit is contained in:
hamid
2026-07-10 18:46:16 +03:30
parent 85488bc25b
commit bc51cf59b4
73 changed files with 3582 additions and 13 deletions
@@ -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() });
},
});
}